forked from LeenkxTeam/LNXSDK
115 lines
3.3 KiB
HLSL
115 lines
3.3 KiB
HLSL
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "HairCalculateCollisionPlanesBindings.h"
|
|
#include "HairCommon.h"
|
|
|
|
JPH_SHADER_FUNCTION_BEGIN(void, main, cHairPerVertexBatch, 1, 1)
|
|
JPH_SHADER_PARAM_THREAD_ID(tid)
|
|
JPH_SHADER_FUNCTION_END
|
|
{
|
|
// Check if this is a valid vertex
|
|
uint vtx = tid.x + cNumStrands; // Skip the root of each strand, it's fixed
|
|
if (vtx >= cNumVertices)
|
|
return;
|
|
|
|
// Load the vertex
|
|
float3 pos = gPositions[vtx].mPosition;
|
|
|
|
// Start with a plane that is far away (i.e. no collision)
|
|
JPH_HairCollisionPlane collision_plane;
|
|
collision_plane.mPlane = float4(1, 0, 0, 1.0e6f);
|
|
collision_plane.mShapeIndex = 0;
|
|
float largest_penetration = -1.0e6f;
|
|
|
|
// Loop over all shapes
|
|
uint current_idx = 0;
|
|
uint current_plane = 0;
|
|
for (uint current_shape_idx = 0;; ++current_shape_idx)
|
|
{
|
|
// Find most facing plane
|
|
float max_distance = -1.0e6f;
|
|
float3 max_plane_normal = float3(0, 0, 0);
|
|
uint max_plane_face_info = 0;
|
|
|
|
// Get number of faces in this shape
|
|
uint nf = gShapeIndices[current_idx++];
|
|
if (nf == 0)
|
|
break;
|
|
|
|
for (uint f = 0; f < nf; ++f)
|
|
{
|
|
// Get the plane
|
|
JPH_Plane plane = gShapePlanes[current_plane++];
|
|
float distance = JPH_PlaneSignedDistance(plane, pos);
|
|
if (distance > max_distance)
|
|
{
|
|
max_distance = distance;
|
|
max_plane_normal = JPH_PlaneGetNormal(plane);
|
|
max_plane_face_info = current_idx;
|
|
}
|
|
|
|
// Skip over vertex start and end
|
|
current_idx += 2;
|
|
}
|
|
|
|
// Project point onto that plane, in local space to the vertex
|
|
float3 closest_point = -max_distance * max_plane_normal;
|
|
|
|
// Check edges if we're outside the hull (when inside we know the closest face is also the closest point to the surface)
|
|
bool is_outside = max_distance > 0.0f;
|
|
if (is_outside)
|
|
{
|
|
// Loop over edges
|
|
float closest_point_dist_sq = 1.0e12f;
|
|
uint vi = gShapeIndices[max_plane_face_info];
|
|
uint vi_end = gShapeIndices[max_plane_face_info + 1];
|
|
float3 p1 = gShapeVertices[gShapeIndices[vi_end - 1]];
|
|
for (; vi < vi_end; ++vi)
|
|
{
|
|
// Get edge points
|
|
float3 p2 = gShapeVertices[gShapeIndices[vi]];
|
|
|
|
// Check if the position is outside the edge (if not, the face will be closer)
|
|
float3 p1_p2 = p2 - p1;
|
|
float3 p1_pos = p1 - pos;
|
|
float3 edge_normal = cross(p1_p2, max_plane_normal);
|
|
if (dot(edge_normal, p1_pos) <= 0.0f)
|
|
{
|
|
// Get closest point on edge
|
|
float3 closest = JPH_GetClosestPointOnLine(p1_pos, p1_p2);
|
|
float distance_sq = dot(closest, closest);
|
|
if (distance_sq < closest_point_dist_sq)
|
|
{
|
|
closest_point_dist_sq = distance_sq;
|
|
closest_point = closest;
|
|
}
|
|
}
|
|
|
|
// Cycle vertex
|
|
p1 = p2;
|
|
}
|
|
}
|
|
|
|
// Check if this is the largest penetration
|
|
float3 normal = -closest_point;
|
|
float normal_length = length(normal);
|
|
float penetration = normal_length;
|
|
if (is_outside)
|
|
penetration = -penetration;
|
|
else
|
|
normal = -normal;
|
|
if (penetration > largest_penetration)
|
|
{
|
|
// Calculate contact plane
|
|
normal = normal_length > 0.0f? normal / normal_length : max_plane_normal;
|
|
collision_plane.mPlane = JPH_PlaneFromPointAndNormal(pos + closest_point, normal);
|
|
collision_plane.mShapeIndex = current_shape_idx;
|
|
largest_penetration = penetration;
|
|
}
|
|
}
|
|
|
|
gCollisionPlanes[vtx] = collision_plane;
|
|
}
|