// 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 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::x(p0), tcg::point_traits::y(p0)); colorFunction.vertexColor(v1, -1); glVertex2d(tcg::point_traits::x(p1), tcg::point_traits::y(p1)); } glEnd(); } //------------------------------------------------------------------------------- template 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 &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 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> SortedFacesVector; const SortedFacesVector &sortedFaces = group->m_sortedFaces; const std::vector &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.get(); } 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 &meshes = meshImage->meshes(); int m, mCount = meshes.size(); for (m = 0; m != mCount; ++m) { TTextureMesh &mesh = *meshes[m]; tcg::list &vertices = mesh.vertices(); tcg::list::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 &meshes = mi.meshes(); int m, mCount = meshes.size(); if (group) { for (m = 0; m != mCount; ++m) tglDrawEdges( *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, 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 final : 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 final : 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); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glLineWidth(1.0f); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Prepare variables const std::vector &meshes = meshImage.meshes(); const TTextureMesh *mesh; typedef std::vector> 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 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.get(); } // 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 (std::min({s[0].x, s[1].x, s[2].x}) > 1.0 || std::min({s[0].y, s[1].y, s[2].y}) > 1.0 || std::max({s[0].x, s[1].x, s[2].x}) < 0.0 || std::max({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(); }