Blob Blame Raw


// TnzCore includes
#include "tgl.h"

// TnzExt includes
#include "ext/ttexturesstorage.h"
#include "ext/plasticdeformerstorage.h"

// tcg includes
#include "tcg/tcg_iterator_ops.h"

#include "ext/meshutils.h"

#include <array>

//********************************************************************************************
//    Templated drawing functions
//********************************************************************************************

namespace
{

struct NoColorFunction {
	void faceColor(int f, int m) {}
	void edgeColor(int e, int m) {}
	void vertexColor(int v, int m) {}
};

//-------------------------------------------------------------------------------

template <typename VerticesContainer, typename PointType, typename ColorFunction>
inline void tglDrawEdges(const TTextureMesh &mesh, const VerticesContainer &vertices,
						 ColorFunction colorFunction)
{
	// Draw the mesh wireframe
	glBegin(GL_LINES);

	TTextureMesh::edges_container::const_iterator et, eEnd = mesh.edges().end();

	for (et = mesh.edges().begin(); et != eEnd; ++et) {
		const TTextureMesh::edge_type &ed = *et;

		int v0 = ed.vertex(0), v1 = ed.vertex(1);

		const PointType &p0 = vertices[v0];
		const PointType &p1 = vertices[v1];

		colorFunction.edgeColor(et.index(), -1);

		colorFunction.vertexColor(v0, -1);
		glVertex2d(tcg::point_traits<PointType>::x(p0), tcg::point_traits<PointType>::y(p0));

		colorFunction.vertexColor(v1, -1);
		glVertex2d(tcg::point_traits<PointType>::x(p1), tcg::point_traits<PointType>::y(p1));
	}

	glEnd();
}

//-------------------------------------------------------------------------------

template <typename ColorFunction>
inline void tglDrawFaces(const TMeshImage &meshImage, ColorFunction colorFunction)
{
	glBegin(GL_TRIANGLES);

	int i = 0;
	for (auto const& mesh : meshImage.meshes()) {
		tcg::list<TTextureVertex> const & vertices = mesh->vertices();
		int const m = i++;

		// Draw the mesh wireframe
		int j = 0;
		for (auto const& ft : mesh->faces()) {
			int const index = j++;

			int v0, v1, v2;
			mesh->faceVertices(index, v0, v1, v2);

			TTextureVertex const& p0 = vertices[v0];
			TTextureVertex const& p1 = vertices[v1];
			TTextureVertex const& p2 = vertices[v2];

			colorFunction.faceColor(index, m);

			colorFunction.vertexColor(v0, m), glVertex2d(p0.P().x, p0.P().y);
			colorFunction.vertexColor(v1, m), glVertex2d(p1.P().x, p1.P().y);
			colorFunction.vertexColor(v2, m), glVertex2d(p2.P().x, p2.P().y);
		}
	}

	glEnd();
}

//-------------------------------------------------------------------------------

template <typename ColorFunction>
inline void tglDrawFaces(const TMeshImage &meshImage, const PlasticDeformerDataGroup *group,
						 ColorFunction colorFunction)
{
	glBegin(GL_TRIANGLES);

	const std::vector<TTextureMeshP> &meshes = meshImage.meshes();

	// Draw faces according to the group's sorted faces list
	// Draw each face individually. Change tile and mesh data only if they change
	int m = -1;
	for (auto const& sft : group->m_sortedFaces) {
		TTextureMesh const* mesh = nullptr;
		double const* dstCoords = nullptr;

		if (m != sft.second) {
			m = sft.second;
			mesh = meshes[m].getPointer();
			dstCoords = group->m_datas[m].m_output.get();
		}

		int v0, v1, v2;
		mesh->faceVertices(sft.first, v0, v1, v2);

		double const * const d0 = dstCoords + (v0 << 1);
		double const * const d1 = dstCoords + (v1 << 1);
		double const * const d2 = dstCoords + (v2 << 1);

		colorFunction.faceColor(sft.first, m);

		colorFunction.vertexColor(v0, m), glVertex2d(d0[0], d0[1]);
		colorFunction.vertexColor(v1, m), glVertex2d(d1[0], d1[1]);
		colorFunction.vertexColor(v2, m), glVertex2d(d2[0], d2[1]);
	}

	glEnd();
}

} // namespace

//********************************************************************************************
//    Mesh Image Utility  functions implementation
//********************************************************************************************

void transform(const TMeshImageP &meshImage, const TAffine &aff)
{
	const std::vector<TTextureMeshP> &meshes = meshImage->meshes();

	int m, mCount = meshes.size();
	for (m = 0; m != mCount; ++m) {
		TTextureMesh &mesh = *meshes[m];

		tcg::list<TTextureMesh::vertex_type> &vertices = mesh.vertices();

		tcg::list<TTextureMesh::vertex_type>::iterator vt, vEnd(vertices.end());
		for (vt = vertices.begin(); vt != vEnd; ++vt)
			vt->P() = aff * vt->P();
	}
}

//-------------------------------------------------------------------------------

void tglDrawEdges(const TMeshImage &mi, const PlasticDeformerDataGroup *group)
{
	const std::vector<TTextureMeshP> &meshes = mi.meshes();

	int m, mCount = meshes.size();
	if (group) {
		for (m = 0; m != mCount; ++m)
			tglDrawEdges<const TPointD *, TPointD, NoColorFunction>(
				*meshes[m], (const TPointD *)group->m_datas[m].m_output.get(), NoColorFunction());
	} else {
		for (m = 0; m != mCount; ++m) {
			const TTextureMesh &mesh = *meshes[m];

			tglDrawEdges<tcg::list<TTextureMesh::vertex_type>, TTextureVertex, NoColorFunction>(
				mesh, mesh.vertices(), NoColorFunction());
		}
	}
}

//-------------------------------------------------------------------------------

void tglDrawFaces(const TMeshImage &image, const PlasticDeformerDataGroup *group)
{
	if (group)
		tglDrawFaces(image, group, NoColorFunction());
	else
		tglDrawFaces(image, NoColorFunction());
}

//********************************************************************************************
//    Colored drawing functions
//********************************************************************************************

namespace
{

struct LinearColorFunction {
	typedef double (*ValueFunc)(const LinearColorFunction *cf, int m, int primitive);

public:
	const TMeshImage &m_meshImg;
	const PlasticDeformerDataGroup *m_group;

	double m_min, m_max;
	double *m_cMin, *m_cMax;

	double m_dt;
	bool m_degenerate;

	ValueFunc m_func;

public:
	LinearColorFunction(const TMeshImage &meshImg,
						const PlasticDeformerDataGroup *group,
						double min, double max,
						double *cMin, double *cMax,
						ValueFunc func)
		: m_meshImg(meshImg), m_group(group), m_min(min), m_max(max), m_cMin(cMin), m_cMax(cMax), m_dt(max - min), m_degenerate(m_dt < 1e-4), m_func(func)
	{
	}

	void operator()(int primitive, int m)
	{
		if (m_degenerate) {
			glColor4d(0.5 * (m_cMin[0] + m_cMax[0]),
					  0.5 * (m_cMin[1] + m_cMax[1]),
					  0.5 * (m_cMin[2] + m_cMax[2]),
					  0.5 * (m_cMin[3] + m_cMax[3]));
			return;
		}

		double val = m_func(this, m, primitive);
		double t = (val - m_min) / m_dt, one_t = (m_max - val) / m_dt;

		glColor4d(one_t * m_cMin[0] + t * m_cMax[0],
				  one_t * m_cMin[1] + t * m_cMax[1],
				  one_t * m_cMin[2] + t * m_cMax[2],
				  one_t * m_cMin[3] + t * m_cMax[3]);
	}
};

//-------------------------------------------------------------------------------

struct LinearVertexColorFunction : public LinearColorFunction, public NoColorFunction {
	LinearVertexColorFunction(const TMeshImage &meshImg,
							  const PlasticDeformerDataGroup *group,
							  double min, double max,
							  double *cMin, double *cMax,
							  ValueFunc func)
		: LinearColorFunction(meshImg, group, min, max, cMin, cMax, func) {}

	void vertexColor(int v, int m) { operator()(v, m); }
};

//-------------------------------------------------------------------------------

struct LinearFaceColorFunction : public LinearColorFunction, public NoColorFunction {
	LinearFaceColorFunction(const TMeshImage &meshImg,
							const PlasticDeformerDataGroup *group,
							double min, double max,
							double *cMin, double *cMax,
							ValueFunc func)
		: LinearColorFunction(meshImg, group, min, max, cMin, cMax, func) {}

	void faceColor(int v, int m) { operator()(v, m); }
};

} // namespace

//===============================================================================

void tglDrawSO(const TMeshImage &image, double minColor[4], double maxColor[4],
			   const PlasticDeformerDataGroup *group, bool deformedDomain)
{
	struct locals {
		static double returnSO(const LinearColorFunction *cf, int m, int f)
		{
			return cf->m_group->m_datas[m].m_so[f];
		}
	};

	double min = 0.0, max = 0.0;
	if (group)
		min = group->m_soMin, max = group->m_soMax;

	LinearFaceColorFunction colorFunction(image, group, min, max, minColor, maxColor, locals::returnSO);

	if (group && deformedDomain)
		tglDrawFaces(image, group, colorFunction);
	else
		tglDrawFaces(image, colorFunction);
}

//-------------------------------------------------------------------------------

void tglDrawRigidity(const TMeshImage &image, double minColor[4], double maxColor[4],
					 const PlasticDeformerDataGroup *group, bool deformedDomain)
{
	struct locals {
		static double returnRigidity(const LinearColorFunction *cf, int m, int v)
		{
			return cf->m_meshImg.meshes()[m]->vertex(v).P().rigidity;
		}
	};

	LinearVertexColorFunction colorFunction(image, group,
											1.0, 1e4, minColor, maxColor, locals::returnRigidity);

	if (group && deformedDomain)
		tglDrawFaces(image, group, colorFunction);
	else
		tglDrawFaces(image, colorFunction);
}

//***********************************************************************************************
//    Texturized drawing  implementation
//***********************************************************************************************

void tglDraw(const TMeshImage &meshImage,
			 const DrawableTextureData &texData, const TAffine &meshToTexAff,
			 const PlasticDeformerDataGroup &group)
{
#ifdef _WIN32
	static PFNGLBLENDFUNCSEPARATEPROC const glBlendFuncSeparate =
		reinterpret_cast<PFNGLBLENDFUNCSEPARATEPROC>(::wglGetProcAddress("glBlendFuncSeparate"));
#endif

	// Prepare OpenGL
	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_HINT_BIT); // Preserve original status bits

	glEnable(GL_BLEND);
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

	glEnable(GL_LINE_SMOOTH);
	glLineWidth(1.0f);

	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

	glBlendFuncSeparate(
		GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
		GL_ONE      , GL_ONE_MINUS_SRC_ALPHA);

	auto const& tiles = texData.m_textureData->m_tileDatas;

	// Prepare each tile's affine
	std::unique_ptr<TAffine[]> tileAff(new TAffine[tiles.size()]);
	{
		std::size_t i = 0;
		for (auto const& tile : tiles) {
			TRectD const &rect = tile.m_tileGeometry;
			TScale const scale(
				1.0 / (rect.x1 - rect.x0),
				1.0 / (rect.y1 - rect.y0));
			TTranslation const translate(-rect.x0, -rect.y0);
			tileAff[i] = scale * translate * meshToTexAff;
		}
	}

	// Draw each face individually, according to the group's sorted faces list.
	// Change tile and mesh data only if they change - improves performance
	int m = -1;
	TTextureMesh const * mesh = nullptr;
	double const * dstCoords = nullptr;
	GLuint texId = -1;
	for (auto const& sft : group.m_sortedFaces) {
		if (m != sft.second) {
			// Change mesh if different from current
			m = sft.second;
			mesh = meshImage.meshes()[m].getPointer();
			dstCoords = group.m_datas[m].m_output.get();
		}

		// Draw each face
		TTextureMesh::face_type const& fc = mesh->face(sft.first);
		TTextureMesh::edge_type const& ed0 = mesh->edge(fc.edge(0));
		TTextureMesh::edge_type const& ed1 = mesh->edge(fc.edge(1));
		TTextureMesh::edge_type const& ed2 = mesh->edge(fc.edge(2));

		int const v0 = ed0.vertex(0);
		int const v1 = ed0.vertex(1);
		int const v2 = ed1.vertex((ed1.vertex(0) == v0) | (ed1.vertex(0) == v1));

		// Edge X's Other Vertex Index (see below)
		int const f = (ed1.vertex(0) == v1) | (ed1.vertex(1) == v1); // ed1 and ed2 will refer to vertexes
		int const g = 1 - f;									                       // with index 2 and these.

		TPointD const& p0 = mesh->vertex(v0).P();
		TPointD const& p1 = mesh->vertex(v1).P();
		TPointD const& p2 = mesh->vertex(v2).P();

		// Draw face against tile
		std::size_t i = 0;
		for (auto const& tileData : tiles) {
			// Map each face vertex to tile coordinates
			std::array<TPointD, 3> const s = {
				tileAff[i] * p0,
				tileAff[i] * p1,
				tileAff[i] * p2,
			};
			++i;

			// Test the face bbox - tile intersection
			if ( (tmin(s[0].x, s[1].x, s[2].x) > 1.0)
				|| (tmin(s[0].y, s[1].y, s[2].y) > 1.0)
				|| (tmax(s[0].x, s[1].x, s[2].x) < 0.0)
				|| (tmax(s[0].y, s[1].y, s[2].y) < 0.0)) {
				continue;
			}

			// If the tile has changed, interrupt the glBegin/glEnd block and bind the
			// OpenGL texture corresponding to the new tile
			if (texId != tileData.m_textureId) {
				texId = tileData.m_textureId;

				// This must be OUTSIDE a glBegin/glEnd block
				glBindTexture(GL_TEXTURE_2D, texId);
			}

			std::array<double const *, 3> const d = {
				dstCoords + (v0 << 1),
				dstCoords + (v1 << 1),
				dstCoords + (v2 << 1),
			};

			// First, draw antialiased face edges on the mesh border.
			glBegin(GL_LINES);
			{
				if (ed0.facesCount() < 2) {
					glTexCoord2d(s[0].x, s[0].y), glVertex2d(d[0][0], d[0][1]);
					glTexCoord2d(s[1].x, s[1].y), glVertex2d(d[1][0], d[1][1]);
				}

				if (ed1.facesCount() < 2) {
					glTexCoord2d(s[f].x, s[f].y), glVertex2d(d[f][0], d[f][1]);
					glTexCoord2d(s[2].x, s[2].y), glVertex2d(d[2][0], d[2][1]);
				}

				if (ed2.facesCount() < 2) {
					glTexCoord2d(s[g].x, s[g].y), glVertex2d(d[g][0], d[g][1]);
					glTexCoord2d(s[2].x, s[2].y), glVertex2d(d[2][0], d[2][1]);
				}
			}
			glEnd();

			// Finally, draw the face
			glBegin(GL_TRIANGLES);
			{
				glTexCoord2d(s[0].x, s[0].y), glVertex2d(d[0][0], d[0][1]);
				glTexCoord2d(s[1].x, s[1].y), glVertex2d(d[1][0], d[1][1]);
				glTexCoord2d(s[2].x, s[2].y), glVertex2d(d[2][0], d[2][1]);
			}
			glEnd();
		}
	}

	glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture

	glPopAttrib();
}