void ofxObjLoader::save(string path, ofMesh& mesh){ path = ofToDataPath(path); GLuint writeMode = GLM_NONE; GLMmodel* m = new GLMmodel(); if(mesh.getNumVertices() > 0){ m->numvertices = mesh.getNumVertices(); m->vertices = new GLfloat[m->numvertices*3+1]; memcpy(&m->vertices[3], &mesh.getVertices()[0].x, sizeof(ofVec3f) * mesh.getNumVertices()); } else { ofLogError("ofxObjLoader::save -- No vertices to save!"); return; } if(mesh.getNumNormals() > 0){ m->numnormals = mesh.getNumNormals(); m->normals = new GLfloat[m->numnormals*3+1]; memcpy(&m->normals[3], &mesh.getNormals()[0].x, sizeof(ofVec3f)*mesh.getNumNormals()); writeMode |= GLM_SMOOTH; } if(mesh.getNumTexCoords() > 0){ m->numtexcoords = mesh.getNumTexCoords(); m->texcoords = new GLfloat[m->numtexcoords*2+1]; memcpy(&m->texcoords[2], &mesh.getTexCoords()[0].x, sizeof(ofVec2f)*mesh.getNumTexCoords()); writeMode |= GLM_TEXTURE; } if(mesh.getNumIndices() > 0){ //create triangles m->numtriangles = mesh.getNumIndices()/3; m->triangles = new GLMtriangle[m->numtriangles]; //add them all to one group m->groups = new GLMgroup(); m->groups->next = NULL; m->groups->material = NULL; string name = "ofMesh"; m->groups->name = (char*)malloc(sizeof(char) * name.length()+1); strcpy(m->groups->name, name.c_str()); m->groups->numtriangles = mesh.getNumIndices()/3; m->groups->triangles = new GLuint[m->groups->numtriangles]; m->numgroups = 1; for(int i = 0; i < mesh.getNumIndices()/3; i++){ memcpy(m->triangles[i].vindices, &mesh.getIndices()[i*3], sizeof(GLuint)*3); memcpy(m->triangles[i].nindices, &mesh.getIndices()[i*3], sizeof(GLuint)*3); memcpy(m->triangles[i].tindices, &mesh.getIndices()[i*3], sizeof(GLuint)*3); m->groups->triangles[i] = i; } } glmWriteOBJ(m, (char*)path.c_str(), writeMode); glmDelete(m); }
//-------------------------------------------------------------- bool load(const string& path, ofMesh& mesh) { ofFile file(path, ofFile::ReadOnly, true); if (!file.exists()) { ofLogError("ofxBinaryMesh::load") << "Cannot open file at " << path; return false; } mesh.clear(); int numVerts = 0; file.read((char *)(&numVerts), sizeof(int)); if (numVerts > 0) { mesh.getVertices().resize(numVerts); file.read((char *)(&(mesh.getVertices())[0]), sizeof(ofPoint) * numVerts); } int numNormals = 0; file.read((char *)(&numNormals), sizeof(int)); if (numNormals > 0) { mesh.getNormals().resize(numNormals); file.read((char *)(&(mesh.getNormals())[0]), sizeof(ofPoint) * numNormals); } int numTexCoords = 0; file.read((char *)(&numTexCoords), sizeof(int)); if (numTexCoords > 0) { mesh.getTexCoords().resize(numTexCoords); file.read((char *)(&(mesh.getTexCoords())[0]), sizeof(ofVec2f) * numTexCoords); } int numColors = 0; file.read((char *)(&numColors), sizeof(int)); if (numColors > 0) { mesh.getColors().resize(numColors); file.read((char *)(&(mesh.getColors())[0]), sizeof(ofFloatColor) * numColors); } int numIndices = 0; file.read((char *)(&numIndices), sizeof(int)); if (numIndices > 0) { mesh.getIndices().resize(numIndices); file.read((char *)(&(mesh.getIndices())[0]), sizeof(ofIndexType) * numIndices); } file.close(); return true; }
void MeshHelper::fuseNeighbours( ofMesh& outputMesh, const ofMesh& sourceMesh, float fuseDistance ) { //@todo tex coords, normals assert( sourceMesh.getMode() == OF_PRIMITIVE_TRIANGLES ); if ( fuseDistance < 0 ) { // fuse close-enough vertices // first define 'close enough' as 1/10000 of smallest dimension of the bounding box width/height/depth ofVec3f tlb, brf; // top left back, bottom right front calculateAABoundingBox( sourceMesh, tlb, brf ); float minDimension = min(brf.x-tlb.x,min(brf.y-tlb.y, brf.z-tlb.z)); fuseDistance = minDimension * 0.00001f; } // now fuse map<int,int> fused; vector<ofVec3f> vertices; for ( int i=0; i<sourceMesh.getNumVertices(); i++ ) { const ofVec3f& vertex = sourceMesh.getVertex(i); //vertex.rotate(10, 10, 10); bool didFuse = false; for ( int j=0; j<vertices.size(); j++ ) { if ( (vertex-vertices[j]).length()<fuseDistance ) { // fuse i to j fused[i] = j; didFuse = true; break; } } if ( !didFuse ) { vertices.push_back( vertex ); fused[i] = vertices.size()-1; } } // build the output mesh outputMesh.clear(); outputMesh.addVertices(vertices); if ( sourceMesh.getNumIndices() > 0 ) { // walk through indices to build up the new mesh const vector<ofIndexType>& indices = sourceMesh.getIndices(); for ( int i=0; i<indices.size(); i+=3 ) { assert( fused.find( indices[i] ) != fused.end() ); assert( fused.find( indices[i+1] ) != fused.end() ); assert( fused.find( indices[i+2] ) != fused.end() ); outputMesh.addTriangle( fused[indices[i]], fused[indices[i+1]], fused[indices[i+2]] ); } } else { // triangles are just triples of vertices for ( int i=0; i<sourceMesh.getNumVertices(); i+=3 ) { outputMesh.addTriangle( fused[i], fused[i+1], fused[i+2] ); } } ofLogNotice("MeshHelper") << "fuseNeighbours: input " << sourceMesh.getNumVertices() << " vertices/" << sourceMesh.getNumIndices() << " indices, output " << outputMesh.getNumVertices() << " vertices/" << outputMesh.getNumIndices() << " indices"; }
void MeshHelper::fuseNeighbours( ofMesh& mesh, float fuseDistance ) { //@todo tex coords, normals assert( mesh.getMode() == OF_PRIMITIVE_TRIANGLES ); int oldNumVerts = mesh.getNumVertices(); int oldNumIndices = mesh.getNumIndices(); if ( fuseDistance < 0 ) { // fuse close-enough vertices // first define 'close enough' as 1/10000 of smallest dimension of the bounding box width/height/depth ofVec3f tlb, brf; // top left back, bottom right front calculateAABoundingBox( mesh, tlb, brf ); float minDimension = min(brf.x-tlb.x,min(brf.y-tlb.y, brf.z-tlb.z)); fuseDistance = minDimension * 0.00001f; } // now fuse map<ofIndexType,ofIndexType> fused; vector<ofVec3f> newVertices; vector<ofIndexType> remove; for ( ofIndexType i=0; i<mesh.getNumVertices(); i++ ) { const ofVec3f& vertex = mesh.getVertex(i); // look at all the earlier vertices bool didFuse = false; for ( ofIndexType j=0; j<newVertices.size(); j++ ) { if ( (vertex-newVertices[j]).length()<fuseDistance ) { // fuse i to j fused[i] = j; remove.push_back(i); didFuse = true; break; } } if ( !didFuse ) { newVertices.push_back( vertex ); fused[i] = newVertices.size()-1; } } // update indices for ( int i=0; i<mesh.getNumIndices(); i++ ) { ofIndexType originalIndex = mesh.getIndex(i); assert( fused.find( originalIndex ) != fused.end() ); if ( fused.find(originalIndex) != fused.end() ) { mesh.getIndices()[i] = fused[originalIndex]; } } // remove the fused for ( int i=remove.size()-1; i>=0; i-- ) { mesh.removeVertex( remove[i] ); } ofLogNotice("MeshHelper") << "fuseNeighbours inplace: input " << oldNumVerts << " vertices/" << oldNumIndices << " indices, output " << mesh.getNumVertices() << " vertices/" << mesh.getNumIndices() << " indices"; }
//-------------------------------------------------------------- void ofxBulletSoftTriMesh::create( ofxBulletWorldSoft* a_world, ofMesh& aMesh, btTransform &a_bt_tr, float a_mass ) { if(a_world == NULL) { ofLogError("ofxBulletSoftTriMesh") << "create(): a_world param is NULL"; return; } if( aMesh.getMode() != OF_PRIMITIVE_TRIANGLES ) { ofLogError("ofxBulletSoftTriMesh") << " only excepts meshes that are triangles"; return; } _world = a_world; _cachedMesh.clear(); _cachedMesh = aMesh; if( bullet_vertices != NULL ) { delete bullet_vertices; bullet_vertices = NULL; } int vertStride = sizeof(btVector3); int indexStride = 3*sizeof(int); int totalVerts = (int)aMesh.getNumVertices(); int totalIndices = (int)aMesh.getNumIndices(); bullet_vertices = new btScalar[ totalVerts * 3 ]; int* bullet_indices = new int[ totalIndices ]; auto& tverts = aMesh.getVertices(); vector< ofIndexType >& tindices = aMesh.getIndices(); for( int i = 0; i < totalVerts; i++ ) { bullet_vertices[i*3+0] = tverts[i].x; bullet_vertices[i*3+1] = tverts[i].y; bullet_vertices[i*3+2] = tverts[i].z; } for( int i = 0; i < totalIndices; i++ ) { bullet_indices[i] = tindices[i]; } _softBody = btSoftBodyHelpers::CreateFromTriMesh( _world->getInfo(), bullet_vertices, bullet_indices, totalIndices/3 ); _softBody->transform( a_bt_tr ); setMass( a_mass, true ); setCreated(_softBody); createInternalUserData(); delete [] bullet_indices; }
void MeshHelper::appendMesh( ofMesh& targetMesh, const ofMesh& toAppend, bool fuse ) { ofIndexType indexOffset = targetMesh.getNumVertices(); targetMesh.addVertices( toAppend.getVertices() ); // append indices const vector<ofIndexType>& indices = toAppend.getIndices(); for ( int i=0; i<indices.size(); i++ ) { targetMesh.addIndex( indices[i]+indexOffset ); } if ( fuse ) fuseNeighbours(targetMesh); }
void buildNormalsAverage(ofMesh& mesh) { vector<ofIndexType>& indices = mesh.getIndices(); vector<ofVec3f> normals(mesh.getNumVertices()); for(int i = 0; i < indices.size(); i += 3) { int i0 = indices[i + 0], i1 = indices[i + 1], i2 = indices[i + 2]; ofVec3f normal = getNormal(mesh.getVertices()[i0], mesh.getVertices()[i1], mesh.getVertices()[i2]); normals[i0] += normal; normals[i1] += normal; normals[i2] += normal; } for(int i = 0; i < normals.size(); i++) { normals[i].normalize(); } mesh.addNormals(normals); }
void ofxMesh::fromMesh(const ofMesh & mesh){ if (mesh.hasVertices()) { getVertices()=mesh.getVertices(); } if (mesh.hasColors()) { getColors()=mesh.getColors(); } if (mesh.hasNormals()) { getNormals()=mesh.getNormals(); } if (mesh.hasTexCoords()) { getTexCoords()=mesh.getTexCoords(); } if (mesh.hasIndices()) { getIndices()=mesh.getIndices(); } }
void ofxMesh::toMesh(ofMesh & mesh){ mesh.clear(); if (hasVertices()) { mesh.getVertices()=getVertices(); } if (hasColors()) { mesh.getColors()=getColors(); } if (hasNormals()) { mesh.getNormals()=getNormals(); } if (hasTexCoords()) { mesh.getTexCoords()=getTexCoords(); } if (hasIndices()) { mesh.getIndices()=getIndices(); } }
glmMesh toGlm(const ofMesh &_mesh){ glmMesh mesh; for (auto &it : _mesh.getColors()) { mesh.addColor(toGlm(it)); } for (auto &it : _mesh.getVertices()) { mesh.addVertex(toGlm(it)); } for (auto &it : _mesh.getNormals()) { mesh.addNormal(toGlm(it)); } for (auto &it : _mesh.getTexCoords()) { mesh.addTexCoord(toGlm(it)); } for (auto &it : _mesh.getIndices()) { mesh.addIndex(toGlm(it)); } GLenum drawMode = ofGetGLPrimitiveMode(_mesh.getMode()); if (drawMode == GL_POINTS) { mesh.setDrawMode(POINTS); } else if (drawMode == GL_LINES){ mesh.setDrawMode(LINES); } else if (drawMode == GL_LINE_STRIP){ mesh.setDrawMode(LINE_STRIP); } else if (drawMode == GL_TRIANGLES){ mesh.setDrawMode(TRIANGLES); } else if (drawMode == GL_TRIANGLE_STRIP){ mesh.setDrawMode(TRIANGLE_STRIP); } return mesh; }
void CloudsVisualSystem3DModelLoader::facetMesh( ofMesh& smoothedMesh, ofMesh& targetMesh ) { //get our vertex, uv and face info vector<ofVec3f>& v = smoothedMesh.getVertices(); vector<ofVec2f>& uv = smoothedMesh.getTexCoords(); vector<ofIndexType>& indices = smoothedMesh.getIndices(); bool hasTC = smoothedMesh.getNumTexCoords(); //use these to store our new mesh info vector<ofVec3f> facetedVertices( indices.size() ); vector<ofVec3f> facetedNormals( indices.size() ); vector<ofVec2f> facetedTexCoords; if(hasTC){ facetedTexCoords.resize( indices.size() ); } vector<ofIndexType> facetedIndices( indices.size() ); //store vertex and uv data for (int i=0; i < indices.size(); i++) { facetedIndices[i] = i; facetedVertices[i] = v[indices[i]]; if(hasTC) facetedTexCoords[i] = uv[indices[i]]; } //calculate our face normals ofVec3f n; for (int i=0; i < facetedIndices.size(); i+=3) { n = normalFrom3Points( facetedVertices[i], facetedVertices[i+1], facetedVertices[i+2]); facetedNormals[i] = n; facetedNormals[i+1] = n; facetedNormals[i+2] = n; } //setup our faceted mesh. this should still work if our targetMesh is our smoothMesh targetMesh.clear(); targetMesh.addVertices( facetedVertices ); targetMesh.addNormals( facetedNormals ); if(hasTC) targetMesh.addTexCoords( facetedTexCoords ); targetMesh.addIndices( facetedIndices ); }
// adjacency list of triangulation vector< set<size_t> > ofxDelaunay2D::adjacencyForTriMesh(ofMesh &triMesh) { vector< set<size_t> > adjacency; adjacency.reserve(triMesh.getNumVertices()); for(size_t i=0; i<(size_t)(triMesh.getNumVertices()); ++i) { adjacency.push_back(set<size_t>()); } for(int i0=0; i0<(int)triMesh.getIndices().size() - 2; i0+=3) { size_t i = triMesh.getIndex(i0); size_t j = triMesh.getIndex(i0 + 1); size_t k = triMesh.getIndex(i0 + 2); adjacency[i].insert(j); adjacency[j].insert(i); adjacency[i].insert(k); adjacency[k].insert(i); adjacency[j].insert(k); adjacency[k].insert(j); } return adjacency; }
//-------------------------------------------------------------- void save(const string& path, const ofMesh& mesh) { ofFile file(path, ofFile::WriteOnly, true); int numVerts = mesh.getNumVertices(); file.write((char *)(&numVerts), sizeof(int)); if (numVerts > 0) { file.write((char *)(&(mesh.getVertices())[0]), sizeof(ofPoint) * numVerts); } int numNormals = mesh.getNumNormals(); file.write((char *)(&numNormals), sizeof(int)); if (numNormals > 0) { file.write((char *)(&(mesh.getNormals())[0]), sizeof(ofPoint) * numNormals); } int numTexCoords = mesh.getNumTexCoords(); file.write((char *)(&numTexCoords), sizeof(int)); if (numTexCoords > 0) { file.write((char *)(&(mesh.getTexCoords())[0]), sizeof(ofVec2f) * numTexCoords); } int numColors = mesh.getNumColors(); file.write((char *)(&numColors), sizeof(int)); if (numColors > 0) { file.write((char *)(&(mesh.getColors())[0]), sizeof(ofFloatColor) * numColors); } int numIndices = mesh.getNumIndices(); file.write((char *)(&numIndices), sizeof(int)); if (numIndices > 0) { file.write((char *)(&(mesh.getIndices())[0]), sizeof(ofIndexType) * numIndices); } file.close(); }
//-------------------------------------------------------------- bool ofxBulletCustomShape::addMesh( ofMesh a_mesh, ofVec3f a_localScaling, bool a_bUseConvexHull ) { if(a_mesh.getMode() != OF_PRIMITIVE_TRIANGLES) { ofLog( OF_LOG_ERROR, "ofxBulletCustomShape :: addMesh : mesh must be set to OF_PRIMITIVE_TRIANGLES!! aborting"); return false; } if(_bAdded == true) { ofLog( OF_LOG_ERROR, "ofxBulletCustomShape :: addMesh : can not call after calling add()" ); return false; } btVector3 localScaling( a_localScaling.x, a_localScaling.y, a_localScaling.z ); auto indicies = a_mesh.getIndices(); auto verticies = a_mesh.getVertices(); btVector3 centroid = btVector3(0, 0, 0); if(!a_bUseConvexHull) { for(int i = 0; i < verticies.size(); i++) { btVector3 tempVec = btVector3(verticies[i].x, verticies[i].y, verticies[i].z); tempVec *= localScaling; centroid += tempVec; } centroid /= (float)verticies.size(); vector<btVector3> newVerts; for ( int i = 0; i < indicies.size(); i++) { btVector3 vertex( verticies[indicies[i]].x, verticies[indicies[i]].y, verticies[indicies[i]].z); vertex *= localScaling; vertex -= centroid; newVerts.push_back(vertex); } btConvexHullShape* convexShape = new btConvexHullShape(&(newVerts[0].getX()), newVerts.size()); convexShape->setMargin( 0.01f ); shapes.push_back( convexShape ); centroids.push_back( ofVec3f(centroid.getX(), centroid.getY(), centroid.getZ()) ); } else { // HULL Building code from example ConvexDecompositionDemo.cpp // btTriangleMesh* trimesh = new btTriangleMesh(); for ( int i = 0; i < indicies.size()/3; i++) { int index0 = indicies[i*3]; int index1 = indicies[i*3+1]; int index2 = indicies[i*3+2]; btVector3 vertex0( verticies[index0].x, verticies[index0].y, verticies[index0].z ); btVector3 vertex1( verticies[index1].x, verticies[index1].y, verticies[index1].z ); btVector3 vertex2( verticies[index2].x, verticies[index2].y, verticies[index2].z ); vertex0 *= localScaling; vertex1 *= localScaling; vertex2 *= localScaling; trimesh->addTriangle(vertex0, vertex1, vertex2); } //cout << "ofxBulletCustomShape :: addMesh : input triangles = " << trimesh->getNumTriangles() << endl; //cout << "ofxBulletCustomShape :: addMesh : input indicies = " << indicies.size() << endl; //cout << "ofxBulletCustomShape :: addMesh : input verticies = " << verticies.size() << endl; btConvexShape* tmpConvexShape = new btConvexTriangleMeshShape(trimesh); //create a hull approximation btShapeHull* hull = new btShapeHull(tmpConvexShape); btScalar margin = tmpConvexShape->getMargin(); hull->buildHull(margin); tmpConvexShape->setUserPointer(hull); centroid = btVector3(0., 0., 0.); for (int i = 0; i < hull->numVertices(); i++) { centroid += hull->getVertexPointer()[i]; } centroid /= (float)hull->numVertices(); //printf("ofxBulletCustomShape :: addMesh : new hull numTriangles = %d\n", hull->numTriangles()); //printf("ofxBulletCustomShape :: addMesh : new hull numIndices = %d\n", hull->numIndices()); //printf("ofxBulletCustomShape :: addMesh : new hull numVertices = %d\n", hull->numVertices()); btConvexHullShape* convexShape = new btConvexHullShape(); for (int i=0;i<hull->numVertices();i++) { convexShape->addPoint(hull->getVertexPointer()[i] - centroid); } delete tmpConvexShape; delete hull; shapes.push_back( convexShape ); centroids.push_back( ofVec3f(centroid.getX(), centroid.getY(), centroid.getZ()) ); } return true; }
//-------------------------------------------------------------- void ofxBulletTriMeshShape::create( btDiscreteDynamicsWorld* a_world, ofMesh& aMesh, btTransform &a_bt_tr, float a_mass, glm::vec3 aAAbbMin, glm::vec3 aAAbbMax ) { if( aMesh.getMode() != OF_PRIMITIVE_TRIANGLES ) { ofLogWarning() << " ofxBulletTriMeshShape :: create : mesh must be using triangles, not creating!!" << endl; return; } if( aMesh.getNumIndices() < 3 ) { ofLogWarning() << " ofxBulletTriMeshShape :: create : mesh must have indices, not creating!" << endl; return; } if( !_bInited || _shape == NULL ) { int vertStride = sizeof(btVector3); int indexStride = 3*sizeof(int); totalVerts = (int)aMesh.getNumVertices(); totalIndices = (int)aMesh.getNumIndices(); const int totalTriangles = totalIndices / 3; if( bullet_indices != NULL ) { removeShape(); } if( bullet_vertices != NULL ) { removeShape(); } if( bullet_indexVertexArrays != NULL ) { removeShape(); } if( _shape != NULL ) { removeShape(); } bullet_vertices = new btVector3[ totalVerts ]; bullet_indices = new int[ totalIndices ]; auto& tverts = aMesh.getVertices(); auto& tindices = aMesh.getIndices(); for( int i = 0; i < totalVerts; i++ ) { bullet_vertices[i].setValue( tverts[i].x, tverts[i].y, tverts[i].z ); } for( int i = 0; i < totalIndices; i++ ) { bullet_indices[i] = (int)tindices[i]; } bullet_indexVertexArrays = new btTriangleIndexVertexArray(totalTriangles, bullet_indices, indexStride, totalVerts, (btScalar*) &bullet_vertices[0].x(), vertStride); // if you are having trouble with objects falling through, try passing in smaller or larger aabbMin and aabbMax // to something closer to the size of your object // // btVector3 aabbMin(-10000,-10000,-10000),aabbMax(10000,10000,10000); if( aAAbbMin.length() > 0 && aAAbbMax.length() > 0 ) { btVector3 aabbMin( aAAbbMin.x, aAAbbMin.y, aAAbbMin.z ); btVector3 aabbMax( aAAbbMax.x, aAAbbMax.y, aAAbbMax.z ); _shape = new btBvhTriangleMeshShape(bullet_indexVertexArrays, true, aabbMin, aabbMax ); } else { _shape = new btBvhTriangleMeshShape(bullet_indexVertexArrays, true, true ); } } ofxBulletRigidBody::create( a_world, _shape, a_bt_tr, a_mass ); createInternalUserData(); updateMesh( a_world, aMesh ); }
//-------------------------------------------------------------- void ofxBulletConvexShape::init( ofMesh& aMesh, ofVec3f a_localScaling, bool a_bUseConvexHull ) { _centroid.zero(); btVector3 centroid = btVector3(0, 0, 0); btVector3 localScaling( a_localScaling.x, a_localScaling.y, a_localScaling.z ); vector <ofIndexType> indicies = aMesh.getIndices(); vector <ofVec3f> verticies = aMesh.getVertices(); if(!a_bUseConvexHull) { for(int i = 0; i < verticies.size(); i++) { btVector3 tempVec = btVector3(verticies[i].x, verticies[i].y, verticies[i].z); tempVec *= localScaling; centroid += tempVec; } centroid /= (float)verticies.size(); vector<btVector3> newVerts; for ( int i = 0; i < indicies.size(); i++) { btVector3 vertex( verticies[indicies[i]].x, verticies[indicies[i]].y, verticies[indicies[i]].z); vertex *= localScaling; vertex -= centroid; newVerts.push_back(vertex); } btConvexHullShape* convexShape = new btConvexHullShape(&(newVerts[0].getX()), newVerts.size()); convexShape->setMargin( 0.01f ); _shape = convexShape; _centroid = ofVec3f(centroid.getX(), centroid.getY(), centroid.getZ() ); } else { // HULL Building code from example ConvexDecompositionDemo.cpp // btTriangleMesh* trimesh = new btTriangleMesh(); for ( int i = 0; i < indicies.size()/3; i++) { int index0 = indicies[i*3]; int index1 = indicies[i*3+1]; int index2 = indicies[i*3+2]; btVector3 vertex0( verticies[index0].x, verticies[index0].y, verticies[index0].z ); btVector3 vertex1( verticies[index1].x, verticies[index1].y, verticies[index1].z ); btVector3 vertex2( verticies[index2].x, verticies[index2].y, verticies[index2].z ); vertex0 *= localScaling; vertex1 *= localScaling; vertex2 *= localScaling; trimesh->addTriangle(vertex0, vertex1, vertex2); } btConvexShape* tmpConvexShape = new btConvexTriangleMeshShape(trimesh); //create a hull approximation btShapeHull* hull = new btShapeHull(tmpConvexShape); btScalar margin = tmpConvexShape->getMargin(); hull->buildHull(margin); tmpConvexShape->setUserPointer(hull); centroid = btVector3(0., 0., 0.); for (int i = 0; i < hull->numVertices(); i++) { centroid += hull->getVertexPointer()[i]; } centroid /= (float)hull->numVertices(); btConvexHullShape* convexShape = new btConvexHullShape(); for (int i=0;i<hull->numVertices();i++) { convexShape->addPoint(hull->getVertexPointer()[i] - centroid); } delete tmpConvexShape; delete hull; _shape = convexShape; _centroid = ofVec3f(centroid.getX(), centroid.getY(), centroid.getZ() ); } _bInited = true; }
vector<float> Model::generateTangents(ofMesh& mesh) { // Tangent generation adapted from algorithm at http://www.terathon.com/code/tangent.html auto vertices = mesh.getVertices(); auto normals = mesh.getNormals(); auto texcoords = mesh.getTexCoords(); auto triangles = mesh.getIndices(); vector<float> tangents(vertices.size()*4); vector<ofVec3f> tan1(vertices.size(), ofVec3f(0,0,0)); vector<ofVec3f> tan2(vertices.size(), ofVec3f(0,0,0)); for (unsigned int i = 0; i < triangles.size(); i += 3) { long i1 = triangles[i]; long i2 = triangles[i+1]; long i3 = triangles[i+2]; const ofVec3f& v1 = vertices[i1]; const ofVec3f& v2 = vertices[i2]; const ofVec3f& v3 = vertices[i3]; const ofVec2f& w1 = texcoords[i1]; const ofVec2f& w2 = texcoords[i2]; const ofVec2f& w3 = texcoords[i3]; float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = w2.x - w1.x; float s2 = w3.x - w1.x; float t1 = w2.y - w1.y; float t2 = w3.y - w1.y; float r = 1.0F / (s1 * t2 - s2 * t1); ofVec3f sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); ofVec3f tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (unsigned int i = 0; i < vertices.size(); ++i) { ofVec3f& n = normals[i]; ofVec3f& t = tan1[i]; // Gram-Schmidt orthogonalize auto tangent = (t - n * n.dot(t)).normalize(); // Calculate handedness float w = (n.cross(t).dot(tan2[i]) < 0.0) ? -1.0 : 1.0; tangents[i*4] = tangent.x; tangents[i*4+1] = tangent.y; tangents[i*4+2] = tangent.z; tangents[i*4+3] = w; } return tangents; }
void CloudsVisualSystem3DModelLoader::smoothMesh( ofMesh& facetedMesh, ofMesh& targetMesh, int precision) { cout << "smoothing mesh" << endl; //get our vertex, uv and face info vector<ofVec3f>& v = facetedMesh.getVertices(); vector<ofVec2f>& uv = facetedMesh.getTexCoords(); vector<ofIndexType>& indices = facetedMesh.getIndices(); bool hasTC = facetedMesh.getNumTexCoords(); //use these to store our new mesh info map<string, unsigned int> mergeMap; vector<ofVec3f> smoothVertices; vector<ofVec3f> smoothNormals; vector<ofVec2f> smoothTexCoords; vector<ofIndexType> smoothIndices; //merge our vertices by pointing near by vertices to the same index for (int i=0; i<v.size(); i++) { mergeMap[ vec3ToString( v[i], precision ) ] = i; } //fill our smoothed vertex array with merged vertices & tex coords smoothVertices.resize( mergeMap.size() ); if(hasTC) smoothTexCoords.resize( mergeMap.size() ); int smoothVertexCount = 0; for (map<string, unsigned int>::iterator it = mergeMap.begin(); it != mergeMap.end(); it++) { smoothVertices[smoothVertexCount] = v[it->second]; if(hasTC) smoothTexCoords[smoothVertexCount] = uv[it->second]; it->second = smoothVertexCount;//store our new vertex index smoothVertexCount++; } //reconstruct our faces by reassigning their indices to the merged vertices smoothIndices.resize( indices.size() ); for (int i=0; i<indices.size(); i++) { //use our old vertex poisition to retrieve our new index smoothIndices[i] = mergeMap[ vec3ToString( v[ indices[i] ], precision ) ]; } //calculate our normals smoothNormals.resize( smoothVertices.size() ); ofVec3f n; for (int i=0; i<smoothIndices.size(); i+=3) { n = normalFrom3Points( smoothVertices[smoothIndices[i]], smoothVertices[smoothIndices[i+1]], smoothVertices[smoothIndices[i+2]] ); smoothNormals[smoothIndices[i]] += n; smoothNormals[smoothIndices[i+1]] += n; smoothNormals[smoothIndices[i+2]] += n; } for (int i=0; i<smoothNormals.size(); i++) { smoothNormals[i].normalize(); } //setup our smoothed mesh. this should still work if our targetMesh is our facetedMesh targetMesh.clear(); targetMesh.addVertices( smoothVertices ); targetMesh.addNormals( smoothNormals ); if(hasTC) targetMesh.addTexCoords( smoothTexCoords ); targetMesh.addIndices( smoothIndices ); }