feat(core): add voxel raycasting for block targeting
This commit is contained in:
1 parent
e84def1b81
commit
f80aa78fc5
2 files changed
+98
No files matched your search
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "World.h"
|
||||||
|
#include "Math.h"
|
||||||
|
|
||||||
|
namespace vf {
|
||||||
|
|
||||||
|
struct RaycastHit {
|
||||||
|
bool hit = false;
|
||||||
|
Vec3i block; // solid voxel that was struck
|
||||||
|
Vec3i normal; // face normal pointing back toward the ray origin
|
||||||
|
float distance = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Amanatides and Woo voxel traversal. Steps the grid from `origin` along
|
||||||
|
// `direction` up to `maxDistance` world units, stopping at the first solid
|
||||||
|
// voxel. `normal` gives the entered face, so `block + normal` is the empty
|
||||||
|
// neighbour where a new block can be placed.
|
||||||
|
RaycastHit raycastVoxels(const World& world, const Vec3& origin, const Vec3& direction,
|
||||||
|
float maxDistance);
|
||||||
|
|
||||||
|
} // namespace vf
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#include "vf/Raycast.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace vf {
|
||||||
|
|
||||||
|
RaycastHit raycastVoxels(const World& world, const Vec3& origin, const Vec3& direction,
|
||||||
|
float maxDistance) {
|
||||||
|
RaycastHit result;
|
||||||
|
Vec3 dir = normalize(direction);
|
||||||
|
if (length(direction) <= 1e-8f) return result;
|
||||||
|
|
||||||
|
int vx = static_cast<int>(std::floor(origin.x));
|
||||||
|
int vy = static_cast<int>(std::floor(origin.y));
|
||||||
|
int vz = static_cast<int>(std::floor(origin.z));
|
||||||
|
|
||||||
|
int stepX = dir.x > 0 ? 1 : (dir.x < 0 ? -1 : 0);
|
||||||
|
int stepY = dir.y > 0 ? 1 : (dir.y < 0 ? -1 : 0);
|
||||||
|
int stepZ = dir.z > 0 ? 1 : (dir.z < 0 ? -1 : 0);
|
||||||
|
|
||||||
|
const float inf = 1e30f;
|
||||||
|
float tDeltaX = dir.x != 0.0f ? std::fabs(1.0f / dir.x) : inf;
|
||||||
|
float tDeltaY = dir.y != 0.0f ? std::fabs(1.0f / dir.y) : inf;
|
||||||
|
float tDeltaZ = dir.z != 0.0f ? std::fabs(1.0f / dir.z) : inf;
|
||||||
|
|
||||||
|
auto boundary = [](float o, int step) {
|
||||||
|
float cell = std::floor(o);
|
||||||
|
return step > 0 ? (cell + 1.0f - o) : (o - cell);
|
||||||
|
};
|
||||||
|
float tMaxX = dir.x != 0.0f ? boundary(origin.x, stepX) * tDeltaX : inf;
|
||||||
|
float tMaxY = dir.y != 0.0f ? boundary(origin.y, stepY) * tDeltaY : inf;
|
||||||
|
float tMaxZ = dir.z != 0.0f ? boundary(origin.z, stepZ) * tDeltaZ : inf;
|
||||||
|
|
||||||
|
Vec3i normal;
|
||||||
|
float t = 0.0f;
|
||||||
|
|
||||||
|
if (world.solidAt(vx, vy, vz)) {
|
||||||
|
result.hit = true;
|
||||||
|
result.block = {vx, vy, vz};
|
||||||
|
result.normal = {0, 0, 0};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (t <= maxDistance) {
|
||||||
|
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
|
||||||
|
vx += stepX;
|
||||||
|
t = tMaxX;
|
||||||
|
tMaxX += tDeltaX;
|
||||||
|
normal = {-stepX, 0, 0};
|
||||||
|
} else if (tMaxY < tMaxZ) {
|
||||||
|
vy += stepY;
|
||||||
|
t = tMaxY;
|
||||||
|
tMaxY += tDeltaY;
|
||||||
|
normal = {0, -stepY, 0};
|
||||||
|
} else {
|
||||||
|
vz += stepZ;
|
||||||
|
t = tMaxZ;
|
||||||
|
tMaxZ += tDeltaZ;
|
||||||
|
normal = {0, 0, -stepZ};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t > maxDistance) break;
|
||||||
|
|
||||||
|
if (world.solidAt(vx, vy, vz)) {
|
||||||
|
result.hit = true;
|
||||||
|
result.block = {vx, vy, vz};
|
||||||
|
result.normal = normal;
|
||||||
|
result.distance = t;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vf
|
||||||
Reference in new issue
Block a user