// Specifying shell geometries based on g_tipPos, g_furHeight, and g_numShells. // You need to call this function whenver the shell needs to be updated static void updateShellGeometry() { // TASK 1 and 3 TODO: finish this function as part of Task 1 and Task 3 int numVertices = g_bunnyMesh.getNumVertices(); g_bunnyMeshCopy = Mesh(g_bunnyMesh); createSmoothNormals(g_bunnyMeshCopy); RigTForm path = getPathAccumRbt(g_world, g_bunnyNode); for(int i = 0; i < g_numShells; i++) { // iterate through each vertex in the mesh for(int j = 0; j < numVertices; j++) { Cvec3 normal = g_bunnyMesh.getVertex(j).getNormal(); Cvec3 n = normal * (((double) g_furHeight) / g_numShells); Cvec3 s = (path * RigTForm(g_tipAtRest[j])).getTranslation(); Cvec3 d = (g_tipPos[j] - s) * (2.0 / (g_numShells * (g_numShells - 1))); Mesh::Vertex v = g_bunnyMeshCopy.getVertex(j); Cvec3 vPos = v.getPosition(); Cvec3 newPos = vPos + (n + (d * (i+1))); v.setPosition(newPos); v.setNormal(newPos - vPos); } uploadMeshToSimpleGeometryPNX(g_bunnyMeshCopy, *(g_bunnyShellGeometries[i]), g_hairyness); } }
static void applySubdivs(Mesh &mesh, int subdivLevel) { for (int i = 0; i < subdivLevel; i++) { // subdivide faces for (int j = 0, n = mesh.getNumFaces(); j < n; j++) { Mesh::Face face = mesh.getFace(j); int verticesAroundFace = face.getNumVertices(); Cvec3 vertexSum = Cvec3(); for (int k = 0; k < verticesAroundFace; k++) { vertexSum += face.getVertex(k).getPosition(); } vertexSum = vertexSum * (1.0 / verticesAroundFace); mesh.setNewFaceVertex(face, vertexSum); } // subdivide edges for (int j = 0, n = mesh.getNumEdges(); j < n; j++) { Mesh::Edge edge = mesh.getEdge(j); Cvec3 vertexSum = (edge.getVertex(0).getPosition() + edge.getVertex(1).getPosition() + mesh.getNewFaceVertex(edge.getFace(0)) + mesh.getNewFaceVertex(edge.getFace(1))) * 0.25; mesh.setNewEdgeVertex(edge, vertexSum); } // subdivide vertices for (int j = 0, n = mesh.getNumVertices(); j < n; j++) { Mesh::Vertex v = mesh.getVertex(j); int numOfVertices = 0; Mesh::VertexIterator vertexIter(v.getIterator()), iterOrigin(vertexIter); Cvec3 accumVertices = Cvec3(); Cvec3 accumFaceVertices = Cvec3(); do { accumVertices += vertexIter.getVertex().getPosition(); accumFaceVertices += mesh.getNewFaceVertex(vertexIter.getFace()); numOfVertices++; } while (++vertexIter != iterOrigin); double factor = 1.0 / (numOfVertices * numOfVertices); Cvec3 vertexVertex = v.getPosition() * ((numOfVertices - 2.0) / numOfVertices) + accumVertices * factor + accumFaceVertices * factor; mesh.setNewVertexVertex(mesh.getVertex(j), vertexVertex); } // subdivide for each level of subdivision we need mesh.subdivide(); } }
static void simpleShadeCube(Mesh& mesh) { Cvec3 normal = Cvec3(0, 1, 0); for (int i = 0; i < mesh.getNumFaces(); ++i) { const Mesh::Face f = mesh.getFace(i); Cvec3 facenorm = f.getNormal(); for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); v.setNormal(facenorm); } } }
void collectVertexVertices(Mesh& m) { vector<vector<Cvec3> > vertexVertices; for (int i = 0; i < m.getNumVertices(); ++i) { const Mesh::Vertex v = m.getVertex(i); Mesh::VertexIterator it(v.getIterator()), it0(it); vector<Cvec3> vertices; vector<Cvec3> faces; do { vertices.push_back(it.getVertex().getPosition()); faces.push_back(m.getNewFaceVertex(it.getFace())); } while (++it != it0); // go around once the 1ring Cvec3 vertex = getVertexVertex(v.getPosition(), vertices, faces); m.setNewVertexVertex(v, vertex); } }
// New function that initialize the dynamics simulation static void initSimulation() { g_tipPos.resize(g_bunnyMesh.getNumVertices(), Cvec3(0)); g_tipVelocity = g_tipPos; g_tipAtRest = g_tipPos; // TASK 1 TODO: initialize g_tipPos to "at-rest" hair tips in world coordinates int numVertices = g_bunnyMesh.getNumVertices(); for(int i = 0; i < numVertices; i++) { Mesh::Vertex v = g_bunnyMesh.getVertex(i); g_tipAtRest[i] = v.getPosition() + (v.getNormal() * g_furHeight); g_tipPos[i] = (getPathAccumRbt(g_world, g_bunnyNode) * RigTForm(g_tipAtRest[i])).getTranslation(); } // Starts hair tip simulation hairsSimulationCallback(0); }
static void updateMeshNormals(Mesh &mesh) { for (int i = 0, n = mesh.getNumVertices(); i < n; i++) { Cvec3 vectorSum = Cvec3(); Mesh::Vertex v = mesh.getVertex(i); Mesh::VertexIterator vertexIter(v.getIterator()), iterOrigin(vertexIter); // walk around the vertex do { vectorSum += vertexIter.getFace().getNormal(); } while (++vertexIter != iterOrigin); if (dot(vectorSum, vectorSum) > CS175_EPS2) { vectorSum.normalize(); } v.setNormal(vectorSum); } }
static void updateShellGeometry() { float xs[] = {0, g_hairyness, 0}; float ys[] = {0, 0, g_hairyness}; vector<Cvec3> prevPos; prevPos.resize(g_bunnyMesh.getNumFaces() * 3); for (int level = 0; level < g_numShells; ++level) { int counter = 0; vector<VertexPNX> verts; for (int i = 0; i < g_bunnyMesh.getNumFaces(); ++i) { const Mesh::Face f = g_bunnyMesh.getFace(i); for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); int index = v.getIndex(); Cvec3 pos = v.getPosition(); Cvec3 normal = v.getNormal(); Cvec2 c = Cvec2(xs[j], ys[j]); Cvec3 n = normal * g_furHeight / g_numShells; Cvec3 s = pos + (n * g_numShells); Cvec3 t = world2bunny(g_tipPos[index]); Cvec3 d = (t - s) / ((g_numShells + 1) * g_numShells / 2); /* Cvec3 d = (world2bunny(g_tipPos[index]) - (normal * g_furHeight)) / (g_numShells - 1); */ if (level == 0) { prevPos[counter] = pos; verts.push_back(VertexPNX(pos, n, c)); } else { Cvec3 new_position = prevPos[counter] + n + (d * level); verts.push_back(VertexPNX(new_position, new_position - prevPos[counter], c)); prevPos[counter] = new_position; } ++counter; } } int numVertices = verts.size(); g_bunnyShellGeometries[level]->upload(&verts[0], numVertices); verts.clear(); } }
//animation timer for mesh static void animateMeshCallback(int whocares) { g_meshObject = Mesh(g_meshObjectCopy); //update animation data, geometry for(int i = 0; i < 8; ++i){ float ang = g_meshAnimationAngle[i]; ang += (g_meshAnimationSpeed * g_meshAngleSpeed[i]); //between 0 and PI if(ang >= M_PI) ang = 0; g_meshAnimationAngle[i] = ang; //between 0 and 1 float scale = sin(ang); //between 0.5 and 3 float lowerBound = 0.5, upperBound = 3.0; scale = lowerBound + (scale * (upperBound - lowerBound)); //update geometry Mesh::Vertex v = g_meshObject.getVertex(i); Cvec3 vPos = v.getPosition(); //multiply by scale v.setPosition(vPos * scale); } //subdivide modified g_meshObject subdivideMesh(g_meshObject, g_meshSubdivisionLvl); //upload geometry uploadMeshGeometry(); //redraw! glutPostRedisplay(); glutTimerFunc(1000/g_animateFramesPerSecond, animateMeshCallback, 0); }
// New function that initialize the dynamics simulation static void initSimulation() { bunnyTransformSet = true; bunnyTransform = (getPathAccumRbt(g_world, g_bunnyNode)); g_tipPos.resize(g_bunnyMesh.getNumVertices(), Cvec3(0)); g_tipStartPos.resize(g_bunnyMesh.getNumVertices(), Cvec3(0)); g_tipVelocity = g_tipPos; // TASK 1 TODO: initialize g_tipPos to "at-rest" hair tips in world coordinates for (int i = 0; i < g_bunnyMesh.getNumVertices(); ++i) { const Mesh::Vertex v = g_bunnyMesh.getVertex(i); Cvec3 pos = v.getPosition(); Cvec3 normal = v.getNormal(); g_tipPos[i] = bunny2world(pos + normal * g_furHeight); g_tipStartPos[i] = bunny2world(pos + normal * g_furHeight); } // Starts hair tip simulation hairsSimulationCallback(0); }
static void initBunnyMeshes() { g_bunnyMesh.load("bunny.mesh"); // TODO: Init the per vertex normal of g_bunnyMesh, using codes from asst7 // ... shadeCube(g_bunnyMesh); // cout << "Finished shading bunny" << endl; // TODO: Initialize g_bunnyGeometry from g_bunnyMesh, similar to vector<VertexPN> verts; for (int i = 0; i < g_bunnyMesh.getNumFaces(); ++i) { const Mesh::Face f = g_bunnyMesh.getFace(i); Cvec3 pos; Cvec3 normal; if (g_flat) normal = f.getNormal(); for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); pos = v.getPosition(); if (!g_flat) normal = v.getNormal(); verts.push_back(VertexPN(pos, normal)); } } // add vertices to bunny geometry int numVertices = verts.size(); g_bunnyGeometry.reset(new SimpleGeometryPN()); g_bunnyGeometry->upload(&verts[0], numVertices); // Now allocate array of SimpleGeometryPNX to for shells, one per layer g_bunnyShellGeometries.resize(g_numShells); for (int i = 0; i < g_numShells; ++i) { g_bunnyShellGeometries[i].reset(new SimpleGeometryPNX()); } }
static void loadMeshGeometry(Mesh& m, GeometryPX& g) { vector<GLfloat> pos, tex; for (int i = 0; i < m.getNumFaces(); ++i) { const Mesh::Face f = m.getFace(i); for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); pos.push_back((GLfloat)(v.getPosition()[0])); pos.push_back((GLfloat)(v.getPosition()[1])); tex.push_back((GLfloat)v.getTexCoords()[0]); tex.push_back((GLfloat)v.getTexCoords()[1]); } } const unsigned int size = pos.size() * sizeof(GLfloat); glBindBuffer(GL_ARRAY_BUFFER, g.posVbo); glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, size, &pos[0]); checkGlErrors(); glBindBuffer(GL_ARRAY_BUFFER, g.texVbo); glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, size, &tex[0]); checkGlErrors(); }
static void initCubeMesh() { if (!meshLoaded) { cubeMesh.load("./cube.mesh"); meshLoaded = true; } // set normals shadeCube(cubeMesh); // collect vertices from each face and map quads to triangles vector<VertexPN> verts; for (int i = 0; i < cubeMesh.getNumFaces(); ++i) { const Mesh::Face f = cubeMesh.getFace(i); Cvec3 pos; Cvec3 normal; if (g_flat) normal = f.getNormal(); for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); pos = v.getPosition(); if (!g_flat) normal = v.getNormal(); verts.push_back(VertexPN(pos, normal)); if (j == 2) { verts.push_back(VertexPN(pos, normal)); } } const Mesh::Vertex v = f.getVertex(0); pos = v.getPosition(); if (!g_flat) normal = v.getNormal(); verts.push_back(VertexPN(pos, normal)); } // add vertices to cube geometry int numVertices = verts.size(); if (!g_cubeGeometryPN) { g_cubeGeometryPN.reset(new SimpleGeometryPN()); } g_cubeGeometryPN->upload(&verts[0], numVertices); }
static void shadeCube(Mesh& mesh) { Cvec3 normal = Cvec3(0, 0, 0); for (int i = 0; i < mesh.getNumVertices(); ++i) { mesh.getVertex(i).setNormal(normal); } for (int i = 0; i < mesh.getNumFaces(); ++i) { const Mesh::Face f = mesh.getFace(i); Cvec3 facenorm = f.getNormal(); for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); v.setNormal(facenorm + v.getNormal()); } } for (int i = 0; i < mesh.getNumVertices(); ++i) { const Mesh::Vertex v = mesh.getVertex(i); if (norm2(v.getNormal()) > .001) { v.setNormal(normalize(v.getNormal())); } } }
void tesselateMesh(Mesh& obj, int rec=1, bool onSphere=false) { BBox bb = obj.calcBBox(); std::cout << "Mesh bbox="<<bb<<std::endl; std::cout << "Flipping mesh..."<<std::endl; obj.calcFlip(); obj.calcEdges(); bool closed = obj.isClosed(); std::cout << "Mesh is "<<(closed?"":"NOT ")<<"closed."<<std::endl; Vec3f center; Vec3f radius; if (onSphere) { // HACK: keep center at (0,0,0), so that we can use translation to concentrate vertices on one side of the sphere //center = (bb.b+bb.a)/2; radius = (bb.b-bb.a)/2; } if (rec == 0 && onSphere) { for(int i=0; i<obj.nbp(); i++) projectOnSphere(obj.PP(i),obj.PN(i),center,radius[0]); obj.calcNormals(); return; } bool groups = obj.getAttrib(Mesh::MESH_POINTS_GROUP); if (!groups) { std::cout << "Creating artificial groups."<<std::endl; // create artificial groups for(int i=0; i<obj.nbp(); i++) { obj.PG(i) = i; obj.GP0(i) = i; } obj.setAttrib(Mesh::MESH_POINTS_GROUP,true); } std::cout << "Input mesh: "<<obj.nbp()<<" points, "<<obj.nbf()<<" faces."<<std::endl; for (int r=0; r<rec; r++) { std::cout << "Tesselation level "<<r+1<<"..."<<std::endl; obj.calcEdges(); std::cout << "Creating new points..."<<std::endl; // first create a new point on each edge for(int e1 = 0 ; e1 < (int)obj.edges.size(); ++e1) { int g1 = obj.getPG(e1); for(std::map< int,Mesh::Edge >::iterator it = obj.edges[e1].begin(), itend = obj.edges[e1].end(); it != itend; ++it) { int g2 = obj.getPG(it->first); int f1p1 = -1, f1p2 = -1; int f2p1 = -1, f2p2 = -1; int i1 = -1; if (it->second.f1 >= 0) { Vec3i fp = obj.getFP(it->second.f1); f1p1 = fp[0], f1p2 = fp[1]; if (obj.getPG(f1p1) != g1) { f1p1 = fp[1]; f1p2 = fp[2]; if (obj.getPG(f1p1) != g1) { f1p1 = fp[2]; f1p2 = fp[0]; // this is the last possible edge } } if (obj.getPG(f1p1) != g1 || (obj.getPG(f1p2) != g2)) { std::cerr << "ERROR: Edge "<<g1<<" - "<<g2<<" not found on face 1 ( "<<it->second.f1<<" = "<<fp<<" = "<<Vec3i(obj.getPG(fp[0]),obj.getPG(fp[1]),obj.getPG(fp[2]))<<" )"<<std::endl; it->second.f1 = -1; continue; } Mesh::Vertex v; v = obj.getP(f1p1); v += obj.getP(f1p2); v.mean(2); //if (onSphere) v.p = projectOnSphere(v.p, center, radius[0]); i1 = obj.addP(v); // replace the face index with the new point index it->second.f1 = i1; } if (it->second.f2 >= 0) { Vec3i fp = obj.getFP(it->second.f2); f2p1 = fp[0], f2p2 = fp[1]; if (obj.getPG(f2p1) != g2) { f2p1 = fp[1]; f2p2 = fp[2]; if (obj.getPG(f2p1) != g2) { f2p1 = fp[2]; f2p2 = fp[0]; // this is the last possible edge } } if (obj.getPG(f2p1) != g2 || (obj.getPG(f2p2) != g1)) { std::cerr << "ERROR: Edge "<<g1<<" - "<<g2<<" not found on face 2 ( "<<it->second.f2<<" = "<<fp<<" = "<<Vec3i(obj.getPG(fp[0]),obj.getPG(fp[1]),obj.getPG(fp[2]))<<" )"<<std::endl; it->second.f2 = -1; continue; } if (f1p1 == f2p2 && f1p2 == f2p1) { // same point as other face it->second.f2 = i1; } else { Mesh::Vertex v; v = obj.getP(f2p1); v += obj.getP(f2p2); v.mean(2); //if (onSphere) v.p = projectOnSphere(v.p, center, radius[0]); int i2; if (i1 == -1) // no other edge, create a new group i2 = obj.addP(v); else { // see of all points are on the same subgroup int gf1p1 = g1; while (obj.getGP0(gf1p1+1) <= -f1p1) ++gf1p1; int gf1p2 = g2; while (obj.getGP0(gf1p2+1) <= -f1p2) ++gf1p2; int gf2p1 = g2; while (obj.getGP0(gf2p1+1) <= -f2p1) ++gf2p1; int gf2p2 = g1; while (obj.getGP0(gf2p2+1) <= -f2p2) ++gf2p2; int g = obj.getPG(i1); i2 = obj.addP(v,g); if (gf1p1 != gf2p2 || gf1p2 != gf2p1) { // create a subgroup obj.GP0(obj.nbg()) = -i2; } } // replace the face index with the new point index it->second.f2 = i2; } } } } // then create new faces std::cout << "Creating new faces..."<<std::endl; std::vector<Vec3i> faces_p; int nbf0 = obj.nbf(); faces_p.reserve(nbf0*4); for(int i=0; i<nbf0; ++i) { Vec3i points = obj.getFP(i); Vec3i edges; edges[0] = obj.getEdgeFace(points[0],points[1]); edges[1] = obj.getEdgeFace(points[1],points[2]); edges[2] = obj.getEdgeFace(points[2],points[0]); if ((unsigned)edges[0] >= (unsigned)obj.nbp() || (unsigned)edges[1] >= (unsigned)obj.nbp() || (unsigned)edges[2] >= (unsigned)obj.nbp()) { std::cerr << "ERROR: invalid edge points " << edges << " in face " <<i<<" = "<<points<<std::endl; continue; } faces_p.push_back(Vec3i(edges[2],points[0],edges[0])); faces_p.push_back(Vec3i(edges[0],points[1],edges[1])); faces_p.push_back(Vec3i(edges[2],edges[0],edges[1])); faces_p.push_back(Vec3i(edges[1],points[2],edges[2])); } obj.faces_p = faces_p; // finally we update the material groups std::cout << "Updating materials..."<<std::endl; for(unsigned int i=0; i<obj.mat_groups.size(); ++i) { obj.mat_groups[i].f0 *= 4; obj.mat_groups[i].nbf *= 4; } // and we recompute the edges if (r < rec-1) { std::cout << "Updating edges..."<<std::endl; obj.calcEdges(); bool closed = obj.isClosed(); std::cout << "Mesh is "<<(closed?"":"NOT ")<<"closed."<<std::endl; } if (onSphere) { for(int i=0; i<obj.nbp(); i++) projectOnSphere(obj.PP(i),obj.PN(i),center,radius[0]); obj.calcNormals(); } std::cout << "Tesselation level "<<r<<" DONE: "<<obj.nbp()<<" points, "<<obj.nbf()<<" faces."<<std::endl; } if (!groups) { // remove artificial groups obj.setAttrib(Mesh::MESH_POINTS_GROUP,false); } obj.calcNormals(); }
static void animateCube(int ms) { float t = (float) ms / (float) g_msBetweenKeyFrames; // scale all vertices in cube for (int i = 0; i < cubeMesh.getNumVertices(); ++i) { const Mesh::Vertex v = cubeMesh.getVertex(i); Cvec3 pos = v.getPosition(); double factor = (1 + (float(g_div_level)/10)) * ((-1 * sin((double) (g_horiz_scale * ms) / (1000 * (vertex_speeds[i] + .5))) + 1) / 2 + .5); pos[0] = vertex_signs[i][0] * (factor / sqrt(3)); pos[1] = vertex_signs[i][1] * (factor / sqrt(3)); pos[2] = vertex_signs[i][2] * (factor / sqrt(3)); v.setPosition(pos); } // copy mesh to temporary mesh for rendering Mesh renderMesh = cubeMesh; // subdivision for (int i = 0; i < g_div_level; ++i) { collectFaceVertices(renderMesh); collectEdgeVertices(renderMesh); collectVertexVertices(renderMesh); renderMesh.subdivide(); } // set normals shadeCube(renderMesh); // collect vertices for each face vector<VertexPN> verts; int q = 0; for (int i = 0; i < renderMesh.getNumFaces(); ++i) { const Mesh::Face f = renderMesh.getFace(i); Cvec3 pos; Cvec3 normal; for (int j = 0; j < f.getNumVertices(); ++j) { const Mesh::Vertex v = f.getVertex(j); pos = v.getPosition(); if (!g_flat) normal = v.getNormal(); else normal = f.getNormal(); verts.push_back(VertexPN(pos, normal)); if (j == 2) { verts.push_back(VertexPN(pos, normal)); } } const Mesh::Vertex v = f.getVertex(0); pos = v.getPosition(); if (!g_flat) normal = v.getNormal(); else normal = f.getNormal(); verts.push_back(VertexPN(pos, normal)); } // dump into geometry int numVertices = verts.size(); g_cubeGeometryPN->upload(&verts[0], numVertices); glutPostRedisplay(); glutTimerFunc(1000/g_animateFramesPerSecond, animateCube, ms + 1000/g_animateFramesPerSecond); }