diff --git a/Adapters/BVHTriangleAdapter.cs b/Adapters/BVHTriangleAdapter.cs new file mode 100644 index 0000000..395ba43 --- /dev/null +++ b/Adapters/BVHTriangleAdapter.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using System; +using System.Collections.Generic; + + +namespace DataStructures { + public interface Triangle { + Vector3 a { get; set; } + Vector3 b { get; set; } + Vector3 c { get; set; } + } + + public class BVHRayHitTest { + public BVHRayHitTest(Ray _ray) { + ray = _ray; + } + public bool NodeTraversalTest(Bounds box) => box.IntersectRay(ray); + public Ray ray { get; set; } + } + + public class BVHTriangleAdapter : IBVHNodeAdapter { + private BVH _bvh; + private Dictionary> triangeToLeafMap = new Dictionary>(); + + + BVH IBVHNodeAdapter.BVH { get => _bvh; set { _bvh = value; }} + + public void CheckMap(Triangle triangle) { + if (!triangeToLeafMap.ContainsKey(triangle)) { + throw new Exception("missing map for shuffled child!"); + } + } + + public BVHNode GetLeaf(Triangle triangle) => triangeToLeafMap[triangle]; + public Vector3 GetObjectPos(Triangle triangle) { + float x = (triangle.a.x + triangle.b.x + triangle.c.x) / 3.0f; + float y = (triangle.a.y + triangle.b.y + triangle.c.y) / 3.0f; + float z = (triangle.a.z + triangle.b.z + triangle.c.z) / 3.0f; + return new Vector3(x, y, z); + } + + public float GetRadius(Triangle triangle) { + Vector3 centroid = new Vector3( + (triangle.a.x + triangle.b.x + triangle.c.x) / 3.0f, + (triangle.a.y + triangle.b.y + triangle.c.y) / 3.0f, + (triangle.a.z + triangle.b.z + triangle.c.z) / 3.0f + ); + return Mathf.Max( + Mathf.Max(Vector3.Distance(centroid, triangle.a), Vector3.Distance(centroid, triangle.b), + Vector3.Distance(centroid, triangle.c))); + } + + public void MapObjectToBVHLeaf(Triangle triangle, BVHNode node) { + triangeToLeafMap[triangle] = node; + } + + public void OnPositionOrSizeChanged(Triangle changed) + { + // the SSObject has changed, so notify the BVH leaf to refit for the object + triangeToLeafMap[changed].RefitObjectChanged(this, changed); + } + + public void UnmapObject(Triangle triangle) { + triangeToLeafMap.Remove(triangle); + } + } +} \ No newline at end of file diff --git a/Adapters/BVHTriangleAdapter.cs.meta b/Adapters/BVHTriangleAdapter.cs.meta new file mode 100644 index 0000000..c59cc51 --- /dev/null +++ b/Adapters/BVHTriangleAdapter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 068a34072d4ee724599ebed5a2664491 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/BVH.cs b/BVH.cs index 3424ca9..2c83032 100644 --- a/BVH.cs +++ b/BVH.cs @@ -54,6 +54,7 @@ public class BVH public BVHNode rootBVH; public IBVHNodeAdapter nAda; public readonly int LEAF_OBJ_MAX; + public readonly float MIN_NODE_VOLUME; public int nodeCount = 0; public int maxDepth = 0; @@ -79,6 +80,38 @@ public List> Traverse(NodeTraversalTest hitTest) return hits; } + public SortedList> SortedTraverse(NodeTraversalTest hitTest) { + SortedList> sortedHit = new SortedList>(); + void traverse(BVHNode node, NodeTraversalTest hitTest, SortedList> hitList) { + if (node == null) return; + if (hitTest(node.Box)) { + hitList[Vector3.Magnitude(node.Box.size)] = node; + traverse(node.Left, hitTest, hitList); + traverse(node.Right, hitTest, hitList); + } + } + traverse(rootBVH, hitTest, sortedHit); + return sortedHit; + } + + public bool IntersectRay(Ray ray, out BVHNode nodeHit) { + BVHNode _hit = null; + void traverse(BVHNode node) { + if (node == null) return; + if (node.Box.IntersectRay(ray)) { + if (node.IsLeaf) { + _hit = node; + return; + } + traverse(node.Left); + traverse(node.Right); + } + } + traverse(rootBVH); + nodeHit = _hit; + return nodeHit != null && nodeHit.IsLeaf; + } + /* public List Traverse(Ray ray) { @@ -159,9 +192,10 @@ public int CountBVHNodes() /// /// /// WARNING! currently this must be 1 to use dynamic BVH updates - public BVH(IBVHNodeAdapter nodeAdaptor, List objects, int LEAF_OBJ_MAX = 1) + public BVH(IBVHNodeAdapter nodeAdaptor, List objects, int LEAF_OBJ_MAX = 1, float MIN_NODE_VOLUME = 0.7f) { this.LEAF_OBJ_MAX = LEAF_OBJ_MAX; + this.MIN_NODE_VOLUME = MIN_NODE_VOLUME; nodeAdaptor.BVH = this; this.nAda = nodeAdaptor; @@ -202,6 +236,26 @@ public void GetAllNodeMatriciesRecursive(BVHNode n, ref List matri if (n.Left != null) GetAllNodeMatriciesRecursive(n.Left, ref matricies, depth + 1); } + public Matrix4x4 GetNodeMatrix(BVHNode node) => Matrix4x4.Translate(node.Box.center) * Matrix4x4.Scale(node.Box.size); + + + public void RenderNode(BVHNode node) { + Matrix4x4 mat = GetNodeMatrix(node); + List matrices = new List(); + matrices.Add(mat); + Mesh mesh = new Mesh(); + mesh.SetVertices(vertices); + mesh.SetIndices(indices, MeshTopology.Lines, 0); + if(_debugRenderMaterial == null) + { + _debugRenderMaterial = new Material(Shader.Find("Standard")) + { + enableInstancing = true + }; + } + Graphics.DrawMeshInstanced(mesh, 0, _debugRenderMaterial, matrices); + } + public void RenderDebug() { if (!SystemInfo.supportsInstancing) @@ -225,6 +279,12 @@ public void RenderDebug() enableInstancing = true }; } + + int iterations = (int)Mathf.Floor((float)matricies.Count / 1023.0f); + for (int i = 0; i < iterations; i++) { + Graphics.DrawMeshInstanced(mesh, 0, _debugRenderMaterial, matricies.GetRange(0, 1023)); + matricies.RemoveRange(0, 1023); + } Graphics.DrawMeshInstanced(mesh, 0, _debugRenderMaterial, matricies); } } diff --git a/BVHNode.cs b/BVHNode.cs index 79957b5..5b7df3f 100644 --- a/BVHNode.cs +++ b/BVHNode.cs @@ -903,10 +903,11 @@ private BVHNode(BVH bvh, BVHNode lparent, List gobjectlist, Axis lastSp // if we have more than (bvh.LEAF_OBJECT_COUNT) objects, then compute the volume and split GObjects = gobjectlist; ComputeVolume(nAda); - SplitNode(nAda); - ChildRefit(nAda, propagate: false); + if (Box.size.magnitude > bvh.MIN_NODE_VOLUME) { + SplitNode(nAda); + ChildRefit(nAda, propagate: false); + } } } - } } diff --git a/README.md b/README.md index f535809..e295e4c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Unity Bounding Volume Heirachy +# Unity Bounding Volume Hierachy ![BVH GIF](https://media.giphy.com/media/ZaomLtyboZSp9zl6WY/giphy.gif)