// Create a polygonal mesh for this torus using parameterization as follows: // u = [0, 2*Pi] traces a circle in the x-y plane with radius torusRadius, // which is the centroid of the torus. A point P on this circle is // given by P = torusRadius*~[cos(u) sin(u) 0]. // v = [0, 2*Pi] traces a circle arond the cross-section (or tube) of the // torus with radius tubeRadius, at a given u. A point Q on this circle // is given by Q = (torusRadius + tubeRadius*cos(v))*e1 + tubeRadius*(~[0 0 1]*sin(v)) // where e1 = ~[sin(u) cos(u) 0]. The tube circle is in a plane spanned // by e1 and the z-axis. void ContactGeometry::Torus::Impl::createPolygonalMesh(PolygonalMesh& mesh) const { // TODO add resolution argument const int numSides = 12; //*resolution; const int numSlices = 36; //*resolution; // add vertices for (int i = 0; i < numSlices; ++i) { Real u = Real((i*2*SimTK_PI)/numSlices); UnitVec3 e1(std::sin(u), std::cos(u), 0); // torus circle aligned with z-axis (z-axis through hole) for (int j = 0; j < numSides; ++j) { Real v = Real((j*2*SimTK_PI)/numSides); Vec3 vtx = (torusRadius + tubeRadius*std::cos(v))*e1 + tubeRadius*std::sin(v)*Vec3(0,0,1); // use ZAXIS? mesh.addVertex(vtx); } } // add faces, be careful to wrap indices for the last slice int numVertices = mesh.getNumVertices(); // cout << "num verts = " << numVertices << endl; for (int i = 0; i < numVertices; ++i) { // cout << "v" << i << ": " << mesh.getVertexPosition(i) << endl; // define counter-clockwise quad faces Array_<int> faceIndices; faceIndices.push_back(i); // u_i,v_i faceIndices.push_back((i+1)%numVertices); // u_i, v_i+1 faceIndices.push_back((i+1+numSides)%numVertices); // u_i+1, v_i+1 faceIndices.push_back((i+numSides)%numVertices); // u_i+1, v_i mesh.addFace(faceIndices); } }
void testCreateMesh() { PolygonalMesh mesh; ASSERT(mesh.getNumFaces() == 0); ASSERT(mesh.getNumVertices() == 0); ASSERT(mesh.addVertex(Vec3(0)) == 0); ASSERT(mesh.addVertex(Vec3(0, 1, 0)) == 1); ASSERT(mesh.addVertex(Vec3(0, 0, 1)) == 2); Array_<int> v; v.push_back(1); v.push_back(2); v.push_back(0); ASSERT(mesh.addFace(v) == 0); ASSERT(mesh.getNumFaces() == 1); ASSERT(mesh.getNumVertices() == 3); ASSERT(mesh.getFaceVertex(0, 0) == 1); ASSERT(mesh.getFaceVertex(0, 1) == 2); ASSERT(mesh.getFaceVertex(0, 2) == 0); ASSERT(mesh.getVertexPosition(0) == Vec3(0)); ASSERT(mesh.getVertexPosition(1) == Vec3(0, 1, 0)); ASSERT(mesh.getVertexPosition(2) == Vec3(0, 0, 1)); // Make sure copy and assignment are shallow. PolygonalMesh mesh2(mesh); // shallow copy construction ASSERT(&mesh2.getImpl() == &mesh.getImpl()); ASSERT(mesh.getImplHandleCount()==2); PolygonalMesh mesh3; mesh3 = mesh; // shallow assignment ASSERT(&mesh3.getImpl() == &mesh.getImpl()); ASSERT(mesh.getImplHandleCount()==3); PolygonalMesh mesh4; mesh4.copyAssign(mesh); // deep copy ASSERT(mesh4.getNumVertices() == mesh.getNumVertices()); ASSERT(&mesh4.getImpl() != &mesh.getImpl()); ASSERT(mesh4.getImplHandleCount()==1); ASSERT(mesh.getImplHandleCount()==3); }
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); }