#include "GeoMeshSimplifier.h" #include "GeoMeshSimpSequence.h" #include "SimplificationMethod.h" #include #include using namespace Geometry; using namespace std; //////////////////////////////////////////////////////////////////////////// // // // MeshSimplifier class // // // //////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- // MeshSimplifier constructor. //--------------------------------------------------------------------------- MeshSimplifier::MeshSimplifier( const Mesh *m, TIPOFUNC upb) { // Saves initial state of mesh. mInitialMesh = new Mesh(); *mInitialMesh = *m; objmesh = m; mGeoMesh = NULL; msimpsequence = NULL; indexMeshLeaves = -1; // Sets the actual progress bar function. mUPB = upb; } //--------------------------------------------------------------------------- // MeshSimplifier destroyer. //--------------------------------------------------------------------------- MeshSimplifier::~MeshSimplifier() { delete mInitialMesh; delete msimpsequence; } //--------------------------------------------------------------------------- // Returns the simplified mesh. //--------------------------------------------------------------------------- Mesh * MeshSimplifier::GetMesh() { return mGeoMesh; } //--------------------------------------------------------------------------- // Set submesh leaves //--------------------------------------------------------------------------- void MeshSimplifier::setMeshLeaves(Index index) { indexMeshLeaves = index; } //--------------------------------------------------------------------------- // Returns the simplification sequence for general meshes. //--------------------------------------------------------------------------- MeshSimplificationSequence *MeshSimplifier::GetSimplificationSequence() { return msimpsequence; } //--------------------------------------------------------------------------- // Sort mesh bones. //--------------------------------------------------------------------------- void MeshSimplifier::sortBones() { bool shared_found; int n; int o; int p; VertexBoneAssignment assign; VertexBuffer *mesh_vb; VertexBuffer *copy_vb; SubMesh *geosubmesh; SubMesh *copysubmesh; std::vector *mesh_bones; std::vector *copy_bones; // Initialize shared flag. shared_found = false; // For each submesh. for (size_t i = 0; i < mGeoMesh->mSubMeshCount; i++) { geosubmesh = &mGeoMesh->mSubMesh[i]; copysubmesh = &mInitialMesh->mSubMesh[i]; // If submesh has shared vertex. if (geosubmesh->mSharedVertexBuffer) { if (shared_found) { continue; } shared_found = true; // Vertex buffers. mesh_vb = mGeoMesh->mVertexBuffer; copy_vb = mInitialMesh->mVertexBuffer; // Bones. mesh_bones = &mGeoMesh->mBones; copy_bones = &mInitialMesh->mBones; } else { // Vertex buffers. mesh_vb = geosubmesh->mVertexBuffer; copy_vb = copysubmesh->mVertexBuffer; // Bones. mesh_bones = &geosubmesh->mBones; copy_bones = ©submesh->mBones; } // If there is submesh bones. if (!mesh_bones->empty()) { assign.boneIndex = 0; assign.weight = 1; mesh_bones->clear(); for (size_t m = 0; m < mesh_vb->mVertexCount; m++) { assign.vertexIndex = (int)m; mesh_bones->push_back(assign); } // Change bones. for (size_t m = 0; m < mesh_bones->size(); m++) { n = (*mesh_bones)[m].vertexIndex; // Initialize o. o = 0; while (!( (mesh_vb->mPosition[n].x == copy_vb->mPosition[o].x) && (mesh_vb->mPosition[n].y == copy_vb->mPosition[o].y) && (mesh_vb->mPosition[n].z == copy_vb->mPosition[o].z) )) { o++; } // Initialize p. p = 0; while ((*copy_bones)[p].vertexIndex != o) { p++; } // Same bone for 'm' and 'p'. (*mesh_bones)[m].boneIndex = (*copy_bones)[p].boneIndex; } } } } //////////////////////////////////////////////////////////////////////////// // // // GeometryBasedSimplifier class // // // //////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- // Constructor. //--------------------------------------------------------------------------- GeometryBasedSimplifier::GeometryBasedSimplifier( const Mesh *m, TIPOFUNC upb) :MeshSimplifier(m,upb) { } //--------------------------------------------------------------------------- // Destroyer. //--------------------------------------------------------------------------- GeometryBasedSimplifier::~GeometryBasedSimplifier() { } //--------------------------------------------------------------------------- // Starts the simplification process. Receives as a parameter the LOD factor in a range of [0,1]. Implements the Simplifier::Simplify method to perform an image based simplification. //--------------------------------------------------------------------------- void GeometryBasedSimplifier::Simplify(Real paramlod) { SimplificationMethod *m_qslim = new SimplificationMethod(objmesh); m_qslim->setMeshLeaves(indexMeshLeaves); msimpsequence = m_qslim->Decimate(paramlod,0,mUPB); mGeoMesh = m_qslim->GetMesh(); // Sort bones. sortBones(); delete m_qslim; } //--------------------------------------------------------------------------- // Starts the simplification process. Receives as a parameter the number of vertices of the resulting mesh. Implements the Simplifier::Simplify method to perform an image based simplification. //--------------------------------------------------------------------------- void GeometryBasedSimplifier::Simplify(uint32 numvertices) { SimplificationMethod *m_qslim = new SimplificationMethod(objmesh); m_qslim->setMeshLeaves(indexMeshLeaves); msimpsequence = m_qslim->Decimate((float)numvertices,1,mUPB); mGeoMesh = m_qslim->GetMesh(); // Sort bones. sortBones(); delete m_qslim; } //////////////////////////////////////////////////////////////////////////// // // // ViewPointDrivenSimplifier class // // // //////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- /// Class constructor. Will call Simplifier class constructor. //--------------------------------------------------------------------------- ViewPointDrivenSimplifier::ViewPointDrivenSimplifier( const Mesh *m, TIPOFUNC upb) :MeshSimplifier(m,upb) { // Set progress update function VMI::mUPB = upb; VMI::width = 256; VMI::height = 256; VMI::bEnableOffScreen = GL_TRUE; VMI::bBeQuiet = GL_FALSE; VMI::bSaveLog = GL_FALSE; VMI::bLoadCamerasFromFile = GL_FALSE; VMI::cameraType = 2; VMI::radius = 1.3; VMI::fov = 60.0; printf("w: %d h: %d\n", VMI::width, VMI::height); printf( "t: %d c: %d o: %d r: %f\n", VMI::numDemandedTriangles, VMI::cameraType, VMI::bEnableOffScreen, VMI::radius); mGeoMesh = new Mesh(); *mGeoMesh = *m; // Transform NoSV Mesh to a SV Mesh. mGeoMesh = mGeoMesh->toSharedVertex(); // Loads the vmi mesh structure for a geometry mesh given. VMI::mesh = initMeshStructure(mGeoMesh); // RGB and Alpha. glutInitDisplayMode(GLUT_DEPTH | GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA); glutInitWindowSize(VMI::width, VMI::height); glutInitWindowPosition(100, 100); VMI::vmiWin = glutCreateWindow("VMI"); glewInit(); VMI::init(); if (VMI::bLoadCamerasFromFile == GL_FALSE) { switch (VMI::cameraType) { case 0: VMI::cameras = VMI::setCameras(VMI::radius, OCTAHEDRON, &VMI::numCameras); printf("Number of cameras: %d\n", OCTAHEDRON); break; case 1: VMI:: cameras = VMI::setCameras(VMI::radius, ICOSAHEDRON, &VMI::numCameras); printf("Number of cameras: %d\n", ICOSAHEDRON); break; case 2: VMI::cameras = VMI::setCameras(VMI::radius, DODECAHEDRON, &VMI::numCameras); printf("Number of cameras: %d\n", DODECAHEDRON); break; default: break; } } VMI::histogram = VMI::initHistogram( VMI::mesh->currentNumTriangles, VMI::numCameras); VMI::initialIs = VMI::initIs(VMI::numCameras); } //--------------------------------------------------------------------------- /// Class destructor. //--------------------------------------------------------------------------- ViewPointDrivenSimplifier::~ViewPointDrivenSimplifier(void) { // Free memory VMI::freeMemory(); } /// Copy constructor //ViewPointDrivenSimplifier(const ViewPointDrivenSimplifier&); /// Assignment operator //ViewPointDrivenSimplifier& operator =(const ViewPointDrivenSimplifier&); /// Starts the simplification process. Receives as a parameter the LOD factor in a range of [0,1]. Implements the Simplifier::Simplify method to perform an image based simplification. void ViewPointDrivenSimplifier::Simplify(Real percent) { VMI::numDemandedTriangles = (int)(VMI::mesh->numTriangles * percent); if ((VMI::numDemandedTriangles == 0) || (VMI::numDemandedTriangles >= VMI::mesh->currentNumTriangles)) { printf("Illegal number of triangles.\n"); } VMI::display(); // Load a geometry mesh for vmi mesh. loadMesh(); // Sort bones. sortBones(); GetMeshSimpSequence(); } //--------------------------------------------------------------------------- /// Starts the simplification process. Receives as a parameter the number of vertices of the resulting mesh. Implements the Simplifier::Simplify method to perform an image based simplification. //--------------------------------------------------------------------------- void ViewPointDrivenSimplifier::Simplify(uint32 numVertices) { float percent; percent = (numVertices * 100.0) / VMI::mesh->numVertices; VMI::numDemandedTriangles = (int)(VMI::mesh->numTriangles * (percent / 100)); if ((VMI::numDemandedTriangles == 0) || (VMI::numDemandedTriangles >= VMI::mesh->currentNumTriangles)) { printf("Illegal number of triangles.\n"); } VMI::display(); // Load a geometry mesh for vmi mesh. loadMesh(); // Sort bones. sortBones(); GetMeshSimpSequence(); } // Returns the simplified mesh. //Mesh * ViewPointDrivenSimplifier::GetMesh() //{ // return mGeoMesh; //} //--------------------------------------------------------------------------- // Gets the VMI mesh simplification sequence. //--------------------------------------------------------------------------- void ViewPointDrivenSimplifier::GetMeshSimpSequence() { unsigned int j = 0; MeshSimplificationSequence::Step current_step; msimpsequence = new MeshSimplificationSequence(); // For each simplification step. for (unsigned int i = 0; i < VMI::mVMISteps.size(); i++) { current_step.mV0 = VMI::mVMISteps[i].mV0; current_step.mV1 = VMI::mVMISteps[i].mV1; current_step.mT0 = VMI::mVMISteps[i].mT0; current_step.mT1 = VMI::mVMISteps[i].mT1; current_step.x = VMI::mVMISteps[i].x; current_step.y = VMI::mVMISteps[i].y; current_step.z = VMI::mVMISteps[i].z; current_step.obligatorio = VMI::mVMISteps[i].obligatory; // For each face modificated. while (j < VMI::mVMISteps[i].mModfaces.size()) { current_step.mModfaces.push_back(VMI::mVMISteps[i].mModfaces[j]); j++; } msimpsequence->mSteps.push_back(current_step); } } //--------------------------------------------------------------------------- // Class for join triangle holes. //--------------------------------------------------------------------------- class _coord_ { public: float x,y,z; inline _coord_( float x = 0.0f, float y = 0.0f, float z = 0.0f) { this->x = x; this->y = y; this->z = z; } inline _coord_(const _coord_ &f) { x = f.x; y=f.y; z=f.z; } inline _coord_ & operator=(const _coord_ &f) { x = f.x; y = f.y; z = f.z; return *this; } inline bool operator<(const _coord_ &f) const { if (xf.x+0.0001) return false; if (yf.y+0.0001) return false; if (zf.z+0.0001) return false; return false; } }; //--------------------------------------------------------------------------- // Initialize the mesh of VMI module. //--------------------------------------------------------------------------- VMI::Mesh * ViewPointDrivenSimplifier::initMeshStructure(Mesh *geoMesh) { GLuint i, j, v1, v2, v3, n, e, t; VMI::Mesh *vmi_mesh; SubMesh *geosubmesh; VertexBuffer *vertex_buffer; int mesh_index_count; int index; // Reallocate memory for vmi mesh object. vmi_mesh = (VMI::Mesh *)malloc(sizeof(VMI::Mesh)); if (vmi_mesh == NULL) { fprintf(stderr, "Error allocating memory\n"); exit(1); } // Shared vertex buffer. vertex_buffer = geoMesh->mVertexBuffer; // Reallocate memory for vertices of mesh. vmi_mesh->vertices = (VMI::Vertex *)malloc(sizeof(VMI::Vertex) * vertex_buffer->mVertexCount); if (vmi_mesh->vertices == NULL) { fprintf(stderr, "Error allocating memory\n"); exit(1); } // Initialize mesh index count. mesh_index_count = 0; for (unsigned int submesh = 0; submesh < mGeoMesh->mSubMeshCount; submesh++) { mesh_index_count += int(mGeoMesh->mSubMesh[submesh].mIndexCount); } // Reallocate memory for indices. vmi_mesh->triangles = (VMI::Triangle *)malloc(sizeof(VMI::Triangle) * (mesh_index_count / 3)); if (vmi_mesh->triangles == NULL) { fprintf(stderr, "Error allocating memory\n"); exit(1); } printf("Adding vertices..."); // Fill up vertices. std::map<_coord_, int> uniquevertices; std::map newindices; for (i = 0; i < vertex_buffer->mVertexCount; i++) { _coord_ vertex_aux; vertex_aux.x= vertex_buffer->mPosition[i].x; vertex_aux.y= vertex_buffer->mPosition[i].y; vertex_aux.z= vertex_buffer->mPosition[i].z; //New index if (uniquevertices.find(vertex_aux)==uniquevertices.end()) { uniquevertices[vertex_aux]= i; newindices[i]= i; VMI::INTVECTOR intvectoraux; intvectoraux.push_back(i); VMI::inversemap[i]= intvectoraux; }else//The map of unique vertices already contains this vertex { int newindex= uniquevertices[vertex_aux]; newindices[i]= newindex; VMI::inversemap[newindex].push_back(i);; } // Vertices start at 0. vmi_mesh->vertices[i].x = vertex_buffer->mPosition[i].x; vmi_mesh->vertices[i].y = vertex_buffer->mPosition[i].y; vmi_mesh->vertices[i].z = vertex_buffer->mPosition[i].z; vmi_mesh->vertices[i].numTriangles = 0; vmi_mesh->vertices[i].triangles = NULL; vmi_mesh->vertices[i].enable = GL_TRUE; } printf("Ok\n"); vmi_mesh->numVertices = int(vertex_buffer->mVertexCount); vmi_mesh->currentNumVertices = int(vertex_buffer->mVertexCount); // Fill up triangles. printf("Adding triangles..."); // Initialize index of triangle. index = 0; // For each submesh. for (unsigned int submesh = 0; submesh < mGeoMesh->mSubMeshCount; submesh++) { // Gets actual submesh. geosubmesh = &mGeoMesh->mSubMesh[submesh]; for (i = 0; i < (geosubmesh->mIndexCount / 3); i++) { vmi_mesh->triangles[index].id = index; vmi_mesh->triangles[index].submesh = submesh; vmi_mesh->triangles[index].indices[0] = newindices[geosubmesh->mIndex[(3 * i)]]; vmi_mesh->triangles[index].indices[1] = newindices[geosubmesh->mIndex[(3 * i) + 1]]; vmi_mesh->triangles[index].indices[2] = newindices[geosubmesh->mIndex[(3 * i) + 2]]; vmi_mesh->triangles[index].area = computeTriangleArea(vmi_mesh->vertices, &vmi_mesh->triangles[index]); //printf("\n%d a: %f",index , vmi_mesh->triangles[index].area); computeTriangleNormal(vmi_mesh->vertices, &vmi_mesh->triangles[index]); vmi_mesh->triangles[index].saliency = 0.0; vmi_mesh->triangles[index].enable = GL_TRUE; for (j = 0; j < 3; j++) { // Adding triangle index adjacent to 3 vertices. v1 = vmi_mesh->triangles[index].indices[j]; // Reallocate memory for the new adjacent triangle. vmi_mesh->vertices[v1].triangles = (GLuint *)realloc(vmi_mesh->vertices[v1].triangles, (vmi_mesh->vertices[v1].numTriangles + 1) * sizeof(GLuint)); VMI::addItem((int *)vmi_mesh->vertices[v1].triangles, (int *)&vmi_mesh->vertices[v1].numTriangles, index); } // Increments triangle count. index++; } } printf("Ok\n"); vmi_mesh->numTriangles = mesh_index_count / 3; vmi_mesh->currentNumTriangles = mesh_index_count / 3; printf("Num Triangles: %d\n",vmi_mesh->numTriangles); // E = 3 T / 2 vmi_mesh->edges = (VMI::Edge *)malloc(sizeof(VMI::Edge) * vmi_mesh->numTriangles * 3); if (vmi_mesh->edges == NULL) { fprintf(stderr, "Error allocating memory\n"); exit(1); } // Init edges for (i=0; inumTriangles * 3; i++) { vmi_mesh->edges[i].triangles = NULL; vmi_mesh->edges[i].numTriangles = 0; } printf("Adding edges..."); n = 0; for (i=0; inumTriangles; i++) { t = 0; v1 = vmi_mesh->triangles[i].indices[0]; v2 = vmi_mesh->triangles[i].indices[1]; v3 = vmi_mesh->triangles[i].indices[2]; if ((e = findEdge(vmi_mesh->edges, n, v1, v2)) == -1) { vmi_mesh->edges[n].u = v1; vmi_mesh->edges[n].v = v2; vmi_mesh->edges[n].enable = GL_TRUE; // Reallocate memory for the new adjacent triangle vmi_mesh->edges[n].triangles = (GLuint *)realloc(vmi_mesh->edges[n].triangles, (vmi_mesh->edges[n].numTriangles + 1) * sizeof(GLuint)); // Adding triangle i adjacent to edge n VMI::addItem((int *)vmi_mesh->edges[n].triangles, (int *)&vmi_mesh->edges[n].numTriangles, i); //printf("n:%d i:%d\n", n, i); // Adding edge n adjacent to triangle i VMI::addItem((int *)vmi_mesh->triangles[i].edges, (int *)&t, n); n++; } else { // Reallocate memory for the new adjacent triangle. vmi_mesh->edges[e].triangles = (GLuint *)realloc(vmi_mesh->edges[e].triangles, (vmi_mesh->edges[e].numTriangles + 1) * sizeof(GLuint)); // Adding triangle i adjacent to edge e VMI::addItem((int *)vmi_mesh->edges[e].triangles, (int *)&vmi_mesh->edges[e].numTriangles, i); //printf("n:%d i:%d\n", e, i); // Adding edge e adjacent to triangle i VMI::addItem((int *)vmi_mesh->triangles[i].edges, (int *)&t, e); } if ((e = findEdge(vmi_mesh->edges, n, v2, v3)) == -1) { vmi_mesh->edges[n].u = v2; vmi_mesh->edges[n].v = v3; vmi_mesh->edges[n].enable = GL_TRUE; // Reallocate memory for the new adjacent triangle vmi_mesh->edges[n].triangles = (GLuint *)realloc(vmi_mesh->edges[n].triangles, (vmi_mesh->edges[n].numTriangles + 1) * sizeof(GLuint)); // Adding triangle i adjacent to edge n VMI::addItem((int *)vmi_mesh->edges[n].triangles, (int *)&vmi_mesh->edges[n].numTriangles, i); //printf("n:%d i:%d\n", n, i); // Adding edge n adjacent to triangle i VMI::addItem((int *)vmi_mesh->triangles[i].edges, (int *)&t, n); n++; } else { // Reallocate memory for the new adjacent triangle vmi_mesh->edges[e].triangles = (GLuint *)realloc(vmi_mesh->edges[e].triangles, (vmi_mesh->edges[e].numTriangles + 1) * sizeof(GLuint)); // Adding triangle i adjacent to edge e VMI::addItem((int *)vmi_mesh->edges[e].triangles, (int *)&vmi_mesh->edges[e].numTriangles, i); //printf("n:%d i:%d\n", e, i); // Adding edge e adjacent to triangle i VMI::addItem((int *)vmi_mesh->triangles[i].edges, (int *)&t, e); } if ((e = findEdge(vmi_mesh->edges, n, v3, v1)) == -1) { vmi_mesh->edges[n].u = v3; vmi_mesh->edges[n].v = v1; vmi_mesh->edges[n].enable = GL_TRUE; // Reallocate memory for the new adjacent triangle vmi_mesh->edges[n].triangles = (GLuint *)realloc(vmi_mesh->edges[n].triangles, (vmi_mesh->edges[n].numTriangles + 1) * sizeof(GLuint)); // Adding triangle i adjacent to edge n VMI::addItem((int *)vmi_mesh->edges[n].triangles, (int *)&vmi_mesh->edges[n].numTriangles, i); //printf("n:%d i:%d\n", n, i); // Adding edge n adjacent to triangle i VMI::addItem((int *)vmi_mesh->triangles[i].edges, (int *)&t, n); n++; } else { // Reallocate memory for the new adjacent triangle vmi_mesh->edges[e].triangles = (GLuint *)realloc(vmi_mesh->edges[e].triangles, (vmi_mesh->edges[e].numTriangles + 1) * sizeof(GLuint)); // Adding triangle i adjacent to edge e VMI::addItem((int *)vmi_mesh->edges[e].triangles, (int *)&vmi_mesh->edges[e].numTriangles, i); //printf("n:%d i:%d\n", e, i); // Adding edge e adjacent to triangle i VMI::addItem((int *)vmi_mesh->triangles[i].edges, (int *)&t, e); } } printf("Ok\n"); vmi_mesh->numEdges = n; return vmi_mesh; } //--------------------------------------------------------------------------- // Gets the geometry mesh of a the vmi mesh given. //--------------------------------------------------------------------------- void ViewPointDrivenSimplifier::loadMesh() { int num_indices; SubMesh *geosubmesh; VertexBuffer *vertex_buffer; // Gets old vertex buffer. vertex_buffer = mGeoMesh->mVertexBuffer; // Initialize auxiliar vertex buffer. mVB = new VertexBuffer(); mVB->mPosition = new Vector3[VMI::mesh->currentNumVertices]; mVB->mNormal = new Vector3[VMI::mesh->currentNumVertices]; mVB->mTexCoords = new Vector2[VMI::mesh->currentNumVertices]; mVB->mVertexInfo = vertex_buffer->mVertexInfo; // For each submesh. for (unsigned int submesh = 0; submesh < mGeoMesh->mSubMeshCount; submesh++) { geosubmesh = &mGeoMesh->mSubMesh[submesh]; delete []geosubmesh->mIndex; // Initialize submesh index count; num_indices = 0; // For each triangle. for (unsigned int i = 0; i < VMI::mesh->numTriangles; i++) { // If is enable and of the current submesh. if ((VMI::mesh->triangles[i].enable) && (VMI::mesh->triangles[i].submesh == submesh)) { // Increments submesh index count. num_indices += 3; } } geosubmesh->mIndexCount = num_indices; geosubmesh->mIndex = new Index[geosubmesh->mIndexCount]; // Initialize number of indices. num_indices = 0; // Fill up indices. for (unsigned int i = 0; i < VMI::mesh->numTriangles; i++) { if ((VMI::mesh->triangles[i].enable) && (VMI::mesh->triangles[i].submesh == submesh)) { geosubmesh->mIndex[num_indices++] = VMI::mesh->triangles[i].indices[0]; geosubmesh->mIndex[num_indices++] = VMI::mesh->triangles[i].indices[1]; geosubmesh->mIndex[num_indices++] = VMI::mesh->triangles[i].indices[2]; } } // For each index. for (unsigned int i = 0; i < geosubmesh->mIndexCount; i++) { findVertex(submesh,i); } geosubmesh->mVertexBuffer = mVB; } delete vertex_buffer; mGeoMesh->mVertexBuffer = mVB; } //--------------------------------------------------------------------------- // Find vertex in auxiliar vertex buffer. //--------------------------------------------------------------------------- void ViewPointDrivenSimplifier::findVertex(size_t submesh, size_t elem) { bool found; int index; unsigned int i; int new_elem; VertexBuffer *vertex_buffer; found = false; // Shared vertex buffer. vertex_buffer = mGeoMesh->mVertexBuffer; index = mGeoMesh->mSubMesh[submesh].mIndex[elem]; i = 0; while (!found && (i < mVB->mVertexCount)) { if ((VMI::mesh->vertices[index].x == mVB->mPosition[i].x) && (VMI::mesh->vertices[index].y == mVB->mPosition[i].y) && (VMI::mesh->vertices[index].z == mVB->mPosition[i].z)) { found = true; // Update index. mGeoMesh->mSubMesh[submesh].mIndex[elem] = i; } // Increments index. i++; } if (!found) { // Last element. new_elem = int(mVB->mVertexCount); // Add to last. mVB->mPosition[new_elem].x = VMI::mesh->vertices[index].x; mVB->mPosition[new_elem].y = VMI::mesh->vertices[index].y; mVB->mPosition[new_elem].z = VMI::mesh->vertices[index].z; mVB->mNormal[new_elem].x = vertex_buffer->mNormal[index].x; mVB->mNormal[new_elem].y = vertex_buffer->mNormal[index].y; mVB->mNormal[new_elem].z = vertex_buffer->mNormal[index].z; mVB->mTexCoords[new_elem].x = vertex_buffer->mTexCoords[index].x; mVB->mTexCoords[new_elem].y = vertex_buffer->mTexCoords[index].y; // Update index. mGeoMesh->mSubMesh[submesh].mIndex[elem] = new_elem; // Increments vertex count. mVB->mVertexCount++; } }