Files
2026-02-28 12:43:44 +08:00

263 lines
9.4 KiB
HLSL

#ifndef GENA_MATH
#define GENA_MATH
inline float3 normalize_safe(float3 v, float3 fallback)
{
float vv = dot(v, v);
return vv > 1e-16f ? v / sqrt(vv) : fallback;
}
inline float3 project_vec(float3 v, float3 onto)
{
onto = normalize(onto);
return dot(v, onto) * onto;
}
inline float3 project_plane(float3 v, float3 n)
{
return v - project_vec(v, n);
}
inline float3 limit_length(float3 v, float maxLen)
{
return min(maxLen, length(v)) * normalize_safe(v, 0.0f);
}
inline float angle(float3 a, float3 b)
{
float d = clamp(dot(normalize(a), normalize(b)), -1.0, 1.0);
return acos(d) * 57.295779513;
}
inline float4 quat_conj(float4 q)
{
return float4(-q.xyz, q.w);
}
inline float4 quat_inv(float4 q)
{
return quat_conj(q);
}
inline float3 quat_rot(float4 q, float3 v)
{
return
dot(q.xyz, v) * q.xyz
+ q.w * q.w * v
+ 2.0 * q.w * cross(q.xyz, v)
- cross(cross(q.xyz, v), q.xyz);
}
inline float max_comp(float3 v)
{
return max(v.x, max(v.y, v.z));
}
float sdf_sphere(float3 p, float radius, float3 c, float r)
{
p -= c;
float totalRadius = radius + r;
return length(p) - totalRadius;
}
float sdf_uni_cubic(float a, float b, float k)
{
float h = max(k - abs(a - b), 0.0f) / k;
return min(a, b) - h * h * h * k * (1.0f / 6.0f);
}
inline float sdf_uni_smooth(float a, float b, float h)
{
return sdf_uni_cubic(a, b, h);
}
float sdf_sub_cubic(float a, float b, float k)
{
float h = max(k - abs(a + b), 0.0f) / k;
return max(a, -b) + h * h * h * k * (1.0f / 6.0f);
}
inline float sdf_sub_smooth(float a, float b, float h)
{
return sdf_sub_cubic(a, b, h);
}
float sdf_int_cubic(float a, float b, float k)
{
float h = max(k - abs(a - b), 0.0f) / k;
return max(a, b) + h * h * h * k * (1.0f / 6.0f);
}
inline float sdf_int_smooth(float a, float b, float h)
{
return sdf_int_cubic(a, b, h);
}
float sdf_box(float3 p, float radius, float3 c, float3 h, float4 q = float4(0.0f, 0.0f, 0.0f, 1.0f), float r = 0.0f)
{
p = quat_rot(quat_inv(q), p - c);
float3 d = abs(p) - h;
float totalRadius = radius + r;
return length(max(d, 0.0f)) + min(max_comp(d), 0.0f) - totalRadius;
}
float sdf_capsule(float3 p, float radius, float3 a, float3 b, float r)
{
float3 ab = b - a;
float3 ap = p - a;
p -= a + saturate(dot(ap, ab) / dot(ab, ab)) * ab;
float totalRadius = radius + r;
return length(p) - totalRadius;
}
float sdf_cylinder(float3 p, float radius, float3 a, float3 b, float r)
{
float result = 0.0f;
float3 ab = b - a;
float3 ap = p - a;
float t = dot(ap, ab) / dot(ab, ab);
float3 q = a + saturate(t) * ab;
float totalRadius = radius + r;
if (t >= 0.0f && t <= 1.0f)
result = length(p - q) - totalRadius;
else
{
float3 c = q + limit_length(project_plane(p - q, ab), totalRadius);
result = length(p - c);
}
return result;
}
struct SdfShape
{
int layer;
int4 data0; // type, operator
// sphere box capsule cylinder
float4 data1; // c.xyz, r c.xyz, r a.xyz, r a.xyz, r
float4 data2; // h.xyz b.xyz b.xyz
float4 data3; // q
};
struct AabbTest
{
float3 startPosition;
float3 position;
float3 normal;
float hit;
int message;
};
float sdf_shape(float3 p, float radius, SdfShape s)
{
float result = 1e32f;
// 0 = Sphere, 1 = Box, 2 = Capsule, 3 = Cylinder
if (s.data0.x == 0)
result = sdf_sphere(p, radius, s.data1.xyz, s.data1.w);
else if (s.data0.x == 1)
result = sdf_box(p, radius, s.data1.xyz, s.data2.xyz, s.data3, s.data1.w);
else if (s.data0.x == 2)
result = sdf_capsule(p, radius, s.data1.xyz, s.data2.xyz, s.data1.w);
else if (s.data0.x == 3)
result = sdf_cylinder(p, radius, s.data1.xyz, s.data2.xyz, s.data1.w);
return result;
}
// 0 = Union, 1 = Subtraction, 2 = Intersection
#define SDF_NEAR_SHAPES(res, p, radius, aiNearShape, numNearShapes, layer) \
{ \
float3 opRes = 1e32f; \
for (int i = 0; i < numNearShapes; ++i) \
{ \
const int iShape = aiNearShape[i]; \
const int op = aSdfShape[iShape].data0.y; \
if ((layer & aSdfShape[iShape].layer) <= 0) \
{ \
continue; \
} \
if (op == 0) \
{ \
opRes.x = sdf_uni_smooth(opRes.x, sdf_shape(p, radius, aSdfShape[iShape]), blendDist); \
} \
else if (op == 1) \
{ \
opRes.y = sdf_uni_smooth(opRes.y, sdf_shape(p, radius, aSdfShape[iShape]), blendDist); \
} \
else if (op == 2) \
{ \
opRes.z = sdf_uni_smooth(opRes.z, sdf_shape(p, radius, aSdfShape[iShape]), blendDist); \
} \
} \
res = sdf_sub_smooth(opRes.x, opRes.y, blendDist); \
if (opRes.z < 1e32f) \
res = sdf_int_smooth(res, opRes.z, blendDist); \
}
struct Aabb
{
float4 boundsMin;
float4 boundsMax;
};
struct AabbNode
{
Aabb aabb;
int nextFree;
int parent;
int childA;
int childB;
int height;
int shapeIndex;
};
inline Aabb aabb_expand(Aabb aabb, float radius)
{
aabb.boundsMin.x -= radius;
aabb.boundsMin.y -= radius;
aabb.boundsMin.z -= radius;
aabb.boundsMax.x += radius;
aabb.boundsMax.y += radius;
aabb.boundsMax.z += radius;
return aabb;
}
inline bool aabb_intersects(Aabb a, Aabb b)
{
return all(a.boundsMin <= b.boundsMax && a.boundsMax >= b.boundsMin);
}
// stmt = statements processing shapeIndex of hit leaf AABB nodes
#define aabb_tree_query(tree, root, rayBounds, extents, stackSize, stmt) \
{ \
int stackTop = 0; \
int stack[stackSize]; \
stack[stackTop] = root; \
\
while (stackTop >= 0) \
{ \
int index = stack[stackTop--]; \
if (index < 0) \
continue; \
\
Aabb tightBounds = tree[index].aabb; \
tightBounds = aabb_expand(tightBounds, extents); \
if (!aabb_intersects(rayBounds, tightBounds)) \
continue; \
if (tree[index].childA < 0) \
{ \
const int shapeIndex = tree[index].shapeIndex; \
\
stmt \
} \
else \
{ \
stackTop = min(stackTop + 1, stackSize - 1); \
stack[stackTop] = tree[index].childA; \
stackTop = min(stackTop + 1, stackSize - 1); \
stack[stackTop] = tree[index].childB; \
} \
} \
}
#endif