Skip to content

Commit

Permalink
Added per triangle picking when BVH has not finished building
Browse files Browse the repository at this point in the history
  • Loading branch information
Kamzik123 committed Nov 28, 2024
1 parent dea4234 commit 9a680a8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 12 deletions.
8 changes: 4 additions & 4 deletions Mafia2Libs/Rendering/Graphics/BoundingVolumeHierarchy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public void SubdivideNode(BVHNode node)
return (axis, splitPos, bestCost);
}

public (float distance, Vector3 pos) Intersect(Ray ray)
public (float distance, Vector3 pos) Intersect(Ray ray, Ray localRay)
{
BVHNode node = Nodes[RootNodeID];
BVHNode[] stack = new BVHNode[64];
Expand All @@ -321,7 +321,7 @@ public void SubdivideNode(BVHNode node)

float t;

if (!Toolkit.Mathematics.Collision.RayIntersectsTriangle(ref ray, ref Vertices[triangle.x].Position, ref Vertices[triangle.y].Position, ref Vertices[triangle.z].Position, out t))
if (!Toolkit.Mathematics.Collision.RayIntersectsTriangle(ref localRay, ref Vertices[triangle.x].Position, ref Vertices[triangle.y].Position, ref Vertices[triangle.z].Position, out t))
{
continue;
}
Expand Down Expand Up @@ -352,8 +352,8 @@ public void SubdivideNode(BVHNode node)
BVHNode child1 = Nodes[node.LeftChild];
BVHNode child2 = Nodes[node.LeftChild + 1];

float? dist1 = ray.Intersects(child1.Bounds);
float? dist2 = ray.Intersects(child2.Bounds);
float? dist1 = localRay.Intersects(child1.Bounds);
float? dist2 = localRay.Intersects(child2.Bounds);

dist1 = dist1 == null ? 0.0f : dist1;
dist2 = dist2 == null ? 0.0f : dist2;
Expand Down
60 changes: 52 additions & 8 deletions Mafia2Libs/Rendering/Graphics/GraphicsClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,18 @@ public PickOutParams Pick(int sx, int sy, int Width, int Height)

if (model.Value is RenderModel mesh)
{
if (!mesh.BVH.FinishedBuilding)
{
continue;
}

var bbox = mesh.BoundingBox;

if (mesh.InstanceTransforms.Count > 0)
{
// We cannot use the per triangle picking method on instances as
// it can take several minutes to complete even on good hardware.
// We just have to deal with the potential picking ray miss.
if (!mesh.BVH.FinishedBuilding)
{
continue;
}

foreach (var transform in mesh.InstanceTransforms)
{
var transposed = Matrix4x4.Transpose(transform.Value);
Expand All @@ -226,7 +229,7 @@ public PickOutParams Pick(int sx, int sy, int Width, int Height)

if (localInstanceRay.Intersects(bbox) == 0.0f) continue;

var bvhInstanceIntersect = mesh.BVH.Intersect(localInstanceRay);
var bvhInstanceIntersect = mesh.BVH.Intersect(ray, localInstanceRay);

if (bvhInstanceIntersect.distance < lowest)
{
Expand All @@ -241,7 +244,22 @@ public PickOutParams Pick(int sx, int sy, int Width, int Height)

if (localRay.Intersects(bbox) == 0.0f) continue; // Pick doesn't seem to work when the camera is inside the bounding volume

var bvhIntersect = mesh.BVH.Intersect(localRay);
if (!mesh.BVH.FinishedBuilding)
{
var triangleIntersect = PerTriangleRayIntersect(ray, localRay, mesh);

if (triangleIntersect.distance < lowest)
{
lowest = triangleIntersect.distance;
lowestRefID = model.Key;
lowestInstanceID = -1;
WorldPosIntersect = triangleIntersect.pos;
}

continue;
}

var bvhIntersect = mesh.BVH.Intersect(ray, localRay);

if (bvhIntersect.distance < lowest)
{
Expand Down Expand Up @@ -305,7 +323,7 @@ public PickOutParams Pick(int sx, int sy, int Width, int Height)

if (localInstanceRay.Intersects(InstanceGizmo.InstanceModel.BoundingBox) == 0.0f) continue;

var bvhInstanceIntersect = InstanceGizmo.InstanceModel.BVH.Intersect(localInstanceRay);
var bvhInstanceIntersect = InstanceGizmo.InstanceModel.BVH.Intersect(ray, localInstanceRay);

if (bvhInstanceIntersect.distance < lowest)
{
Expand All @@ -325,6 +343,32 @@ public PickOutParams Pick(int sx, int sy, int Width, int Height)
return OutputParams;
}

private (float distance, Vector3 pos) PerTriangleRayIntersect(Ray ray, Ray localRay, RenderModel mesh)
{
(float distance, Vector3 pos) val = (float.MaxValue, Vector3.Zero);

for (var i = 0; i < mesh.LODs[0].Indices.Length / 3; i++)
{
var v0 = mesh.LODs[0].Vertices[mesh.LODs[0].Indices[i * 3]].Position;
var v1 = mesh.LODs[0].Vertices[mesh.LODs[0].Indices[i * 3 + 1]].Position;
var v2 = mesh.LODs[0].Vertices[mesh.LODs[0].Indices[i * 3 + 2]].Position;
float t;

if (!Toolkit.Mathematics.Collision.RayIntersectsTriangle(ref localRay, ref v0, ref v1, ref v2, out t)) continue;

var worldPosition = ray.Position + t * ray.Direction;
var distance = (worldPosition - ray.Position).LengthSquared();

if (distance < val.distance)
{
val.distance = distance;
val.pos = worldPosition;
}
}

return val;
}

public void Frame()
{
ClearRenderStack();
Expand Down

0 comments on commit 9a680a8

Please sign in to comment.