// Copyright (C) 2002-2008 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CMeshManipulator.h" #include "SMesh.h" #include "CMeshBuffer.h" #include "SAnimatedMesh.h" #include "os.h" namespace irr { namespace scene { static inline core::vector3df getAngleWeight(const core::vector3df& v1, const core::vector3df& v2, const core::vector3df& v3) { // Calculate this triangle's weight for each of its three vertices // start by calculating the lengths of its sides const f32 a = v2.getDistanceFromSQ(v3); const f32 asqrt = sqrtf(a); const f32 b = v1.getDistanceFromSQ(v3); const f32 bsqrt = sqrtf(b); const f32 c = v1.getDistanceFromSQ(v2); const f32 csqrt = sqrtf(c); // use them to find the angle at each vertex return core::vector3df( acosf((b + c - a) / (2.f * bsqrt * csqrt)), acosf((-b + c + a) / (2.f * asqrt * csqrt)), acosf((b - c + a) / (2.f * bsqrt * asqrt))); } //! Flips the direction of surfaces. Changes backfacing triangles to frontfacing //! triangles and vice versa. //! \param mesh: Mesh on which the operation is performed. void CMeshManipulator::flipSurfaces(scene::IMesh* mesh) const { if (!mesh) return; const u32 bcount = mesh->getMeshBufferCount(); for (u32 b=0; bgetMeshBuffer(b); const u32 idxcnt = buffer->getIndexCount(); u16* idx = buffer->getIndices(); s32 tmp; for (u32 i=0; igetMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b); void* v = buffer->getVertices(); u32 vtxcnt = buffer->getVertexCount(); switch(buffer->getVertexType()) { case video::EVT_STANDARD: { for ( i=0; igetMeshBufferCount(); for (u32 b=0; bgetMeshBuffer(b); void* v = buffer->getVertices(); const u32 vtxcnt = buffer->getVertexCount(); u32 i; switch(buffer->getVertexType()) { case video::EVT_STANDARD: { for ( i=0; igetVertexCount(); const u32 idxcnt = buffer->getIndexCount(); const u16* idx = buffer->getIndices(); if (!smooth) { for (u32 i=0; igetPosition(idx[i+0]); const core::vector3df& v2 = buffer->getPosition(idx[i+1]); const core::vector3df& v3 = buffer->getPosition(idx[i+2]); const core::vector3df normal = core::plane3d(v1, v2, v3).Normal; buffer->getNormal(idx[i+0]) = normal; buffer->getNormal(idx[i+1]) = normal; buffer->getNormal(idx[i+2]) = normal; } } else { u32 i; for ( i = 0; i!= vtxcnt; ++i ) buffer->getNormal(i).set( 0.f, 0.f, 0.f ); for ( i=0; igetPosition(idx[i+0]); const core::vector3df& v2 = buffer->getPosition(idx[i+1]); const core::vector3df& v3 = buffer->getPosition(idx[i+2]); core::vector3df normal = core::plane3d(v1, v2, v3).Normal; if (angleWeighted) normal *= getAngleWeight(v1,v2,v3); buffer->getNormal(idx[i+0]) += normal; buffer->getNormal(idx[i+1]) += normal; buffer->getNormal(idx[i+2]) += normal; } for ( i = 0; i!= vtxcnt; ++i ) buffer->getNormal(i).normalize(); } } //! Recalculates all normals of the mesh. //! \param mesh: Mesh on which the operation is performed. void CMeshManipulator::recalculateNormals(scene::IMesh* mesh, bool smooth, bool angleWeighted) const { if (!mesh) return; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b), smooth, angleWeighted); } //! Applies a transformation /** \param buffer: Meshbuffer on which the operation is performed. \param m: matrix. */ void CMeshManipulator::transform(scene::IMeshBuffer* buffer, const core::matrix4& m) const { const u32 vtxcnt = buffer->getVertexCount(); if (!vtxcnt) return; core::aabbox3df bufferbox; // first transform { m.transformVect(buffer->getPosition(0)); m.rotateVect(buffer->getNormal(0)); buffer->getNormal(0).normalize(); bufferbox.reset(buffer->getPosition(0)); } for ( u32 i=1 ;i < vtxcnt; ++i) { m.transformVect(buffer->getPosition(i)); m.rotateVect(buffer->getNormal(i)); buffer->getNormal(i).normalize(); bufferbox.addInternalPoint(buffer->getPosition(i)); } buffer->setBoundingBox(bufferbox); } //! Applies a transformation /** \param mesh: Mesh on which the operation is performed. \param m: matrix. */ void CMeshManipulator::transform(scene::IMesh* mesh, const core::matrix4& m) const { if (!mesh) return; core::aabbox3df meshbox; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b); transform(buffer, m); if (b == 0) meshbox.reset(buffer->getBoundingBox()); else meshbox.addInternalBox(buffer->getBoundingBox()); } mesh->setBoundingBox( meshbox ); } //! Scales the actual mesh, not a scene node. void CMeshManipulator::scale(scene::IMesh* mesh, const core::vector3df& factor) const { if (!mesh) return; core::aabbox3df meshbox; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b); scale(buffer, factor); if (b == 0) meshbox.reset(buffer->getBoundingBox()); else meshbox.addInternalBox(buffer->getBoundingBox()); } mesh->setBoundingBox( meshbox ); } //! Scales the actual meshbuffer, not a scene node. void CMeshManipulator::scale(scene::IMeshBuffer* buffer, const core::vector3df& factor) const { if (!buffer) return; const u32 vtxcnt = buffer->getVertexCount(); core::aabbox3df bufferbox; if (vtxcnt != 0) bufferbox.reset(buffer->getPosition(0) * factor); for (u32 i=0; igetPosition(i) *= factor; bufferbox.addInternalPoint(buffer->getPosition(i)); } buffer->setBoundingBox(bufferbox); } //! Scale the texture coords of a mesh. void CMeshManipulator::scaleTCoords(scene::IMesh* mesh, const core::vector2df& factor, u32 layer) const { if (!mesh) return; const u32 bcount = mesh->getMeshBufferCount(); for (u32 b=0; bgetMeshBuffer(b), factor, layer); } //! Scale the level-th texture coords of a meshbuffer. void CMeshManipulator::scaleTCoords(scene::IMeshBuffer* buffer, const core::vector2df& factor, u32 level) const { if (!buffer || ((level>1) && (buffer->getVertexType() != video::EVT_2TCOORDS))) return; const u32 vtxcnt = buffer->getVertexCount(); if (level==1) { for (u32 i=0; igetTCoords(i) *= factor; } else { for (u32 i=0; iVertices[i].TCoords2 *= factor; } } //! Clones a static IMesh into a modifyable SMesh. SMesh* CMeshManipulator::createMeshCopy(scene::IMesh* mesh) const { if (!mesh) return 0; SMesh* clone = new SMesh(); const u32 meshBufferCount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b)->getVertexType()) { case video::EVT_STANDARD: { SMeshBuffer* buffer = new SMeshBuffer(*(SMeshBuffer*)mesh->getMeshBuffer(b)); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_2TCOORDS: { SMeshBufferLightMap* buffer = new SMeshBufferLightMap(*(SMeshBufferLightMap*)mesh->getMeshBuffer(b)); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_TANGENTS: { SMeshBufferTangents* buffer = new SMeshBufferTangents(*(SMeshBufferTangents*)mesh->getMeshBuffer(b)); clone->addMeshBuffer(buffer); buffer->drop(); } break; }// end switch }// end for all mesh buffers clone->BoundingBox = mesh->getBoundingBox(); return clone; } //! Creates a planar texture mapping on the mesh //! \param mesh: Mesh on which the operation is performed. //! \param resolution: resolution of the planar mapping. This is the value //! specifying which is the releation between world space and //! texture coordinate space. void CMeshManipulator::makePlanarTextureMapping(scene::IMesh* mesh, f32 resolution=0.01f) const { if (!mesh) return; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b); u32 idxcnt = buffer->getIndexCount(); u16* idx = buffer->getIndices(); for (u32 i=0; igetPosition(idx[i+0]), buffer->getPosition(idx[i+1]), buffer->getPosition(idx[i+2])); p.Normal.X = fabsf(p.Normal.X); p.Normal.Y = fabsf(p.Normal.Y); p.Normal.Z = fabsf(p.Normal.Z); // calculate planar mapping worldspace coordinates if (p.Normal.X > p.Normal.Y && p.Normal.X > p.Normal.Z) { for (u32 o=0; o!=3; ++o) { buffer->getTCoords(idx[i+o]).X = buffer->getPosition(idx[i+o]).Y * resolution; buffer->getTCoords(idx[i+o]).Y = buffer->getPosition(idx[i+o]).Z * resolution; } } else if (p.Normal.Y > p.Normal.X && p.Normal.Y > p.Normal.Z) { for (u32 o=0; o!=3; ++o) { buffer->getTCoords(idx[i+o]).X = buffer->getPosition(idx[i+o]).X * resolution; buffer->getTCoords(idx[i+o]).Y = buffer->getPosition(idx[i+o]).Z * resolution; } } else { for (u32 o=0; o!=3; ++o) { buffer->getTCoords(idx[i+o]).X = buffer->getPosition(idx[i+o]).X * resolution; buffer->getTCoords(idx[i+o]).Y = buffer->getPosition(idx[i+o]).Y * resolution; } } } } } //! Creates a copy of the mesh, which will only consist of unique primitives IMesh* CMeshManipulator::createMeshUniquePrimitives(IMesh* mesh) const { if (!mesh) return 0; SMesh* clone = new SMesh(); const u32 meshBufferCount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b)->getIndexCount(); const u16* idx = mesh->getMeshBuffer(b)->getIndices(); switch(mesh->getMeshBuffer(b)->getVertexType()) { case video::EVT_STANDARD: { SMeshBuffer* buffer = new SMeshBuffer(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); video::S3DVertex* v = (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); buffer->Vertices.reallocate(idxCnt); buffer->Indices.reallocate(idxCnt); for (s32 i=0; iVertices.push_back( v[idx[i + 0 ]] ); buffer->Vertices.push_back( v[idx[i + 1 ]] ); buffer->Vertices.push_back( v[idx[i + 2 ]] ); buffer->Indices.push_back( i + 0 ); buffer->Indices.push_back( i + 1 ); buffer->Indices.push_back( i + 2 ); } buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_2TCOORDS: { SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); buffer->Vertices.reallocate(idxCnt); buffer->Indices.reallocate(idxCnt); for (s32 i=0; iVertices.push_back( v[idx[i + 0 ]] ); buffer->Vertices.push_back( v[idx[i + 1 ]] ); buffer->Vertices.push_back( v[idx[i + 2 ]] ); buffer->Indices.push_back( i + 0 ); buffer->Indices.push_back( i + 1 ); buffer->Indices.push_back( i + 2 ); } buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_TANGENTS: { SMeshBufferTangents* buffer = new SMeshBufferTangents(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); video::S3DVertexTangents* v = (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); buffer->Vertices.reallocate(idxCnt); buffer->Indices.reallocate(idxCnt); for (s32 i=0; iVertices.push_back( v[idx[i + 0 ]] ); buffer->Vertices.push_back( v[idx[i + 1 ]] ); buffer->Vertices.push_back( v[idx[i + 2 ]] ); buffer->Indices.push_back( i + 0 ); buffer->Indices.push_back( i + 1 ); buffer->Indices.push_back( i + 2 ); } buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); clone->addMeshBuffer(buffer); buffer->drop(); } break; }// end switch }// end for all mesh buffers clone->BoundingBox = mesh->getBoundingBox(); return clone; } //! Creates a copy of a mesh, which will have identical vertices welded together IMesh* CMeshManipulator::createMeshWelded(IMesh *mesh, f32 tolerance) const { SMesh* clone = new SMesh(); clone->BoundingBox = mesh->getBoundingBox(); core::array redirects; for (u32 b=0; bgetMeshBufferCount(); ++b) { // reset redirect list redirects.set_used(mesh->getMeshBuffer(b)->getVertexCount()); u16* indices = 0; u32 indexCount = 0; core::array* outIdx = 0; switch(mesh->getMeshBuffer(b)->getVertexType()) { case video::EVT_STANDARD: { SMeshBuffer* buffer = new SMeshBuffer(); buffer->BoundingBox = mesh->getMeshBuffer(b)->getBoundingBox(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); clone->addMeshBuffer(buffer); buffer->drop(); video::S3DVertex* v = (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); u32 vertexCount = mesh->getMeshBuffer(b)->getVertexCount(); indices = mesh->getMeshBuffer(b)->getIndices(); indexCount = mesh->getMeshBuffer(b)->getIndexCount(); outIdx = &buffer->Indices; buffer->Vertices.reallocate(vertexCount); for (u32 i=0; i < vertexCount; ++i) { bool found = false; for (u32 j=0; j < i; ++j) { if ( v[i].Pos.equals( v[j].Pos, tolerance) && v[i].Normal.equals( v[j].Normal, tolerance) && v[i].TCoords.equals( v[j].TCoords ) && (v[i].Color == v[j].Color) ) { redirects[i] = redirects[j]; found = true; break; } } if (!found) { redirects[i] = buffer->Vertices.size(); buffer->Vertices.push_back(v[i]); } } break; } case video::EVT_2TCOORDS: { SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); buffer->BoundingBox = mesh->getMeshBuffer(b)->getBoundingBox(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); clone->addMeshBuffer(buffer); buffer->drop(); video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); u32 vertexCount = mesh->getMeshBuffer(b)->getVertexCount(); indices = mesh->getMeshBuffer(b)->getIndices(); indexCount = mesh->getMeshBuffer(b)->getIndexCount(); outIdx = &buffer->Indices; buffer->Vertices.reallocate(vertexCount); for (u32 i=0; i < vertexCount; ++i) { bool found = false; for (u32 j=0; j < i; ++j) { if ( v[i].Pos.equals( v[j].Pos, tolerance) && v[i].Normal.equals( v[j].Normal, tolerance) && v[i].TCoords.equals( v[j].TCoords ) && v[i].TCoords2.equals( v[j].TCoords2 ) && (v[i].Color == v[j].Color) ) { redirects[i] = redirects[j]; found = true; break; } } if (!found) { redirects[i] = buffer->Vertices.size(); buffer->Vertices.push_back(v[i]); } } break; } case video::EVT_TANGENTS: { SMeshBufferTangents* buffer = new SMeshBufferTangents(); buffer->BoundingBox = mesh->getMeshBuffer(b)->getBoundingBox(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); clone->addMeshBuffer(buffer); buffer->drop(); video::S3DVertexTangents* v = (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); u32 vertexCount = mesh->getMeshBuffer(b)->getVertexCount(); indices = mesh->getMeshBuffer(b)->getIndices(); indexCount = mesh->getMeshBuffer(b)->getIndexCount(); outIdx = &buffer->Indices; buffer->Vertices.reallocate(vertexCount); for (u32 i=0; i < vertexCount; ++i) { bool found = false; for (u32 j=0; j < i; ++j) { if ( v[i].Pos.equals( v[j].Pos, tolerance) && v[i].Normal.equals( v[j].Normal, tolerance) && v[i].TCoords.equals( v[j].TCoords ) && v[i].Tangent.equals( v[j].Tangent, tolerance ) && v[i].Binormal.equals( v[j].Binormal, tolerance ) && (v[i].Color == v[j].Color) ) { redirects[i] = redirects[j]; found = true; break; } } if (!found) { redirects[i] = buffer->Vertices.size(); buffer->Vertices.push_back(v[i]); } } break; } default: os::Printer::log("Cannot create welded mesh, vertex type unsupported", ELL_ERROR); break; } // write the buffer's index list core::array &Indices = *outIdx; Indices.set_used(indexCount); for (u32 i=0; igetMeshBufferCount(); u32 b; for (b=0; bgetMeshBuffer(b)->getIndexCount(); const u16* idx = mesh->getMeshBuffer(b)->getIndices(); SMeshBufferTangents* buffer = new SMeshBufferTangents(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); // copy vertices buffer->Vertices.reallocate(idxCnt); switch(mesh->getMeshBuffer(b)->getVertexType()) { case video::EVT_STANDARD: { video::S3DVertex* v = (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); for (u32 i=0; iVertices.push_back( video::S3DVertexTangents( v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords)); } break; case video::EVT_2TCOORDS: { video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); for (u32 i=0; iVertices.push_back(video::S3DVertexTangents( v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords)); } break; case video::EVT_TANGENTS: { video::S3DVertexTangents* v = (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); for (u32 i=0; iVertices.push_back(v[idx[i]]); } break; } // create new indices buffer->Indices.set_used(idxCnt); for (u32 i=0; iIndices[i] = i; buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); // add new buffer clone->addMeshBuffer(buffer); buffer->drop(); } clone->BoundingBox = mesh->getBoundingBox(); // now calculate tangents for (b=0; bgetMeshBuffer(b)->getVertexCount(); const u32 idxCnt = clone->getMeshBuffer(b)->getIndexCount(); u16* idx = clone->getMeshBuffer(b)->getIndices(); video::S3DVertexTangents* v = (video::S3DVertexTangents*)clone->getMeshBuffer(b)->getVertices(); if (smooth) { u32 i; for ( i = 0; i!= vtxCnt; ++i ) { if (recalculateNormals) v[i].Normal.set( 0.f, 0.f, 0.f ); v[i].Tangent.set( 0.f, 0.f, 0.f ); v[i].Binormal.set( 0.f, 0.f, 0.f ); } //Each vertex gets the sum of the tangents and binormals from the faces around it for ( i=0; igetMeshBufferCount(); u32 b; for (b=0; bgetMeshBuffer(b)->getIndexCount(); const u16* idx = mesh->getMeshBuffer(b)->getIndices(); SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); buffer->Material = mesh->getMeshBuffer(b)->getMaterial(); // copy vertices buffer->Vertices.reallocate(idxCnt); switch(mesh->getMeshBuffer(b)->getVertexType()) { case video::EVT_STANDARD: { video::S3DVertex* v = (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); for (u32 i=0; iVertices.push_back( video::S3DVertex2TCoords( v[idx[i]].Pos, v[idx[i]].Color, v[idx[i]].TCoords, v[idx[i]].TCoords)); } break; case video::EVT_2TCOORDS: { video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); for (u32 i=0; iVertices.push_back(v[idx[i]]); } break; case video::EVT_TANGENTS: { video::S3DVertexTangents* v = (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); for (u32 i=0; iVertices.push_back(video::S3DVertex2TCoords( v[idx[i]].Pos, v[idx[i]].Color, v[idx[i]].TCoords, v[idx[i]].TCoords)); } break; } // create new indices buffer->Indices.set_used(idxCnt); for (u32 i=0; iIndices[i] = i; buffer->setBoundingBox(mesh->getMeshBuffer(b)->getBoundingBox()); // add new buffer clone->addMeshBuffer(buffer); buffer->drop(); } clone->BoundingBox = mesh->getBoundingBox(); return clone; } void CMeshManipulator::calculateTangents( core::vector3df& normal, core::vector3df& tangent, core::vector3df& binormal, const core::vector3df& vt1, const core::vector3df& vt2, const core::vector3df& vt3, // vertices const core::vector2df& tc1, const core::vector2df& tc2, const core::vector2df& tc3) // texture coords { // choose one of them: //#define USE_NVIDIA_GLH_VERSION // use version used by nvidia in glh headers #define USE_IRR_VERSION #ifdef USE_IRR_VERSION core::vector3df v1 = vt1 - vt2; core::vector3df v2 = vt3 - vt1; normal = v2.crossProduct(v1); normal.normalize(); // binormal f32 deltaX1 = tc1.X - tc2.X; f32 deltaX2 = tc3.X - tc1.X; binormal = (v1 * deltaX2) - (v2 * deltaX1); binormal.normalize(); // tangent f32 deltaY1 = tc1.Y - tc2.Y; f32 deltaY2 = tc3.Y - tc1.Y; tangent = (v1 * deltaY2) - (v2 * deltaY1); tangent.normalize(); // adjust core::vector3df txb = tangent.crossProduct(binormal); if (txb.dotProduct(normal) < 0.0f) { tangent *= -1.0f; binormal *= -1.0f; } #endif // USE_IRR_VERSION #ifdef USE_NVIDIA_GLH_VERSION tangent.set(0,0,0); binormal.set(0,0,0); core::vector3df v1(vt2.X - vt1.X, tc2.X - tc1.X, tc2.Y - tc1.Y); core::vector3df v2(vt3.X - vt1.X, tc3.X - tc1.X, tc3.Y - tc1.Y); core::vector3df txb = v1.crossProduct(v2); if ( !core::iszero ( txb.X ) ) { tangent.X = -txb.Y / txb.X; binormal.X = -txb.Z / txb.X; } v1.X = vt2.Y - vt1.Y; v2.X = vt3.Y - vt1.Y; txb = v1.crossProduct(v2); if ( !core::iszero ( txb.X ) ) { tangent.Y = -txb.Y / txb.X; binormal.Y = -txb.Z / txb.X; } v1.X = vt2.Z - vt1.Z; v2.X = vt3.Z - vt1.Z; txb = v1.crossProduct(v2); if ( !core::iszero ( txb.X ) ) { tangent.Z = -txb.Y / txb.X; binormal.Z = -txb.Z / txb.X; } tangent.normalize(); binormal.normalize(); normal = tangent.crossProduct(binormal); normal.normalize(); binormal = tangent.crossProduct(normal); binormal.normalize(); core::plane3d pl(vt1, vt2, vt3); if(normal.dotProduct(pl.Normal) < 0.0f ) normal *= -1.0f; #endif // USE_NVIDIA_GLH_VERSION } //! Returns amount of polygons in mesh. s32 CMeshManipulator::getPolyCount(scene::IMesh* mesh) const { if (!mesh) return 0; s32 trianglecount = 0; for (u32 g=0; ggetMeshBufferCount(); ++g) trianglecount += mesh->getMeshBuffer(g)->getIndexCount() / 3; return trianglecount; } //! Returns amount of polygons in mesh. s32 CMeshManipulator::getPolyCount(scene::IAnimatedMesh* mesh) const { if (mesh && mesh->getFrameCount() != 0) return getPolyCount(mesh->getMesh(0)); return 0; } //! create a new AnimatedMesh and adds the mesh to it IAnimatedMesh * CMeshManipulator::createAnimatedMesh(scene::IMesh* mesh, scene::E_ANIMATED_MESH_TYPE type) const { return new SAnimatedMesh(mesh, type); } } // end namespace scene } // end namespace irr