// 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"
//********************************************************************************************
// 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 m, mCount = meshImage.meshes().size();
for (m = 0; m != mCount; ++m) {
const TTextureMesh &mesh = *meshImage.meshes()[m];
const tcg::list<TTextureVertex> &vertices = mesh.vertices();
// Draw the mesh wireframe
TTextureMesh::faces_container::const_iterator ft, fEnd = mesh.faces().end();
for (ft = mesh.faces().begin(); ft != fEnd; ++ft) {
int v0, v1, v2;
mesh.faceVertices(ft.index(), v0, v1, v2);
const TTextureVertex &p0 = vertices[v0];
const TTextureVertex &p1 = vertices[v1];
const TTextureVertex &p2 = vertices[v2];
colorFunction.faceColor(ft.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);
// Draw faces according to the group's sorted faces list
typedef std::vector<std::pair<int, int>> SortedFacesVector;
const SortedFacesVector &sortedFaces = group->m_sortedFaces;
const std::vector<TTextureMeshP> &meshes = meshImage.meshes();
int m = -1;
const TTextureMesh *mesh;
const double *dstCoords;
int v0, v1, v2;
// Draw each face individually. Change tile and mesh data only if they change
SortedFacesVector::const_iterator sft, sfEnd(sortedFaces.end());
for (sft = sortedFaces.begin(); sft != sfEnd; ++sft) {
int f = sft->first, m_ = sft->second;
if (m != m_) {
m = m_;
mesh = meshes[m].getPointer();
dstCoords = group->m_datas[m].m_output;
}
mesh->faceVertices(f, v0, v1, v2);
const double *d0 = dstCoords + (v0 << 1), *d1 = dstCoords + (v1 << 1), *d2 = dstCoords + (v2 << 1);
colorFunction.faceColor(f, m);
colorFunction.vertexColor(v0, m), glVertex2d(*d0, *(d0 + 1));
colorFunction.vertexColor(v1, m), glVertex2d(*d1, *(d1 + 1));
colorFunction.vertexColor(v2, m), glVertex2d(*d2, *(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, 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)
{
typedef MeshTexturizer::TextureData::TileData TileData;
// Prepare OpenGL
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_HINT_BIT); // Preserve original status bits
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
// Prepare variables
const std::vector<TTextureMeshP> &meshes = meshImage.meshes();
const TTextureMesh *mesh;
typedef std::vector<std::pair<int, int>> SortedFacesVector;
const SortedFacesVector &sortedFaces = group.m_sortedFaces;
const MeshTexturizer::TextureData *td = texData.m_textureData;
int t, tCount = td->m_tileDatas.size();
GLuint texId = -1;
int m = -1;
const double *dstCoords;
int v0, v1, v2;
int e1ovi, e2ovi; // Edge X's Other Vertex Index (see below)
// Prepare each tile's affine
std::vector<TAffine> tileAff(tCount);
for (t = 0; t != tCount; ++t) {
const TileData &tileData = td->m_tileDatas[t];
const TRectD &tileRect = tileData.m_tileGeometry;
tileAff[t] = TScale(1.0 / (tileRect.x1 - tileRect.x0), 1.0 / (tileRect.y1 - tileRect.y0)) *
TTranslation(-tileRect.x0, -tileRect.y0) *
meshToTexAff;
}
// Draw each face individually, according to the group's sorted faces list.
// Change tile and mesh data only if they change - improves performance
SortedFacesVector::const_iterator sft, sfEnd(sortedFaces.end());
for (sft = sortedFaces.begin(); sft != sfEnd; ++sft) {
int f = sft->first, m_ = sft->second;
if (m != m_) {
// Change mesh if different from current
m = m_;
mesh = meshes[m].getPointer();
dstCoords = group.m_datas[m].m_output;
}
// Draw each face
const TTextureMesh::face_type &fc = mesh->face(f);
const TTextureMesh::edge_type &ed0 = mesh->edge(fc.edge(0)),
&ed1 = mesh->edge(fc.edge(1)),
&ed2 = mesh->edge(fc.edge(2));
{
v0 = ed0.vertex(0);
v1 = ed0.vertex(1);
v2 = ed1.vertex((ed1.vertex(0) == v0) | (ed1.vertex(0) == v1));
e1ovi = (ed1.vertex(0) == v1) | (ed1.vertex(1) == v1); // ed1 and ed2 will refer to vertexes
e2ovi = 1 - e1ovi; // with index 2 and these.
}
const TPointD &p0 = mesh->vertex(v0).P(), &p1 = mesh->vertex(v1).P(), &p2 = mesh->vertex(v2).P();
for (t = 0; t != tCount; ++t) {
// Draw face against tile
const TileData &tileData = td->m_tileDatas[t];
// Map each face vertex to tile coordinates
TPointD s[3] = {tileAff[t] * p0, tileAff[t] * p1, tileAff[t] * p2};
// 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 (tileData.m_textureId != texId) {
texId = tileData.m_textureId;
glBindTexture(GL_TEXTURE_2D, tileData.m_textureId); // This must be OUTSIDE a glBegin/glEnd block
}
const double *d[3] = {dstCoords + (v0 << 1),
dstCoords + (v1 << 1),
dstCoords + (v2 << 1)};
/*
Now, draw primitives. A note about pixel arithmetic, here.
Since line antialiasing in OpenGL just manipulates output fragments' alpha components,
we must require that the input texture is NONPREMULTIPLIED.
Furthermore, this function does not rely on the assumption that the output alpha component
is discarded (as it happens when drawing on screen). This means that just using a simple
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) is not an option, since this way THE INPUT
SRC ALPHA GETS MULTIPLIED BY ITSELF - see glBlendFunc's docs - and that shows.
The solution is to separate the rendering of RGB and M components - the formers use
GL_SRC_ALPHA, while the latter uses GL_ONE. The result is a PREMULTIPLIED image.
*/
// First, draw antialiased face edges on the mesh border.
bool drawEd0 = (ed0.facesCount() < 2),
drawEd1 = (ed1.facesCount() < 2),
drawEd2 = (ed2.facesCount() < 2);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
glBegin(GL_LINES);
{
if (drawEd0) {
glTexCoord2d(s[0].x, s[0].y), glVertex2d(*d[0], *(d[0] + 1));
glTexCoord2d(s[1].x, s[1].y), glVertex2d(*d[1], *(d[1] + 1));
}
if (drawEd1) {
glTexCoord2d(s[e1ovi].x, s[e1ovi].y), glVertex2d(*d[e1ovi], *(d[e1ovi] + 1));
glTexCoord2d(s[2].x, s[2].y), glVertex2d(*d[2], *(d[2] + 1));
}
if (drawEd2) {
glTexCoord2d(s[e2ovi].x, s[e2ovi].y), glVertex2d(*d[e2ovi], *(d[e2ovi] + 1));
glTexCoord2d(s[2].x, s[2].y), glVertex2d(*d[2], *(d[2] + 1));
}
}
glEnd();
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glBegin(GL_LINES);
{
if (drawEd0) {
glTexCoord2d(s[0].x, s[0].y), glVertex2d(*d[0], *(d[0] + 1));
glTexCoord2d(s[1].x, s[1].y), glVertex2d(*d[1], *(d[1] + 1));
}
if (drawEd1) {
glTexCoord2d(s[e1ovi].x, s[e1ovi].y), glVertex2d(*d[e1ovi], *(d[e1ovi] + 1));
glTexCoord2d(s[2].x, s[2].y), glVertex2d(*d[2], *(d[2] + 1));
}
if (drawEd2) {
glTexCoord2d(s[e2ovi].x, s[e2ovi].y), glVertex2d(*d[e2ovi], *(d[e2ovi] + 1));
glTexCoord2d(s[2].x, s[2].y), glVertex2d(*d[2], *(d[2] + 1));
}
}
glEnd();
// Finally, draw the face
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
glBegin(GL_TRIANGLES);
{
glTexCoord2d(s[0].x, s[0].y), glVertex2d(*d[0], *(d[0] + 1));
glTexCoord2d(s[1].x, s[1].y), glVertex2d(*d[1], *(d[1] + 1));
glTexCoord2d(s[2].x, s[2].y), glVertex2d(*d[2], *(d[2] + 1));
}
glEnd();
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glBegin(GL_TRIANGLES);
{
glTexCoord2d(s[0].x, s[0].y), glVertex2d(*d[0], *(d[0] + 1));
glTexCoord2d(s[1].x, s[1].y), glVertex2d(*d[1], *(d[1] + 1));
glTexCoord2d(s[2].x, s[2].y), glVertex2d(*d[2], *(d[2] + 1));
}
glEnd();
}
}
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
glPopAttrib();
}