static int FindMaterialIndex(const RNArray<R3Material *>& materials, const char *name) { // Return material with matching name for (int i = 0; i < materials.NEntries(); i++) { R3Material *material = materials.Kth(i); if (!strcmp(name, material->Name())) return i; } // No matching material found return -1; }
R3Plane:: R3Plane(const RNArray<R3Point *>& points, RNBoolean polygon_vertices) { // Initialize plane v = R3null_vector; d = 0; // Check number of points int npoints = points.NEntries(); if (npoints < 3) { *this = R3null_plane; return; } // Compute centroid R3Point c = R3Centroid(points); // Check if points form (counter-clockwise) boundary of polygon if (polygon_vertices) { // Compute best normal for counter-clockwise array of points using newell's method const R3Point *p1 = points[npoints-1]; for (int i = 0; i < npoints; i++) { const R3Point *p2 = points[i]; v[0] += (p1->Y() - p2->Y()) * (p1->Z() + p2->Z()); v[1] += (p1->Z() - p2->Z()) * (p1->X() + p2->X()); v[2] += (p1->X() - p2->X()) * (p1->Y() + p2->Y()); p1 = p2; } // Normalize v.Normalize(); } else { // Compute principle axes R3Triad triad = R3PrincipleAxes(c, points); // Select direction of least variation v = triad[2]; } // Compute d from centroid and normal d = -(v[0]*c[0] + v[1]*c[1] + v[2]*c[2]); }
void GLUTRedraw(void) { // Set viewing transformation viewer->Camera().Load(); // Clear window glClearColor(200.0/255.0, 200.0/255.0, 200.0/255.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set lights static GLfloat light0_position[] = { 3.0, 4.0, 5.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light0_position); static GLfloat light1_position[] = { -3.0, -2.0, -3.0, 0.0 }; glLightfv(GL_LIGHT1, GL_POSITION, light1_position); // Show all points if (show_points) { glEnable(GL_LIGHTING); static GLfloat material[4] = { 0, 0, 1, 1 }; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, material); for (int i = 0; i < all_points.NEntries(); i++) { TestPoint *point = all_points[i]; R3Sphere(point->position, 0.01).Draw(); } } // Show nearby points if (selected_point && !nearby_points.IsEmpty()) { glEnable(GL_LIGHTING); static GLfloat material[4] = { 1, 0, 0, 1 }; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, material); for (int i = 0; i < nearby_points.NEntries(); i++) { TestPoint *point = nearby_points.Kth(i); R3Sphere(point->position, 0.02).Draw(); } } // Show closest point if (selected_point && closest_point) { glEnable(GL_LIGHTING); static GLfloat material[4] = { 0, 1, 0, 1 }; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, material); R3Sphere(closest_point->position, 0.03).Draw(); } // Show selected point if (selected_point) { glEnable(GL_LIGHTING); static GLfloat material[4] = { 1, 1, 1, 1 }; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, material); R3Sphere(selected_point->position, 0.05).Draw(); } // Show constraints if (0 && show_constraints) { glDisable(GL_LIGHTING); glColor3f(0.5, 0, 0); R3Sphere(selected_point->position, min_nearby_distance).Outline(); R3Sphere(selected_point->position, max_nearby_distance).Outline(); } // Show KD tree if (show_kdtree) { glDisable(GL_LIGHTING); glColor3f(0, 0, 0); kdtree->Outline(); } // Swap buffers glutSwapBuffers(); }
void GLUTMouse(int button, int state, int x, int y) { // Invert y coordinate y = GLUTwindow_height - y; // Process mouse button event if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN)) { // Check for double click static RNTime click_time; const RNScalar max_double_click_elapsed = 0.5; RNBoolean double_click = (click_time.Elapsed() < max_double_click_elapsed); click_time.Read(); // Select closest point to cursor if (double_click) { selected_point = NULL; RNLength closest_distance = 10; R2Point cursor_position(x, y); for (int i = 0; i < all_points.NEntries(); i++) { TestPoint *point = all_points[i]; const R3Point& world_position = point->position; R2Point screen_position = viewer->ViewportPoint(world_position); RNLength distance = R2Distance(screen_position, cursor_position); if (distance < closest_distance) { selected_point = point; closest_distance = distance; } } // Find closest points closest_point = NULL; nearby_points.Empty(); if (selected_point) { if (max_nearby_points > 0) { kdtree->FindClosest(selected_point, min_nearby_distance, max_nearby_distance, max_nearby_points, nearby_points); closest_point = (nearby_points.NEntries() > 0) ? nearby_points.Head() : NULL; if (print_debug) printf("Found %d points\n", nearby_points.NEntries()); } else { kdtree->FindAll(selected_point, min_nearby_distance, max_nearby_distance, nearby_points); closest_point = kdtree->FindClosest(selected_point); if (print_debug) printf("Found %d points\n", nearby_points.NEntries()); } } } } // Remember button state int b = (button == GLUT_LEFT_BUTTON) ? 0 : ((button == GLUT_MIDDLE_BUTTON) ? 1 : 2); GLUTbutton[b] = (state == GLUT_DOWN) ? 1 : 0; // Remember modifiers GLUTmodifiers = glutGetModifiers(); // Remember mouse position GLUTmouse[0] = x; GLUTmouse[1] = y; // Redraw glutPostRedisplay(); }
int R3Model:: ReadObjFile(const char *filename) { // Open file FILE *fp = fopen(filename, "r"); if (!fp) { RNFail("Unable to open file %s", filename); return 0; } // Determine directory name (for texture image files) char dirname[1024]; strncpy(dirname, filename, 1024); char *endp = strrchr(dirname, '/'); if (!endp) endp = strrchr(dirname, '\\'); if (!endp) strcpy(dirname, "."); else *endp = '\0'; // Read body char buffer[1024]; int line_count = 0; int material_index =-1; RNArray<R2Point *> texture_coords; RNArray<R3TriangleVertex *> verts; RNArray<R3Triangle *> tris; while (fgets(buffer, 1023, fp)) { // Increment line counter line_count++; // Skip white space char *bufferp = buffer; while (isspace(*bufferp)) bufferp++; // Skip blank lines and comments if (*bufferp == '#') continue; if (*bufferp == '\0') continue; // Get keyword char keyword[80]; if (sscanf(bufferp, "%s", keyword) != 1) { RNFail("Syntax error on line %d in file %s", line_count, filename); return 0; } // Check keyword if (!strcmp(keyword, "v")) { // Read vertex coordinates double x, y, z; if (sscanf(bufferp, "%s%lf%lf%lf", keyword, &x, &y, &z) != 4) { RNFail("Syntax error on line %d in file %s", line_count, filename); return 0; } // Create vertex R3TriangleVertex *vertex = new R3TriangleVertex(R3Point(x, y, z)); verts.Insert(vertex); } else if (!strcmp(keyword, "vt")) { // Read texture coordinates double u, v; if (sscanf(bufferp, "%s%lf%lf", keyword, &u, &v) != 3) { RNFail("Syntax error on line %d in file %s", line_count, filename); return 0; } // Create texture coordinates R2Point *vt = new R2Point(u, v); texture_coords.Insert(vt); } else if (!strcmp(keyword, "f")) { // Read vertex indices int quad = 1; char s1[128], s2[128], s3[128], s4[128] = { '\0' }; if (sscanf(bufferp, "%s%s%s%s%s", keyword, s1, s2, s3, s4) != 5) { quad = 0;; if (sscanf(bufferp, "%s%s%s%s", keyword, s1, s2, s3) != 4) { RNFail("Syntax error on line %d in file %s", line_count, filename); return 0; } } // Parse vertex indices int vi1 = -1, vi2 = -1, vi3 = -1, vi4 = -1; int ti1 = -1, ti2 = -1, ti3 = -1, ti4 = -1; char *p1 = strchr(s1, '/'); if (p1) { *p1 = 0; vi1 = atoi(s1); p1++; if (*p1) ti1 = atoi(p1); } else { vi1 = atoi(s1); ti1 = vi1; } char *p2 = strchr(s2, '/'); if (p2) { *p2 = 0; vi2 = atoi(s2); p2++; if (*p2) ti2 = atoi(p2); } else { vi2 = atoi(s2); ti2 = vi2; } char *p3 = strchr(s3, '/'); if (p3) { *p3 = 0; vi3 = atoi(s3); p3++; if (*p3) ti3 = atoi(p3); } else { vi3 = atoi(s3); ti3 = vi3; } if (quad) { char *p4 = strchr(s4, '/'); if (p4) { *p4 = 0; vi4 = atoi(s4); p4++; if (*p4) ti4 = atoi(p4); } else { vi4 = atoi(s4); ti4 = vi4; } } // Get vertices R3TriangleVertex *v1 = verts.Kth(vi1-1); R3TriangleVertex *v2 = verts.Kth(vi2-1); R3TriangleVertex *v3 = verts.Kth(vi3-1); R3TriangleVertex *v4 = (quad) ? verts.Kth(vi4-1) : NULL; // Assign texture coordinates if ((ti1 >= 0) && (ti1 < texture_coords.NEntries())) v1->SetTextureCoords(*(texture_coords.Kth(ti1-1))); if ((ti2 >= 0) && (ti2 < texture_coords.NEntries())) v2->SetTextureCoords(*(texture_coords.Kth(ti2-1))); if ((ti3 >= 0) && (ti3 < texture_coords.NEntries())) v3->SetTextureCoords(*(texture_coords.Kth(ti3-1))); if (quad) { if ((ti4 >= 0) && (ti4 < texture_coords.NEntries())) v4->SetTextureCoords(*(texture_coords.Kth(ti4-1))); } // Check vertices if ((v1 == v2) || (v2 == v3) || (v1 == v3)) continue; if ((quad) && ((v4 == v1) || (v4 == v2) || (v4 == v3))) quad = 0; // Create default material, if needed if (material_index == -1) { R3Brdf *brdf = new R3Brdf(RNRgb(0.2, 0.2, 0.2), RNRgb(0.8, 0.8, 0.8), RNRgb(0.0, 0.0, 0.0), RNRgb(0.0, 0.0, 0.0), 0.2, 1.0, 1.0); R3Material *material = new R3Material(brdf, "Default"); materials.Insert(material); RNArray<R3Triangle *> *mat_tris = new RNArray<R3Triangle *>(); material_triangles.Insert(mat_tris); material_index = 0; } // Get material assert(material_index >= 0); R3Material *material = materials.Kth(material_index); // Create first triangle R3Triangle *triangle = new R3Triangle(v1, v2, v3); tris.Insert(triangle); triangle_materials.Insert(material); material_triangles[material_index]->Insert(triangle); // Create second triangle if (quad) { R3Triangle *triangle = new R3Triangle(v1, v3, v4); tris.Insert(triangle); triangle_materials.Insert(material); material_triangles[material_index]->Insert(triangle); } } else if (!strcmp(keyword, "mtllib")) { // Read fields char mtlname[1024]; if (sscanf(bufferp, "%s%s", keyword, mtlname) != 2) { RNFail("Syntax error on line %d in file %s", line_count, filename); return 0; } // Read materials if (!ReadObjMtlFile(dirname, mtlname)) return 0; } else if (!strcmp(keyword, "usemtl")) { // Read fields char mtlname[1024]; if (sscanf(bufferp, "%s%s", keyword, mtlname) != 2) { RNFail("Syntax error on line %d in file %s", line_count, filename); return 0; } // Find material material_index = FindMaterialIndex(materials, mtlname); if (material_index == -1) { fprintf(stderr, "Unable to find material %s at on line %d in file %s", mtlname, line_count, filename); return 0; } } } // Create triangle array triangles = new R3TriangleArray(verts, tris); // Delete texture coordinates for (int i = 0; i < texture_coords.NEntries(); i++) { R2Point *vt = texture_coords.Kth(i); delete vt; } // Close file fclose(fp); // Return success return 1; }
void R3MeshSearchTree:: FindAll(const R3Point& query_position, const R3Vector& query_normal, RNArray<R3MeshIntersection *>& hits, RNScalar min_distance_squared, RNScalar max_distance_squared, int (*IsCompatible)(const R3Point&, const R3Vector&, R3Mesh *, R3MeshFace *, void *), void *compatible_data, R3MeshFace *face) const { // Check distance to plane const R3Plane& plane = mesh->FacePlane(face); RNScalar plane_signed_distance = R3SignedDistance(plane, query_position); RNScalar plane_distance_squared = plane_signed_distance * plane_signed_distance; if (plane_distance_squared >= max_distance_squared) return; // Check distance to bounding box RNScalar bbox_distance_squared = DistanceSquared(query_position, mesh->FaceBBox(face), max_distance_squared); if (bbox_distance_squared >= max_distance_squared) return; // Check compatibility if (IsCompatible) { if (!(*IsCompatible)(query_position, query_normal, mesh, face, compatible_data)) return; } // Get face vertices R3MeshVertex *v0 = mesh->VertexOnFace(face, 0); R3MeshVertex *v1 = mesh->VertexOnFace(face, 1); R3MeshVertex *v2 = mesh->VertexOnFace(face, 2); // Get vertex positions const R3Point& p0 = mesh->VertexPosition(v0); const R3Point& p1 = mesh->VertexPosition(v1); const R3Point& p2 = mesh->VertexPosition(v2); // Project query point onto face plane const R3Vector& face_normal = mesh->FaceNormal(face); R3Point plane_point = query_position - plane_signed_distance * face_normal; // Check sides of edges R3Vector e0 = p1 - p0; e0.Normalize(); R3Vector n0 = mesh->FaceNormal(face) % e0; R3Plane s0(p0, n0); RNScalar b0 = R3SignedDistance(s0, plane_point); R3Vector e1 = p2 - p1; e1.Normalize(); R3Vector n1 = mesh->FaceNormal(face) % e1; R3Plane s1(p1, n1); RNScalar b1 = R3SignedDistance(s1, plane_point); R3Vector e2 = p0 - p2; e2.Normalize(); R3Vector n2 = mesh->FaceNormal(face) % e2; R3Plane s2(p2, n2); RNScalar b2 = R3SignedDistance(s2, plane_point); // Initialize hit info R3MeshIntersection hit; hit.type = R3_MESH_NULL_TYPE; // Consider plane_point's position in relation to edges of the triangle if ((b0 >= 0) && (b1 >= 0) && (b2 >= 0)) { // Point is inside face if (plane_distance_squared >= min_distance_squared) { hit.type = R3_MESH_FACE_TYPE; hit.vertex = NULL; hit.edge = NULL; hit.face = face; hit.point = plane_point; hit.t = sqrt(plane_distance_squared); } } else { // Point is outside face -- check each edge if (b0 < 0) { // Outside edge0 R3Vector edge_vector = p1 - p0; RNScalar edge_length = edge_vector.Length(); if (edge_length > 0) { edge_vector /= edge_length; R3Vector point_vector = plane_point - p0; RNScalar t = edge_vector.Dot(point_vector); if (t <= 0) { RNScalar distance_squared = DistanceSquared(query_position, p0); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_VERTEX_TYPE; hit.vertex = v0; hit.edge = mesh->EdgeOnVertex(hit.vertex, face); hit.face = face; hit.point = p0; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } else if (t >= edge_length) { RNScalar distance_squared = DistanceSquared(query_position, p1); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_VERTEX_TYPE; hit.vertex = v1; hit.edge = mesh->EdgeOnVertex(hit.vertex, face); hit.face = face; hit.point = p1; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } else { R3Point point = p0 + t * edge_vector; RNScalar distance_squared = DistanceSquared(query_position, point); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_EDGE_TYPE; hit.vertex = NULL; hit.edge = mesh->EdgeOnFace(face, 0); hit.face = face; hit.point = point; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } } } if (b1 < 0) { // Outside edge1 R3Vector edge_vector = p2 - p1; RNScalar edge_length = edge_vector.Length(); if (edge_length > 0) { edge_vector /= edge_length; R3Vector point_vector = plane_point - p1; RNScalar t = edge_vector.Dot(point_vector); if (t <= 0) { RNScalar distance_squared = DistanceSquared(query_position, p1); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_VERTEX_TYPE; hit.vertex = v1; hit.edge = mesh->EdgeOnVertex(hit.vertex, face); hit.face = face; hit.point = p1; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } else if (t >= edge_length) { RNScalar distance_squared = DistanceSquared(query_position, p2); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_VERTEX_TYPE; hit.vertex = v2; hit.edge = mesh->EdgeOnVertex(hit.vertex, face); hit.face = face; hit.point = p2; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } else { R3Point point = p1 + t * edge_vector; RNScalar distance_squared = DistanceSquared(query_position, point); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_EDGE_TYPE; hit.vertex = NULL; hit.edge = mesh->EdgeOnFace(face, 1); hit.face = face; hit.point = point; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } } } if (b2 < 0) { // Outside edge2 R3Vector edge_vector = p0 - p2; RNScalar edge_length = edge_vector.Length(); if (edge_length > 0) { edge_vector /= edge_length; R3Vector point_vector = plane_point - p2; RNScalar t = edge_vector.Dot(point_vector); if (t <= 0) { RNScalar distance_squared = DistanceSquared(query_position, p2); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_VERTEX_TYPE; hit.vertex = v2; hit.edge = mesh->EdgeOnVertex(hit.vertex, face); hit.face = face; hit.point = p2; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } else if (t >= edge_length) { RNScalar distance_squared = DistanceSquared(query_position, p0); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_VERTEX_TYPE; hit.vertex = v0; hit.edge = mesh->EdgeOnVertex(hit.vertex, face); hit.face = face; hit.point = p0; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } else { R3Point point = p2 + t * edge_vector; RNScalar distance_squared = DistanceSquared(query_position, point); if ((distance_squared >= min_distance_squared) && (distance_squared < max_distance_squared)) { hit.type = R3_MESH_EDGE_TYPE; hit.vertex = NULL; hit.edge = mesh->EdgeOnFace(face, 2); hit.face = face; hit.point = point; hit.t = sqrt(distance_squared); max_distance_squared = distance_squared; } } } } } // Insert hit if (hit.type != R3_MESH_NULL_TYPE) { hits.Insert(new R3MeshIntersection(hit)); } }