374 lines
11 KiB
HLSL
374 lines
11 KiB
HLSL
#ifndef DISTANCEFUNCTIONS_INCLUDE
|
|
#define DISTANCEFUNCTIONS_INCLUDE
|
|
|
|
#include "SurfacePoint.cginc"
|
|
#include "Transform.cginc"
|
|
#include "Bounds.cginc"
|
|
|
|
struct Sphere : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 center = s.center * colliderToSolver.scale;
|
|
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
|
|
|
|
if (s.is2D())
|
|
pnt[2] = 0;
|
|
|
|
float radius = s.size.x * cmax(colliderToSolver.scale.xyz);
|
|
float distanceToCenter = length(pnt);
|
|
|
|
float4 normal = pnt / (distanceToCenter + EPSILON);
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(center + normal * (radius + s.contactOffset));
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
};
|
|
|
|
struct Box : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 center = s.center * colliderToSolver.scale;
|
|
float4 size = s.size * colliderToSolver.scale * 0.5f;
|
|
|
|
// clamp the point to the surface of the box:
|
|
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
|
|
|
|
if (s.is2D())
|
|
pnt[2] = 0;
|
|
|
|
// get minimum distance for each axis:
|
|
float4 distances = size - abs(pnt);
|
|
|
|
if (distances.x >= 0 && distances.y >= 0 && distances.z >= 0)
|
|
{
|
|
projectedPoint.normal = float4(0,0,0,0);
|
|
projectedPoint.pos = pnt;
|
|
|
|
// find minimum distance in all three axes and the axis index:
|
|
if (distances.y < distances.x && distances.y < distances.z)
|
|
{
|
|
projectedPoint.normal[1] = sign(pnt[1]);
|
|
projectedPoint.pos[1] = size[1] * projectedPoint.normal[1];
|
|
}
|
|
else if (distances.z < distances.x && distances.z < distances.y)
|
|
{
|
|
projectedPoint.normal[2] = sign(pnt[2]);
|
|
projectedPoint.pos[2] = size[2] * projectedPoint.normal[2];
|
|
}
|
|
else
|
|
{
|
|
projectedPoint.normal[0] = sign(pnt[0]);
|
|
projectedPoint.pos[0] = size[0] * projectedPoint.normal[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
projectedPoint.pos = clamp(pnt, -size, size);
|
|
projectedPoint.normal = normalizesafe(pnt - projectedPoint.pos);
|
|
}
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(projectedPoint.pos + center + projectedPoint.normal * s.contactOffset);
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(projectedPoint.normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
};
|
|
|
|
struct Capsule : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 center = s.center * colliderToSolver.scale;
|
|
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
|
|
|
|
if (s.is2D())
|
|
pnt[2] = 0;
|
|
|
|
int direction = (int)s.size.z;
|
|
float height;
|
|
float radius;
|
|
float4 halfVector = float4(0,0,0,0);
|
|
|
|
if (direction == 0)
|
|
{
|
|
radius = s.size.x * max(colliderToSolver.scale[1], colliderToSolver.scale[2]);
|
|
height = max(radius, s.size.y * 0.5f * colliderToSolver.scale[0]);
|
|
halfVector[0] = height - radius;
|
|
}
|
|
else if (direction == 1)
|
|
{
|
|
radius = s.size.x * max(colliderToSolver.scale[2], colliderToSolver.scale[0]);
|
|
height = max(radius, s.size.y * 0.5f * colliderToSolver.scale[1]);
|
|
halfVector[1] = height - radius;
|
|
}
|
|
else
|
|
{
|
|
radius = s.size.x * max(colliderToSolver.scale[0], colliderToSolver.scale[1]);
|
|
height = max(radius, s.size.y * 0.5f * colliderToSolver.scale[2]);
|
|
halfVector[2] = height - radius;
|
|
}
|
|
|
|
float mu;
|
|
float4 centerLine = NearestPointOnEdge(-halfVector, halfVector, pnt, mu);
|
|
float4 centerToPoint = pnt - centerLine;
|
|
float distanceToCenter = length(centerToPoint);
|
|
|
|
float4 normal = centerToPoint / (distanceToCenter + EPSILON);
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(center + centerLine + normal * (radius + s.contactOffset));
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
};
|
|
|
|
struct BIHNode
|
|
{
|
|
int firstChild; /**< index of the first child node. The second one is right after the first.*/
|
|
int start; /**< index of the first element in this node.*/
|
|
int count; /**< amount of elements in this node.*/
|
|
|
|
int axis; /**< axis of the split plane (0,1,2 = x,y,z)*/
|
|
float min_; /**< minimum split plane*/
|
|
float max_; /**< maximum split plane*/
|
|
};
|
|
|
|
struct TriangleMeshHeader
|
|
{
|
|
int firstNode;
|
|
int nodeCount;
|
|
int firstTriangle;
|
|
int triangleCount;
|
|
int firstVertex;
|
|
int vertexCount;
|
|
};
|
|
|
|
struct Triangle
|
|
{
|
|
int i1;
|
|
int i2;
|
|
int i3;
|
|
aabb b;
|
|
};
|
|
|
|
struct TriangleMesh : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
|
|
CachedTri tri;
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos);
|
|
|
|
if (s.is2D())
|
|
pnt[2] = 0;
|
|
|
|
float4 bary = FLOAT4_ZERO;
|
|
float4 nearestPoint = NearestPointOnTri(tri, pnt, bary);
|
|
float4 normal = normalizesafe(pnt - nearestPoint);
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * s.contactOffset);
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
|
|
};
|
|
|
|
struct HeightFieldHeader
|
|
{
|
|
int firstSample;
|
|
int sampleCount;
|
|
};
|
|
|
|
struct Heightfield : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
|
|
CachedTri tri;
|
|
float4 triNormal;
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 pnt = colliderToSolver.InverseTransformPoint(pos);
|
|
|
|
float4 bary;
|
|
float4 nearestPoint = NearestPointOnTri(tri, pnt, bary);
|
|
float4 normal = normalizesafe(pnt - nearestPoint);
|
|
|
|
// flip the contact normal if it points below ground: (doesn't work with holes)
|
|
//OneSidedNormal(triNormal, normal);
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPoint(nearestPoint + normal * s.contactOffset);
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
|
|
};
|
|
|
|
struct DistanceFieldHeader
|
|
{
|
|
int firstNode;
|
|
int nodeCount;
|
|
};
|
|
|
|
struct DFNode
|
|
{
|
|
float4 distancesA;
|
|
float4 distancesB;
|
|
float4 center;
|
|
int firstChild;
|
|
|
|
// add 12 bytes of padding to ensure correct memory alignment:
|
|
int pad0;
|
|
int pad1;
|
|
int pad2;
|
|
|
|
float4 GetNormalizedPos(float4 position)
|
|
{
|
|
float4 corner = center - float4(center[3],center[3],center[3],center[3]);
|
|
return (position - corner) / (center[3] * 2);
|
|
}
|
|
|
|
float4 SampleWithGradient(float4 position)
|
|
{
|
|
float4 nPos = GetNormalizedPos(position);
|
|
|
|
// trilinear interpolation of distance:
|
|
float4 x = distancesA + (distancesB - distancesA) * nPos[0];
|
|
float2 y = x.xy + (x.zw - x.xy) * nPos[1];
|
|
float dist = y[0] + (y[1] - y[0]) * nPos[2];
|
|
|
|
// gradient estimation:
|
|
// x == 0
|
|
float2 a = distancesA.xy + (distancesA.zw - distancesA.xy) * nPos[1];
|
|
float x0 = a[0] + (a[1] - a[0]) * nPos[2];
|
|
|
|
// x == 1
|
|
a = distancesB.xy + (distancesB.zw - distancesB.xy) * nPos[1];
|
|
float x1 = a[0] + (a[1] - a[0]) * nPos[2];
|
|
|
|
// y == 0
|
|
float y0 = x[0] + (x[1] - x[0]) * nPos[2];
|
|
|
|
// y == 1
|
|
float y1 = x[2] + (x[3] - x[2]) * nPos[2];
|
|
|
|
return float4(x1 - x0, y1 - y0, y[1] - y[0], dist);
|
|
|
|
}
|
|
|
|
int GetOctant(float4 position)
|
|
{
|
|
int index = 0;
|
|
if (position[0] > center[0]) index |= 4;
|
|
if (position[1] > center[1]) index |= 2;
|
|
if (position[2] > center[2]) index |= 1;
|
|
return index;
|
|
}
|
|
};
|
|
|
|
struct DistanceField : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
|
|
StructuredBuffer<DistanceFieldHeader> distanceFieldHeaders;
|
|
StructuredBuffer<DFNode> dfNodes;
|
|
|
|
float4 DFTraverse(float4 particlePosition,
|
|
in DistanceFieldHeader header)
|
|
{
|
|
int stack[12];
|
|
int stackTop = 0;
|
|
|
|
stack[stackTop++] = 0;
|
|
|
|
while (stackTop > 0)
|
|
{
|
|
// pop node index from the stack:
|
|
int nodeIndex = stack[--stackTop];
|
|
DFNode node = dfNodes[header.firstNode + nodeIndex];
|
|
|
|
// if the child node exists, recurse down the df octree:
|
|
if (node.firstChild >= 0)
|
|
stack[stackTop++] = node.firstChild + node.GetOctant(particlePosition);
|
|
else
|
|
return node.SampleWithGradient(particlePosition);
|
|
}
|
|
return FLOAT4_ZERO;
|
|
}
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 pnt = colliderToSolver.InverseTransformPoint(pos);
|
|
|
|
if (s.is2D())
|
|
pnt[2] = 0;
|
|
|
|
float4 sample = DFTraverse(pnt, distanceFieldHeaders[s.dataIndex]);
|
|
float4 normal = float4(normalize(sample.xyz), 0);
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPoint(pnt - normal * (sample[3] - s.contactOffset));
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
};
|
|
|
|
struct EdgeMeshHeader
|
|
{
|
|
int firstNode;
|
|
int nodeCount;
|
|
int firstEdge;
|
|
int edgeCount;
|
|
int firstVertex;
|
|
int vertexCount;
|
|
};
|
|
|
|
struct Edge
|
|
{
|
|
int i1;
|
|
int i2;
|
|
aabb b;
|
|
};
|
|
|
|
struct EdgeMesh : IDistanceFunction
|
|
{
|
|
shape s;
|
|
transform colliderToSolver;
|
|
int dataOffset;
|
|
|
|
CachedEdge edge;
|
|
|
|
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
|
|
{
|
|
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos);
|
|
|
|
if (s.is2D())
|
|
pnt[2] = 0;
|
|
|
|
float mu = 0;
|
|
float4 nearestPoint = NearestPointOnEdge(edge, pnt, mu);
|
|
float4 normal = normalizesafe(pnt - nearestPoint);
|
|
|
|
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * s.contactOffset);
|
|
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
|
projectedPoint.bary = float4(1,0,0,0);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
#endif |