263 lines
9.4 KiB
HLSL
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
|