/// <summary> /// Search in the BVH tree using a flexible "locator" object, which should return the /// "distance" of a BVH Node object. Returns a list of results in increasing distance /// up to a relative bound and absolute maximum. /// </summary> /// <param name="distance_max">The maximum distance to look for.</param> /// <param name="relative_bound">Stop returning more items after 'relative_bound' times /// the first item's distance.</param> /// <param name="locator">A delegate, like Locate() but passed also the current 'distance_max'.</param> /// <returns>A list of instances of the concrete BVHNodeBase subclass.</returns> public List <BVHNodeBase> LocateList(float distance_max, float relative_bound, DistanceToNodeEx locator) { var output_list = new List <BVHNodeBase>(); if (!(root is BVHNode)) { if (root != null) { float distance = locator(root, distance_max); if (distance < distance_max) { output_list.Add(root); } } return(output_list); } var heapq = new HeapQ <LocNext>(); BVHNodeBase base_node = root; float distance_1 = 0; while (distance_1 < distance_max) { if (base_node is BVHNode) { var node = (BVHNode)base_node; float d_left = locator(node.left, distance_max); if (d_left < distance_max) { if (!(node.left is BVHNode)) { distance_max = Mathf.Min(distance_max, d_left * relative_bound); } heapq.Push(new LocNext(d_left, node.left)); } float d_right = locator(node.right, distance_max); if (d_right < distance_max) { if (!(node.right is BVHNode)) { distance_max = Mathf.Min(distance_max, d_right * relative_bound); } heapq.Push(new LocNext(d_right, node.right)); } } else { output_list.Add(base_node); } if (heapq.Empty) { break; } LocNext next = heapq.Pop(); distance_1 = next.distance; base_node = next.node; } return(output_list); }
/// <summary> /// Search in the BVH tree using a flexible "locator" delegate, which should return the /// "distance" of a BVH Node object. /// </summary> /// <param name="distance">The maximum distance to look for.</param> /// <param name="locator">A delegate. A typical implementation has got two cases: if /// given an instance of a concrete class that the caller knows about, it should return /// the distance to that; otherwise, it should return the distance to the bounding box. /// The present algorithm assumes that when a bounding box grows, the distance returned /// by "locator" decreases or stays constant.</param> /// <returns>A instance of the concrete BVHNodeBase subclass.</returns> public BVHNodeBase Locate(float distance_max, DistanceToNode locator) { if (!(root is BVHNode)) { if (root != null) { float distance = locator(root, distance_max); if (distance < distance_max) { return(root); } } return(null); } var heapq = new HeapQ <LocNext>(); BVHNodeBase base_node = root; while (true) { if (base_node is BVHNode) { var node = (BVHNode)base_node; float d_left = locator(node.left, distance_max); if (d_left < distance_max) { if (!(node.left is BVHNode)) { distance_max = d_left; } heapq.Push(new LocNext(d_left, node.left)); } float d_right = locator(node.right, distance_max); if (d_right < distance_max) { if (!(node.right is BVHNode)) { distance_max = d_right; } heapq.Push(new LocNext(d_right, node.right)); } } else { return(base_node); } if (heapq.Empty) { break; } LocNext next = heapq.Pop(); base_node = next.node; } return(null); }
public void RemoveObject(BVHNodeBase old_object) { most_recent_node = null; if (old_object == root) { root = null; return; } BVHNodeBase keep; BVHNode parent = old_object.bvh_parent; if (parent.left == old_object) { keep = parent.right; } else { keep = parent.left; } BVHNode grandparent = parent.bvh_parent; keep.bvh_parent = grandparent; if (grandparent == null) { root = keep; } else { if (grandparent.left == parent) { grandparent.left = keep; } else { grandparent.right = keep; } UpdateBounds(grandparent); } }
public void AddObject(BVHNodeBase new_object) { if (root == null) { new_object.bvh_parent = null; root = new_object; return; } // 0. start at 'most_recent_node', and go up as long as its // bounding box does not contain new_object's bounding box. BVHNodeBase walk = most_recent_node; if (walk == null) { walk = root; } else { while (walk != root) { if (walk.bbox.Contains(new_object.bbox)) { break; } walk = walk.bvh_parent; } } // 1. first we traverse the node looking for the best leaf float newObSAH = SA(new_object); while (walk is BVHNode) { var curNode = (BVHNode)walk; // find the best way to add this object.. 3 options.. // 1. send to left node (L+N,R) // 2. send to right node (L,R+N) // 3. merge and pushdown left-and-right node (L+R,N) // we tend to avoid option 3 by the 0.3f factor below, because it means // that an unknown number of nodes get their depth increased. /* first a performance hack which also helps to randomly even out the * two sides in case 'new_object' is between both the bounding box of * 'left' and of 'right' */ var right = curNode.right; bool contains_right = right.bbox.Contains(new_object.bbox); var left = curNode.left; if (left.bbox.Contains(new_object.bbox)) { if (contains_right && _NextRandomBit()) { walk = right; } else { walk = left; } continue; } else if (contains_right) { walk = right; continue; } float leftSAH = SA(left); float rightSAH = SA(right); float sendLeftSAH = rightSAH + SA(left, new_object); // (L+N,R) float sendRightSAH = leftSAH + SA(right, new_object); // (L,R+N) float mergedLeftAndRightSAH = SA(left, right) + newObSAH; // (L+R,N) if (mergedLeftAndRightSAH < 0.3f * Mathf.Min(sendLeftSAH, sendRightSAH)) { break; } else { if (sendLeftSAH < sendRightSAH) { walk = left; } else { walk = right; } } } // 2. then we add the object and map it to our leaf BVHNode parent = walk.bvh_parent; most_recent_node = parent; var new_node = new BVHNode { bbox = walk.bbox, left = walk, right = new_object, bvh_parent = parent }; walk.bvh_parent = new_node; new_object.bvh_parent = new_node; if (parent == null) { Debug.Assert(walk == root); root = new_node; } else if (parent.left == walk) { parent.left = new_node; } else { parent.right = new_node; } UpdateBounds(new_node); }
internal LocNext(float distance, BVHNodeBase node) { this.distance = distance; this.node = node; }
float SA(BVHNodeBase n1, BVHNodeBase n2) /* return the SA() of the union */ { return(SA(Vector3.Max(n1.bbox.max, n2.bbox.max) - Vector3.Min(n1.bbox.min, n2.bbox.min))); }
float SA(BVHNodeBase node) { return(SA(node.bbox.max - node.bbox.min)); }