#ifndef DC_COLLISION_SDF_H
#define DC_COLLISION_SDF_H
#include <godot_cpp/variant/vector3.hpp>
#include "dc_generator.h"
#include <cmath>
namespace godot {
// ---------------------------------------------------------------------------
// DCCollisionSDF — static SDF collision queries against a DCGenerator.
// Extracted from DCTerrain's get_distance / get_surface_normal / get_capsule_push.
// These are pure density field queries with no per-frame state.
// ---------------------------------------------------------------------------
class DCCollisionSDF {
public:
// Returns signed distance to terrain surface (positive = outside solid, negative = inside).
// Uses SDF convention: negate density (density is positive inside solid).
static float get_distance(DCGenerator* generator, const Vector3& position) {
return -generator->evaluate(position.x, position.y, position.z, 0.0f);
}
// Returns outward-pointing surface normal at position via central differences.
// h: step size for gradient computation (typically voxel_size * 0.01).
static Vector3 get_surface_normal(DCGenerator* generator, const Vector3& position, float h) {
float x = position.x, y = position.y, z = position.z;
float gx = generator->evaluate(x + h, y, z, 0.0f) - generator->evaluate(x - h, y, z, 0.0f);
float gy = generator->evaluate(x, y + h, z, 0.0f) - generator->evaluate(x, y - h, z, 0.0f);
float gz = generator->evaluate(x, y, z + h, 0.0f) - generator->evaluate(x, y, z - h, 0.0f);
// Gradient points toward increasing density (into solid).
// Negate to get outward-pointing surface normal (away from solid).
float len = std::sqrt(gx * gx + gy * gy + gz * gz);
if (len < 1e-8f) return Vector3(0, 1, 0); // degenerate: default to up
float inv = -1.0f / len;
return Vector3(gx * inv, gy * inv, gz * inv);
}
// Capsule collision query: returns push-out vector if capsule intersects terrain.
// Returns zero vector if no collision. Tests bottom, center, and top of capsule.
static Vector3 get_capsule_push(DCGenerator* generator, const Vector3& position,
float radius, float height, float voxel_size) {
float half_h = height * 0.5f;
Vector3 points[3] = {
Vector3(position.x, position.y - half_h + radius, position.z), // bottom
position, // center
Vector3(position.x, position.y + half_h - radius, position.z), // top
};
float h = voxel_size * 0.01f;
Vector3 total_push(0, 0, 0);
for (int i = 0; i < 3; i++) {
float dist = get_distance(generator, points[i]);
if (dist < radius) {
// Penetrating — push out along surface normal
Vector3 normal = get_surface_normal(generator, points[i], h);
float penetration = radius - dist;
total_push += normal * penetration;
}
}
return total_push;
}
};
} // namespace godot
#endif // DC_COLLISION_SDF_H