<commit_before>// POPBufferWriter.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>

#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
#include "tiny_obj_loader.h"

#include <fstream>
using namespace std;

/*
	Structure to hold a record that should be written to file
*/
struct rec
{
	short x, y, z;
	char u, v;
};

/*
	Structure to hold min and max values for transformation
*/
struct minmax
{
	float minx, miny, minz;
	float maxx, maxy, maxz;
};

/*
	Structure to hold a vertex entry that is not transformed yet
*/
struct entry
{
	float vx, vy, vz;
	float nx, ny, nz;
};

struct float2 { float u, v; };
struct float3 { float x, y, z; };

/*
	Octahedron normal coding
*/

//TODO Add conversion code here

int main()
{
	const string inputfile = "models/bunny.obj";
	const string modelName = "bunny";
	const char* dataFileName = "out.pop";

	/*
		Prepare output file
	*/
	int i;
	FILE *f;
	struct rec r;
	fopen_s(&f, dataFileName, "wb");
	if (!f)
		return 1;

	/*
		Parse obj file
	*/
	tinyobj::attrib_t attrib;
	std::vector<tinyobj::shape_t> shapes;
	std::vector<tinyobj::material_t> materials;
	std::string err;

	bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, inputfile.c_str());
	if (!err.empty()) { // `err` may contain warning message.
		std::cerr << err << std::endl;
	}
	if (!ret || shapes.size() < 1) {
		exit(1);
	}

	// Create a vector to hold the entries
	std::vector<entry> entries;
	int vertexCount = shapes[0].mesh.num_face_vertices.size() * 3;
	std::cout << "Found " << vertexCount << " vertices in file " << inputfile << std::endl;
	entries.reserve(vertexCount);
	int counter = 0;

	// Init min and max values
	minmax minMaxValues;
	minMaxValues.minx = HUGE_VALF;
	minMaxValues.maxx = -HUGE_VALF;
	minMaxValues.miny = HUGE_VALF;
	minMaxValues.maxy = -HUGE_VALF;
	minMaxValues.minz = HUGE_VALF;
	minMaxValues.maxz = -HUGE_VALF;

	/*
		Loop over file content and create output
	*/
	size_t index_offset = 0;
	for (size_t f = 0; f < shapes[0].mesh.num_face_vertices.size(); f++) {
		size_t fv = shapes[0].mesh.num_face_vertices[f];

		// Loop over vertices in the face.
		for (size_t v = 0; v < fv; v++) {
			// access to vertex
			tinyobj::index_t idx = shapes[0].mesh.indices[index_offset + v];

			// create an entry per vertex and update the min and max values if necessary
			entry e;
			e.vx = attrib.vertices[3 * idx.vertex_index + 0];
			if (e.vx < minMaxValues.minx) minMaxValues.minx = e.vx;
			if (e.vx > minMaxValues.maxx) minMaxValues.maxx = e.vx;

			e.vy = attrib.vertices[3 * idx.vertex_index + 1];
			if (e.vy < minMaxValues.miny) minMaxValues.miny = e.vy;
			if (e.vy > minMaxValues.maxy) minMaxValues.maxy = e.vy;

			e.vz = attrib.vertices[3 * idx.vertex_index + 2];
			if (e.vz < minMaxValues.minz) minMaxValues.minz = e.vz;
			if (e.vz > minMaxValues.maxz) minMaxValues.maxz = e.vz;

			e.nx = attrib.normals[3 * idx.normal_index + 0];
			e.ny = attrib.normals[3 * idx.normal_index + 1];
			e.nz = attrib.normals[3 * idx.normal_index + 2];

			entries.push_back(e);
			counter++;
		}
		index_offset += fv;
	}

	// Output some debugging values
	std::cout << "MaxX: " << minMaxValues.maxx << " MaxY:" << minMaxValues.maxy << " MaxZ: " << minMaxValues.maxz << std::endl;
	std::cout << "MinX: " << minMaxValues.minx << " MinY:" << minMaxValues.miny << " MinZ: " << minMaxValues.minz << std::endl;

	// Write meta values to meta data file
	ofstream metaFile;
	metaFile.open("out.json");
	metaFile << "{\n";
	metaFile << "\"name\": \"" << modelName << "\",\n";
	metaFile << "\"data\": \"" << dataFileName << "\",\n";
	metaFile << "\"numVertices\": \"" << counter << "\",\n";
	metaFile << "\"xmin\": " << minMaxValues.minx << ",\n";
	metaFile << "\"ymin\": " << minMaxValues.miny << ",\n";
	metaFile << "\"zmin\": " << minMaxValues.minz << ",\n";
	metaFile << "\"xmax\": " << minMaxValues.maxx << ",\n";
	metaFile << "\"ymax\": " << minMaxValues.maxy << ",\n";
	metaFile << "\"zmax\": " << minMaxValues.maxz << ",\n";
	metaFile << "\"factor\": " << USHRT_MAX << "\n";
	metaFile << "}\n";
	metaFile.close();

	/*
		Write new values (normalized to range of unsigned short)
	*/
	for (i = 0;i < counter; i++)
	{
		entry e = entries[i];
		r.x = floor((e.vx - minMaxValues.minx) / (minMaxValues.maxx - minMaxValues.minx) * USHRT_MAX);
		r.y = floor((e.vy - minMaxValues.miny) / (minMaxValues.maxy - minMaxValues.miny) * USHRT_MAX);
		r.z = floor((e.vz - minMaxValues.minz) / (minMaxValues.maxz - minMaxValues.minz) * USHRT_MAX);
		r.u = 0;
		r.v = 0;
		fwrite(&r, sizeof(struct rec), 1, f);
	}

	std::cout << "Exported " << counter << " vertices" << std::endl;
	fclose(f);
	cin.get();
}
<commit_msg>Added octahedron normal export to writer<commit_after>// POPBufferWriter.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>

#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
#include "tiny_obj_loader.h"

#include <fstream>
using namespace std;

/*
	Structure to hold a record that should be written to file
*/
struct rec
{
	short x, y, z;
	char u, v;
};

/*
	Structure to hold min and max values for transformation
*/
struct minmax
{
	float minx, miny, minz, minu, minv;
	float maxx, maxy, maxz, maxu, maxv;
};

/*
	Structure to hold a vertex entry that is not transformed yet
*/
struct entry
{
	float vx, vy, vz;
	float nx, ny, nz;
	float nu, nv;
};

struct float2 { float u, v; };
struct float3 { float x, y, z; };

/*
	Octahedron normal coding
*/

//TODO Add conversion code here
float wrapOctahedronNormalValue(float v1, float v2)
{
	return (1.0f - fabs(v2)) * (v1 >= 0.0f ? 1.0 : -1.0);
}

void encodeOctahedronNormal(float nx, float ny, float nz, float & octNorU, float & octNorV)
{
	float absSum = fabs(nx) + fabs(ny) + fabs(nz);

	nx /= absSum;
	ny /= absSum;

	if (nz < 0.0f)
	{
		float tmp = nx;
		nx = wrapOctahedronNormalValue(nx, ny);
		ny = wrapOctahedronNormalValue(ny, tmp);
	}

	octNorU = nx * 0.5f + 0.5f;
	octNorV = ny * 0.5f + 0.5f;
}

int main()
{
	const string inputfile = "models/bunny.obj";
	const string modelName = "bunny";
	const char* dataFileName = "bunny.pop";
	const char* metaFileName = "bunny.json";

	/*
		Prepare output file
	*/
	int i;
	FILE *f;
	struct rec r;
	fopen_s(&f, dataFileName, "wb");
	if (!f)
		return 1;

	/*
		Parse obj file
	*/
	cout << "Reading file " << inputfile << endl;

	tinyobj::attrib_t attrib;
	std::vector<tinyobj::shape_t> shapes;
	std::vector<tinyobj::material_t> materials;
	std::string err;

	bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, inputfile.c_str());
	if (!err.empty()) { // `err` may contain warning message.
		std::cerr << err << std::endl;
	}
	if (!ret || shapes.size() < 1) {
		exit(1);
	}

	// Create a vector to hold the entries
	std::vector<entry> entries;
	int vertexCount = shapes[0].mesh.num_face_vertices.size() * 3;
	std::cout << "Found " << vertexCount << " vertices in file " << inputfile << std::endl;
	entries.reserve(vertexCount);
	int counter = 0;

	// Init min and max values
	minmax minMaxValues;
	minMaxValues.minx = HUGE_VALF;
	minMaxValues.maxx = -HUGE_VALF;
	minMaxValues.miny = HUGE_VALF;
	minMaxValues.maxy = -HUGE_VALF;
	minMaxValues.minz = HUGE_VALF;
	minMaxValues.maxz = -HUGE_VALF;

	/*
		Loop over file content and create output
	*/
	size_t index_offset = 0;
	for (size_t f = 0; f < shapes[0].mesh.num_face_vertices.size(); f++) {
		size_t fv = shapes[0].mesh.num_face_vertices[f];

		// Loop over vertices in the face.
		for (size_t v = 0; v < fv; v++) {
			// access to vertex
			tinyobj::index_t idx = shapes[0].mesh.indices[index_offset + v];

			// create an entry per vertex and update the min and max values if necessary
			entry e;
			e.vx = attrib.vertices[3 * idx.vertex_index + 0];
			if (e.vx < minMaxValues.minx) minMaxValues.minx = e.vx;
			if (e.vx > minMaxValues.maxx) minMaxValues.maxx = e.vx;

			e.vy = attrib.vertices[3 * idx.vertex_index + 1];
			if (e.vy < minMaxValues.miny) minMaxValues.miny = e.vy;
			if (e.vy > minMaxValues.maxy) minMaxValues.maxy = e.vy;

			e.vz = attrib.vertices[3 * idx.vertex_index + 2];
			if (e.vz < minMaxValues.minz) minMaxValues.minz = e.vz;
			if (e.vz > minMaxValues.maxz) minMaxValues.maxz = e.vz;

			// convert normals to octahedron normals
			e.nx = attrib.normals[3 * idx.normal_index + 0];
			e.ny = attrib.normals[3 * idx.normal_index + 1];
			e.nz = attrib.normals[3 * idx.normal_index + 2];
			encodeOctahedronNormal(e.nx, e.ny, e.nz, e.nu, e.nv);

			entries.push_back(e);
			counter++;
		}
		index_offset += fv;
	}

	// Output some debugging values
	std::cout << "MaxX: " << minMaxValues.maxx << " MaxY:" << minMaxValues.maxy << " MaxZ: " << minMaxValues.maxz << std::endl;
	std::cout << "MinX: " << minMaxValues.minx << " MinY:" << minMaxValues.miny << " MinZ: " << minMaxValues.minz << std::endl;

	// Write meta values to meta data file
	ofstream metaFile;
	metaFile.open(metaFileName);
	metaFile << "{\n";
	metaFile << "\"name\": \"" << modelName << "\",\n";
	metaFile << "\"data\": \"" << dataFileName << "\",\n";
	metaFile << "\"numVertices\": \"" << counter << "\",\n";
	metaFile << "\"xmin\": " << minMaxValues.minx << ",\n";
	metaFile << "\"ymin\": " << minMaxValues.miny << ",\n";
	metaFile << "\"zmin\": " << minMaxValues.minz << ",\n";
	metaFile << "\"xmax\": " << minMaxValues.maxx << ",\n";
	metaFile << "\"ymax\": " << minMaxValues.maxy << ",\n";
	metaFile << "\"zmax\": " << minMaxValues.maxz << ",\n";
	metaFile << "\"factor\": " << USHRT_MAX << "\n";
	metaFile << "}\n";
	metaFile.close();

	/*
		Write new values (normalized to range of unsigned short)
	*/
	for (i = 0;i < counter; i++)
	{
		entry e = entries[i];

		r.x = floor((e.vx - minMaxValues.minx) / (minMaxValues.maxx - minMaxValues.minx) * USHRT_MAX);
		r.y = floor((e.vy - minMaxValues.miny) / (minMaxValues.maxy - minMaxValues.miny) * USHRT_MAX);
		r.z = floor((e.vz - minMaxValues.minz) / (minMaxValues.maxz - minMaxValues.minz) * USHRT_MAX);
		r.u = floor(e.nu * UCHAR_MAX);
		r.v = floor(e.nv * UCHAR_MAX);

		fwrite(&r, sizeof(struct rec), 1, f);
	}

	std::cout << "Exported " << counter << " vertices" << std::endl;
	fclose(f);
	cin.get();
}
<|endoftext|>


<commit_before>#ifndef SILICIUM_REACTIVE_SOCKET_OBSERVABLE_HPP
#define SILICIUM_REACTIVE_SOCKET_OBSERVABLE_HPP

#include <silicium/fast_variant.hpp>
#include <silicium/override.hpp>
#include <silicium/observable.hpp>
#include <silicium/exchange.hpp>
#include <boost/config.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/range/iterator_range.hpp>

namespace Si
{
	struct incoming_bytes
	{
		char const *begin = nullptr, *end = nullptr;

		incoming_bytes() BOOST_NOEXCEPT
		{
		}

		incoming_bytes(char const *begin, char const *end) BOOST_NOEXCEPT
			: begin(begin)
			, end(end)
		{
		}
	};

	typedef Si::fast_variant<incoming_bytes, boost::system::error_code> received_from_socket;

	struct socket_observable : observable<received_from_socket>
	{
		typedef received_from_socket element_type;
		typedef boost::iterator_range<char *> buffer_type;

		socket_observable(boost::asio::ip::tcp::socket &socket, buffer_type buffer)
			: socket(&socket)
			, buffer(buffer)
		{
			assert(!buffer.empty());
		}

		virtual void async_get_one(observer<element_type> &receiver) SILICIUM_OVERRIDE
		{
			assert(!receiver_);
			receiver_ = &receiver;
			socket->async_receive(boost::asio::buffer(buffer.begin(), buffer.size()), [this](boost::system::error_code error, std::size_t bytes_received)
			{
				assert(receiver_);
				if (error)
				{
					if (error == boost::asio::error::operation_aborted)
					{
						return;
					}
					exchange(this->receiver_, nullptr)->got_element(error);
				}
				else
				{
					auto * const receiver = exchange(this->receiver_, nullptr);
					receiver->got_element(incoming_bytes{buffer.begin(), buffer.begin() + bytes_received});
				}
			});
		}

		virtual void cancel() SILICIUM_OVERRIDE
		{
			assert(receiver_);
			socket->cancel();
			receiver_ = nullptr;
		}

	private:

		boost::asio::ip::tcp::socket *socket;
		buffer_type buffer;
		observer<element_type> *receiver_ = nullptr;
	};
}

#endif
<commit_msg>devirtualize socket_observable<commit_after>#ifndef SILICIUM_REACTIVE_SOCKET_OBSERVABLE_HPP
#define SILICIUM_REACTIVE_SOCKET_OBSERVABLE_HPP

#include <silicium/fast_variant.hpp>
#include <silicium/override.hpp>
#include <silicium/observer.hpp>
#include <silicium/exchange.hpp>
#include <boost/config.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/range/iterator_range.hpp>

namespace Si
{
	struct incoming_bytes
	{
		char const *begin = nullptr, *end = nullptr;

		incoming_bytes() BOOST_NOEXCEPT
		{
		}

		incoming_bytes(char const *begin, char const *end) BOOST_NOEXCEPT
			: begin(begin)
			, end(end)
		{
		}
	};

	typedef Si::fast_variant<incoming_bytes, boost::system::error_code> received_from_socket;

	struct socket_observable
	{
		typedef received_from_socket element_type;
		typedef boost::iterator_range<char *> buffer_type;

		socket_observable(boost::asio::ip::tcp::socket &socket, buffer_type buffer)
			: socket(&socket)
			, buffer(buffer)
		{
			assert(!buffer.empty());
		}

		void async_get_one(observer<element_type> &receiver)
		{
			assert(!receiver_);
			receiver_ = &receiver;
			socket->async_receive(boost::asio::buffer(buffer.begin(), buffer.size()), [this](boost::system::error_code error, std::size_t bytes_received)
			{
				assert(receiver_);
				if (error)
				{
					if (error == boost::asio::error::operation_aborted)
					{
						return;
					}
					exchange(this->receiver_, nullptr)->got_element(error);
				}
				else
				{
					auto * const receiver = exchange(this->receiver_, nullptr);
					receiver->got_element(incoming_bytes{buffer.begin(), buffer.begin() + bytes_received});
				}
			});
		}

		void cancel()
		{
			assert(receiver_);
			socket->cancel();
			receiver_ = nullptr;
		}

	private:

		boost::asio::ip::tcp::socket *socket;
		buffer_type buffer;
		observer<element_type> *receiver_ = nullptr;
	};
}

#endif
<|endoftext|>


<commit_before>// ================ INCLUDES ===================
#include <Arduino.h>

#include <Debug.h>
#include <MouseMove.h>
#include <IndicatorLEDs.h>

// ================= DEBUG =====================
#define PRINT_FREE_RAM      //print free RAM and headers

Debug debug;

// ======= INSTANTIATION THE KEYBOARD ==========
#include "config.h"
#include <instantiations_ports.h>
#include <instantiations_codes.h>
#include <instantiations_matrix.h>              //layout table is with instantiations_matrix.h

// ================== MAIN =====================
void setup()
{
    rowPort1_R.begin();
    colPort0_R.begin();
    indicatorLEDs.begin(&layerState_DH);
    Keyboard.begin();
    Mouse.begin();

#ifdef PRINT_FREE_RAM
    delay(1000);        //time for OS to detect USB before printing
    Keyboard.print(F("keybrd_DH.ino "));
    debug.print_free_RAM();
#endif //end PRINT_FREE_RAM
}

void loop()
{
    matrix_L.scan();
    matrix_R.scan();
    indicatorLEDs.blink();
    mouseMove.send();

    //debug.print_scans_per_second();
    //debug.print_microseconds_per_scan();
}
<commit_msg>move Wire.begin() to sketch<commit_after>// ================ INCLUDES ===================
#include <Arduino.h>

#include <Debug.h>
#include <MouseMove.h>
#include <IndicatorLEDs.h>

// ================= DEBUG =====================
#define PRINT_FREE_RAM      //print free RAM and headers

Debug debug;

// ======= INSTANTIATION THE KEYBOARD ==========
#include "config.h"
#include <instantiations_ports.h>
#include <instantiations_codes.h>
#include <instantiations_matrix.h>              //layout table is with instantiations_matrix.h

// ================== MAIN =====================
void setup()
{
    Wire.begin(); //todo has not been tested yet,
        //Wire.begin() must be called before rowPort.begin() colPort.begin()
    rowPort1_R.begin();
    colPort0_R.begin();
    indicatorLEDs.begin(&layerState_DH);
    Keyboard.begin();
    Mouse.begin();

#ifdef PRINT_FREE_RAM
    delay(1000);        //time for OS to detect USB before printing
    Keyboard.print(F("keybrd_DH.ino "));
    debug.print_free_RAM();
#endif //end PRINT_FREE_RAM
}

void loop()
{
    matrix_L.scan();
    matrix_R.scan();
    indicatorLEDs.blink();
    mouseMove.send();

    //debug.print_scans_per_second();
    //debug.print_microseconds_per_scan();
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include "astutil.h"
#include "bb.h"
#include "bitVec.h"
#include "expr.h"
#include "stmt.h"
#include "view.h"

BasicBlock::BasicBlock()
  : id(nextid++) {}

#define BB_START()                              \
  basicBlock = new BasicBlock()

// The assert tests that the expression we are adding to this basic block
// has not already been deleted.
#define BB_ADD(expr)                            \
  INT_ASSERT(expr);                             \
  basicBlock->exprs.add(expr)

#define BB_ADD_LS(exprls)                       \
  for_alist(expr, exprls) {                     \
    BB_ADD(expr);                               \
  }

#define BB_STOP()                               \
  fn->basicBlocks->add(Steal())

#define BB_RESTART()                            \
  BB_STOP();                                    \
  BB_START()

#define BBB(stmt)                               \
    buildBasicBlocks(fn, stmt)

#define BB_THREAD(src, dst)                     \
  dst->ins.add(src);                            \
  src->outs.add(dst)

//# Statics
BasicBlock* BasicBlock::basicBlock;
Map<LabelSymbol*,Vec<BasicBlock*>*> BasicBlock::gotoMaps;
Map<LabelSymbol*,BasicBlock*> BasicBlock::labelMaps;
int BasicBlock::nextid;

// Returns true if the class invariants have been preserved.
bool BasicBlock::isOK()
{
  // Right now we just test that all entries are valid.
  forv_Vec(Expr, expr, exprs)
    if (expr == 0) return false;
  return true;
}

// Reset the shared statics.
void BasicBlock::reset(FnSymbol* fn)
{
  if (fn->basicBlocks)
  {
    forv_Vec(BasicBlock, bb, *fn->basicBlocks)
      delete bb;
    delete fn->basicBlocks;
  }
  fn->basicBlocks = new Vec<BasicBlock*>();
  gotoMaps.clear();
  labelMaps.clear();
  nextid = 0;
}

BasicBlock* BasicBlock::Steal()
{
  BasicBlock* temp = basicBlock;
  basicBlock = 0;
  return temp;
}

// This is the top-level (public) builder function.
void buildBasicBlocks(FnSymbol* fn)
{
  BasicBlock::reset(fn);
  BasicBlock::basicBlock = new BasicBlock();
  BasicBlock::buildBasicBlocks(fn, fn->body);
  fn->basicBlocks->add(BasicBlock::Steal());
}

void BasicBlock::buildBasicBlocks(FnSymbol* fn, Expr* stmt)
{
  if (!stmt) return;

  if (BlockStmt* s = toBlockStmt(stmt))
  {
    // If a loop statement.
    if (s->blockInfo)
    {
      BasicBlock* top = basicBlock;
      BB_RESTART();
      // Assumes that blockInfo is never null.
      BB_ADD(s->blockInfo);
      BasicBlock* loopTop = basicBlock;
      for_alist(stmt, s->body) {
        BBB(stmt);
      }
      BasicBlock* loopBottom = basicBlock;
      BB_RESTART();
      BasicBlock* bottom = basicBlock;
      BB_THREAD(top, loopTop);
      BB_THREAD(loopBottom, bottom);
      BB_THREAD(loopBottom, loopTop);
      BB_THREAD(top, bottom);
    }
    else
    {
      for_alist(stmt, s->body)
        BBB(stmt);
    }
  }
  else if (CondStmt* s = toCondStmt(stmt))
  {
    INT_ASSERT(s->condExpr);
    BB_ADD(s->condExpr);
    BasicBlock* top = basicBlock;
    BB_RESTART();
    BB_THREAD(top, basicBlock);
    BBB(s->thenStmt);
    BasicBlock* thenBottom = basicBlock;
    BB_RESTART();
    if (s->elseStmt)
    {
      BB_THREAD(top, basicBlock);
      BBB(s->elseStmt);
      BasicBlock* elseBottom = basicBlock;
      BB_RESTART();
      BB_THREAD(elseBottom, basicBlock);
    }
    else
    {
      BB_THREAD(top, basicBlock);
    }
    BB_THREAD(thenBottom, basicBlock);
  } else if (GotoStmt* s = toGotoStmt(stmt)) {
    LabelSymbol* label = toLabelSymbol(toSymExpr(s->label)->var);
    if (BasicBlock* bb = labelMaps.get(label)) {
      BB_THREAD(basicBlock, bb);
    } else {
      Vec<BasicBlock*>* vbb = gotoMaps.get(label);
      if (!vbb)
        vbb = new Vec<BasicBlock*>();
      vbb->add(basicBlock);
      gotoMaps.put(label, vbb);
    }
    BB_ADD(s); // Put the goto at the end of its block.
    BB_RESTART();
  } else {
    DefExpr* def = toDefExpr(stmt);
    if (def && toLabelSymbol(def->sym)) {
      // If a label appears in the middle of a block,
      // we start a new block.
      if (basicBlock->exprs.count() > 0)
      {
        BasicBlock* top = basicBlock;
        BB_RESTART();
        BB_THREAD(top, basicBlock);
      }
      BB_ADD(def); // Put the label def at the start of its block.

      // OK, this statement is a label def, so get the label.
      LabelSymbol* label = toLabelSymbol(def->sym);

      // See if we have any unresolved references to this label,
      // and resolve them.
      if (Vec<BasicBlock*>* vbb = gotoMaps.get(label)) {
        forv_Vec(BasicBlock, bb, *vbb) {
          BB_THREAD(bb, basicBlock);
        }
      }
      labelMaps.put(label, basicBlock);
    } else {
      BB_ADD(stmt);
    }
  }
}

// Returns true if the basic block structure is OK, false otherwise.
bool verifyBasicBlocks(FnSymbol* fn)
{
  forv_Vec(BasicBlock, bb, *fn->basicBlocks)
  {
    if (! bb->isOK())
      return false;
  }
  return true;
}

void buildLocalsVectorMap(FnSymbol* fn,
                          Vec<Symbol*>& locals,
                          Map<Symbol*,int>& localMap) {
  int i = 0;
  forv_Vec(BasicBlock, bb, *fn->basicBlocks) {
    forv_Vec(Expr, expr, bb->exprs) {
      if (DefExpr* def = toDefExpr(expr)) {
        if (toVarSymbol(def->sym)) {
          locals.add(def->sym);
          localMap.put(def->sym, i++);
        }
      }
    }
  }
}

//#define DEBUG_FLOW
void backwardFlowAnalysis(FnSymbol* fn,
                          Vec<BitVec*>& GEN,
                          Vec<BitVec*>& KILL,
                          Vec<BitVec*>& IN,
                          Vec<BitVec*>& OUT) {
  bool iterate = true;
  while (iterate) {
    iterate = false;
    int i = 0;
    forv_Vec(BasicBlock, bb, *fn->basicBlocks) {
      for (int j = 0; j < IN.v[i]->ndata; j++) {
        unsigned new_in = (OUT.v[i]->data[j] & ~KILL.v[i]->data[j]) | GEN.v[i]->data[j];
        if (new_in != IN.v[i]->data[j]) {
          IN.v[i]->data[j] = new_in;
          iterate = true;
        }
        unsigned new_out = 0;
        forv_Vec(BasicBlock, bbout, bb->outs) {
          new_out = new_out | IN.v[bbout->id]->data[j];
        }
        if (new_out != OUT.v[i]->data[j]) {
          OUT.v[i]->data[j] = new_out;
          iterate = true;
        }
      }
      i++;
    }
#ifdef DEBUG_FLOW
    printf("IN\n"); printBitVectorSets(IN);
    printf("OUT\n"); printBitVectorSets(OUT);
#endif
  }
}

void forwardFlowAnalysis(FnSymbol* fn,
                         Vec<BitVec*>& GEN,
                         Vec<BitVec*>& KILL,
                         Vec<BitVec*>& IN,
                         Vec<BitVec*>& OUT,
                         bool intersect) {
  int nbbq = fn->basicBlocks->n; // size of bb queue
  Vec<int> bbq;
  BitVec bbs(nbbq);
  int iq = -1, nq = nbbq-1;      // index to first and last bb in bbq
  for (int i = 0; i < fn->basicBlocks->n; i++) {
    bbq.add(i);
    bbs.set(i);
  }
  while (iq != nq) {
    iq = (iq + 1) % nbbq;
    int i = bbq.v[iq];
    bbs.unset(i);
#ifdef DEBUG_FLOW
    if (iq == 0) {
      printf("IN\n"); printBitVectorSets(IN);
      printf("OUT\n"); printBitVectorSets(OUT);
    }
#endif
    BasicBlock* bb = fn->basicBlocks->v[i];
    bool change = false;
    for (int j = 0; j < IN.v[i]->ndata; j++) {
      if (bb->ins.n > 0) {
        unsigned new_in = (intersect) ? (unsigned)(-1) : 0;
        forv_Vec(BasicBlock, bbin, bb->ins) {
          if (intersect)
            new_in &= OUT.v[bbin->id]->data[j];
          else
            new_in |= OUT.v[bbin->id]->data[j];
        }
        if (new_in != IN.v[i]->data[j]) {
          IN.v[i]->data[j] = new_in;
          change = true;
        }
      }
      unsigned new_out = (IN.v[i]->data[j] & ~KILL.v[i]->data[j]) | GEN.v[i]->data[j];
      if (new_out != OUT.v[i]->data[j]) {
        OUT.v[i]->data[j] = new_out;
        change = true;
      }
    }
    if (change) {
      forv_Vec(BasicBlock, bbout, bb->outs) {
        if (!bbs.get(bbout->id)) {
          nq = (nq + 1) % nbbq;
          bbs.set(bbout->id);
          bbq.v[nq] = bbout->id;
        }
      }
    }
  }
}

void printBasicBlocks(FnSymbol* fn) {
  forv_Vec(BasicBlock, b, *fn->basicBlocks) {
    printf("%2d:  ", b->id);
    forv_Vec(BasicBlock, bb, b->ins) {
      printf("%d ", bb->id);
    }
    printf(" >  ");
    forv_Vec(BasicBlock, bb, b->outs) {
      printf("%d ", bb->id);
    }
    printf("\n");
    forv_Vec(Expr, expr, b->exprs) {
      if (expr)
        list_view_noline(expr);
      else
        printf("0 (null)\n");
    }
    printf("\n");
  }
}

void printLocalsVector(Vec<Symbol*> locals, Map<Symbol*,int>& localMap) {
  printf("Local Variables\n");
  forv_Vec(Symbol, local, locals) {
    printf("%2d: %s[%d]\n", localMap.get(local), local->name, local->id);
  }
  printf("\n");
}

void printDefsVector(Vec<SymExpr*> defs, Map<SymExpr*,int>& defMap) {
  printf("Variable Definitions\n");
  forv_Vec(SymExpr, def, defs) {
    printf("%2d: %s[%d] in %d\n", defMap.get(def), def->var->name,
           def->var->id, def->getStmtExpr()->id);
  }
  printf("\n");
}

void printLocalsVectorSets(Vec<BitVec*>& sets, Vec<Symbol*> locals) {
  int i = 0;
  forv_Vec(BitVec, set, sets) {
    printf("%2d: ", i);
    for (int j = 0; j < set->size; j++) {
      if (set->get(j))
        printf("%s[%d] ", locals.v[j]->name, locals.v[j]->id);
    }
    printf("\n");
    i++;
  }
  printf("\n");
}

void printBitVectorSets(Vec<BitVec*>& sets) {
  int i = 0;
  forv_Vec(BitVec, set, sets) {
    printf("%2d: ", i);
    for (int j = 0; j < set->size; j++) {
      printf("%d", (set->get(j)) ? 1 : 0);
      if ((j+1) % 10 == 0) printf(" ");
    }
    printf("\n");
    i++;
  }
  printf("\n");
}
<commit_msg>Use do { ... } while (0) idiom in multiline macros.<commit_after>#include <cstdlib>
#include "astutil.h"
#include "bb.h"
#include "bitVec.h"
#include "expr.h"
#include "stmt.h"
#include "view.h"

BasicBlock::BasicBlock()
  : id(nextid++) {}

#define BB_START()                              \
  basicBlock = new BasicBlock()

// The assert tests that the expression we are adding to this basic block
// has not already been deleted.
#define BB_ADD(expr)                            \
  do {                                          \
    INT_ASSERT(expr);                           \
    basicBlock->exprs.add(expr);                \
  } while (0)

#define BB_STOP()                               \
  fn->basicBlocks->add(Steal())

#define BB_RESTART()                            \
  do {                                          \
    BB_STOP();                                  \
    BB_START();                                 \
  } while (0)

#define BBB(stmt)                               \
    buildBasicBlocks(fn, stmt)

#define BB_THREAD(src, dst)                     \
  do {                                          \
    dst->ins.add(src);                          \
    src->outs.add(dst);                         \
  } while (0)

//# Statics
BasicBlock* BasicBlock::basicBlock;
Map<LabelSymbol*,Vec<BasicBlock*>*> BasicBlock::gotoMaps;
Map<LabelSymbol*,BasicBlock*> BasicBlock::labelMaps;
int BasicBlock::nextid;

// Returns true if the class invariants have been preserved.
bool BasicBlock::isOK()
{
  // Right now we just test that all entries are valid.
  forv_Vec(Expr, expr, exprs)
    if (expr == 0) return false;
  return true;
}

// Reset the shared statics.
void BasicBlock::reset(FnSymbol* fn)
{
  if (fn->basicBlocks)
  {
    forv_Vec(BasicBlock, bb, *fn->basicBlocks)
      delete bb;
    delete fn->basicBlocks;
  }
  fn->basicBlocks = new Vec<BasicBlock*>();
  gotoMaps.clear();
  labelMaps.clear();
  nextid = 0;
}

BasicBlock* BasicBlock::Steal()
{
  BasicBlock* temp = basicBlock;
  basicBlock = 0;
  return temp;
}

// This is the top-level (public) builder function.
void buildBasicBlocks(FnSymbol* fn)
{
  BasicBlock::reset(fn);
  BasicBlock::basicBlock = new BasicBlock();
  BasicBlock::buildBasicBlocks(fn, fn->body);
  fn->basicBlocks->add(BasicBlock::Steal());
}

void BasicBlock::buildBasicBlocks(FnSymbol* fn, Expr* stmt)
{
  if (!stmt) return;

  if (BlockStmt* s = toBlockStmt(stmt))
  {
    // If a loop statement.
    if (s->blockInfo)
    {
      BasicBlock* top = basicBlock;
      BB_RESTART();
      // Assumes that blockInfo is never null.
      BB_ADD(s->blockInfo);
      BasicBlock* loopTop = basicBlock;
      for_alist(stmt, s->body) {
        BBB(stmt);
      }
      BasicBlock* loopBottom = basicBlock;
      BB_RESTART();
      BasicBlock* bottom = basicBlock;
      BB_THREAD(top, loopTop);
      BB_THREAD(loopBottom, bottom);
      BB_THREAD(loopBottom, loopTop);
      BB_THREAD(top, bottom);
    }
    else
    {
      for_alist(stmt, s->body)
        BBB(stmt);
    }
  }
  else if (CondStmt* s = toCondStmt(stmt))
  {
    INT_ASSERT(s->condExpr);
    BB_ADD(s->condExpr);
    BasicBlock* top = basicBlock;
    BB_RESTART();
    BB_THREAD(top, basicBlock);
    BBB(s->thenStmt);
    BasicBlock* thenBottom = basicBlock;
    BB_RESTART();
    if (s->elseStmt)
    {
      BB_THREAD(top, basicBlock);
      BBB(s->elseStmt);
      BasicBlock* elseBottom = basicBlock;
      BB_RESTART();
      BB_THREAD(elseBottom, basicBlock);
    }
    else
    {
      BB_THREAD(top, basicBlock);
    }
    BB_THREAD(thenBottom, basicBlock);
  } else if (GotoStmt* s = toGotoStmt(stmt)) {
    LabelSymbol* label = toLabelSymbol(toSymExpr(s->label)->var);
    if (BasicBlock* bb = labelMaps.get(label)) {
      BB_THREAD(basicBlock, bb);
    } else {
      Vec<BasicBlock*>* vbb = gotoMaps.get(label);
      if (!vbb)
        vbb = new Vec<BasicBlock*>();
      vbb->add(basicBlock);
      gotoMaps.put(label, vbb);
    }
    BB_ADD(s); // Put the goto at the end of its block.
    BB_RESTART();
  } else {
    DefExpr* def = toDefExpr(stmt);
    if (def && toLabelSymbol(def->sym)) {
      // If a label appears in the middle of a block,
      // we start a new block.
      if (basicBlock->exprs.count() > 0)
      {
        BasicBlock* top = basicBlock;
        BB_RESTART();
        BB_THREAD(top, basicBlock);
      }
      BB_ADD(def); // Put the label def at the start of its block.

      // OK, this statement is a label def, so get the label.
      LabelSymbol* label = toLabelSymbol(def->sym);

      // See if we have any unresolved references to this label,
      // and resolve them.
      if (Vec<BasicBlock*>* vbb = gotoMaps.get(label)) {
        forv_Vec(BasicBlock, bb, *vbb) {
          BB_THREAD(bb, basicBlock);
        }
      }
      labelMaps.put(label, basicBlock);
    } else {
      BB_ADD(stmt);
    }
  }
}

// Returns true if the basic block structure is OK, false otherwise.
bool verifyBasicBlocks(FnSymbol* fn)
{
  forv_Vec(BasicBlock, bb, *fn->basicBlocks)
  {
    if (! bb->isOK())
      return false;
  }
  return true;
}

void buildLocalsVectorMap(FnSymbol* fn,
                          Vec<Symbol*>& locals,
                          Map<Symbol*,int>& localMap) {
  int i = 0;
  forv_Vec(BasicBlock, bb, *fn->basicBlocks) {
    forv_Vec(Expr, expr, bb->exprs) {
      if (DefExpr* def = toDefExpr(expr)) {
        if (toVarSymbol(def->sym)) {
          locals.add(def->sym);
          localMap.put(def->sym, i++);
        }
      }
    }
  }
}

//#define DEBUG_FLOW
void backwardFlowAnalysis(FnSymbol* fn,
                          Vec<BitVec*>& GEN,
                          Vec<BitVec*>& KILL,
                          Vec<BitVec*>& IN,
                          Vec<BitVec*>& OUT) {
  bool iterate = true;
  while (iterate) {
    iterate = false;
    int i = 0;
    forv_Vec(BasicBlock, bb, *fn->basicBlocks) {
      for (int j = 0; j < IN.v[i]->ndata; j++) {
        unsigned new_in = (OUT.v[i]->data[j] & ~KILL.v[i]->data[j]) | GEN.v[i]->data[j];
        if (new_in != IN.v[i]->data[j]) {
          IN.v[i]->data[j] = new_in;
          iterate = true;
        }
        unsigned new_out = 0;
        forv_Vec(BasicBlock, bbout, bb->outs) {
          new_out = new_out | IN.v[bbout->id]->data[j];
        }
        if (new_out != OUT.v[i]->data[j]) {
          OUT.v[i]->data[j] = new_out;
          iterate = true;
        }
      }
      i++;
    }
#ifdef DEBUG_FLOW
    printf("IN\n"); printBitVectorSets(IN);
    printf("OUT\n"); printBitVectorSets(OUT);
#endif
  }
}

void forwardFlowAnalysis(FnSymbol* fn,
                         Vec<BitVec*>& GEN,
                         Vec<BitVec*>& KILL,
                         Vec<BitVec*>& IN,
                         Vec<BitVec*>& OUT,
                         bool intersect) {
  int nbbq = fn->basicBlocks->n; // size of bb queue
  Vec<int> bbq;
  BitVec bbs(nbbq);
  int iq = -1, nq = nbbq-1;      // index to first and last bb in bbq
  for (int i = 0; i < fn->basicBlocks->n; i++) {
    bbq.add(i);
    bbs.set(i);
  }
  while (iq != nq) {
    iq = (iq + 1) % nbbq;
    int i = bbq.v[iq];
    bbs.unset(i);
#ifdef DEBUG_FLOW
    if (iq == 0) {
      printf("IN\n"); printBitVectorSets(IN);
      printf("OUT\n"); printBitVectorSets(OUT);
    }
#endif
    BasicBlock* bb = fn->basicBlocks->v[i];
    bool change = false;
    for (int j = 0; j < IN.v[i]->ndata; j++) {
      if (bb->ins.n > 0) {
        unsigned new_in = (intersect) ? (unsigned)(-1) : 0;
        forv_Vec(BasicBlock, bbin, bb->ins) {
          if (intersect)
            new_in &= OUT.v[bbin->id]->data[j];
          else
            new_in |= OUT.v[bbin->id]->data[j];
        }
        if (new_in != IN.v[i]->data[j]) {
          IN.v[i]->data[j] = new_in;
          change = true;
        }
      }
      unsigned new_out = (IN.v[i]->data[j] & ~KILL.v[i]->data[j]) | GEN.v[i]->data[j];
      if (new_out != OUT.v[i]->data[j]) {
        OUT.v[i]->data[j] = new_out;
        change = true;
      }
    }
    if (change) {
      forv_Vec(BasicBlock, bbout, bb->outs) {
        if (!bbs.get(bbout->id)) {
          nq = (nq + 1) % nbbq;
          bbs.set(bbout->id);
          bbq.v[nq] = bbout->id;
        }
      }
    }
  }
}

void printBasicBlocks(FnSymbol* fn) {
  forv_Vec(BasicBlock, b, *fn->basicBlocks) {
    printf("%2d:  ", b->id);
    forv_Vec(BasicBlock, bb, b->ins) {
      printf("%d ", bb->id);
    }
    printf(" >  ");
    forv_Vec(BasicBlock, bb, b->outs) {
      printf("%d ", bb->id);
    }
    printf("\n");
    forv_Vec(Expr, expr, b->exprs) {
      if (expr)
        list_view_noline(expr);
      else
        printf("0 (null)\n");
    }
    printf("\n");
  }
}

void printLocalsVector(Vec<Symbol*> locals, Map<Symbol*,int>& localMap) {
  printf("Local Variables\n");
  forv_Vec(Symbol, local, locals) {
    printf("%2d: %s[%d]\n", localMap.get(local), local->name, local->id);
  }
  printf("\n");
}

void printDefsVector(Vec<SymExpr*> defs, Map<SymExpr*,int>& defMap) {
  printf("Variable Definitions\n");
  forv_Vec(SymExpr, def, defs) {
    printf("%2d: %s[%d] in %d\n", defMap.get(def), def->var->name,
           def->var->id, def->getStmtExpr()->id);
  }
  printf("\n");
}

void printLocalsVectorSets(Vec<BitVec*>& sets, Vec<Symbol*> locals) {
  int i = 0;
  forv_Vec(BitVec, set, sets) {
    printf("%2d: ", i);
    for (int j = 0; j < set->size; j++) {
      if (set->get(j))
        printf("%s[%d] ", locals.v[j]->name, locals.v[j]->id);
    }
    printf("\n");
    i++;
  }
  printf("\n");
}

void printBitVectorSets(Vec<BitVec*>& sets) {
  int i = 0;
  forv_Vec(BitVec, set, sets) {
    printf("%2d: ", i);
    for (int j = 0; j < set->size; j++) {
      printf("%d", (set->get(j)) ? 1 : 0);
      if ((j+1) % 10 == 0) printf(" ");
    }
    printf("\n");
    i++;
  }
  printf("\n");
}
<|endoftext|>


<commit_before>/* -*-c++-*-
 *
 * Copyright (C) 2008 Stuart Buchanan
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#include <algorithm>
#include <vector>
#include <string>
#include <map>

#include <boost/tuple/tuple_comparison.hpp>

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Matrix>
#include <osg/NodeVisitor>

#include <osgDB/ReadFile>
#include <osgDB/FileUtils>

#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx>

#include "ShaderGeometry.hxx"
#include "TreeBin.hxx"

#define SG_TREE_QUAD_TREE_DEPTH 3
#define SG_TREE_FADE_OUT_LEVELS 10

using namespace osg;

namespace simgear
{

// Tree instance scheme:
// vertex - local position of quad vertex.
// normal - x y scaling, z number of varieties
// fog coord - rotation
// color - xyz of tree quad origin, replicated 4 times.
//
// The tree quad is rendered twice, with different rotations, to
// create the crossed tree geometry.

struct TreesBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback
{
    TreesBoundingBoxCallback() {}
    TreesBoundingBoxCallback(const TreesBoundingBoxCallback&, const CopyOp&) {}
    META_Object(simgear, TreesBoundingBoxCallback);
    virtual BoundingBox computeBound(const Drawable&) const;
};

BoundingBox
TreesBoundingBoxCallback::computeBound(const Drawable& drawable) const
{
    BoundingBox bb;
    const Geometry* geom = static_cast<const Geometry*>(&drawable);
    const Vec3Array* v = static_cast<const Vec3Array*>(geom->getVertexArray());
    const Vec3Array* pos = static_cast<const Vec3Array*>(geom->getColorArray());
    const Vec3Array* params
        = static_cast<const Vec3Array*>(geom->getNormalArray());
    const FloatArray* rot
        = static_cast<const FloatArray*>(geom->getFogCoordArray());
    float w = (*params)[0].x();
    float h = (*params)[0].y();
    Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList();
    FloatArray::const_iterator rotitr = rot->begin();
    for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(),
             psend = primSets.end();
         psitr != psend;
         ++psitr, ++rotitr) {
        Matrixd trnsfrm = (Matrixd::scale(w, w, h)
                           * Matrixd::rotate(*rotitr, Vec3(0.0f, 0.0f, 1.0f)));
        DrawArrays* da = static_cast<DrawArrays*>(psitr->get());
        GLint psFirst = da->getFirst();
        GLint psEndVert = psFirst + da->getCount();
        for (GLint i = psFirst;i < psEndVert; ++i) {
            Vec3 pt = (*v)[i];
            pt = pt * trnsfrm;
            pt += (*pos)[i];
            bb.expandBy(pt);
        }
    }
    return bb;
}

Geometry* makeSharedTreeGeometry(int numQuads)
{
    // generate a repeatable random seed
    mt seed;
    mt_init(&seed, unsigned(123));
    // set up the coords
    osg::Vec3Array* v = new osg::Vec3Array;
    osg::Vec2Array* t = new osg::Vec2Array;
    v->reserve(numQuads * 4);
    t->reserve(numQuads * 4);
    for (int i = 0; i < numQuads; ++i) {
        // Apply a random scaling factor and texture index.
        float h = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f;
        float cw = h * .5;
        v->push_back(Vec3(0.0f, -cw, 0.0f));
        v->push_back(Vec3(0.0f, cw, 0.0f));
        v->push_back(Vec3(0.0f, cw, h));
        v->push_back(Vec3(0.0f,-cw, h));
        // The texture coordinate range is not the entire coordinate
        // space, as the texture has a number of different trees on
        // it. Here we assign random coordinates and let the shader
        // choose the variety.
        float variety = mt_rand(&seed);
        t->push_back(Vec2(variety, 0.0f));
        t->push_back(Vec2(variety + 1.0f, 0.0f));
        t->push_back(Vec2(variety + 1.0f, 0.25f));
        t->push_back(Vec2(variety, 0.25f));
    }
    Geometry* result = new Geometry;
    result->setVertexArray(v);
    result->setTexCoordArray(0, t);
    result->setComputeBoundingBoxCallback(new TreesBoundingBoxCallback);
    result->setUseDisplayList(false);
    return result;
}

ref_ptr<Geometry> sharedTreeGeometry;

Geometry* createTreeGeometry(float width, float height, int varieties)
{
    if (!sharedTreeGeometry)
        sharedTreeGeometry = makeSharedTreeGeometry(1600);
    Geometry* quadGeom = simgear::clone(sharedTreeGeometry.get(),
                                        CopyOp::SHALLOW_COPY);
    Vec3Array* params = new Vec3Array;
    params->push_back(Vec3(width, height, (float)varieties));
    quadGeom->setNormalArray(params);
    quadGeom->setNormalBinding(Geometry::BIND_OVERALL);
    // Positions
    quadGeom->setColorArray(new Vec3Array);
    quadGeom->setColorBinding(Geometry::BIND_PER_VERTEX);
    FloatArray* rotation = new FloatArray(2);
    (*rotation)[0] = 0.0;
    (*rotation)[1] = PI_2;
    quadGeom->setFogCoordArray(rotation);
    quadGeom->setFogCoordBinding(Geometry::BIND_PER_PRIMITIVE_SET);
    // The primitive sets render the same geometry, but the second
    // will rotated 90 degrees by the vertex shader, which uses the
    // fog coordinate as a rotation.
    for (int i = 0; i < 2; ++i)
        quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS));
    return quadGeom;
}

EffectGeode* createTreeGeode(float width, float height, int varieties)
{
    EffectGeode* result = new EffectGeode;
    result->addDrawable(createTreeGeometry(width, height, varieties));
    return result;
}

void addTreeToLeafGeode(Geode* geode, const SGVec3f& p)
{
    Vec3 pos = toOsg(p);
    unsigned int numDrawables = geode->getNumDrawables();
    Geometry* geom
        = static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
    Vec3Array* posArray = static_cast<Vec3Array*>(geom->getColorArray());
    if (posArray->size()
        >= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
        Vec3Array* paramsArray
            = static_cast<Vec3Array*>(geom->getNormalArray());
        Vec3 params = (*paramsArray)[0];
        geom = createTreeGeometry(params.x(), params.y(), params.z());
        posArray = static_cast<Vec3Array*>(geom->getColorArray());
        geode->addDrawable(geom);
    }
    posArray->insert(posArray->end(), 4, pos);
    size_t numVerts = posArray->size();
    for (int i = 0; i < 2; ++i) {
        DrawArrays* primSet
            = static_cast<DrawArrays*>(geom->getPrimitiveSet(i));
        primSet->setCount(numVerts);
    }
}

typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;

static EffectMap treeEffectMap;

// Helper classes for creating the quad tree
namespace
{
struct MakeTreesLeaf
{
    MakeTreesLeaf(float range, int varieties, float width, float height,
        Effect* effect) :
        _range(range),  _varieties(varieties),
        _width(width), _height(height), _effect(effect) {}

    MakeTreesLeaf(const MakeTreesLeaf& rhs) :
        _range(rhs._range),
        _varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
        _effect(rhs._effect)
    {}

    LOD* operator() () const
    {
        LOD* result = new LOD;

        // Create a series of LOD nodes so trees cover decreases slightly
        // gradually with distance from _range to 2*_range
        for (float i = 0.0; i < SG_TREE_FADE_OUT_LEVELS; i++)
        {
            EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
            geode->setEffect(_effect.get());
            result->addChild(geode, 0, _range * (1.0 + i / (SG_TREE_FADE_OUT_LEVELS - 1.0)));
        }
        return result;
    }
    float _range;
    int _varieties;
    float _width;
    float _height;
    ref_ptr<Effect> _effect;
};

struct AddTreesLeafObject
{
    void operator() (LOD* lod, const TreeBin::Tree& tree) const
    {
        Geode* geode = static_cast<Geode*>(lod->getChild(int(tree.position.x() * 10.0f) % lod->getNumChildren()));
        addTreeToLeafGeode(geode, tree.position);
    }
};

struct GetTreeCoord
{
    Vec3 operator() (const TreeBin::Tree& tree) const
    {
        return toOsg(tree.position);
    }
};

typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
                        GetTreeCoord> ShaderGeometryQuadtree;
}

struct TreeTransformer
{
    TreeTransformer(Matrix& mat_) : mat(mat_) {}
    TreeBin::Tree operator()(const TreeBin::Tree& tree) const
    {
        Vec3 pos = toOsg(tree.position);
        return TreeBin::Tree(toSG(pos * mat));
    }
    Matrix mat;
};

// We may end up with a quadtree with many empty leaves. One might say
// that we should avoid constructing the leaves in the first place,
// but this node visitor tries to clean up after the fact.

struct QuadTreeCleaner : public osg::NodeVisitor
{
    QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
    {
    }
    void apply(LOD& lod)
    {
        for (int i  = lod.getNumChildren() - 1; i >= 0; --i) {
            EffectGeode* geode = dynamic_cast<EffectGeode*>(lod.getChild(i));
            if (!geode)
                continue;
            bool geodeEmpty = true;
            for (unsigned j = 0; j < geode->getNumDrawables(); ++j) {
                const Geometry* geom = dynamic_cast<Geometry*>(geode->getDrawable(j));
                if (!geom) {
                    geodeEmpty = false;
                    break;
                }
                for (unsigned k = 0; k < geom->getNumPrimitiveSets(); k++) {
                    const PrimitiveSet* ps = geom->getPrimitiveSet(k);
                    if (ps->getNumIndices() > 0) {
                        geodeEmpty = false;
                        break;
                    }
                }
            }
            if (geodeEmpty)
                lod.removeChildren(i, 1);
        }
    }
};

// This actually returns a MatrixTransform node. If we rotate the whole
// forest into the local Z-up coordinate system we can reuse the
// primitive tree geometry for all the forests of the same type.

osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform,
                         const SGReaderWriterOptions* options)
{
    Matrix transInv = Matrix::inverse(transform);
    static Matrix ident;
    // Set up some shared structures.
    ref_ptr<Group> group;
    MatrixTransform* mt = new MatrixTransform(transform);

    SGTreeBinList::iterator i;

    for (i = forestList.begin(); i != forestList.end(); ++i) {
        TreeBin* forest = *i;

        ref_ptr<Effect> effect;
        EffectMap::iterator iter = treeEffectMap.find(forest->texture);

        if ((iter == treeEffectMap.end())||
            (!iter->second.lock(effect)))
        {
            SGPropertyNode_ptr effectProp = new SGPropertyNode;
            makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
            SGPropertyNode* params = makeChild(effectProp, "parameters");
            // emphasize n = 0
            params->getChild("texture", 0, true)->getChild("image", 0, true)
                ->setStringValue(forest->texture);
            effect = makeEffect(effectProp, true, options);
            if (iter == treeEffectMap.end())
                treeEffectMap.insert(EffectMap::value_type(forest->texture, effect));
            else
                iter->second = effect; // update existing, but empty observer
        }

        // Now, create a quadtree for the forest.
        ShaderGeometryQuadtree
            quadtree(GetTreeCoord(), AddTreesLeafObject(),
                     SG_TREE_QUAD_TREE_DEPTH,
                     MakeTreesLeaf(forest->range, forest->texture_varieties,
                                   forest->width, forest->height, effect));
        // Transform tree positions from the "geocentric" positions we
        // get from the scenery polys into the local Z-up coordinate
        // system.
        std::vector<TreeBin::Tree> rotatedTrees;
        rotatedTrees.reserve(forest->_trees.size());
        std::transform(forest->_trees.begin(), forest->_trees.end(),
                       std::back_inserter(rotatedTrees),
                       TreeTransformer(transInv));
        quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end());
        group = quadtree.getRoot();

        for (size_t i = 0; i < group->getNumChildren(); ++i)
            mt->addChild(group->getChild(i));

        delete forest;
    }

    forestList.clear();
    QuadTreeCleaner cleaner;
    mt->accept(cleaner);
    return mt;
}

}
<commit_msg>Reduce the height of tree UV coordinates to work around mipmap issues.<commit_after>/* -*-c++-*-
 *
 * Copyright (C) 2008 Stuart Buchanan
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#include <algorithm>
#include <vector>
#include <string>
#include <map>

#include <boost/tuple/tuple_comparison.hpp>

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Matrix>
#include <osg/NodeVisitor>

#include <osgDB/ReadFile>
#include <osgDB/FileUtils>

#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx>

#include "ShaderGeometry.hxx"
#include "TreeBin.hxx"

#define SG_TREE_QUAD_TREE_DEPTH 3
#define SG_TREE_FADE_OUT_LEVELS 10

using namespace osg;

namespace simgear
{

// Tree instance scheme:
// vertex - local position of quad vertex.
// normal - x y scaling, z number of varieties
// fog coord - rotation
// color - xyz of tree quad origin, replicated 4 times.
//
// The tree quad is rendered twice, with different rotations, to
// create the crossed tree geometry.

struct TreesBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback
{
    TreesBoundingBoxCallback() {}
    TreesBoundingBoxCallback(const TreesBoundingBoxCallback&, const CopyOp&) {}
    META_Object(simgear, TreesBoundingBoxCallback);
    virtual BoundingBox computeBound(const Drawable&) const;
};

BoundingBox
TreesBoundingBoxCallback::computeBound(const Drawable& drawable) const
{
    BoundingBox bb;
    const Geometry* geom = static_cast<const Geometry*>(&drawable);
    const Vec3Array* v = static_cast<const Vec3Array*>(geom->getVertexArray());
    const Vec3Array* pos = static_cast<const Vec3Array*>(geom->getColorArray());
    const Vec3Array* params
        = static_cast<const Vec3Array*>(geom->getNormalArray());
    const FloatArray* rot
        = static_cast<const FloatArray*>(geom->getFogCoordArray());
    float w = (*params)[0].x();
    float h = (*params)[0].y();
    Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList();
    FloatArray::const_iterator rotitr = rot->begin();
    for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(),
             psend = primSets.end();
         psitr != psend;
         ++psitr, ++rotitr) {
        Matrixd trnsfrm = (Matrixd::scale(w, w, h)
                           * Matrixd::rotate(*rotitr, Vec3(0.0f, 0.0f, 1.0f)));
        DrawArrays* da = static_cast<DrawArrays*>(psitr->get());
        GLint psFirst = da->getFirst();
        GLint psEndVert = psFirst + da->getCount();
        for (GLint i = psFirst;i < psEndVert; ++i) {
            Vec3 pt = (*v)[i];
            pt = pt * trnsfrm;
            pt += (*pos)[i];
            bb.expandBy(pt);
        }
    }
    return bb;
}

Geometry* makeSharedTreeGeometry(int numQuads)
{
    // generate a repeatable random seed
    mt seed;
    mt_init(&seed, unsigned(123));
    // set up the coords
    osg::Vec3Array* v = new osg::Vec3Array;
    osg::Vec2Array* t = new osg::Vec2Array;
    v->reserve(numQuads * 4);
    t->reserve(numQuads * 4);
    for (int i = 0; i < numQuads; ++i) {
        // Apply a random scaling factor and texture index.
        float h = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f;
        float cw = h * .5;
        v->push_back(Vec3(0.0f, -cw, 0.0f));
        v->push_back(Vec3(0.0f, cw, 0.0f));
        v->push_back(Vec3(0.0f, cw, h));
        v->push_back(Vec3(0.0f,-cw, h));
        // The texture coordinate range is not the entire coordinate
        // space, as the texture has a number of different trees on
        // it. Here we assign random coordinates and let the shader
        // choose the variety.
        // Height isn't quite 0.25 to allow for UV map bleeding when
        // mipmaps are generated.
        float variety = mt_rand(&seed);
        t->push_back(Vec2(variety, 0.0f));
        t->push_back(Vec2(variety + 1.0f, 0.0f));
        t->push_back(Vec2(variety + 1.0f, 0.246f));
        t->push_back(Vec2(variety, 0.246f));
    }
    Geometry* result = new Geometry;
    result->setVertexArray(v);
    result->setTexCoordArray(0, t);
    result->setComputeBoundingBoxCallback(new TreesBoundingBoxCallback);
    result->setUseDisplayList(false);
    return result;
}

ref_ptr<Geometry> sharedTreeGeometry;

Geometry* createTreeGeometry(float width, float height, int varieties)
{
    if (!sharedTreeGeometry)
        sharedTreeGeometry = makeSharedTreeGeometry(1600);
    Geometry* quadGeom = simgear::clone(sharedTreeGeometry.get(),
                                        CopyOp::SHALLOW_COPY);
    Vec3Array* params = new Vec3Array;
    params->push_back(Vec3(width, height, (float)varieties));
    quadGeom->setNormalArray(params);
    quadGeom->setNormalBinding(Geometry::BIND_OVERALL);
    // Positions
    quadGeom->setColorArray(new Vec3Array);
    quadGeom->setColorBinding(Geometry::BIND_PER_VERTEX);
    FloatArray* rotation = new FloatArray(2);
    (*rotation)[0] = 0.0;
    (*rotation)[1] = PI_2;
    quadGeom->setFogCoordArray(rotation);
    quadGeom->setFogCoordBinding(Geometry::BIND_PER_PRIMITIVE_SET);
    // The primitive sets render the same geometry, but the second
    // will rotated 90 degrees by the vertex shader, which uses the
    // fog coordinate as a rotation.
    for (int i = 0; i < 2; ++i)
        quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS));
    return quadGeom;
}

EffectGeode* createTreeGeode(float width, float height, int varieties)
{
    EffectGeode* result = new EffectGeode;
    result->addDrawable(createTreeGeometry(width, height, varieties));
    return result;
}

void addTreeToLeafGeode(Geode* geode, const SGVec3f& p)
{
    Vec3 pos = toOsg(p);
    unsigned int numDrawables = geode->getNumDrawables();
    Geometry* geom
        = static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
    Vec3Array* posArray = static_cast<Vec3Array*>(geom->getColorArray());
    if (posArray->size()
        >= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
        Vec3Array* paramsArray
            = static_cast<Vec3Array*>(geom->getNormalArray());
        Vec3 params = (*paramsArray)[0];
        geom = createTreeGeometry(params.x(), params.y(), params.z());
        posArray = static_cast<Vec3Array*>(geom->getColorArray());
        geode->addDrawable(geom);
    }
    posArray->insert(posArray->end(), 4, pos);
    size_t numVerts = posArray->size();
    for (int i = 0; i < 2; ++i) {
        DrawArrays* primSet
            = static_cast<DrawArrays*>(geom->getPrimitiveSet(i));
        primSet->setCount(numVerts);
    }
}

typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;

static EffectMap treeEffectMap;

// Helper classes for creating the quad tree
namespace
{
struct MakeTreesLeaf
{
    MakeTreesLeaf(float range, int varieties, float width, float height,
        Effect* effect) :
        _range(range),  _varieties(varieties),
        _width(width), _height(height), _effect(effect) {}

    MakeTreesLeaf(const MakeTreesLeaf& rhs) :
        _range(rhs._range),
        _varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
        _effect(rhs._effect)
    {}

    LOD* operator() () const
    {
        LOD* result = new LOD;

        // Create a series of LOD nodes so trees cover decreases slightly
        // gradually with distance from _range to 2*_range
        for (float i = 0.0; i < SG_TREE_FADE_OUT_LEVELS; i++)
        {
            EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
            geode->setEffect(_effect.get());
            result->addChild(geode, 0, _range * (1.0 + i / (SG_TREE_FADE_OUT_LEVELS - 1.0)));
        }
        return result;
    }
    float _range;
    int _varieties;
    float _width;
    float _height;
    ref_ptr<Effect> _effect;
};

struct AddTreesLeafObject
{
    void operator() (LOD* lod, const TreeBin::Tree& tree) const
    {
        Geode* geode = static_cast<Geode*>(lod->getChild(int(tree.position.x() * 10.0f) % lod->getNumChildren()));
        addTreeToLeafGeode(geode, tree.position);
    }
};

struct GetTreeCoord
{
    Vec3 operator() (const TreeBin::Tree& tree) const
    {
        return toOsg(tree.position);
    }
};

typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
                        GetTreeCoord> ShaderGeometryQuadtree;
}

struct TreeTransformer
{
    TreeTransformer(Matrix& mat_) : mat(mat_) {}
    TreeBin::Tree operator()(const TreeBin::Tree& tree) const
    {
        Vec3 pos = toOsg(tree.position);
        return TreeBin::Tree(toSG(pos * mat));
    }
    Matrix mat;
};

// We may end up with a quadtree with many empty leaves. One might say
// that we should avoid constructing the leaves in the first place,
// but this node visitor tries to clean up after the fact.

struct QuadTreeCleaner : public osg::NodeVisitor
{
    QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
    {
    }
    void apply(LOD& lod)
    {
        for (int i  = lod.getNumChildren() - 1; i >= 0; --i) {
            EffectGeode* geode = dynamic_cast<EffectGeode*>(lod.getChild(i));
            if (!geode)
                continue;
            bool geodeEmpty = true;
            for (unsigned j = 0; j < geode->getNumDrawables(); ++j) {
                const Geometry* geom = dynamic_cast<Geometry*>(geode->getDrawable(j));
                if (!geom) {
                    geodeEmpty = false;
                    break;
                }
                for (unsigned k = 0; k < geom->getNumPrimitiveSets(); k++) {
                    const PrimitiveSet* ps = geom->getPrimitiveSet(k);
                    if (ps->getNumIndices() > 0) {
                        geodeEmpty = false;
                        break;
                    }
                }
            }
            if (geodeEmpty)
                lod.removeChildren(i, 1);
        }
    }
};

// This actually returns a MatrixTransform node. If we rotate the whole
// forest into the local Z-up coordinate system we can reuse the
// primitive tree geometry for all the forests of the same type.

osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform,
                         const SGReaderWriterOptions* options)
{
    Matrix transInv = Matrix::inverse(transform);
    static Matrix ident;
    // Set up some shared structures.
    ref_ptr<Group> group;
    MatrixTransform* mt = new MatrixTransform(transform);

    SGTreeBinList::iterator i;

    for (i = forestList.begin(); i != forestList.end(); ++i) {
        TreeBin* forest = *i;

        ref_ptr<Effect> effect;
        EffectMap::iterator iter = treeEffectMap.find(forest->texture);

        if ((iter == treeEffectMap.end())||
            (!iter->second.lock(effect)))
        {
            SGPropertyNode_ptr effectProp = new SGPropertyNode;
            makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
            SGPropertyNode* params = makeChild(effectProp, "parameters");
            // emphasize n = 0
            params->getChild("texture", 0, true)->getChild("image", 0, true)
                ->setStringValue(forest->texture);
            effect = makeEffect(effectProp, true, options);
            if (iter == treeEffectMap.end())
                treeEffectMap.insert(EffectMap::value_type(forest->texture, effect));
            else
                iter->second = effect; // update existing, but empty observer
        }

        // Now, create a quadtree for the forest.
        ShaderGeometryQuadtree
            quadtree(GetTreeCoord(), AddTreesLeafObject(),
                     SG_TREE_QUAD_TREE_DEPTH,
                     MakeTreesLeaf(forest->range, forest->texture_varieties,
                                   forest->width, forest->height, effect));
        // Transform tree positions from the "geocentric" positions we
        // get from the scenery polys into the local Z-up coordinate
        // system.
        std::vector<TreeBin::Tree> rotatedTrees;
        rotatedTrees.reserve(forest->_trees.size());
        std::transform(forest->_trees.begin(), forest->_trees.end(),
                       std::back_inserter(rotatedTrees),
                       TreeTransformer(transInv));
        quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end());
        group = quadtree.getRoot();

        for (size_t i = 0; i < group->getNumChildren(); ++i)
            mt->addChild(group->getChild(i));

        delete forest;
    }

    forestList.clear();
    QuadTreeCleaner cleaner;
    mt->accept(cleaner);
    return mt;
}

}
<|endoftext|>


<commit_before>// screen-dump.cxx -- dump a copy of the opengl screen buffer to a file
//
// Contributed by Richard Kaszeta <bofh@me.umn.edu>, started October 1999.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// $Id$

#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#if defined(__CYGWIN__)  /* && !defined(USING_X) */
#define WIN32
#endif

#if defined(WIN32)  /* MINGW and MSC predefine WIN32 */
# include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#include <simgear/compiler.h>

#include SG_GL_H

#include "screen-dump.hxx"

#define RGB3 3			// 3 bytes of color info per pixel
#define RGBA 4			// 4 bytes of color+alpha info

bool sg_glWritePPMFile(const char *filename, GLubyte *buffer, int win_width, int win_height, int mode)
{
    int i, j, k, q;
    unsigned char *ibuffer;
    FILE *fp;
    int pixelSize = mode==GL_RGBA?4:3;

    ibuffer = (unsigned char *) malloc(win_width*win_height*RGB3);

    if ( (fp = fopen(filename, "wb")) == NULL ) {
	printf("Warning: cannot open %s\n", filename);
	return false;
    }

    fprintf(fp, "P6\n# CREATOR: glReadPixel()\n%d %d\n%d\n",
	    win_width, win_height, UCHAR_MAX);
    q = 0;
    for (i = 0; i < win_height; i++)
	for (j = 0; j < win_width; j++)
	    for (k = 0; k < RGB3; k++)
		ibuffer[q++] = (unsigned char)
		    *(buffer + (pixelSize*((win_height-1-i)*win_width+j)+k));
    fwrite(ibuffer, sizeof(unsigned char), RGB3*win_width*win_height, fp);
    fclose(fp);
    free(ibuffer);

    printf("wrote file (%d x %d pixels, %d bytes)\n",
	   win_width, win_height, RGB3*win_width*win_height);
    return true;
}

// dump the screen buffer to a ppm file
bool sg_glDumpWindow(const char *filename, int win_width, int win_height) {
    GLubyte *buffer;
    bool result;

    buffer = (GLubyte *) malloc(win_width*win_height*RGBA);

    // read window contents from color buffer with glReadPixels
    glFinish();
    glReadPixels(0, 0, win_width, win_height,
		 GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    result = sg_glWritePPMFile( filename, buffer, win_width, win_height,
				GL_RGBA );
    free(buffer);

    return result;
}

<commit_msg>Nick WARNE: add file name to screenshot info line<commit_after>// screen-dump.cxx -- dump a copy of the opengl screen buffer to a file
//
// Contributed by Richard Kaszeta <bofh@me.umn.edu>, started October 1999.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// $Id$

#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#if defined(__CYGWIN__)  /* && !defined(USING_X) */
#define WIN32
#endif

#if defined(WIN32)  /* MINGW and MSC predefine WIN32 */
# include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#include <simgear/compiler.h>

#include SG_GL_H

#include "screen-dump.hxx"

#define RGB3 3			// 3 bytes of color info per pixel
#define RGBA 4			// 4 bytes of color+alpha info

bool sg_glWritePPMFile(const char *filename, GLubyte *buffer, int win_width, int win_height, int mode)
{
    int i, j, k, q;
    unsigned char *ibuffer;
    FILE *fp;
    int pixelSize = mode==GL_RGBA?4:3;

    ibuffer = (unsigned char *) malloc(win_width*win_height*RGB3);

    if ( (fp = fopen(filename, "wb")) == NULL ) {
	printf("Warning: cannot open %s\n", filename);
	return false;
    }

    fprintf(fp, "P6\n# CREATOR: glReadPixel()\n%d %d\n%d\n",
	    win_width, win_height, UCHAR_MAX);
    q = 0;
    for (i = 0; i < win_height; i++)
	for (j = 0; j < win_width; j++)
	    for (k = 0; k < RGB3; k++)
		ibuffer[q++] = (unsigned char)
		    *(buffer + (pixelSize*((win_height-1-i)*win_width+j)+k));
    fwrite(ibuffer, sizeof(unsigned char), RGB3*win_width*win_height, fp);
    fclose(fp);
    free(ibuffer);

    printf("wrote file '%s' (%d x %d pixels, %d bytes)\n",
	   filename, win_width, win_height, RGB3*win_width*win_height);
    return true;
}

// dump the screen buffer to a ppm file
bool sg_glDumpWindow(const char *filename, int win_width, int win_height) {
    GLubyte *buffer;
    bool result;

    buffer = (GLubyte *) malloc(win_width*win_height*RGBA);

    // read window contents from color buffer with glReadPixels
    glFinish();
    glReadPixels(0, 0, win_width, win_height,
		 GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    result = sg_glWritePPMFile( filename, buffer, win_width, win_height,
				GL_RGBA );
    free(buffer);

    return result;
}

<|endoftext|>


<commit_before>#include "TaskIcon.h"
#include <cmath>

const UINT icon_id = 100;
const UINT id_exit = 101;

static const wchar_t *const icon_label = L"Xbox Controller Battery Level";
static const wchar_t *const task_class = L"xinput-batlev_task-icon";
static const wchar_t icon_emoji[] = L"🎮";
static std::atomic<size_t> task_refcount = 0;

TaskIcon::TaskIcon()
    : icon_(128, 128, 2)
{
    RegClass();
    hwnd_ = CreateWindowExW(
        0, task_class, icon_label,
        0, 0, 0, 0, 0, nullptr,
        nullptr, hinstance, this
        );

    menu_ = CreatePopupMenu();
    if (!menu_) CheckLastError();

    MENUINFO menuinfo = { sizeof(menuinfo) };
    menuinfo.fMask = MIM_STYLE;
    menuinfo.dwStyle = MNS_AUTODISMISS;
    SetMenuInfo(menu_, &menuinfo);

    InsertMenuW(menu_, 0, MF_BYPOSITION, id_exit, L"Exit");

    CheckHR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(*dwrite_),
        (IUnknown **)&dwrite_)
        );

    CheckHR(dwrite_->CreateTextFormat(
        L"Segoe UI Emoji",
        nullptr,
        DWRITE_FONT_WEIGHT_NORMAL,
        DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL,
        42,
        L"en-US",
        &font_
        ));

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.uVersion = NOTIFYICON_VERSION_4;
    idata.uCallbackMessage = WM_TASKICON;
    idata.hIcon = MakeIcon(false);
    idata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    wcscpy_s(idata.szTip, icon_label);
    Shell_NotifyIconW(NIM_ADD, &idata);
}

TaskIcon::~TaskIcon()
{
    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    Shell_NotifyIconW(NIM_DELETE, &idata);

    if (menu_)
        DestroyMenu(menu_);
    if (hwnd_)
        DestroyWindow(hwnd_);
    UnregClass();
}

void TaskIcon::MessageLoop()
{
    // Recheck once every 5 seconds
    SetTimer(hwnd_, icon_id, 5 * 1000, nullptr);

    // Initial update
    UpdateIcon();

    MSG msg;
    while (GetMessageW(&msg, hwnd_, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);

        if (stop_)
        {
            break;
        }
    }
}

void TaskIcon::Stop()
{
    stop_ = true;
}

LRESULT TaskIcon::StaticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    if (msg == WM_CREATE)
    {
        auto cdata = (CREATESTRUCTW *)lparam;
        SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)cdata->lpCreateParams);
        return 0;
    }

    auto task_ptr = (TaskIcon *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
    if (task_ptr)
    {
        return task_ptr->WndProc(msg, wparam, lparam);
    }
    return DefWindowProcW(hwnd, msg, wparam, lparam);
}

LRESULT TaskIcon::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_TIMER:
        {
            UpdateIcon();
            return 0;
        }
        case WM_TASKICON:
        {
            if (LOWORD(lparam) == WM_RBUTTONUP)
            {
                POINT cpos;
                GetCursorPos(&cpos);

                SetForegroundWindow(hwnd_);
                TrackPopupMenuEx(
                    menu_,
                    TPM_BOTTOMALIGN | TPM_LEFTALIGN,
                    cpos.x, cpos.y,
                    hwnd_,
                    nullptr
                    );
            }
            return 0;
        }
        case WM_COMMAND:
        {
            if (wparam == id_exit)
            {
                stop_ = true;
            }
            return 0;
        }
    }

    return DefWindowProcW(hwnd_, msg, wparam, lparam);
}

void TaskIcon::UpdateIcon()
{
    XINPUT_BATTERY_INFORMATION info;
    if (XInputGetBatteryInformation(0, BATTERY_DEVTYPE_GAMEPAD, &info) != ERROR_SUCCESS)
    {
        info.BatteryType = BATTERY_TYPE_DISCONNECTED;
    }

    switch (info.BatteryType)
    {
        case BATTERY_TYPE_WIRED:
            batlevel_ = 1;
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_ALKALINE:
        case BATTERY_TYPE_NIMH:
            switch (info.BatteryLevel)
            {
                case BATTERY_LEVEL_EMPTY:
                    batlevel_ = 0;
                    break;
                case BATTERY_LEVEL_LOW:
                    batlevel_ = 0.3f;
                    break;
                case BATTERY_LEVEL_MEDIUM:
                    batlevel_ = 0.6f;
                    break;
                case BATTERY_LEVEL_FULL:
                    batlevel_ = 1;
                    break;
            }
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_UNKNOWN:
            batlevel_ = 0;
            nocontroller_ = false;
            unknown_ = true;

        case BATTERY_TYPE_DISCONNECTED:
        default:
            batlevel_ = 0;
            nocontroller_ = true;
            unknown_ = false;
            break;
    }

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.hIcon = MakeIcon(true);
    idata.uFlags = NIF_ICON;
    Shell_NotifyIconW(NIM_MODIFY, &idata);
}

HICON TaskIcon::MakeIcon(bool redraw)
{
    auto icon = icon_.GetIcon();
    if (!icon || redraw)
    {
        ComPtr<ID2D1RenderTarget> target;
        icon_.Lock(&target);

        target->BeginDraw();
        target->Clear();

        auto size = target->GetSize();
        auto batrect = D2D1::RectF(5, size.height / 2 - 16, size.width - 8, size.height / 2 + 16);
        auto posend = D2D1::RectF(size.width - 8, size.height / 2 - 8, size.width, size.height / 2 + 8);
        auto fillrect = D2D1::RectF(batrect.left + 5, batrect.top + 5, batrect.right - 5, batrect.bottom - 5);
        fillrect.right = (1 - batlevel_) * fillrect.left + batlevel_ * fillrect.right;

        ComPtr<ID2D1SolidColorBrush> brush;
        CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brush));

        target->DrawRectangle(batrect, brush, 5);
        target->FillRectangle(posend, brush);
        target->FillRectangle(fillrect, brush);

        target->DrawText(
            icon_emoji,
            ARRAYSIZE(icon_emoji) - 1,
            font_,
            D2D1::RectF(
                size.width / 3,
                size.height / 4.f,
                size.width,
                size.height
                ),
            brush,
            D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
            );

        if (nocontroller_)
        {
            ComPtr<ID2D1SolidColorBrush> errbrush;
            CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &errbrush));
            auto center = D2D1::Point2F(16, size.height / 2 + 16);
            auto radiusx = std::sqrtf(2) * 7;
            auto tl = D2D1::Point2F(center.x - radiusx, center.y - radiusx);
            auto tr = D2D1::Point2F(center.x + radiusx, center.y - radiusx);
            auto bl = D2D1::Point2F(center.x - radiusx, center.y + radiusx);
            auto br = D2D1::Point2F(center.x + radiusx, center.y + radiusx);

            target->FillEllipse(
                D2D1::Ellipse(
                    center,
                    16, 16
                    ),
                errbrush
                );
            target->DrawLine(tl, br, brush, 5);
            target->DrawLine(tr, bl, brush, 5);
        }

        CheckHR(target->EndDraw());

        icon_.Unlock();
        icon = icon_.GetIcon();
    }
    return icon;
}

void TaskIcon::RegClass()
{
    if (task_refcount++)
        return;

    WNDCLASSEXW wndc = { sizeof(wndc) };
    wndc.cbWndExtra = sizeof(TaskIcon *);
    wndc.hInstance = hinstance;
    wndc.lpfnWndProc = TaskIcon::StaticWndProc;
    wndc.lpszClassName = task_class;
    RegisterClassExW(&wndc);
}

void TaskIcon::UnregClass()
{
    if (--task_refcount)
        return;

    UnregisterClassW(task_class, hinstance);
}
<commit_msg>Adds tooltip<commit_after>#include "TaskIcon.h"
#include <cmath>

const UINT icon_id = 100;
const UINT id_exit = 101;

static const wchar_t *const icon_label = L"Xbox Controller Battery Level";
static const wchar_t *const task_class = L"xinput-batlev_task-icon";
static const wchar_t icon_emoji[] = L"🎮";
static std::atomic<size_t> task_refcount = 0;

TaskIcon::TaskIcon()
    : icon_(128, 128, 2)
{
    RegClass();
    hwnd_ = CreateWindowExW(
        0, task_class, icon_label,
        0, 0, 0, 0, 0, nullptr,
        nullptr, hinstance, this
        );

    menu_ = CreatePopupMenu();
    if (!menu_) CheckLastError();

    MENUINFO menuinfo = { sizeof(menuinfo) };
    menuinfo.fMask = MIM_STYLE;
    menuinfo.dwStyle = MNS_AUTODISMISS;
    SetMenuInfo(menu_, &menuinfo);

    InsertMenuW(menu_, 0, MF_BYPOSITION, id_exit, L"Exit");

    CheckHR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(*dwrite_),
        (IUnknown **)&dwrite_)
        );

    CheckHR(dwrite_->CreateTextFormat(
        L"Segoe UI Emoji",
        nullptr,
        DWRITE_FONT_WEIGHT_NORMAL,
        DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL,
        42,
        L"en-US",
        &font_
        ));

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.uVersion = NOTIFYICON_VERSION_4;
    idata.uCallbackMessage = WM_TASKICON;
    idata.hIcon = MakeIcon(false);
    idata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    wcscpy_s(idata.szTip, icon_label);
    Shell_NotifyIconW(NIM_ADD, &idata);
}

TaskIcon::~TaskIcon()
{
    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    Shell_NotifyIconW(NIM_DELETE, &idata);

    if (menu_)
        DestroyMenu(menu_);
    if (hwnd_)
        DestroyWindow(hwnd_);
    UnregClass();
}

void TaskIcon::MessageLoop()
{
    // Recheck once every 5 seconds
    SetTimer(hwnd_, icon_id, 5 * 1000, nullptr);

    // Initial update
    UpdateIcon();

    MSG msg;
    while (GetMessageW(&msg, hwnd_, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);

        if (stop_)
        {
            break;
        }
    }
}

void TaskIcon::Stop()
{
    stop_ = true;
}

LRESULT TaskIcon::StaticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    if (msg == WM_CREATE)
    {
        auto cdata = (CREATESTRUCTW *)lparam;
        SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)cdata->lpCreateParams);
        return 0;
    }

    auto task_ptr = (TaskIcon *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
    if (task_ptr)
    {
        return task_ptr->WndProc(msg, wparam, lparam);
    }
    return DefWindowProcW(hwnd, msg, wparam, lparam);
}

LRESULT TaskIcon::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_TIMER:
        {
            UpdateIcon();
            return 0;
        }
        case WM_TASKICON:
        {
            if (LOWORD(lparam) == WM_RBUTTONUP)
            {
                POINT cpos;
                GetCursorPos(&cpos);

                SetForegroundWindow(hwnd_);
                TrackPopupMenuEx(
                    menu_,
                    TPM_BOTTOMALIGN | TPM_LEFTALIGN,
                    cpos.x, cpos.y,
                    hwnd_,
                    nullptr
                    );
            }
            return 0;
        }
        case WM_COMMAND:
        {
            if (wparam == id_exit)
            {
                stop_ = true;
            }
            return 0;
        }
    }

    return DefWindowProcW(hwnd_, msg, wparam, lparam);
}

void TaskIcon::UpdateIcon()
{
    XINPUT_BATTERY_INFORMATION info;
    if (XInputGetBatteryInformation(0, BATTERY_DEVTYPE_GAMEPAD, &info) != ERROR_SUCCESS)
    {
        info.BatteryType = BATTERY_TYPE_DISCONNECTED;
    }

    wchar_t *tip = L"";
    switch (info.BatteryType)
    {
        case BATTERY_TYPE_WIRED:
            batlevel_ = 1;
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_ALKALINE:
        case BATTERY_TYPE_NIMH:
            switch (info.BatteryLevel)
            {
                case BATTERY_LEVEL_EMPTY:
                    batlevel_ = 0;
                    tip = L"Controller battery is empty";
                    break;
                case BATTERY_LEVEL_LOW:
                    batlevel_ = 0.3f;
                    tip = L"Controller battery is getting low";
                    break;
                case BATTERY_LEVEL_MEDIUM:
                    batlevel_ = 0.6f;
                    tip = L"Controller battery is OK";
                    break;
                case BATTERY_LEVEL_FULL:
                    batlevel_ = 1;
                    tip = L"Controller battery is full";
                    break;
            }
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_UNKNOWN:
            batlevel_ = 0;
            nocontroller_ = false;
            unknown_ = true;
            tip = L"Battery level couldn't be read";

        case BATTERY_TYPE_DISCONNECTED:
        default:
            batlevel_ = 0;
            nocontroller_ = true;
            unknown_ = false;
            tip = L"No controller connected";
            break;
    }

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.hIcon = MakeIcon(true);
    idata.uFlags = NIF_ICON | NIF_TIP;
    swprintf_s(idata.szTip, tip);

    Shell_NotifyIconW(NIM_MODIFY, &idata);
}

HICON TaskIcon::MakeIcon(bool redraw)
{
    auto icon = icon_.GetIcon();
    if (!icon || redraw)
    {
        ComPtr<ID2D1RenderTarget> target;
        icon_.Lock(&target);

        target->BeginDraw();
        target->Clear();

        auto size = target->GetSize();
        auto batrect = D2D1::RectF(5, size.height / 2 - 16, size.width - 8, size.height / 2 + 16);
        auto posend = D2D1::RectF(size.width - 8, size.height / 2 - 8, size.width, size.height / 2 + 8);
        auto fillrect = D2D1::RectF(batrect.left + 5, batrect.top + 5, batrect.right - 5, batrect.bottom - 5);
        fillrect.right = (1 - batlevel_) * fillrect.left + batlevel_ * fillrect.right;

        ComPtr<ID2D1SolidColorBrush> brush;
        CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brush));

        target->DrawRectangle(batrect, brush, 5);
        target->FillRectangle(posend, brush);
        target->FillRectangle(fillrect, brush);

        target->DrawText(
            icon_emoji,
            ARRAYSIZE(icon_emoji) - 1,
            font_,
            D2D1::RectF(
                size.width / 3,
                size.height / 4.f,
                size.width,
                size.height
                ),
            brush,
            D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
            );

        if (nocontroller_)
        {
            ComPtr<ID2D1SolidColorBrush> errbrush;
            CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &errbrush));
            auto center = D2D1::Point2F(16, size.height / 2 + 16);
            auto radiusx = std::sqrtf(2) * 7;
            auto tl = D2D1::Point2F(center.x - radiusx, center.y - radiusx);
            auto tr = D2D1::Point2F(center.x + radiusx, center.y - radiusx);
            auto bl = D2D1::Point2F(center.x - radiusx, center.y + radiusx);
            auto br = D2D1::Point2F(center.x + radiusx, center.y + radiusx);

            target->FillEllipse(
                D2D1::Ellipse(
                    center,
                    16, 16
                    ),
                errbrush
                );
            target->DrawLine(tl, br, brush, 5);
            target->DrawLine(tr, bl, brush, 5);
        }

        CheckHR(target->EndDraw());

        icon_.Unlock();
        icon = icon_.GetIcon();
    }
    return icon;
}

void TaskIcon::RegClass()
{
    if (task_refcount++)
        return;

    WNDCLASSEXW wndc = { sizeof(wndc) };
    wndc.cbWndExtra = sizeof(TaskIcon *);
    wndc.hInstance = hinstance;
    wndc.lpfnWndProc = TaskIcon::StaticWndProc;
    wndc.lpszClassName = task_class;
    RegisterClassExW(&wndc);
}

void TaskIcon::UnregClass()
{
    if (--task_refcount)
        return;

    UnregisterClassW(task_class, hinstance);
}
<|endoftext|>


<commit_before>#include "Corners.h"
#include "TilesetGenerator.h"
#include "TileGenerator.h"
#include <functional>
#include <stdexcept>
#include <sstream>
#include <boost/filesystem.hpp>
#include "common.h"

namespace tilegen
{

TilesetGenerator::TilesetGenerator(const Options& options,
                                   std::unique_ptr<partition::TilePartitionerBase> tile_partitioner) :
    options(options),
    mTilePartitioner(std::move(tile_partitioner))
{
    for (auto& terrain : options.terrains)
    {
        images.addTerrain(terrain.first, terrain.second.fileName, options.filename,
                          terrain.second.offsetX, terrain.second.offsetY, options.tileFormat.resolution);
    }
}

void TilesetGenerator::generate(std::function<void(const sf::Texture&, std::string)> callback)
{
    mo.setResolution(options.tileFormat.resolution);
    boost::filesystem::path p(options.relativeOutputDirectory);
    for (const auto& clique : options.cliques)
    {
        CornersGenerator cp(clique, static_cast<size_t>(CORNERS));
        std::pair<size_t, size_t> tileset_resolution = cp.size_2d(options.tileFormat.resolution);
        size_t res_x = tileset_resolution.first;
        size_t res_y = tileset_resolution.second;
        std::unique_ptr<sf::RenderTexture> output{getBlankImage(res_x, res_y)};

        auto filename = getOutputImageFilename(clique);

        // MetaOutput.addTileset, addTile should use this version of filename;
        // relative to output dir, not options dir!
        mo.addTileset(clique, filename, res_x, res_y);
        generateClique(clique, *output, filename);
        output->display();

        p.append(filename);
        callback(output->getTexture(), p.string());
        p.remove_filename();
    }
}

void TilesetGenerator::generateClique(const Options::Clique& clique, sf::RenderTexture& image, std::string filename)
{
    CornersGenerator cp(clique, static_cast<size_t>(CORNERS));
    for (auto it = cp.cbegin(); it != cp.cend(); ++it)
    {
        const auto& corner_terrains = *it;
        std::pair<size_t, size_t> tp = it.coordinates_2d();
        TileGenerator::generate(image, tp.first, tp.second, corner_terrains,
                                images, options, *mTilePartitioner.get());
        mo.addTile(corner_terrains, filename,
                   tp.first*options.tileFormat.resolution,
                   tp.second*options.tileFormat.resolution);
    }
}

std::string TilesetGenerator::getOutputImageFilename(const Options::Clique& clique) const
{
    std::stringstream ss;
    for (auto terrain : clique)
    {
        ss << terrain << ".";
    }
    ss << options.tileFormat.fileType;
    return ss.str();
}

std::unique_ptr<sf::RenderTexture> TilesetGenerator::getBlankImage(size_t res_x, size_t res_y) const
{
    std::unique_ptr<sf::RenderTexture> output{std::make_unique<sf::RenderTexture>()};
    output->create(res_x, res_y);
    output->clear(sf::Color(0,0,0,255));
    return output;
}

} // namespace tilegen
<commit_msg>Use clearer names in TilesetGenerator<commit_after>#include "Corners.h"
#include "TilesetGenerator.h"
#include "TileGenerator.h"
#include <functional>
#include <stdexcept>
#include <sstream>
#include <boost/filesystem.hpp>
#include "common.h"

namespace tilegen
{

TilesetGenerator::TilesetGenerator(const Options& options,
                                   std::unique_ptr<partition::TilePartitionerBase> tile_partitioner) :
    options(options),
    mTilePartitioner(std::move(tile_partitioner))
{
    for (auto& terrain : options.terrains)
    {
        images.addTerrain(terrain.first, terrain.second.fileName, options.filename,
                          terrain.second.offsetX, terrain.second.offsetY, options.tileFormat.resolution);
    }
}

void TilesetGenerator::generate(std::function<void(const sf::Texture&, std::string)> callback)
{
    mo.setResolution(options.tileFormat.resolution);
    boost::filesystem::path p(options.relativeOutputDirectory);
    for (const auto& clique : options.cliques)
    {
        CornersGenerator corners_generator(clique, static_cast<size_t>(CORNERS));
        std::pair<size_t, size_t> tileset_resolution = corners_generator.size_2d(options.tileFormat.resolution);
        size_t res_x = tileset_resolution.first;
        size_t res_y = tileset_resolution.second;
        std::unique_ptr<sf::RenderTexture> output{getBlankImage(res_x, res_y)};

        auto filename = getOutputImageFilename(clique);

        // MetaOutput.addTileset, addTile should use this version of filename;
        // relative to output dir, not options dir!
        mo.addTileset(clique, filename, res_x, res_y);
        generateClique(clique, *output, filename);
        output->display();

        p.append(filename);
        callback(output->getTexture(), p.string());
        p.remove_filename();
    }
}

void TilesetGenerator::generateClique(const Options::Clique& clique, sf::RenderTexture& image, std::string filename)
{
    CornersGenerator cp(clique, static_cast<size_t>(CORNERS));
    for (auto it = cp.cbegin(); it != cp.cend(); ++it)
    {
        const auto& corner_terrains = *it;
        std::pair<size_t, size_t> tile_position = it.coordinates_2d();
        TileGenerator::generate(image, tile_position.first, tile_position.second, corner_terrains,
                                images, options, *mTilePartitioner.get());
        mo.addTile(corner_terrains, filename,
                   tile_position.first*options.tileFormat.resolution,
                   tile_position.second*options.tileFormat.resolution);
    }
}

std::string TilesetGenerator::getOutputImageFilename(const Options::Clique& clique) const
{
    std::stringstream ss;
    for (auto terrain : clique)
    {
        ss << terrain << ".";
    }
    ss << options.tileFormat.fileType;
    return ss.str();
}

std::unique_ptr<sf::RenderTexture> TilesetGenerator::getBlankImage(size_t res_x, size_t res_y) const
{
    std::unique_ptr<sf::RenderTexture> output{std::make_unique<sf::RenderTexture>()};
    output->create(res_x, res_y);
    output->clear(sf::Color(0,0,0,255));
    return output;
}

} // namespace tilegen
<|endoftext|>