void testLoadObjFile() { string file; file += "# This is a comment\n"; file += "v -1.0 1.0 2.0\n"; file += "v -2.0 2.0 3.0\n"; file += "v -3.0 3.0 \\\n"; file += "4.0\n"; file += "v -4.0 4.0 5.0\n"; file += "v -5.0 5.0 6.0\n"; file += "f 1 2 3\n"; file += "f 2// 3/4/5 4//2\n"; file += "f -3 -2/3/4 -1\n"; file += "f 1 3\\\n"; file += "5 2\n"; PolygonalMesh mesh; stringstream stream(file); mesh.loadObjFile(stream); ASSERT(mesh.getNumVertices() == 5); ASSERT(mesh.getNumFaces() == 4); for (int i = 0; i < mesh.getNumVertices(); i++) { const Vec3& pos = mesh.getVertexPosition(i); ASSERT(pos[0] == -(i+1)); ASSERT(pos[1] == i+1); ASSERT(pos[2] == i+2); } for (int i = 0; i < 3; i++) { ASSERT(mesh.getNumVerticesForFace(i) == 3); ASSERT(mesh.getFaceVertex(i, 0) == i); ASSERT(mesh.getFaceVertex(i, 1) == i+1); ASSERT(mesh.getFaceVertex(i, 2) == i+2); } ASSERT(mesh.getNumVerticesForFace(3) == 4); ASSERT(mesh.getFaceVertex(3, 0) == 0); ASSERT(mesh.getFaceVertex(3, 1) == 2); ASSERT(mesh.getFaceVertex(3, 2) == 4); ASSERT(mesh.getFaceVertex(3, 3) == 1); }
ContactGeometry::TriangleMesh::Impl::Impl (const PolygonalMesh& mesh, bool smooth) : ContactGeometryImpl(), smooth(smooth) { // Create the mesh, triangulating faces as necessary. Array_<Vec3> vertexPositions; Array_<int> faceIndices; for (int i = 0; i < mesh.getNumVertices(); i++) vertexPositions.push_back(mesh.getVertexPosition(i)); for (int i = 0; i < mesh.getNumFaces(); i++) { int numVert = mesh.getNumVerticesForFace(i); if (numVert < 3) continue; // Ignore it. if (numVert == 3) { faceIndices.push_back(mesh.getFaceVertex(i, 0)); faceIndices.push_back(mesh.getFaceVertex(i, 1)); faceIndices.push_back(mesh.getFaceVertex(i, 2)); } else if (numVert == 4) { // Split it into two triangles. faceIndices.push_back(mesh.getFaceVertex(i, 0)); faceIndices.push_back(mesh.getFaceVertex(i, 1)); faceIndices.push_back(mesh.getFaceVertex(i, 2)); faceIndices.push_back(mesh.getFaceVertex(i, 2)); faceIndices.push_back(mesh.getFaceVertex(i, 3)); faceIndices.push_back(mesh.getFaceVertex(i, 0)); } else { // Add a vertex at the center, then split it into triangles. Vec3 center(0); for (int j = 0; j < numVert; j++) center += vertexPositions[mesh.getFaceVertex(i, j)]; center /= numVert; vertexPositions.push_back(center); int newIndex = vertexPositions.size()-1; for (int j = 0; j < numVert-1; j++) { faceIndices.push_back(mesh.getFaceVertex(i, j)); faceIndices.push_back(mesh.getFaceVertex(i, j+1)); faceIndices.push_back(newIndex); } // Close the face (thanks, Alexandra Zobova). faceIndices.push_back(mesh.getFaceVertex(i, numVert-1)); faceIndices.push_back(mesh.getFaceVertex(i, 0)); faceIndices.push_back(newIndex); } } init(vertexPositions, faceIndices); // Make sure the mesh normals are oriented correctly. Vec3 origin(0); for (int i = 0; i < 3; i++) origin += vertices[faces[0].vertices[i]].pos; origin /= 3; // this is the face centroid const UnitVec3 direction = -faces[0].normal; // Calculate a ray origin that is guaranteed to be outside the // mesh. If the topology is right (face 0 normal points outward), we'll be // outside on the side containing face 0. If it is wrong, we'll be outside // on the opposite side of the mesh. Then we'll shoot a ray back along the // direction we came from (that is, towards the interior of the mesh from // outside). We'll hit *some* face. If the topology is right, the hit // face's normal will be pointing back at us. If it is wrong, the face // normal will also be pointing inwards, in roughly the same direction as // the ray. origin -= max(obb.bounds.getSize())*direction; Real distance; int face; Vec2 uv; bool intersects = intersectsRay(origin, direction, distance, face, uv); assert(intersects); // Now dot the hit face normal with the ray direction; correct topology // will have them pointing in more-or-less opposite directions. if (dot(faces[face].normal, direction) > 0) { // We need to invert the mesh topology. for (int i = 0; i < (int) faces.size(); i++) { Face& f = faces[i]; int temp = f.vertices[0]; f.vertices[0] = f.vertices[1]; f.vertices[1] = temp; temp = f.edges[1]; f.edges[1] = f.edges[2]; f.edges[2] = temp; f.normal *= -1; } for (int i = 0; i < (int) vertices.size(); i++) vertices[i].normal *= -1; } }
void VisualizerProtocol::drawPolygonalMesh(const PolygonalMesh& mesh, const Transform& X_GM, const Vec3& scale, const Vec4& color, int representation) { const void* impl = &mesh.getImpl(); map<const void*, unsigned short>::const_iterator iter = meshes.find(impl); if (iter != meshes.end()) { // This mesh was already cached; just reference it by index number. drawMesh(X_GM, scale, color, (short)representation, iter->second, 0); return; } // This is a new mesh, so we need to send it to the visualizer. Build lists // of vertices and faces, triangulating as necessary. vector<float> vertices; vector<unsigned short> faces; for (int i = 0; i < mesh.getNumVertices(); i++) { Vec3 pos = mesh.getVertexPosition(i); vertices.push_back((float) pos[0]); vertices.push_back((float) pos[1]); vertices.push_back((float) pos[2]); } for (int i = 0; i < mesh.getNumFaces(); i++) { int numVert = mesh.getNumVerticesForFace(i); if (numVert < 3) continue; // Ignore it. if (numVert == 3) { faces.push_back((unsigned short) mesh.getFaceVertex(i, 0)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 1)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 2)); } else if (numVert == 4) { // Split it into two triangles. faces.push_back((unsigned short) mesh.getFaceVertex(i, 0)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 1)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 2)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 2)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 3)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 0)); } else { // Add a vertex at the center, then split it into triangles. Vec3 center(0); for (int j = 0; j < numVert; j++) { Vec3 pos = mesh.getVertexPosition(mesh.getFaceVertex(i,j)); center += pos; } center /= numVert; vertices.push_back((float) center[0]); vertices.push_back((float) center[1]); vertices.push_back((float) center[2]); const unsigned newIndex = (unsigned)(vertices.size()/3-1); for (int j = 0; j < numVert-1; j++) { faces.push_back((unsigned short) mesh.getFaceVertex(i, j)); faces.push_back((unsigned short) mesh.getFaceVertex(i, j+1)); faces.push_back((unsigned short) newIndex); } // Close the face (thanks, Alexandra Zobova). faces.push_back((unsigned short) mesh.getFaceVertex(i, numVert-1)); faces.push_back((unsigned short) mesh.getFaceVertex(i, 0)); faces.push_back((unsigned short) newIndex); } } SimTK_ERRCHK1_ALWAYS(vertices.size() <= 65535*3, "VisualizerProtocol::drawPolygonalMesh()", "Can't display a DecorativeMesh with more than 65535 vertices;" " received one with %llu.", (unsigned long long)vertices.size()); SimTK_ERRCHK1_ALWAYS(faces.size() <= 65535*3, "VisualizerProtocol::drawPolygonalMesh()", "Can't display a DecorativeMesh with more than 65535 vertices;" " received one with %llu.", (unsigned long long)faces.size()); const int index = NumPredefinedMeshes + (int)meshes.size(); SimTK_ERRCHK_ALWAYS(index <= 65535, "VisualizerProtocol::drawPolygonalMesh()", "Too many unique DecorativeMesh objects; max is 65535."); meshes[impl] = (unsigned short)index; // insert new mesh WRITE(outPipe, &DefineMesh, 1); unsigned short numVertices = (unsigned short)(vertices.size()/3); unsigned short numFaces = (unsigned short)(faces.size()/3); WRITE(outPipe, &numVertices, sizeof(short)); WRITE(outPipe, &numFaces, sizeof(short)); WRITE(outPipe, &vertices[0], (unsigned)(vertices.size()*sizeof(float))); WRITE(outPipe, &faces[0], (unsigned)(faces.size()*sizeof(short))); drawMesh(X_GM, scale, color, (short) representation, (unsigned short)index, 0); }