CodeWalker/CodeWalker.Core/Utils/TriangleBVH.cs

211 lines
6.7 KiB
C#

using Collections.Pooled;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Text;
namespace CodeWalker
{
public class TriangleBVH : TriangleBVHNode
{
public TriangleBVH(TriangleBVHItem[] tris, int depth = 8)
{
if (tris is null || tris.Length == 0)
return;
var min = new Vector3(float.MaxValue);
var max = new Vector3(float.MinValue);
for (int i = 0; i < tris.Length; i++)
{
var tri = tris[i];
tri.UpdateBox();
Vector3.Min(ref min, ref tri.Box.Minimum, out min);
Vector3.Max(ref max, ref tri.Box.Maximum, out max);
}
_Box = new BoundingBox(min, max);
Build(tris, depth);
}
}
public class TriangleBVHNode
{
public TriangleBVHItem[] Triangles { get; set; }
public TriangleBVHNode? Node1 { get; set; }
public TriangleBVHNode? Node2 { get; set; }
public BoundingBox _Box;
public BoundingBox Box => _Box;
public void Build(TriangleBVHItem[] tris, int depth)
{
if (tris.Length <= 10) depth = 0;
if (depth <= 0)
{
Triangles = tris;
Node1 = null;
Node2 = null;
}
else
{
var min = _Box.Minimum;
var max = _Box.Maximum;
var cen = _Box.Center;
var siz = _Box.Size;
BoundingBox b1, b2;
if ((siz.X >= siz.Y) && (siz.X >= siz.Z))
{
b1 = new BoundingBox(min, new Vector3(cen.X, max.Y, max.Z));
b2 = new BoundingBox(new Vector3(cen.X, min.Y, min.Z), max);
}
else if (siz.Y >= siz.Z)
{
b1 = new BoundingBox(min, new Vector3(max.X, cen.Y, max.Z));
b2 = new BoundingBox(new Vector3(min.X, cen.Y, min.Z), max);
}
else
{
b1 = new BoundingBox(min, new Vector3(max.X, max.Y, cen.Z));
b2 = new BoundingBox(new Vector3(min.X, min.Y, cen.Z), max);
}
using var l1 = new PooledList<TriangleBVHItem>();
using var l2 = new PooledList<TriangleBVHItem>();
for (int i = 0; i < tris.Length; i++)
{
var tri = tris[i];
if (tri.Box.Contains(ref b1) != ContainmentType.Disjoint)// (tri.Box.Intersects(b1))
{
l1.Add(tri);
}
if (tri.Box.Contains(ref b2) != ContainmentType.Disjoint)// (tri.Box.Intersects(b2))
{
l2.Add(tri);
}
}
if (l1.Count > 0)
{
Node1 = new TriangleBVHNode();
Node1._Box = b1;
Node1.Build(l1.ToArray(), depth - 1);
}
if (l2.Count > 0)
{
Node2 = new TriangleBVHNode();
Node2._Box = b2;
Node2.Build(l2.ToArray(), depth - 1);
}
}
}
public TriangleBVHItem? RayIntersect(ref Ray ray, ref float hitdist)
{
if (!ray.Intersects(ref _Box))
return null;
TriangleBVHItem? hit = null;
if (Triangles is not null)
{
foreach(var tri in Triangles)
{
if (ray.Intersects(ref tri.Corner1, ref tri.Corner2, ref tri.Corner3, out float d) && d < hitdist && d > 0)
{
hitdist = d;
hit = tri;
}
}
}
if (Node1 is not null)
{
var hd = hitdist;
var h = Node1.RayIntersect(ref ray, ref hd);
if (h != null && hd < hitdist)
{
hitdist = hd;
hit = h;
}
}
if (Node2 is not null)
{
var hd = hitdist;
var h = Node2.RayIntersect(ref ray, ref hd);
if (h != null && hd < hitdist)
{
hitdist = hd;
hit = h;
}
}
return hit;
}
}
public abstract class TriangleBVHItem
{
public Vector3 Corner1;
public Vector3 Corner2;
public Vector3 Corner3;
public BoundingBox Box;
public Vector3 Center
{
get => (Corner1 + Corner2 + Corner3) * 0.3333333f;
set
{
var delta = value - Center;
Corner1 += delta;
Corner2 += delta;
Corner3 += delta;
}
}
public Quaternion Orientation
{
get => _Orientation;
set
{
Quaternion.Invert(ref _Orientation, out var inv);
var delta = value * inv;
var cen = Center;
Corner1 = cen + delta.Multiply(Corner1 - cen);
Corner2 = cen + delta.Multiply(Corner2 - cen);
Corner3 = cen + delta.Multiply(Corner3 - cen);
_Orientation = value;
}
}
private Quaternion _Orientation = Quaternion.Identity;
public Vector3 Scale
{
get => _Scale;
set
{
Quaternion.Invert(ref _Orientation, out var inv);
var delta = value / _Scale;
var cen = Center;
Corner1 = cen + _Orientation.Multiply(inv.Multiply(Corner1 - cen) * delta);
Corner2 = cen + _Orientation.Multiply(inv.Multiply(Corner2 - cen) * delta);
Corner3 = cen + _Orientation.Multiply(inv.Multiply(Corner3 - cen) * delta);
_Scale = value;
}
}
private Vector3 _Scale = Vector3.One;
public void UpdateBox()
{
var min = new Vector3(float.MaxValue);
var max = new Vector3(float.MinValue);
Vector3.Min(ref min, ref Corner1, out min);
Vector3.Min(ref min, ref Corner2, out min);
Vector3.Min(ref min, ref Corner3, out min);
Vector3.Max(ref max, ref Corner1, out max);
Vector3.Max(ref max, ref Corner2, out max);
Vector3.Max(ref max, ref Corner3, out max);
Box = new BoundingBox(min, max);
}
}
}