RNBoolean R3Model:: Intersects(const R3Ray& ray, R3Point *hit_point, R3Vector *hit_normal, RNScalar *hit_t, int *hit_triangle_index) const { // Check triangles if (!triangles) return FALSE; // Check bounding box if (!R3Intersects(ray, BBox())) return FALSE; // Check each triangle for intersection RNScalar min_t = FLT_MAX; for (int i = 0; i < NTriangles(); i++) { const R3Triangle *triangle = Triangle(i); R3Point point; R3Vector normal; RNScalar t; if (R3Intersects(ray, *triangle, &point, &normal, &t) == R3_POINT_CLASS_ID) { if (t < min_t) { if (hit_point) *hit_point = point; if (hit_normal) *hit_normal = normal; if (hit_triangle_index) *hit_triangle_index = i; if (hit_t) *hit_t = t; min_t = t; } } } // Return whether hit any triangle return (min_t == FLT_MAX) ? FALSE : TRUE; }
void R3MeshSearchTree:: FindIntersection(const R3Ray& ray, R3MeshIntersection& closest, RNScalar min_t, RNScalar& max_t, int (*IsCompatible)(const R3Point&, const R3Vector&, R3Mesh *, R3MeshFace *, void *), void *compatible_data, R3MeshFace *face) const { // Check compatibility if (IsCompatible) { if (!(*IsCompatible)(ray.Start(), ray.Vector(), mesh, face, compatible_data)) return; } // Check intersection with plane (this is redundant, but allows checking min_t and max_t) RNScalar plane_t; if (!R3Intersects(ray, mesh->FacePlane(face), NULL, &plane_t)) return; if (plane_t >= max_t) return; if (plane_t < min_t) return; // Check intersection with face R3MeshIntersection face_intersection; if (!mesh->Intersection(ray, face, &face_intersection)) return; if (face_intersection.t >= max_t) return; if (face_intersection.t < min_t) return; // Update closest intersection closest.type = R3_MESH_FACE_TYPE; closest.face = face; closest.point = face_intersection.point; closest.t = face_intersection.t; max_t = face_intersection.t; }
void R3MeshSearchTree:: InsertFace(R3MeshFace *face) { // Check if face intersects box if (!R3Intersects(mesh, face, BBox())) return; // Create container R3MeshSearchTreeFace *face_container = new R3MeshSearchTreeFace(mesh, face); assert(face_container); // Insert face into root InsertFace(face_container, root, BBox(), 0); }
void R3MeshSearchTree:: InsertFace(R3MeshSearchTreeFace *face, R3MeshSearchTreeNode *node, const R3Box& node_box, int depth) { // Check if face intersects box if (!R3Intersects(mesh, face->face, node_box)) return; // Check if interior node if (node->children[0]) { // Interior node -- Insert into children assert(node->children[1]); const R3Box& face_box = mesh->FaceBBox(face->face); if (face_box[RN_LO][node->split_dimension] <= node->split_coordinate) { R3Box node0_box(node_box); node0_box[RN_HI][node->split_dimension] = node->split_coordinate; InsertFace(face, node->children[0], node0_box, depth + 1); } if (face_box[RN_HI][node->split_dimension] >= node->split_coordinate) { R3Box node1_box(node_box); node1_box[RN_LO][node->split_dimension] = node->split_coordinate; InsertFace(face, node->children[1], node1_box, depth + 1); } } else { // Check face area RNScalar node_diagonal = node_box.DiagonalLength(); if (node_diagonal == 0) return; RNScalar node_area = node_diagonal * node_diagonal; RNScalar area_ratio = face->area / node_area; if ((area_ratio >= max_area_ratio) || (depth >= max_depth)) { // Face is too big/deep to be sorted into children, insert into big faces list node->big_faces.Insert(face); assert(face->reference_count >= 0); face->reference_count++; } else { // Leaf node -- Check if there is room for this face if (node->small_faces.NEntries() < max_faces_per_node) { // Simply insert face into list node->small_faces.Insert(face); face->reference_count++; } else { // Create two children node->children[0] = new R3MeshSearchTreeNode(node); node->children[1] = new R3MeshSearchTreeNode(node); node->split_dimension = node_box.LongestAxis(); node->split_coordinate = node_box.AxisCenter(node->split_dimension); nnodes += 2; // Re-insert faces into subtree InsertFace(face, node, node_box, depth+1); for (int i = 0; i < node->small_faces.NEntries(); i++) { InsertFace(node->small_faces[i], node, node_box, depth+1); } // Clear out faces from node that is now interior for (int i = 0; i < node->small_faces.NEntries(); i++) { assert(node->small_faces[i]->reference_count > 0); node->small_faces[i]->reference_count--; } node->small_faces.Empty(); } } } }
static RNBoolean R3Intersects(R3Mesh *mesh, R3MeshFace *face, const R3Box& box) { // Check triangle bounding box and plane if (R3Contains(box, mesh->FaceBBox(face))) return TRUE; if (!R3Intersects(mesh->FaceBBox(face), box)) return FALSE; if (!R3Intersects(mesh->FacePlane(face), box)) return FALSE; // Make polygon R3Point points[16]; points[0] = mesh->VertexPosition(mesh->VertexOnFace(face, 0)); points[1] = mesh->VertexPosition(mesh->VertexOnFace(face, 1)); points[2] = mesh->VertexPosition(mesh->VertexOnFace(face, 2)); int npoints = 3; // Clip polygon against each side for (RNDirection dir = RN_LO; dir <= RN_HI; dir++) { for (RNDimension dim = RN_X; dim <= RN_Z; dim++) { assert(npoints < 15); points[npoints] = points[0]; for (int i = 0; i < npoints; i++) { R3Point& p1 = points[i]; R3Point& p2 = points[i+1]; RNScalar d1 = p1[dim] - box[dir][dim]; RNScalar d2 = p2[dim] - box[dir][dim]; if (dir == RN_LO) { d1 = -d1; d2 = -d2; } if (d1 < 0) { // Inside if (d2 > 0) { // Outside // Insert a point at crossing RNScalar denom = d2 + -d1; R3Point p = (d2/denom)*p1 + (-d1/denom)*p2; for (int j = npoints; j > i; j--) points[j+1] = points[j]; points[i+1] = p; npoints++; i += 2; } } else if (d1 > 0) { // Outside if (d2 < 0) { // Inside // Replace p1 with point at crossing RNScalar denom = -d2 + d1; R3Point p = (-d2/denom)*p1 + (d1/denom)*p2; points[i] = p; } else { // Remove p1 for (int j = i; j < npoints; j++) points[j] = points[j+1]; npoints--; i--; } } } // Check number of points if (npoints == 0) return FALSE; assert(npoints < 16); assert(npoints > 0); } } // Triangle survived all clips return TRUE; }
void R3MeshSearchTree:: FindIntersection(const R3Ray& ray, R3MeshIntersection& closest, RNScalar min_t, RNScalar& max_t, int (*IsCompatible)(const R3Point&, const R3Vector&, R3Mesh *, R3MeshFace *, void *), void *compatible_data, R3MeshSearchTreeNode *node, const R3Box& node_box) const { // Find intersection with bounding box RNScalar node_box_t; if (!R3Intersects(ray, node_box, NULL, NULL, &node_box_t)) return; if (node_box_t > max_t && !R3Contains(node_box, ray.Start())) return; // Update based on closest intersection to each big face for (int i = 0; i < node->big_faces.NEntries(); i++) { // Get face container and check mark R3MeshSearchTreeFace *face_container = node->big_faces[i]; if (face_container->mark == mark) continue; face_container->mark = mark; // Find closest point in mesh face FindIntersection(ray, closest, min_t, max_t, IsCompatible, compatible_data, face_container->face); } // Check if node is interior if (node->children[0]) { assert(node->children[1]); assert(node->small_faces.IsEmpty()); // Compute distance from query point to split plane RNScalar side = ray.Start()[node->split_dimension] - node->split_coordinate; RNScalar vec = ray.Vector()[node->split_dimension]; RNScalar plane_t = (side*vec < 0) ? -side/vec : RN_INFINITY; // Search children nodes if (side <= 0) { // Search negative side first if (plane_t >= min_t) { R3Box child_box(node_box); child_box[RN_HI][node->split_dimension] = node->split_coordinate; FindIntersection(ray, closest, min_t, max_t, IsCompatible, compatible_data, node->children[0], child_box); } if (plane_t < max_t) { R3Box child_box(node_box); child_box[RN_LO][node->split_dimension] = node->split_coordinate; FindIntersection(ray, closest, min_t, max_t, IsCompatible, compatible_data, node->children[1], child_box); } } else { // Search positive side first if (plane_t >= min_t) { R3Box child_box(node_box); child_box[RN_LO][node->split_dimension] = node->split_coordinate; FindIntersection(ray, closest, min_t, max_t, IsCompatible, compatible_data, node->children[1], child_box); } if (plane_t < max_t) { R3Box child_box(node_box); child_box[RN_HI][node->split_dimension] = node->split_coordinate; FindIntersection(ray, closest, min_t, max_t, IsCompatible, compatible_data, node->children[0], child_box); } } } else { // Update based on distance to each small face for (int i = 0; i < node->small_faces.NEntries(); i++) { // Get face container and check mark R3MeshSearchTreeFace *face_container = node->small_faces[i]; if (face_container->mark == mark) continue; face_container->mark = mark; // Find closest point in mesh face FindIntersection(ray, closest, min_t, max_t, IsCompatible, compatible_data, face_container->face); } } }