Skip to content
This repository has been archived by the owner on Nov 25, 2024. It is now read-only.

Commit

Permalink
Fix raycasting
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliver-makes-code committed Nov 18, 2023
1 parent 9b9deec commit 88b432c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 27 deletions.
36 changes: 21 additions & 15 deletions Common/Collision/AABB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ public double SlideWith(AABB other, dvec3 delta, out dvec3 normal) {
enter[i] = other.Min[i] - Max[i];
exit[i] = other.Max[i] - Min[i];
} else if (delta[i] < 0) {
enter[i] = Min[i] - other.Max[i];
exit[i] = Max[i] - other.Min[i];
enter[i] = other.Max[i] - Min[i];
exit[i] = other.Min[i] - Max[i];
} else if (CollidesWithOnAxis(other, i)) {
enter[i] = 0;
exit[i] = double.MaxValue;
Expand All @@ -83,12 +83,12 @@ public double SlideWith(AABB other, dvec3 delta, out dvec3 normal) {
}

dvec3
enterDiv = enter / length,
exitDiv = exit / length;
enterDiv = SafeDivide(enter, delta, double.MinValue),
exitDiv = SafeDivide(exit, delta, double.MaxValue);

double
enterDivMax = double.MinValue,
exitDivMin = exitDiv.Min();
exitDivMin = exitDiv.MinElement;

int maxDir = 0;

Expand Down Expand Up @@ -116,7 +116,6 @@ public bool PointInside(dvec3 point)

public bool RayIntersects(dvec3 rayOrigin, dvec3 rayDest, out dvec3 enter, out ivec3 normal) {
var delta = rayDest - rayOrigin;
double length = delta.Length;

enter = new(0);
normal = new(0);
Expand All @@ -128,27 +127,27 @@ public bool RayIntersects(dvec3 rayOrigin, dvec3 rayDest, out dvec3 enter, out i

for (int i = 0; i < 3; i++) {
if (delta[i] < 0) {
dEnter[i] = rayOrigin[i] - Max[i];
exit[i] = rayOrigin[i] - Min[i];
dEnter[i] = Max[i] - rayOrigin[i];
exit[i] = Min[i] - rayOrigin[i];
} else if (delta[i] > 0) {
dEnter[i] = rayOrigin[i] - Min[i];
exit[i] = rayOrigin[i] - Max[i];
dEnter[i] = Min[i] - rayOrigin[i];
exit[i] = Max[i] - rayOrigin[i];
} else if (PointInsideOnAxis(rayOrigin, i)) {
dEnter[i] = 0;
exit[i] = double.MaxValue;
} else {
enter[i] = double.MaxValue;
dEnter[i] = double.MaxValue;
exit[i] = -1;
}
}

dvec3
enterDiv = dEnter / length,
exitDiv = exit / length;
enterDiv = SafeDivide(dEnter, delta, double.MinValue),
exitDiv = SafeDivide(exit, delta, double.MaxValue);

double
enterDivMax = double.MinValue,
exitDivMin = exitDiv.Min();
exitDivMin = exitDiv.MinElement;

int maxDir = 0;

Expand All @@ -163,7 +162,7 @@ public bool RayIntersects(dvec3 rayOrigin, dvec3 rayDest, out dvec3 enter, out i
[maxDir] = -Math.Sign(delta[maxDir])
};

enter = rayOrigin + dEnter * -normal;
enter = rayOrigin + enterDivMax * delta;

return
enterDivMax <= exitDivMin &&
Expand All @@ -178,4 +177,11 @@ private bool CollidesWithOnAxis(AABB other, int axis)

private bool PointInsideOnAxis(dvec3 point, int axis)
=> point[axis] > Min[axis] && point[axis] < Max[axis];

private static dvec3 SafeDivide(dvec3 a, dvec3 b, double defaultValue)
=> new(
b.x == 0 ? defaultValue : a.x / b.x,
b.y == 0 ? defaultValue : a.y / b.y,
b.z == 0 ? defaultValue : a.z / b.z
);
}
28 changes: 16 additions & 12 deletions Common/World/Raycast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
namespace Voxel.Common.World;

public static class Raycast {
public static HitResult? Cast(this BlockView world, dvec3 start, dvec3 end) {
var delta = end - start;
public static HitResult? Cast(this BlockView world, dvec3 rayOrigin, dvec3 rayDest) {
var delta = rayDest - rayOrigin;

double
// Delta
Expand All @@ -25,27 +25,31 @@ public static class Raycast {
tDeltaY = 1 / Math.Abs(deltaY),
tDeltaZ = 1 / Math.Abs(deltaZ),
// tMax
tMaxX = GetTMax(start.x, tDeltaX, stepX),
tMaxY = GetTMax(start.y, tDeltaY, stepY),
tMaxZ = GetTMax(start.z, tDeltaZ, stepZ),
tMaxX = GetTMax(rayOrigin.x, tDeltaX, stepX),
tMaxY = GetTMax(rayOrigin.y, tDeltaY, stepY),
tMaxZ = GetTMax(rayOrigin.z, tDeltaZ, stepZ),
// Positions
x = start.x,
y = start.y,
z = start.z;
x = rayOrigin.x,
y = rayOrigin.y,
z = rayOrigin.z;

var endPos = end.WorldToBlockPosition();
var endPos = rayDest.WorldToBlockPosition();

while (true) {
var blockPos = new dvec3(x, y, z).WorldToBlockPosition();

if (world.GetBlock(blockPos).IsSolidBlock) {
if (new AABB(blockPos, blockPos + new dvec3(1))
.RayIntersects(start, end, out var pos, out var normal)) {
.RayIntersects(rayOrigin, rayDest, out var pos, out var normal)) {
return new(blockPos, pos, normal);
}
}

if (blockPos == endPos)

if (x * stepX > endPos.x * stepX)
return null;
if (y * stepY > endPos.y * stepY)
return null;
if (z * stepZ > endPos.z * stepZ)
return null;

switch (tMaxX < tMaxY) {
Expand Down
14 changes: 14 additions & 0 deletions Test/Tests/BlockViewSuite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ double RandomCoord()
Assert(hit?.BlockPos == randomPos.WorldToBlockPosition() - ivec3.UnitY, "Hit correct block");
Assert((hit?.WorldPos - randomPos)?.Length < 0.0001f, $"Hit correct world position");
}
},
["Single Diagonal Cast"] = () => {
var mock = new BlockViewMock(pos => pos.y < 0 ? Blocks.Stone : Blocks.Air);

var rayOrigin = new dvec3(0, 5, 0);
var rayDest = new dvec3(5, -5, 5);

var maybeCast = mock.Cast(rayOrigin, rayDest);
Assert(maybeCast != null, "Cast succeeded");
if (maybeCast == null)
return;
var cast = maybeCast.Value;
Assert(cast.BlockPos == new ivec3(2, -1, 2), "Hit correct block");
Assert(cast.WorldPos == new dvec3(2.5, 0, 2.5), "Hit correct world position");
}
};
}

0 comments on commit 88b432c

Please sign in to comment.