void display() { // Set up correct OpenGL projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(dmin[0], dmax[0], dmin[1], dmax[1]); glMatrixMode(GL_MODELVIEW); // Specify that we want to draw triangle outlines glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Black on white. glClearColor(1,1,1,0); glColor3f(0,0,0); // Clear the screen. glClear(GL_COLOR_BUFFER_BIT); for(FaceID f: m.faces()){ glBegin(GL_POLYGON); for(Walker w = m.walker(f); !w.full_circle(); w = w.next()) glVertex3dv(m.pos(w.vertex()).get()); glEnd(); } // Draw flipper. glColor3f(1,0,0); glBegin(GL_LINES); Walker hew = m.walker(*flipper); glVertex3dv(m.pos(hew.vertex()).get()); glVertex3dv(m.pos(hew.opp().vertex()).get()); glEnd(); glFinish(); }
/* * Checks if an edge is a boundary and if not, checks it for being locally Delaunay. * If it is not locally delaunay it is flipped and all its neighbor edges are checked, ad nauseam. */ void recursiveDelaunayFlip(Manifold &m, Walker w, bool isAffected) { if ( !boundary(m, w.halfedge()) ) { // Check if the current halfedge is locally Delaunay using the inCircle function Vec3d p1 = m.pos(w.opp().vertex()); Vec3d p2 = m.pos(w.vertex()); Vec3d p3 = m.pos(w.next().vertex()); Vec3d p4 = m.pos(w.opp().next().vertex()); // This seems to return erroneus values every time if (isAffected == true) { cout << "Affected quadrillateral:" << endl; cout << "p1: " << p1 << endl; cout << "p2: " << p2 << endl; cout << "p3: " << p3 << endl; cout << "p4: " << p4 << endl; } if ( inCircle( p1, p3, p2, p4 ) || inCircle(p1, p2, p4, p3) ) { // Since either point was in a triangle circumcircle, flip the edge m.flip_edge(w.halfedge()); cout << "Edge to be flipped: " << p1 << ", " << p2 << ". Other vertices: " << p3 << ", " << p4 << endl; // Recursively check all the edges that share a neighbour with the flipped edge recursiveDelaunayFlip(m, w.next(), true); recursiveDelaunayFlip(m, w.prev(), true); recursiveDelaunayFlip(m, w.opp().next(), true); recursiveDelaunayFlip(m, w.opp().prev(), true); } } }
bool obj_save(const string& filename, Manifold& m) { ofstream os(filename.data()); if(os.bad()) return false; VertexAttributeVector<int> vmap; int k = 0; for(VertexIDIterator v = m.vertices_begin(); v != m.vertices_end(); ++v){ Vec3d p = m.pos(*v); os << "v "<< p[0] << " " << p[1] << " " << p[2] << "\n"; vmap[*v] = k++; } for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f){ vector<int> verts; for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()){ int idx = vmap[w.vertex()]; assert(static_cast<size_t>(idx) < m.no_vertices()); // move subscript range from 0..size-1 to 1..size according to OBJ standards verts.push_back(idx + 1); } os << "f "; for(size_t i = 0; i < verts.size() ; ++i){ os << verts[i] << " "; } os<<endl; } return true; }
AmbientOcclusionRenderer::AmbientOcclusionRenderer(const Manifold& m, bool smooth, VertexAttributeVector<double>& field, double max_val): SimpleShaderRenderer(vss,fss) { GLint old_prog; glGetIntegerv(GL_CURRENT_PROGRAM, &old_prog); glUseProgram(prog); GLuint scalar_attrib = glGetAttribLocation(prog, "scalar"); glUniform1fARB(glGetUniformLocationARB(prog, "scalar_max"), max_val); glNewList(display_list,GL_COMPILE); for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) { if(!smooth) glNormal3dv(normal(m, *f).get()); if(no_edges(m, *f)== 3) glBegin(GL_TRIANGLES); else glBegin(GL_POLYGON); for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()) { Vec3d n(normal(m, w.vertex())); if(smooth) glNormal3dv(n.get()); glVertexAttrib1d(scalar_attrib, field[w.vertex()]); glVertex3dv(m.pos(w.vertex()).get()); } glEnd(); } glEndList(); glUseProgram(old_prog); }
LineFieldRenderer::LineFieldRenderer(const Manifold& m, bool smooth, VertexAttributeVector<Vec3d>& lines, float _r): SimpleShaderRenderer(vss,fss), r(_r) { float noise_scale = 10.0f/r; float line_scale = 0.003f; GLint old_prog; glGetIntegerv(GL_CURRENT_PROGRAM, &old_prog); glUseProgram(prog); glUniform1fARB(glGetUniformLocationARB(prog, "scale_line"),line_scale*noise_scale); glUniform1fARB(glGetUniformLocationARB(prog, "noise_scale"),noise_scale); glUniform1iARB(glGetUniformLocationARB(prog, "noise_tex"),0); GLuint direction = glGetAttribLocation(prog, "direction"); glNewList(display_list,GL_COMPILE); for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) { if(!smooth) glNormal3dv(normal(m, *f).get()); if(no_edges(m, *f) == 3) glBegin(GL_TRIANGLES); else glBegin(GL_POLYGON); for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()) { Vec3d n(normal(m, w.vertex())); if(smooth) glNormal3dv(n.get()); Vec3d d = lines[w.vertex()]; d = normalize(d-n*dot(n,d)); glVertexAttrib3dv(direction, d.get()); glVertex3dv(m.pos(w.vertex()).get()); } glEnd(); } glBindTexture(GL_TEXTURE_3D, 0); glEndList(); glUseProgram(old_prog); }
CircleFieldRenderer::CircleFieldRenderer(const Manifold& m, bool smooth, VertexAttributeVector<Vec2d>& field, float gamma): SimpleShaderRenderer(vss, fss) { GLint old_prog; glGetIntegerv(GL_CURRENT_PROGRAM, &old_prog); glUseProgram(prog); GLuint scalar_attrib = glGetAttribLocation(prog, "circlepos"); // static float& gamma = CreateCVar("display.scalar_field_renderer.gamma",2.2f); glUniform1fARB(glGetUniformLocationARB(prog, "gamma"), gamma); glNewList(display_list,GL_COMPILE); for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) { if(!smooth) glNormal3dv(normal(m, *f).get()); if(no_edges(m, *f)== 3) glBegin(GL_TRIANGLES); else glBegin(GL_POLYGON); for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()) { Vec3d n(normal(m, w.vertex())); if(smooth) glNormal3dv(n.get()); glVertexAttrib2dv(scalar_attrib, field[w.vertex()].get()); glVertex3dv(m.pos(w.vertex()).get()); } glEnd(); } glEndList(); glUseProgram(old_prog); }
void mean_curvature_smooth(Manifold& m, bool implicit, double lambda) { using EigMat = SparseMatrix<double>; using EigVec = VectorXd; int N = (int)m.no_vertices(); VertexAttributeVector<int> indices(m.allocated_vertices()); VertexAttributeVector<double> areas(m.allocated_vertices()); int i=0; for(auto v: m.vertices()) { indices[v] = i++; areas[v] = mixed_area(m, v); } EigMat K(N,N); // Sparse matrix initialized with 0 EigVec X(N),Y(N),Z(N); EigVec Xp(N), Yp(N), Zp(N); //----------------------------------------------------------- // Student implementation //----------------------------------------------------------- double epsilon = 1e-5; for (auto vkey : m.vertices()) { int i = indices[vkey]; for (auto w = m.walker(vkey); !w.full_circle(); w = w.circulate_vertex_ccw()) { int j = indices[w.vertex()]; assert(i != j); if (i > j or w.face() == HMesh::InvalidFaceID or w.opp().face() == HMesh::InvalidFaceID) { continue; // Avoid recomputation } auto pi = m.pos(w.opp().vertex()); auto pj = m.pos(w.vertex()); auto pl = m.pos(w.opp().next().vertex()); auto pk = m.pos(w.next().vertex()); double cot_alpha_ij = dot(pj - pk, pi - pk) / ( cross(pi - pk, pj - pk).length() + epsilon); double cot_beta_ij = dot(pj - pl, pi - pl) / ( cross(pi - pl, pj - pl).length() + epsilon); double Ai = areas[w.opp().vertex()]; double Aj = areas[w.vertex()]; double Lij = (cot_alpha_ij + cot_beta_ij) / sqrt(Ai*Aj + epsilon); K.coeffRef(i, j) = Lij; K.coeffRef(j, i) = Lij; K.coeffRef(i, i) -= Lij; K.coeffRef(j, j) -= Lij; } } EigMat I(N,N); for (int i = 0; i < N; i++) { I.coeffRef(i, i) = 1; } K = I - K*lambda; for (auto vkey : m.vertices()) { auto p = m.pos(vkey); int i = indices[vkey]; X.coeffRef(i) = p[0]; Y.coeffRef(i) = p[1]; Z.coeffRef(i) = p[2]; } // Solve SimplicialLLT<EigMat> solver(K); Xp = solver.solve(X); Yp = solver.solve(Y); Zp = solver.solve(Z); // End student implementation //----------------------------------------------------------- for(auto v: m.vertices()) { int i = indices[v]; m.pos(v) = Vec3d(Xp[i], Yp[i], Zp[i]); } }
int main(int argc, char** argv) { /* * Read and parse a point set. */ /* Open a data stream for reading. * We first open data.txt. There is also kote1.txt which contains height * values in addition to x,y positions. */ ifstream data("data.txt"); vector<Vec2d> pts; if(data.good()) while(!data.eof()) { double x,y; data >> x >> y; if(data.good()) { Vec2d p(x,y); pts.push_back(p); dmin = v_min(p,dmin); dmax = v_max(p,dmax); } } cout << "Loaded " << pts.size() << " points " << endl; Vec2d trans((dmax[0]+dmin[0])/2,(dmax[1]+dmin[1])/2); double skal = 2/max(dmax[0]-dmin[0],dmax[1]-dmin[1]); /* Træk trans fra alle punkter og gang med 'skal'*/ for (int i = 0; i < pts.size(); i++) { pts[i] -= trans; pts[i] *= skal; } /* * Build a triangle mesh with a single triangle consisting of the * first three vertices. */ create_single_triangle_manifold(Vec3f(0, 3, 0), Vec3f(4.5, -1.5, 0), Vec3f(-4.5, -1.5, 0), m); // Initially just split the triangle by inserting the first point VertexID v = m.split_face_by_vertex(*m.faces_begin()); m.pos(v) = Vec3d(pts[0][0], pts[0][1], 0); // Now insert all of the remaining points for (int i = 1; i < pts.size(); i++) { Vec3d insertionPoint = Vec3d(pts[i][0], pts[i][1], 0); VertexID insertionVertex; // Loop over all the faces and find the face that contains the point for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); f++) { Walker w = m.walker(*f); bool isLeftOf = true; while (!w.full_circle()) { // If the point to be inserted is not to the left of the halfedge, then break the while loop and continue to the next face if (!leftOf(m.pos(w.circulate_face_ccw().vertex()), m.pos(w.vertex()), insertionPoint)) { isLeftOf = false; break; } w = w.circulate_face_cw(); } // if we found the face the point belongs to then insert it and break the for loop if (isLeftOf == true) { insertionVertex = m.split_face_by_vertex(*f); m.pos(insertionVertex) = insertionPoint; break; } } // Now loop over all the edges affected by the inserted point. // Note that we are assuming that the point was inserted, if not then this will crash spectacularly. Walker w = m.walker(insertionVertex); // Keep track of the next halfedge pointing TO the inserted vertex HalfEdgeID next_edge = w.circulate_vertex_ccw().opp().halfedge(); HalfEdgeAttributeVector<int> touched; while (!w.full_circle()) { // Iterate over the face of the current halfedge until we reach the next edge pointing TO the inserted vertex if(w.halfedge() != next_edge) { // Check if the current halfedge is locally Delaunay using the inCircle function recursiveDelaunayFlip(m, w, false); // Update the walker to be the next halfedge in the current face. w = w.circulate_face_ccw(); } else { // If we are the next edge pointing to the inserted vertex then go to opposite halfedge. This means we are now looking at the halfedge pointing AWAY from the inserted vertex. w = w.opp(); // Remember to update the next_edge to be the next halfedge pointing to the inserted vertex. next_edge = w.circulate_vertex_ccw().opp().halfedge(); } } } /* * Initialize GLUT, the system used to show OpenGL windows. */ glutInit(&argc, argv); glutInitWindowSize(512,512); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow("Delaunay"); glutDisplayFunc(display); // This function is called from glut to draw glutKeyboardFunc(keyfun); // Parse keyboard input // Pass control to glut glutMainLoop(); return 0; }
int main(int argc, char** argv) { // LOAD OBJ Manifold m; if(argc>1) { ArgExtracter ae(argc, argv); do_aabb = ae.extract("-A"); do_obb = ae.extract("-O"); ae.extract("-x", vol_dim[0]); ae.extract("-y", vol_dim[1]); ae.extract("-z", vol_dim[2]); do_ray_tests = ae.extract("-R"); flip_normals = ae.extract("-f"); string file = ae.get_last_arg(); cout << "loading " << file << "... " << flush; load(file, m); cout << " done" << endl; } else { string fn("../../data/bunny-little.x3d"); x3d_load(fn, m); } cout << "Volume dimensions " << vol_dim << endl; if(!valid(m)) { cout << "Not a valid manifold" << endl; exit(0); } triangulate_by_edge_face_split(m); Vec3d p0, p7; bbox(m, p0, p7); Mat4x4d T = fit_bounding_volume(p0,p7,10); cout << "Transformation " << T << endl; for(VertexIDIterator v = m.vertices_begin(); v != m.vertices_end(); ++v) m.pos(*v) = T.mul_3D_point(m.pos(*v)); RGridf grid(vol_dim,FLT_MAX); Util::Timer tim; float T_build_obb=0, T_build_aabb=0, T_dist_obb=0, T_dist_aabb=0, T_ray_obb=0, T_ray_aabb=0; if(do_obb) { cout << "Building OBB Tree" << endl; tim.start(); OBBTree obb_tree; build_OBBTree(m, obb_tree); T_build_obb = tim.get_secs(); cout << "Computing distances from OBB Tree" << endl; tim.start(); DistCompCache<OBBTree> dist(&obb_tree); for_each_voxel(grid, dist); T_dist_obb = tim.get_secs(); cout << "Saving distance field" << endl; save_raw_float("obb_dist.raw", grid); if(do_ray_tests) { cout << "Ray tests on OBB Tree" << endl; tim.start(); RayCast<OBBTree> ray(&obb_tree); for_each_voxel(grid, ray); T_ray_obb = tim.get_secs(); cout << "Saving ray volume" << endl; save_raw_float("obb_ray.raw", grid); } } if(do_aabb) { cout << "Building AABB Tree" << endl; tim.start(); AABBTree aabb_tree; build_AABBTree(m, aabb_tree); T_build_aabb = tim.get_secs(); cout << "Computing distances from AABB Tree" << endl; tim.start(); DistCompCache<AABBTree> dist(&aabb_tree); for_each_voxel(grid, dist); T_dist_aabb = tim.get_secs(); cout << "Saving distance field" << endl; save_raw_float("aabb_dist.raw", grid); if(do_ray_tests) { cout << "Ray tests on AABB tree" << endl; tim.start(); RayCast<AABBTree> ray(&aabb_tree); for_each_voxel(grid, ray); T_ray_aabb = tim.get_secs(); cout << "Saving ray volume" << endl; save_raw_float("aabb_ray.raw", grid); } } cout.width(10); cout << "Poly"; cout.width(11); cout <<"build_obb"; cout.width(12); cout << "build_aabb"; cout.width(10); cout << "dist_obb" ; cout.width(10); cout << "dist_aabb"; cout.width(10); cout << "ray_obb" ; cout.width(10); cout << "ray_aabb"; cout << endl; cout.precision(4); cout.width(10); cout << m.no_faces() << " "; cout.width(10); cout << T_build_obb; cout.width(12); cout << T_build_aabb; cout.width(10); cout << T_dist_obb; cout.width(10); cout << T_dist_aabb; cout.width(10); cout << T_ray_obb; cout.width(10); cout << T_ray_aabb; cout << endl; }