// Copyright (C) 2002-2007 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 "IMesh.h" #include "SMesh.h" #include "SMeshBuffer.h" #include "SMeshBufferLightMap.h" #include "SMeshBufferTangents.h" #include "IAnimatedMesh.h" #include "SAnimatedMesh.h" #include "os.h" namespace irr { namespace scene { //! Recalculates the normals in vertex array. //! This template function was a member of the CMeshManipulator class, but //! visual studio 6.0 didn't like it. template inline void recalculateNormalsT_Flat(VTXTYPE* v, int vtxcnt, u16* idx, int idxcnt) { for (int i=0; i p(v[idx[i+0]].Pos, v[idx[i+1]].Pos, v[idx[i+2]].Pos); v[idx[i+0]].Normal = p.Normal; v[idx[i+1]].Normal = p.Normal; v[idx[i+2]].Normal = p.Normal; } } template inline void recalculateNormalsT_Smooth(VTXTYPE* v, int vtxcnt, u16* idx, int idxcnt) { s32 i; for ( i = 0; i!= vtxcnt; ++i ) { v[i].Normal.set ( 0.f, 0.f, 0.f ); } for ( i=0; i p(v[idx[i+0]].Pos, v[idx[i+1]].Pos, v[idx[i+2]].Pos); v[idx[i+0]].Normal += p.Normal; v[idx[i+1]].Normal += p.Normal; v[idx[i+2]].Normal += p.Normal; } for ( i = 0; i!= vtxcnt; ++i ) { v[i].Normal.normalize (); } } //! Recalculates normals in a vertex array. //! This template function was a member of the CMeshManipulator class, but //! visual studio 6.0 didn't like it. template inline void makePlanarMappingT(VERTEXTYPE *v, int vtxcnt, u16* idx, int idxcnt, f32 resolution) { for (int i=0; i p(v[idx[i+0]].Pos, v[idx[i+1]].Pos, v[idx[i+2]].Pos); p.Normal.X = (f32)(fabs(p.Normal.X)); p.Normal.Y = (f32)(fabs(p.Normal.Y)); p.Normal.Z = (f32)(fabs(p.Normal.Z)); // calculate planar mapping worldspace coordinates if (p.Normal.X > p.Normal.Y && p.Normal.X > p.Normal.Z) { for (s32 o=0; o<3; ++o) { v[idx[i+o]].TCoords.X = v[idx[i+o]].Pos.Y * resolution; v[idx[i+o]].TCoords.Y = v[idx[i+o]].Pos.Z * resolution; } } else if (p.Normal.Y > p.Normal.X && p.Normal.Y > p.Normal.Z) { for (s32 o=0; o<3; ++o) { v[idx[i+o]].TCoords.X = v[idx[i+o]].Pos.X * resolution; v[idx[i+o]].TCoords.Y = v[idx[i+o]].Pos.Z * resolution; } } else { for (s32 o=0; o<3; ++o) { v[idx[i+o]].TCoords.X = v[idx[i+o]].Pos.X * resolution; v[idx[i+o]].TCoords.Y = v[idx[i+o]].Pos.Y * resolution; } } } } //! 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(); u32 idxcnt = buffer->getIndexCount(); u16* idx = buffer->getIndices(); switch(buffer->getVertexType()) { case video::EVT_STANDARD: { video::S3DVertex* v = (video::S3DVertex*)buffer->getVertices(); if (!smooth) recalculateNormalsT_Flat(v, vtxcnt, idx, idxcnt); else recalculateNormalsT_Smooth(v, vtxcnt, idx, idxcnt); } break; case video::EVT_2TCOORDS: { video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)buffer->getVertices(); if (!smooth) recalculateNormalsT_Flat(v, vtxcnt, idx, idxcnt); else recalculateNormalsT_Smooth(v, vtxcnt, idx, idxcnt); } break; case video::EVT_TANGENTS: { // TODO: recalculate tangent and binormal video::S3DVertexTangents* v = (video::S3DVertexTangents*)buffer->getVertices(); if (!smooth) recalculateNormalsT_Flat(v, vtxcnt, idx, idxcnt); else recalculateNormalsT_Smooth(v, vtxcnt, idx, idxcnt); } } } //! Recalculates all normals of the mesh. //! \param mesh: Mesh on which the operation is performed. void CMeshManipulator::recalculateNormals(scene::IMesh* mesh, bool smooth) const { if (!mesh) return; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b), smooth); } //! Applies a transformation /** \param mesh: Mesh on which the operation is performed. \param m: matrix. */ void CMeshManipulator::transformMesh(scene::IMesh* mesh, const core::matrix4& m) const { if (!mesh) return; core::aabbox3df meshbox; core::aabbox3df bufferbox; u32 i; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b); const u32 vtxcnt = buffer->getVertexCount(); const u32 vtxPitch = buffer->getVertexPitch (); video::S3DVertex* v = (video::S3DVertex*) buffer->getVertices(); for ( i=0; i < 1; ++i) { m.transformVect ( v->Pos); m.rotateVect ( v->Normal ); v->Normal.normalize(); bufferbox.reset( v->Pos); v = (video::S3DVertex*) ((u8*) v + vtxPitch); } for ( ;i < vtxcnt; ++i) { m.transformVect ( v->Pos); m.rotateVect ( v->Normal ); v->Normal.normalize(); bufferbox.addInternalPoint( v->Pos); v = (video::S3DVertex*) ((u8*) v + vtxPitch); } buffer->setBoundingBox(bufferbox); if (b == 0) meshbox.reset(buffer->getBoundingBox()); else meshbox.addInternalBox(buffer->getBoundingBox()); } mesh->setBoundingBox( meshbox ); } //! Scales the whole mesh. //! \param mesh: Mesh on which the operation is performed. void CMeshManipulator::scaleMesh(scene::IMesh* mesh, const core::vector3df& scale) const { if (!mesh) return; core::aabbox3df meshbox; const u32 bcount = mesh->getMeshBufferCount(); for ( u32 b=0; bgetMeshBuffer(b); void* v = buffer->getVertices(); const u32 vtxcnt = buffer->getVertexCount(); core::aabbox3df bufferbox; u32 i; switch(buffer->getVertexType()) { case video::EVT_STANDARD: { if (vtxcnt != 0) bufferbox.reset(((video::S3DVertex*)v)[0].Pos * scale); for ( i=0; isetBoundingBox( bufferbox ); if (b == 0) meshbox.reset(buffer->getBoundingBox()); else meshbox.addInternalBox(buffer->getBoundingBox()); } mesh->setBoundingBox( meshbox ); } //! 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)->getVertexCount(); const u32 idxCnt = mesh->getMeshBuffer(b)->getIndexCount(); const u16* idx = mesh->getMeshBuffer(b)->getIndices(); u32 i; 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(); for (i=0; iVertices.push_back(v[i]); for (i=0; iIndices.push_back(idx[i]); 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(); for (i=0; iVertices.push_back(v[i]); for (i=0; iIndices.push_back(idx[i]); 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(); for (i=0; iVertices.push_back(v[i]); for (i=0; iIndices.push_back(idx[i]); 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 vtxcnt = buffer->getVertexCount(); u32 idxcnt = buffer->getIndexCount(); u16* idx = buffer->getIndices(); switch(buffer->getVertexType()) { case video::EVT_STANDARD: { video::S3DVertex* v = (video::S3DVertex*)buffer->getVertices(); makePlanarMappingT(v, vtxcnt, idx, idxcnt, resolution); } break; case video::EVT_2TCOORDS: { video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)buffer->getVertices(); makePlanarMappingT(v, vtxcnt, idx, idxcnt, resolution); } break; case video::EVT_TANGENTS: { video::S3DVertexTangents* v = (video::S3DVertexTangents*)buffer->getVertices(); makePlanarMappingT(v, vtxcnt, idx, idxcnt, resolution); } break; } } } //! 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(); 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 ); } 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(); 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 ); } 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(); 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 ); } clone->addMeshBuffer(buffer); buffer->drop(); } break; }// end switch }// end for all mesh buffers clone->BoundingBox = mesh->getBoundingBox(); return clone; } //! Creates a copy of the mesh, which will only consist of S3DVertexTangents vertices. IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh) const { if (!mesh) return 0; // copy mesh and fill data into SMeshBufferTangents SMesh* clone = new SMesh(); const u32 meshBufferCount = mesh->getMeshBufferCount(); 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 switch(mesh->getMeshBuffer(b)->getVertexType()) { case video::EVT_STANDARD: { video::S3DVertex* v = (video::S3DVertex*)mesh->getMeshBuffer(b)->getVertices(); for (s32 i=0; iVertices.push_back( video::S3DVertexTangents( v[idx[i]].Pos, v[idx[i]].Color, v[idx[i]].TCoords)); } break; case video::EVT_2TCOORDS: { video::S3DVertex2TCoords* v = (video::S3DVertex2TCoords*)mesh->getMeshBuffer(b)->getVertices(); for (s32 i=0; iVertices.push_back(video::S3DVertexTangents( v[idx[i]].Pos, v[idx[i]].Color, v[idx[i]].TCoords)); } break; case video::EVT_TANGENTS: { video::S3DVertexTangents* v = (video::S3DVertexTangents*)mesh->getMeshBuffer(b)->getVertices(); for (s32 i=0; iVertices.push_back(v[idx[i]]); } break; } // create new indices buffer->Indices.set_used(idxCnt); for (s32 i=0; iIndices[i] = i; // add new buffer clone->addMeshBuffer(buffer); buffer->drop(); } clone->BoundingBox = mesh->getBoundingBox(); // now calculate tangents for (b=0; bgetMeshBuffer(b)->getIndexCount(); u16* idx = clone->getMeshBuffer(b)->getIndices(); video::S3DVertexTangents* v = (video::S3DVertexTangents*)clone->getMeshBuffer(b)->getVertices(); for (s32 i=0; i 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