added convex hull colliders (mesh colliders with fundamental differences), also fixed mesh v mesh because of some dumb assumption that the BVH was in worldspace
This commit is contained in:
parent
b523878912
commit
9c25bf9a9a
@ -6,12 +6,12 @@
|
||||
"metadata": {
|
||||
"holdable": true,
|
||||
"physics": {
|
||||
"gravity": [ 0, 0, 0 ],
|
||||
// "gravity": [ 0, -1, 0 ],
|
||||
"mass": 100,
|
||||
"inertia": false,
|
||||
|
||||
"mass": 1,
|
||||
"type": "aabb",
|
||||
"recenter": false
|
||||
// "type": "bounding box"
|
||||
"type": "mesh"
|
||||
// "type": "hull"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@
|
||||
"inertia": false,
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
// "type": "mesh"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,7 @@ namespace pod {
|
||||
CAPSULE,
|
||||
TRIANGLE,
|
||||
MESH,
|
||||
CONVEX_HULL,
|
||||
};
|
||||
|
||||
struct SupportPoint {
|
||||
@ -107,6 +108,12 @@ namespace pod {
|
||||
const uf::Mesh* mesh;
|
||||
};
|
||||
|
||||
// MIGHT contain additional data, so far mirrors the above
|
||||
struct ConvexHullBVH {
|
||||
pod::BVH* bvh;
|
||||
const uf::Mesh* mesh;
|
||||
};
|
||||
|
||||
typedef uint32_t CollisionMask;
|
||||
|
||||
|
||||
@ -147,6 +154,7 @@ namespace pod {
|
||||
pod::Capsule capsule;
|
||||
pod::TriangleWithNormal triangle;
|
||||
pod::MeshBVH mesh;
|
||||
pod::ConvexHullBVH convexHull;
|
||||
};
|
||||
};
|
||||
|
||||
@ -176,7 +184,8 @@ namespace pod {
|
||||
bool isStatic = false;
|
||||
|
||||
float mass = 1.0f;
|
||||
float inverseMass = 1.0f;
|
||||
float inverseMass = 1.0f; // for fast division
|
||||
int32_t viewIndex = -1; // -1 means it's not an aliased view
|
||||
|
||||
/*alignas(16)*/ pod::Vector3f offset = {};
|
||||
|
||||
@ -232,9 +241,9 @@ namespace pod {
|
||||
bool warmupSolver = true; // cache manifold data to warm up the solver
|
||||
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
|
||||
bool psgContactSolver = true; // use PSG contact solver
|
||||
bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
|
||||
bool useGjk = true; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
|
||||
bool fixedStep = true; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
|
||||
uint32_t substeps = 1; // number of substeps per frame tick
|
||||
uint32_t substeps = 4; // number of substeps per frame tick
|
||||
uint32_t reserveCount = 32; // amount of elements to reserve for vectors used in this system, to-do: have it tie to a memory pool allocator
|
||||
|
||||
// increasing these make things lag for reasons I can imagine why
|
||||
@ -333,7 +342,7 @@ namespace uf {
|
||||
pod::PhysicsBody& UF_API create( uf::Object& object, const pod::Sphere& sphere, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( uf::Object& object, const pod::Plane& plane, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( uf::Object& object, const pod::Capsule& capsule, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( uf::Object&, const uf::Mesh& mesh, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( uf::Object&, const uf::Mesh& mesh, float mass = 0.0f, const pod::Vector3f& = {}, bool convex = false );
|
||||
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object&, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object& object, const pod::AABB& aabb, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
@ -341,7 +350,7 @@ namespace uf {
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object& object, const pod::Plane& plane, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object& object, const pod::Capsule& capsule, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object& object, const pod::TriangleWithNormal& tri, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object&, const uf::Mesh& mesh, float mass = 0.0f, const pod::Vector3f& = {} );
|
||||
pod::PhysicsBody& UF_API create( pod::World&, uf::Object&, const uf::Mesh& mesh, float mass = 0.0f, const pod::Vector3f& = {}, bool convex = false );
|
||||
|
||||
void UF_API destroy( uf::Object& );
|
||||
void UF_API destroy( pod::PhysicsBody& );
|
||||
|
||||
@ -56,7 +56,7 @@ namespace uf {
|
||||
template<typename T> pod::Transform<T> /*UF_API*/ inverse(const pod::Transform<T>& t);
|
||||
template<typename T> pod::Vector3t<T> /*UF_API*/ apply( const pod::Transform<T>& transform, const pod::Vector3t<T>& point );
|
||||
template<typename T> pod::Vector3t<T> /*UF_API*/ applyInverse(const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint);
|
||||
|
||||
template<typename T> pod::Transform<T> /*UF_API*/ relative(const pod::Transform<T>& a, const pod::Transform<T>& b);
|
||||
|
||||
template<typename T> uf::stl::string /*UF_API*/ toString( const pod::Transform<T>&, bool flatten = true );
|
||||
template<typename T> ext::json::Value /*UF_API*/ encode( const pod::Transform<T>&, bool flatten = true, const ext::json::EncodingSettings& = {} );
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::initialize( pod::Transform<T>& transform ) {
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::initialize( pod::Transform<T>& transform ) {
|
||||
transform.position = {0, 0, 0};
|
||||
transform.scale = {1, 1, 1};
|
||||
|
||||
@ -7,17 +8,20 @@ template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::initialize( po
|
||||
transform.forward = {0, 0, 1};
|
||||
|
||||
transform.orientation = {0, 0, 0, 1};
|
||||
|
||||
transform.model = uf::matrix::identity();
|
||||
|
||||
transform.reference = NULL;
|
||||
transform.reference = nullptr;
|
||||
|
||||
return transform;
|
||||
}
|
||||
template<typename T> pod::Transform<T> /*UF_API*/ uf::transform::initialize() {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> /*UF_API*/ uf::transform::initialize() {
|
||||
pod::Transform<T> transform;
|
||||
return transform = uf::transform::initialize(transform);
|
||||
return uf::transform::initialize(transform);
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::lookAt( pod::Transform<T>& transform, const pod::Vector3t<T>& at ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::lookAt( pod::Transform<T>& transform, const pod::Vector3t<T>& at ) {
|
||||
pod::Vector3t<T> forward = uf::vector::normalize( at - transform.position );
|
||||
pod::Vector3t<T> right = uf::vector::normalize(uf::vector::cross( forward, transform.up ));
|
||||
pod::Vector3t<T> up = uf::vector::normalize(uf::vector::cross(at, right));
|
||||
@ -25,132 +29,139 @@ template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::lookAt( pod::T
|
||||
transform.up = up;
|
||||
transform.right = right;
|
||||
transform.forward = forward;
|
||||
|
||||
transform.orientation = uf::quaternion::lookAt( at, up );
|
||||
|
||||
return transform;
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::move( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::move( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta ) {
|
||||
transform.position += (axis * delta);
|
||||
return transform;
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::move( pod::Transform<T>& transform, const pod::Vector3t<T>& delta ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::move( pod::Transform<T>& transform, const pod::Vector3t<T>& delta ) {
|
||||
transform.position += delta;
|
||||
return transform;
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::reorient( pod::Transform<T>& transform ) {
|
||||
/*
|
||||
transform.up = uf::vector::normalize(uf::quaternion::rotate(transform.orientation, pod::Vector3{0.0f, 1.0f, 0.0f}));
|
||||
transform.right = uf::vector::normalize(uf::quaternion::rotate(transform.orientation, pod::Vector3{1.0f, 0.0f, 0.0f}));
|
||||
transform.forward = uf::vector::normalize(uf::quaternion::rotate(transform.orientation, pod::Vector3{0.0f, 0.0f, 1.0f}));
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::reorient( pod::Transform<T>& transform ) {
|
||||
pod::Quaternion<T> q = transform.orientation;
|
||||
transform.forward = { 2 * (q.x * q.z + q.w * q.y), 2 * (q.y * q.x - q.w * q.x), 1 - 2 * (q.x * q.x + q.y * q.y) };
|
||||
transform.up = { 2 * (q.x * q.y - q.w * q.z), 1 - 2 * (q.x * q.x + q.z * q.z), 2 * (q.y * q.z + q.w * q.x)};
|
||||
transform.right = { 1 - 2 * (q.y * q.y + q.z * q.z), 2 * (q.x * q.y + q.w * q.z), 2 * (q.x * q.z - q.w * q.y)};
|
||||
return transform;
|
||||
}
|
||||
template<typename T> pod::Transform<T> /*UF_API*/ uf::transform::reorient( const pod::Transform<T>& _transform ) {
|
||||
/*
|
||||
transform.up = uf::vector::normalize(uf::quaternion::rotate(transform.orientation, pod::Vector3{0.0f, 1.0f, 0.0f}));
|
||||
transform.right = uf::vector::normalize(uf::quaternion::rotate(transform.orientation, pod::Vector3{1.0f, 0.0f, 0.0f}));
|
||||
transform.forward = uf::vector::normalize(uf::quaternion::rotate(transform.orientation, pod::Vector3{0.0f, 0.0f, 1.0f}));
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> /*UF_API*/ uf::transform::reorient( const pod::Transform<T>& _transform ) {
|
||||
pod::Transform<T> transform = _transform;
|
||||
pod::Quaternion<T> q = transform.orientation;
|
||||
transform.forward = { 2 * (q.x * q.z + q.w * q.y), 2 * (q.y * q.x - q.w * q.x), 1 - 2 * (q.x * q.x + q.y * q.y) };
|
||||
transform.up = { 2 * (q.x * q.y - q.w * q.z), 1 - 2 * (q.x * q.x + q.z * q.z), 2 * (q.y * q.z + q.w * q.x)};
|
||||
transform.right = { 1 - 2 * (q.y * q.y + q.z * q.z), 2 * (q.x * q.y + q.w * q.z), 2 * (q.x * q.z - q.w * q.y)};
|
||||
return transform;
|
||||
return uf::transform::reorient(transform);
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::rotate( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::rotate( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta ) {
|
||||
pod::Quaternion<> quat = uf::quaternion::axisAngle( axis, delta );
|
||||
|
||||
transform.orientation = uf::vector::normalize(uf::quaternion::multiply(quat, transform.orientation));
|
||||
transform = uf::transform::reorient(transform);
|
||||
|
||||
return transform;
|
||||
return uf::transform::reorient(transform);
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::rotate( pod::Transform<T>& transform, const pod::Quaternion<T>& quat ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::rotate( pod::Transform<T>& transform, const pod::Quaternion<T>& quat ) {
|
||||
transform.orientation = uf::vector::normalize(uf::quaternion::multiply(quat, transform.orientation));
|
||||
transform = uf::transform::reorient(transform);
|
||||
|
||||
return transform;
|
||||
return uf::transform::reorient(transform);
|
||||
}
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::scale( pod::Transform<T>& transform, const pod::Vector3t<T>& factor ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::scale( pod::Transform<T>& transform, const pod::Vector3t<T>& factor ) {
|
||||
transform.scale = factor;
|
||||
return transform;
|
||||
}
|
||||
template<typename T> inline pod::Transform<T> /*UF_API*/ uf::transform::condense( const pod::Transform<T>& transform ) {
|
||||
|
||||
template<typename T>
|
||||
inline pod::Transform<T> /*UF_API*/ uf::transform::condense( const pod::Transform<T>& transform ) {
|
||||
return uf::transform::fromMatrix( uf::transform::model( transform, false ) );
|
||||
}
|
||||
template<typename T> pod::Transform<T> /*UF_API*/ uf::transform::flatten( const pod::Transform<T>& transform, size_t depth ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> /*UF_API*/ uf::transform::flatten( const pod::Transform<T>& transform, size_t depth ) {
|
||||
if ( !transform.reference ) return transform;
|
||||
// if ( depth == SIZE_MAX ) return uf::transform::condense( transform );
|
||||
|
||||
pod::Transform<T> combined = transform;
|
||||
combined.reference = NULL;
|
||||
combined.reference = nullptr;
|
||||
const pod::Transform<T>* pointer = transform.reference;
|
||||
|
||||
while ( pointer && depth-- > 0 ) {
|
||||
// for some reason I can't just += for the camera
|
||||
combined.position = {
|
||||
combined.position.x + pointer->position.x,
|
||||
combined.position.y + pointer->position.y,
|
||||
combined.position.z + pointer->position.z,
|
||||
};
|
||||
pod::Quaternion<T> orientation = pointer->orientation;
|
||||
combined.orientation = uf::quaternion::multiply( orientation, combined.orientation );
|
||||
combined.orientation = uf::quaternion::multiply( pointer->orientation, combined.orientation );
|
||||
combined.scale = {
|
||||
combined.scale.x * pointer->scale.x,
|
||||
combined.scale.y * pointer->scale.y,
|
||||
combined.scale.z * pointer->scale.z,
|
||||
};
|
||||
combined.model = pointer->model * combined.model;
|
||||
|
||||
if ( pointer == pointer->reference ) break;
|
||||
pointer = pointer->reference;
|
||||
}
|
||||
return combined = uf::transform::reorient(combined);
|
||||
return uf::transform::reorient(combined);
|
||||
}
|
||||
template<typename T> pod::Matrix4t<T> /*UF_API*/ uf::transform::model( const pod::Transform<T>& transform, bool flatten, size_t depth ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Matrix4t<T> /*UF_API*/ uf::transform::model( const pod::Transform<T>& transform, bool flatten, size_t depth ) {
|
||||
if ( flatten ) {
|
||||
pod::Transform<T> flatten = uf::transform::flatten(transform, depth);
|
||||
return uf::matrix::translate( uf::matrix::identity(), flatten.position ) *
|
||||
uf::quaternion::matrix( flatten.orientation ) *
|
||||
uf::matrix::scale( uf::matrix::identity(), flatten.scale ) *
|
||||
flatten.model;
|
||||
pod::Transform<T> flat = uf::transform::flatten(transform, depth);
|
||||
return uf::matrix::translate( uf::matrix::identity(), flat.position ) *
|
||||
uf::quaternion::matrix( flat.orientation ) *
|
||||
uf::matrix::scale( uf::matrix::identity(), flat.scale ) *
|
||||
flat.model;
|
||||
}
|
||||
|
||||
pod::Matrix4t<T> matrix = uf::matrix::identity();
|
||||
const pod::Transform<T>* pointer = &transform;
|
||||
|
||||
do {
|
||||
pod::Matrix4t<T> model = uf::matrix::translate( uf::matrix::identity(), pointer->position ) *
|
||||
pod::Matrix4t<T> modelMat = uf::matrix::translate( uf::matrix::identity(), pointer->position ) *
|
||||
uf::quaternion::matrix( pointer->orientation ) *
|
||||
uf::matrix::scale( uf::matrix::identity(), pointer->scale ) *
|
||||
pointer->model;
|
||||
matrix = model * matrix;
|
||||
matrix = modelMat * matrix;
|
||||
|
||||
if ( pointer == pointer->reference ) break;
|
||||
pointer = pointer->reference;
|
||||
} while ( pointer && --depth >= 0 );
|
||||
} while ( pointer && --depth > 0 );
|
||||
|
||||
return matrix;
|
||||
}
|
||||
template<typename T> pod::Transform<T> uf::transform::fromMatrix( const pod::Matrix4t<T>& matrix ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> uf::transform::fromMatrix( const pod::Matrix4t<T>& matrix ) {
|
||||
pod::Transform<T> transform;
|
||||
transform.position = uf::matrix::multiply<float>( matrix, pod::Vector4f{ 0, 0, 0, 1 } );
|
||||
transform.orientation = uf::quaternion::fromMatrix( matrix );
|
||||
transform.model = matrix;
|
||||
return transform = reorient( transform );
|
||||
return reorient( transform );
|
||||
}
|
||||
|
||||
template<typename T> pod::Transform<T>& /*UF_API*/ uf::transform::reference( pod::Transform<T>& transform, const pod::Transform<T>& parent, bool reorient ) {
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::reference( pod::Transform<T>& transform, const pod::Transform<T>& parent, bool reorient ) {
|
||||
transform.reference = const_cast<pod::Transform<T>*>(&parent);
|
||||
if ( !reorient ) {
|
||||
transform.reference = const_cast<pod::Transform<T>*>(&parent);
|
||||
return transform;
|
||||
}
|
||||
|
||||
transform.position = parent.position - transform.position;
|
||||
// reorient orientation here too
|
||||
transform.reference = const_cast<pod::Transform<T>*>(&parent);
|
||||
return transform;
|
||||
}
|
||||
template<typename T> pod::Transform<T> /*UF_API*/ uf::transform::interpolate( const pod::Transform<T>& from, const pod::Transform<T>& to, float factor, bool reorient ) {
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> /*UF_API*/ uf::transform::interpolate( const pod::Transform<T>& from, const pod::Transform<T>& to, float factor, bool reorient ) {
|
||||
pod::Transform transform = to;
|
||||
transform.position = uf::vector::lerp( from.position, to.position, factor );
|
||||
transform.orientation = uf::quaternion::slerp( from.orientation, to.orientation, factor );
|
||||
@ -159,48 +170,50 @@ template<typename T> pod::Transform<T> /*UF_API*/ uf::transform::interpolate( co
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> uf::transform::inverse(const pod::Transform<T>& t) {
|
||||
pod::Transform<T> inv;
|
||||
pod::Transform<T> inv;
|
||||
|
||||
// Inverse orientation = conjugate of normalized quaternion
|
||||
pod::Quaternion<T> qInv = uf::quaternion::conjugate(uf::vector::normalize(t.orientation));
|
||||
pod::Quaternion<T> qInv = uf::quaternion::conjugate(uf::vector::normalize(t.orientation));
|
||||
|
||||
// Inverse scale is reciprocal (with protection against zero)
|
||||
pod::Vector3t<T> sInv {
|
||||
(fabs(t.scale.x) > 1e-8) ? (1.0f / t.scale.x) : 0.0f,
|
||||
(fabs(t.scale.y) > 1e-8) ? (1.0f / t.scale.y) : 0.0f,
|
||||
(fabs(t.scale.z) > 1e-8) ? (1.0f / t.scale.z) : 0.0f
|
||||
};
|
||||
pod::Vector3t<T> sInv {
|
||||
(fabs(t.scale.x) > 1e-8) ? (1.0f / t.scale.x) : 0.0f,
|
||||
(fabs(t.scale.y) > 1e-8) ? (1.0f / t.scale.y) : 0.0f,
|
||||
(fabs(t.scale.z) > 1e-8) ? (1.0f / t.scale.z) : 0.0f
|
||||
};
|
||||
|
||||
inv.scale = sInv;
|
||||
inv.orientation = qInv;
|
||||
inv.scale = sInv;
|
||||
inv.orientation = qInv;
|
||||
|
||||
// To invert translation: apply inverse orientation/scale
|
||||
pod::Vector3t<T> negPos = -t.position;
|
||||
inv.position = uf::quaternion::rotate(qInv, pod::Vector3f{ negPos.x * sInv.x,
|
||||
negPos.y * sInv.y,
|
||||
negPos.z * sInv.z });
|
||||
pod::Vector3t<T> negPos = -t.position;
|
||||
inv.position = uf::quaternion::rotate(qInv, pod::Vector3f{
|
||||
negPos.x * sInv.x,
|
||||
negPos.y * sInv.y,
|
||||
negPos.z * sInv.z
|
||||
});
|
||||
|
||||
// Update basis vectors
|
||||
inv = uf::transform::reorient(inv);
|
||||
|
||||
return inv;
|
||||
return uf::transform::reorient(inv);
|
||||
}
|
||||
|
||||
|
||||
template<typename T> // Normalizes a vector
|
||||
pod::Vector3t<T> /*UF_API*/ uf::transform::apply( const pod::Transform<T>& transform, const pod::Vector3t<T>& point ) {
|
||||
// return uf::transform::model( transform ) * point;
|
||||
return uf::matrix::multiply( uf::transform::model( transform ), point );
|
||||
}
|
||||
|
||||
// Apply inverse transform to a point
|
||||
template<typename T>
|
||||
pod::Vector3t<T> uf::transform::applyInverse(const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint) {
|
||||
pod::Transform<T> inv = inverse(t);
|
||||
return uf::transform::apply(inv, worldPoint);
|
||||
pod::Vector3t<T> /*UF_API*/ uf::transform::apply( const pod::Transform<T>& t, const pod::Vector3t<T>& p ) {
|
||||
return uf::quaternion::rotate( t.orientation, p * t.scale ) + t.position;
|
||||
}
|
||||
|
||||
template<typename T> // Normalizes a vector
|
||||
template<typename T>
|
||||
pod::Vector3t<T> uf::transform::applyInverse( const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint ) {
|
||||
pod::Vector3t<T> translated = worldPoint - t.position;
|
||||
return uf::quaternion::rotate(uf::quaternion::inverse(t.orientation), translated) / t.scale;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> uf::transform::relative( const pod::Transform<T>& a, const pod::Transform<T>& b ) {
|
||||
pod::Transform<T> rel;
|
||||
auto invOriA = uf::quaternion::inverse(a.orientation);
|
||||
rel.position = uf::quaternion::rotate(invOriA, b.position - a.position);
|
||||
rel.orientation = invOriA * b.orientation;
|
||||
return rel;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uf::stl::string /*UF_API*/ uf::transform::toString( const pod::Transform<T>& t, bool flatten ) {
|
||||
pod::Transform<T> transform = flatten ? uf::transform::flatten(t) : t;
|
||||
uf::stl::stringstream ss;
|
||||
@ -218,46 +231,26 @@ ext::json::Value /*UF_API*/ uf::transform::encode( const pod::Transform<T>& t, b
|
||||
json["model"] = uf::matrix::encode(transform.model, settings);
|
||||
return json;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T>& /*UF_API*/ uf::transform::decode( const ext::json::Value& _json, pod::Transform<T>& transform ) {
|
||||
ext::json::Value json = _json;
|
||||
// transform.position = uf::vector::decode<T, 3>(json["position"], transform.position);
|
||||
// transform.scale = uf::vector::decode<T, 3>(json["scale"], transform.scale);
|
||||
// transform.orientation = uf::vector::decode<T, 4>(json["orientation"], transform.orientation);
|
||||
// if ( ext::json::isArray(json["position"]) || ext::json::isObject(json["position"]) )
|
||||
transform.position = uf::vector::decode<T, 3>(json["position"], transform.position);
|
||||
// if ( ext::json::isArray(json["scale"]) || ext::json::isObject(json["scale"]) )
|
||||
transform.scale = uf::vector::decode<T, 3>(json["scale"], transform.scale);
|
||||
// if ( ext::json::isArray(json["orientation"]) || ext::json::isObject(json["orientation"]) )
|
||||
transform.orientation = uf::vector::decode<T, 4>(json["orientation"], transform.orientation);
|
||||
ext::json::Value json = _json; // cringe null behavior
|
||||
transform.position = uf::vector::decode<T, 3>(json["position"], transform.position);
|
||||
transform.scale = uf::vector::decode<T, 3>(json["scale"], transform.scale);
|
||||
transform.orientation = uf::vector::decode<T, 4>(json["orientation"], transform.orientation);
|
||||
|
||||
if ( ext::json::isObject(json["rotation"]) && !ext::json::isNull(json["rotation"]["axis"]) && !ext::json::isNull(json["rotation"]["angle"]) ) {
|
||||
pod::Vector3t<T> axis = uf::vector::decode<T, 3>(json["rotation"]["axis"]);
|
||||
T angle = json["rotation"]["angle"].as<T>();
|
||||
transform.orientation = uf::quaternion::axisAngle( axis, angle );
|
||||
}
|
||||
// if ( ext::json::isArray(json["model"]) )
|
||||
transform.model = uf::matrix::decode<T, 4, 4>(json["model"], transform.model);
|
||||
|
||||
transform.model = uf::matrix::decode<T, 4, 4>(json["model"], transform.model);
|
||||
return transform;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
pod::Transform<T> /*UF_API*/ uf::transform::decode( const ext::json::Value& _json, const pod::Transform<T>& t ) {
|
||||
ext::json::Value json = _json;
|
||||
pod::Transform<T> /*UF_API*/ uf::transform::decode( const ext::json::Value& json, const pod::Transform<T>& t ) {
|
||||
pod::Transform<T> transform = t;
|
||||
// transform.position = uf::vector::decode<T, 3>(json["position"], transform.position);
|
||||
// transform.scale = uf::vector::decode<T, 3>(json["scale"], transform.scale);
|
||||
// transform.orientation = uf::vector::decode<T, 4>(json["orientation"], transform.orientation);
|
||||
// if ( ext::json::isArray(json["position"]) || ext::json::isObject(json["position"]) )
|
||||
transform.position = uf::vector::decode<T, 3>(json["position"], transform.position);
|
||||
// if ( ext::json::isArray(json["scale"]) || ext::json::isObject(json["scale"]) )
|
||||
transform.scale = uf::vector::decode<T, 3>(json["scale"], transform.scale);
|
||||
// if ( ext::json::isArray(json["orientation"]) || ext::json::isObject(json["orientation"]) )
|
||||
transform.orientation = uf::vector::decode<T, 4>(json["orientation"], transform.orientation);
|
||||
if ( ext::json::isObject(json["rotation"]) && !ext::json::isNull(json["rotation"]["axis"]) && !ext::json::isNull(json["rotation"]["angle"]) ) {
|
||||
pod::Vector3t<T> axis = uf::vector::decode<T, 3>(json["rotation"]["axis"]);
|
||||
T angle = json["rotation"]["angle"].as<T>();
|
||||
transform.orientation = uf::quaternion::axisAngle( axis, angle );
|
||||
}
|
||||
// if ( ext::json::isArray(json["model"]) )
|
||||
transform.model = uf::matrix::decode<T, 4, 4>(json["model"], transform.model);
|
||||
return transform;
|
||||
return uf::transform::decode(json, transform);
|
||||
}
|
||||
@ -1317,7 +1317,8 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
if ( ext::json::isObject( phyziks ) ) {
|
||||
uf::stl::string type = phyziks["type"].as<uf::stl::string>();
|
||||
|
||||
if ( type != "mesh" ) {
|
||||
bool isMesh = type == "mesh" || type == "hull";
|
||||
if ( !isMesh ) {
|
||||
auto min = bounds.min; // uf::matrix::multiply<float>( model, bounds.min, 1.0f );
|
||||
auto max = bounds.max; // uf::matrix::multiply<float>( model, bounds.max, 1.0f );
|
||||
|
||||
@ -1330,7 +1331,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
if ( ext::json::isNull( metadataJson["physics"]["max"] ) ) metadataJson["physics"]["max"] = uf::vector::encode( max );
|
||||
}
|
||||
#if !UF_GRAPH_EXTENDED
|
||||
if ( type == "mesh" ) {
|
||||
if ( isMesh ) {
|
||||
auto& physicsBody = entity.getComponent<pod::PhysicsBody>();
|
||||
float mass = phyziks["mass"].as(physicsBody.mass);
|
||||
|
||||
@ -1338,8 +1339,9 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
physicsBody.material.restitution = phyziks["restitution"].as(physicsBody.material.restitution);
|
||||
physicsBody.inertiaTensor = uf::vector::decode( phyziks["inertia"], physicsBody.inertiaTensor );
|
||||
physicsBody.gravity = uf::vector::decode( phyziks["gravity"], physicsBody.gravity );
|
||||
|
||||
uf::physics::impl::create( entity.as<uf::Object>(), mesh, mass );
|
||||
auto center = uf::vector::decode( phyziks["center"], pod::Vector3f{} );
|
||||
|
||||
uf::physics::impl::create( entity.as<uf::Object>(), mesh, mass, center, type != "mesh" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1938,15 +1940,16 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
}
|
||||
}
|
||||
// bind mesh to physics state
|
||||
// to-do: figure out why the mesh just suddenly breaks when re-streamed in dreamcast (could just be the version of reactphysics)
|
||||
// to-do: test if this works in the internal physics system (the entire reason why I wrote it was because of this mess)
|
||||
{
|
||||
auto phyziks = tag["physics"];
|
||||
if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"];
|
||||
else metadataJson["physics"] = phyziks;
|
||||
|
||||
if ( ext::json::isObject( phyziks ) ) {
|
||||
uf::stl::string type = phyziks["type"].as<uf::stl::string>();
|
||||
if ( type == "mesh" ) {
|
||||
uf::stl::string type = phyziks["type"].as<uf::stl::string>();
|
||||
bool isMesh = type == "mesh" || type == "hull";
|
||||
if ( isMesh ) {
|
||||
bool exists = entity.hasComponent<pod::PhysicsBody>();
|
||||
if ( exists ) {
|
||||
uf::physics::impl::destroy( entity );
|
||||
@ -1959,8 +1962,9 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
physicsBody.material.restitution = phyziks["restitution"].as(physicsBody.material.restitution);
|
||||
physicsBody.inertiaTensor = uf::vector::decode( phyziks["inertia"], physicsBody.inertiaTensor );
|
||||
physicsBody.gravity = uf::vector::decode( phyziks["gravity"], physicsBody.gravity );
|
||||
auto center = uf::vector::decode( phyziks["center"], pod::Vector3f{} );
|
||||
|
||||
uf::physics::impl::create( entity.as<uf::Object>(), mesh, mass );
|
||||
uf::physics::impl::create( entity.as<uf::Object>(), mesh, mass, center, type != "mesh" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,16 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
pod::AABB computeConvexHullAABB( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, pod::AABB bounds = { { FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } } ) {
|
||||
for ( size_t i = 0; i < view.vertex.count; ++i ) {
|
||||
pod::Vector3f v = ::getVertex( view, positions, i );
|
||||
bounds.min = uf::vector::min( bounds.min, v );
|
||||
bounds.max = uf::vector::max( bounds.max, v );
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
FORCE_INLINE pod::AABB mergeAabb( const pod::AABB& a, const pod::AABB& b ) {
|
||||
return {
|
||||
uf::vector::min( a.min, b.min ),
|
||||
@ -106,6 +116,21 @@ namespace {
|
||||
transform.position + body.collider.mesh.bvh->bounds[0].min,
|
||||
transform.position + body.collider.mesh.bvh->bounds[0].max,
|
||||
};
|
||||
const auto& meshData = *body.collider.mesh.mesh;
|
||||
pod::AABB bounds = { { FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } };
|
||||
for ( const auto& view : meshData.buffer_views ) ::computeConvexHullAABB( view, view["position"], bounds );
|
||||
return ::transformAabbToWorld( bounds, transform );
|
||||
} break;
|
||||
case pod::ShapeType::CONVEX_HULL: {
|
||||
if ( body.collider.convexHull.bvh && !body.collider.convexHull.bvh->bounds.empty() )
|
||||
return {
|
||||
transform.position + body.collider.convexHull.bvh->bounds[0].min,
|
||||
transform.position + body.collider.convexHull.bvh->bounds[0].max,
|
||||
};
|
||||
const auto& meshData = *body.collider.convexHull.mesh;
|
||||
pod::AABB bounds = { { FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } };
|
||||
for ( const auto& view : meshData.buffer_views ) ::computeConvexHullAABB( view, view["position"], bounds );
|
||||
return ::transformAabbToWorld( bounds, transform );
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
@ -280,4 +305,8 @@ namespace {
|
||||
ASSERT_COLLIDER_TYPES( AABB, MESH );
|
||||
REVERSE_COLLIDER( a, b, meshAabb );
|
||||
}
|
||||
bool aabbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( AABB, CONVEX_HULL );
|
||||
REVERSE_COLLIDER( a, b, hullAabb );
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ namespace {
|
||||
|
||||
void queryFlatOverlaps( const pod::BVH& bvh, pod::BVH::pairs_t& outPairs );
|
||||
void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs );
|
||||
void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, const pod::Transform<>& relTransform, pod::BVH::pairs_t& outPairs );
|
||||
|
||||
void postprocessPairs( pod::BVH::pairs_t& pairs ) {
|
||||
std::sort(pairs.begin(), pairs.end());
|
||||
@ -263,6 +264,42 @@ namespace {
|
||||
// mark as clean
|
||||
bvh.dirty = false;
|
||||
}
|
||||
|
||||
void buildConvexHullBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t capacity = 1 ) {
|
||||
const auto& views = mesh.buffer_views;
|
||||
UF_ASSERT( !views.empty() );
|
||||
|
||||
uint32_t hullCount = views.size();
|
||||
|
||||
bvh.indices.clear();
|
||||
bvh.nodes.clear();
|
||||
bvh.indices.reserve( hullCount );
|
||||
|
||||
// stores bounds
|
||||
uf::stl::vector<pod::AABB> bounds;
|
||||
bounds.reserve( hullCount );
|
||||
|
||||
// populate initial indices and bounds
|
||||
for ( size_t viewID = 0; viewID < hullCount; ++viewID ) {
|
||||
const auto& view = views[viewID];
|
||||
auto aabb = ::computeConvexHullAABB( view, view["position"] );
|
||||
|
||||
bounds.emplace_back( aabb );
|
||||
bvh.indices.emplace_back( viewID );
|
||||
}
|
||||
|
||||
UF_ASSERT( !bounds.empty() );
|
||||
|
||||
// recursively build BVH from indices
|
||||
if ( uf::physics::impl::settings.useBvhSahMeshes ) ::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity );
|
||||
else ::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity );
|
||||
|
||||
// flatten if requested
|
||||
if ( uf::physics::impl::settings.flattenBvhMeshes ) ::flattenBVH( bvh, 0 );
|
||||
|
||||
// mark as clean
|
||||
bvh.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -528,6 +565,43 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
void traverseNodePair( const pod::BVH& bvhA, pod::BVH::index_t nodeAID, const pod::BVH& bvhB, pod::BVH::index_t nodeBID, const pod::Transform<>& relTransform, pod::BVH::pairs_t& pairs ) {
|
||||
const auto& nodeA = bvhA.nodes[nodeAID];
|
||||
const auto& nodeB = bvhB.nodes[nodeBID];
|
||||
|
||||
if ( nodeA.isAsleep() && nodeB.isAsleep() ) return;
|
||||
|
||||
pod::AABB boundsB_in_A = ::transformAabbToWorld(bvhB.bounds[nodeBID], relTransform);
|
||||
if ( !::aabbOverlap( bvhA.bounds[nodeAID], boundsB_in_A ) ) return;
|
||||
|
||||
if ( nodeA.getCount() > 0 && nodeB.getCount() > 0 ) {
|
||||
for ( auto i = 0; i < nodeA.getCount(); ++i ) {
|
||||
for ( auto j = 0; j < nodeB.getCount(); ++j ) {
|
||||
pairs.emplace_back(bvhA.indices[nodeA.start + i], bvhB.indices[nodeB.start + j]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( nodeA.getCount() == 0 && nodeB.getCount() == 0 ) {
|
||||
if ( ::aabbSurfaceArea(bvhA.bounds[nodeAID]) > ::aabbSurfaceArea(boundsB_in_A) ) {
|
||||
::traverseNodePair( bvhA, nodeA.left, bvhB, nodeBID, relTransform, pairs );
|
||||
::traverseNodePair( bvhA, nodeA.right, bvhB, nodeBID, relTransform, pairs );
|
||||
} else {
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.left, relTransform, pairs );
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.right, relTransform, pairs );
|
||||
}
|
||||
}
|
||||
else if ( nodeA.getCount() == 0 ) {
|
||||
::traverseNodePair( bvhA, nodeA.left, bvhB, nodeBID, relTransform, pairs );
|
||||
::traverseNodePair( bvhA, nodeA.right, bvhB, nodeBID, relTransform, pairs );
|
||||
}
|
||||
else if ( nodeB.getCount() == 0 ) {
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.left, relTransform, pairs );
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.right, relTransform, pairs );
|
||||
}
|
||||
}
|
||||
|
||||
void traverseBVH( const pod::BVH& bvh, pod::BVH::index_t nodeID, pod::BVH::pairs_t& pairs ) {
|
||||
const auto& node = bvh.nodes[nodeID];
|
||||
|
||||
@ -568,6 +642,17 @@ namespace {
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
|
||||
void queryOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, const pod::Transform<>& relTransform, pod::BVH::pairs_t& outPairs ) {
|
||||
if ( !bvhA.flattened.empty() && !bvhB.flattened.empty() ) return ::queryFlatOverlaps( bvhA, bvhB, relTransform, outPairs );
|
||||
|
||||
if ( bvhA.nodes.empty() || bvhB.nodes.empty() ) return;
|
||||
outPairs.reserve(uf::physics::impl::settings.reserveCount);
|
||||
::traverseNodePair(bvhA, 0, bvhB, 0, relTransform, outPairs);
|
||||
UF_EXCEPTION("unimplemented");
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -775,6 +860,77 @@ namespace {
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
|
||||
void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, const pod::Transform<>& relTransform, pod::BVH::pairs_t& outPairs ) {
|
||||
auto& nodesA = bvhA.flattened;
|
||||
auto& boundsA = bvhA.flatBounds;
|
||||
auto& indicesA = bvhA.indices;
|
||||
|
||||
auto& nodesB = bvhB.flattened;
|
||||
auto& boundsB = bvhB.flatBounds;
|
||||
auto& indicesB = bvhB.indices;
|
||||
|
||||
if ( nodesA.empty() || nodesB.empty() ) return;
|
||||
outPairs.reserve(uf::physics::impl::settings.reserveCount);
|
||||
|
||||
static thread_local uf::stl::vector<std::pair<pod::BVH::index_t, pod::BVH::index_t>> stack;
|
||||
stack.clear();
|
||||
stack.emplace_back(0, 0);
|
||||
|
||||
while ( !stack.empty() ) {
|
||||
auto [a, b] = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
const auto& nodeA = bvhA.flattened[a];
|
||||
const auto& nodeB = bvhB.flattened[b];
|
||||
|
||||
if ( nodeA.isAsleep() && nodeB.isAsleep() ) continue;
|
||||
|
||||
/*
|
||||
pod::AABB worldBoundsA = ::transformAabbToWorld( boundsA[a], tA );
|
||||
pod::AABB worldBoundsB = ::transformAabbToWorld( boundsB[b], tB );
|
||||
if ( !::aabbOverlap( worldBoundsA, worldBoundsB ) ) continue;
|
||||
*/
|
||||
pod::AABB boundsB_in_A = ::transformAabbToWorld(boundsB[b], relTransform);
|
||||
if ( !::aabbOverlap( boundsA[a], boundsB_in_A ) ) continue;
|
||||
|
||||
bool isLeafA = (nodeA.getCount() > 0);
|
||||
bool isLeafB = (nodeB.getCount() > 0);
|
||||
|
||||
if ( isLeafA && isLeafB ) {
|
||||
for ( pod::BVH::index_t ia = 0; ia < nodeA.getCount(); ++ia ) {
|
||||
for ( pod::BVH::index_t ib = 0; ib < nodeB.getCount(); ++ib ) {
|
||||
auto indexA = bvhA.indices[nodeA.start + ia];
|
||||
auto indexB = bvhB.indices[nodeB.start + ib];
|
||||
|
||||
// if ( indexA > indexB ) std::swap( indexA, indexB );
|
||||
outPairs.emplace_back(indexA, indexB);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( isLeafA ) {
|
||||
pod::BVH::index_t rightB = bvhB.flattened[b + 1].skipIndex;
|
||||
stack.emplace_back(a, b + 1);
|
||||
stack.emplace_back(a, rightB);
|
||||
}
|
||||
else if ( isLeafB ) {
|
||||
pod::BVH::index_t rightA = bvhA.flattened[a + 1].skipIndex;
|
||||
stack.emplace_back(a + 1, b);
|
||||
stack.emplace_back(rightA, b);
|
||||
}
|
||||
else {
|
||||
pod::BVH::index_t rightA = bvhA.flattened[a + 1].skipIndex;
|
||||
pod::BVH::index_t rightB = bvhB.flattened[b + 1].skipIndex;
|
||||
|
||||
stack.emplace_back(a + 1, b + 1);
|
||||
stack.emplace_back(a + 1, rightB);
|
||||
stack.emplace_back(rightA, b + 1);
|
||||
stack.emplace_back(rightA, rightB);
|
||||
}
|
||||
}
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
|
||||
void queryFlatBVH( const pod::BVH& bvh, const pod::AABB& bounds, uf::stl::vector<pod::BVH::index_t>& outIndices ) {
|
||||
auto& nodes = bvh.flattened;
|
||||
auto& indices = bvh.indices;
|
||||
|
||||
@ -94,4 +94,8 @@ namespace {
|
||||
ASSERT_COLLIDER_TYPES( CAPSULE, MESH );
|
||||
REVERSE_COLLIDER( a, b, meshCapsule );
|
||||
}
|
||||
bool capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CAPSULE, CONVEX_HULL );
|
||||
REVERSE_COLLIDER( a, b, hullCapsule );
|
||||
}
|
||||
}
|
||||
86
engine/src/utils/math/physics/convexHull.inl
Normal file
86
engine/src/utils/math/physics/convexHull.inl
Normal file
@ -0,0 +1,86 @@
|
||||
namespace {
|
||||
bool hullGeneric( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
const auto& hull = a;
|
||||
const auto& body = b;
|
||||
|
||||
const auto& bvh = *hull.collider.convexHull.bvh;
|
||||
const auto& meshData = *hull.collider.convexHull.mesh;
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = ::transformAabbToLocal( body.bounds, ::getTransform( hull ) );
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
::queryBVH( bvh, bounds, candidates );
|
||||
|
||||
bool hit = false;
|
||||
// do collision per hull
|
||||
for ( auto hullID : candidates ) {
|
||||
auto hullView = ::physicsBodyHullView( hull, hullID );
|
||||
|
||||
pod::Simplex simplex;
|
||||
if ( !::gjk( hullView, body, simplex ) ) continue;
|
||||
|
||||
auto result = ::epa( hullView, body, simplex );
|
||||
|
||||
if ( !uf::vector::isValid(result.point) ) continue;
|
||||
|
||||
manifold.points.emplace_back(result);
|
||||
hit = true;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool hullAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CONVEX_HULL, AABB );
|
||||
return hullGeneric(a, b, manifold, eps);
|
||||
}
|
||||
bool hullSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CONVEX_HULL, SPHERE );
|
||||
return hullGeneric(a, b, manifold, eps);
|
||||
}
|
||||
bool hullPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CONVEX_HULL, PLANE );
|
||||
return hullGeneric(a, b, manifold, eps);
|
||||
}
|
||||
bool hullCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CONVEX_HULL, CAPSULE );
|
||||
return hullGeneric(a, b, manifold, eps);
|
||||
}
|
||||
bool hullMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CONVEX_HULL, MESH );
|
||||
REVERSE_COLLIDER( a, b, meshHull );
|
||||
}
|
||||
bool hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( CONVEX_HULL, CONVEX_HULL );
|
||||
const auto& bvhA = *a.collider.convexHull.bvh;
|
||||
const auto& bvhB = *b.collider.convexHull.bvh;
|
||||
|
||||
auto tA = ::getTransform( a );
|
||||
auto tB = ::getTransform( b );
|
||||
auto relTransform = uf::transform::relative( tA, tB );
|
||||
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
::queryOverlaps( bvhA, bvhB, relTransform, pairs );
|
||||
|
||||
bool hit = false;
|
||||
|
||||
for (auto [ viewIdA, viewIdB ] : pairs ) {
|
||||
auto viewA = ::physicsBodyHullView( a, viewIdA );
|
||||
auto viewB = ::physicsBodyHullView( b, viewIdB );
|
||||
|
||||
pod::Simplex simplex;
|
||||
if ( !::gjk( viewA, viewB, simplex ) ) continue;
|
||||
|
||||
auto result = ::epa( viewA, viewB, simplex );
|
||||
|
||||
if ( !uf::vector::isValid(result.point) ) continue;
|
||||
|
||||
manifold.points.emplace_back(result);
|
||||
hit = true;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
pod::Contact epa( const pod::PhysicsBody& a, const pod::PhysicsBody& b, const pod::Simplex& simplex, uint32_t maxIterations = 64, float eps = EPS ) {
|
||||
pod::Contact epa( const pod::PhysicsBody& a, const pod::PhysicsBody& b, const pod::Simplex& simplex, uint32_t maxIterations, float eps ) {
|
||||
UF_ASSERT( ::isValidSimplex(simplex) );
|
||||
|
||||
auto faces = ::initialPolytope(simplex);
|
||||
|
||||
@ -12,6 +12,18 @@ namespace {
|
||||
( dir.z >= 0.0f ) ? body.bounds.max.z : body.bounds.min.z
|
||||
};
|
||||
} break;
|
||||
case pod::ShapeType::PLANE: {
|
||||
const auto& plane = body.collider.plane;
|
||||
pod::Vector3f n = plane.normal;
|
||||
float d = plane.offset;
|
||||
|
||||
pod::Vector3f basePoint = n * d;
|
||||
float dot = uf::vector::dot( dir, n );
|
||||
if ( std::fabs(dot) > 0.9999f ) return basePoint;
|
||||
|
||||
pod::Vector3f tangent = uf::vector::normalize( dir - (n * dot) );
|
||||
return basePoint + tangent * 100000.0f;
|
||||
} break;
|
||||
case pod::ShapeType::CAPSULE: {
|
||||
auto up = uf::quaternion::rotate( transform.orientation, pod::Vector3f{0,1,0} );
|
||||
auto p1 = transform.position + up * body.collider.capsule.halfHeight;
|
||||
@ -29,8 +41,34 @@ namespace {
|
||||
if ( d1 > d2 ) return tri.points[1];
|
||||
return tri.points[2];
|
||||
} break;
|
||||
case pod::ShapeType::CONVEX_HULL: {
|
||||
const auto transform = ::getTransform( body );
|
||||
const auto& mesh = *body.collider.convexHull.mesh;
|
||||
auto selectedViewIdx = body.viewIndex;
|
||||
|
||||
pod::Vector3f localDir = uf::quaternion::rotate( uf::quaternion::inverse(transform.orientation), dir );
|
||||
float maxDist = -FLT_MAX;
|
||||
pod::Vector3f furthestVertex;
|
||||
|
||||
for ( auto viewIdx = 0; viewIdx < mesh.buffer_views.size(); ++viewIdx ) {
|
||||
if ( 0 <= selectedViewIdx && selectedViewIdx != viewIdx ) continue; // cringe, but saves code duplication (could just alter the bounds above)
|
||||
const auto& view = mesh.buffer_views[viewIdx];
|
||||
auto& positions = view["position"];
|
||||
for ( size_t i = 0; i < view.vertex.count; ++i ) {
|
||||
pod::Vector3f v = ::getVertex( view, positions, i );
|
||||
float dist = uf::vector::dot( v, localDir );
|
||||
if ( dist > maxDist ) {
|
||||
maxDist = dist;
|
||||
furthestVertex = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uf::transform::apply( transform, furthestVertex );
|
||||
} break;
|
||||
|
||||
default: {
|
||||
UF_EXCEPTION("unsupported shape: {}", (int)body.collider.type);
|
||||
} break;
|
||||
}
|
||||
return {};
|
||||
@ -150,7 +188,7 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gjk( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Simplex& simplex, int maxIterations = 20, float eps = EPS ) {
|
||||
bool gjk( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Simplex& simplex, int maxIterations, float eps ) {
|
||||
const float eps2 = eps * eps;
|
||||
auto dir = ::getPosition( b ) - ::getPosition( a );
|
||||
if ( uf::vector::magnitude( dir ) < eps2 ) dir = {1,0,0}; // fallback direction
|
||||
@ -205,4 +243,70 @@ namespace {
|
||||
return !simplex.pts.empty();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// GJK ray-cast
|
||||
namespace {
|
||||
pod::Vector3f closestPointOnSimplex( const pod::Vector3f& x, pod::Vector3f* simplex, int& sCount ) {
|
||||
if ( sCount == 1 ) {
|
||||
return simplex[0];
|
||||
} else if ( sCount == 2 ) {
|
||||
auto p = ::closestPointOnSegment( x, simplex[0], simplex[1] );
|
||||
return p;
|
||||
} else if ( sCount == 3 ) {
|
||||
auto p = ::closestPointOnTriangle( x, simplex[0], simplex[1], simplex[2] );
|
||||
return p;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
bool gjk( const pod::Ray& ray, const pod::PhysicsBody& body, float maxDist, float& outT, pod::Vector3f& outNormal, float eps ) {
|
||||
float t = 0.0f;
|
||||
pod::Vector3f x = ray.origin;
|
||||
pod::Vector3f n = {0, 0, 0};
|
||||
|
||||
pod::Vector3f supportPoint = ::support( body, -ray.direction );
|
||||
pod::Vector3f v = x - supportPoint;
|
||||
|
||||
pod::Vector3f simplex[4];
|
||||
int sCount = 0;
|
||||
|
||||
for ( int iter = 0; iter < 32; ++iter ) {
|
||||
float vSq = uf::vector::dot( v, v );
|
||||
// origin was inside the shape to begin with, or we perfectly converged
|
||||
if ( vSq < eps * eps ) break;
|
||||
|
||||
pod::Vector3f dir = -v;
|
||||
pod::Vector3f p = ::support( body, dir );
|
||||
pod::Vector3f w = x - p;
|
||||
|
||||
if ( uf::vector::dot( v, w ) > 0.0f ) {
|
||||
float vDotD = uf::vector::dot( v, ray.direction );
|
||||
if ( vDotD >= 0.0f ) return false; // miss
|
||||
|
||||
float dt = uf::vector::dot( v, w ) / vDotD;
|
||||
t = t - dt;
|
||||
|
||||
if ( t > maxDist ) return false;
|
||||
|
||||
x = ray.origin + ray.direction * t;
|
||||
n = uf::vector::normalize( v );
|
||||
|
||||
// reset
|
||||
sCount = 0;
|
||||
}
|
||||
|
||||
simplex[sCount++] = p;
|
||||
|
||||
pod::Vector3f closest = ::closestPointOnSimplex( x, simplex, sCount );
|
||||
v = x - closest;
|
||||
|
||||
if ( sCount == 4 ) break; // collided
|
||||
}
|
||||
|
||||
outT = t;
|
||||
outNormal = n;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -11,30 +11,35 @@ namespace {
|
||||
bool aabbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool aabbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool aabbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool aabbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
bool sphereSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool sphereAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool spherePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool sphereCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool sphereMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool sphereHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
bool planeAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool planeSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool planePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool planeCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool planeMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool planeHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
bool capsuleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool capsuleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool capsulePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool capsuleSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool capsuleMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
bool meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool meshPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool meshCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
bool triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
|
||||
@ -42,10 +47,23 @@ namespace {
|
||||
bool trianglePlane( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
|
||||
bool triangleCapsule( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
bool hullAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool hullSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool hullPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool hullCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool hullMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
bool hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
|
||||
|
||||
// ugh
|
||||
FORCE_INLINE bool aabbOverlap( const pod::AABB& a, const pod::AABB& b, float eps = EPS );
|
||||
|
||||
pod::Vector3f aabbCenter( const pod::AABB& aabb );
|
||||
pod::Vector3f getVertex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, size_t index );
|
||||
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID, const pod::PhysicsBody& body, bool fast = false );
|
||||
|
||||
// to-do: define maxIterations as a setting
|
||||
bool gjk( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Simplex& simplex, int maxIterations = 20, float eps = EPS );
|
||||
pod::Contact epa( const pod::PhysicsBody& a, const pod::PhysicsBody& b, const pod::Simplex& simplex, uint32_t maxIterations = 64, float eps = EPS );
|
||||
bool gjk( const pod::Ray& ray, const pod::PhysicsBody& body, float maxDist, float& outT, pod::Vector3f& outNormal, float eps = EPS );
|
||||
|
||||
void queryBVH( const pod::BVH& bvh, const pod::AABB& bounds, uf::stl::vector<pod::BVH::index_t>& indices );
|
||||
void queryBVH( const pod::BVH& bvh, const pod::AABB& bounds, uf::stl::vector<pod::BVH::index_t>& indices, pod::BVH::index_t nodeID );
|
||||
@ -53,6 +71,7 @@ namespace {
|
||||
void queryBVH( const pod::BVH& bvh, const pod::Ray& ray, uf::stl::vector<pod::BVH::index_t>& indices, pod::BVH::index_t nodeID, float maxDist = FLT_MAX );
|
||||
void queryOverlaps( const pod::BVH& bvh, pod::BVH::pairs_t& outPairs );
|
||||
void queryOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs );
|
||||
void queryOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, const pod::Transform<>& relTransform, pod::BVH::pairs_t& outPairs );
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -107,7 +126,7 @@ namespace {
|
||||
}
|
||||
|
||||
// returns an absolute transform while also allowing offsetting the collision body
|
||||
// to-do: find a succint way to explain this madness
|
||||
// to-do: find a succinct way to explain this madness
|
||||
pod::Transform<> getTransform( const pod::PhysicsBody& body ) {
|
||||
pod::Transform<> t;
|
||||
t.position = body.offset;
|
||||
@ -120,6 +139,26 @@ namespace {
|
||||
return ::getTransform( body ).position;
|
||||
}
|
||||
|
||||
pod::PhysicsBody physicsBodyHullView( const pod::PhysicsBody& body, int32_t index = -1 ) {
|
||||
pod::PhysicsBody view = body;
|
||||
view.viewIndex = index;
|
||||
return view;
|
||||
}
|
||||
|
||||
pod::PhysicsBody physicsBodyTriView( const pod::PhysicsBody& body, const pod::TriangleWithNormal triangle ) {
|
||||
pod::PhysicsBody view = body;
|
||||
view.collider.type = pod::ShapeType::TRIANGLE;
|
||||
view.collider.triangle = triangle;
|
||||
// assume triangle is already transformed
|
||||
view.offset = {};
|
||||
view.transform = NULL;
|
||||
return view;
|
||||
}
|
||||
pod::PhysicsBody physicsBodyTriView( const pod::PhysicsBody& body, size_t triID ) {
|
||||
auto tri = ::fetchTriangle( *body.collider.mesh.mesh, triID, body );
|
||||
return ::physicsBodyTriView( body, tri );
|
||||
}
|
||||
|
||||
bool shouldCollide( const pod::Collider& a, const pod::Collider& b ) {
|
||||
return ( a.category & b.mask ) && ( b.category & a.mask );
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "capsule.inl"
|
||||
#include "triangle.inl"
|
||||
#include "mesh.inl"
|
||||
#include "convexHull.inl"
|
||||
#include "ray.inl"
|
||||
#include "bvh.inl"
|
||||
#include "gjk.inl"
|
||||
@ -149,7 +150,7 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
|
||||
// do not do it for planes
|
||||
bool shouldReorient = true;
|
||||
if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) shouldReorient = false;
|
||||
if ( a.collider.type == pod::ShapeType::PLANE || b.collider.type == pod::ShapeType::PLANE ) shouldReorient = false;
|
||||
//if ( a.collider.type == pod::ShapeType::PLANE || b.collider.type == pod::ShapeType::PLANE ) shouldReorient = false;
|
||||
if ( shouldReorient ) {
|
||||
for ( auto& c : manifold.points ) c.normal = ::orientNormalToAB( a, b, c.normal );
|
||||
}
|
||||
@ -440,14 +441,23 @@ pod::PhysicsBody& uf::physics::impl::create( pod::World& world, uf::Object& obje
|
||||
uf::physics::impl::updateInertia( body );
|
||||
return body;
|
||||
}
|
||||
pod::PhysicsBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const uf::Mesh& mesh, float mass, const pod::Vector3f& offset ) {
|
||||
pod::PhysicsBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const uf::Mesh& mesh, float mass, const pod::Vector3f& offset, bool convex ) {
|
||||
auto& body = uf::physics::impl::create( world, object, mass, offset );
|
||||
body.collider.type = pod::ShapeType::MESH;
|
||||
body.collider.mesh.mesh = &mesh;
|
||||
|
||||
body.collider.mesh.bvh = new pod::BVH;
|
||||
auto& bvh = *body.collider.mesh.bvh;
|
||||
::buildMeshBVH( bvh, mesh, uf::physics::impl::settings.meshBvhCapacity );
|
||||
if ( !convex ) {
|
||||
body.collider.type = pod::ShapeType::MESH;
|
||||
body.collider.mesh.mesh = &mesh;
|
||||
body.collider.mesh.bvh = new pod::BVH;
|
||||
|
||||
auto& bvh = *body.collider.mesh.bvh;
|
||||
::buildMeshBVH( bvh, mesh, uf::physics::impl::settings.meshBvhCapacity );
|
||||
} else {
|
||||
body.collider.type = pod::ShapeType::CONVEX_HULL;
|
||||
body.collider.convexHull.mesh = &mesh;
|
||||
body.collider.convexHull.bvh = new pod::BVH;
|
||||
|
||||
auto& bvh = *body.collider.convexHull.bvh;
|
||||
::buildConvexHullBVH( bvh, mesh/*, uf::physics::impl::settings.meshBvhCapacity*/ );
|
||||
}
|
||||
|
||||
body.bounds = ::computeAABB( body );
|
||||
uf::physics::impl::updateInertia( body );
|
||||
@ -483,10 +493,10 @@ pod::PhysicsBody& uf::physics::impl::create( uf::Object& object, const pod::Caps
|
||||
auto& world = scene.getComponent<pod::World>();
|
||||
return create( world, object, capsule, mass, offset );
|
||||
}
|
||||
pod::PhysicsBody& uf::physics::impl::create( uf::Object& object, const uf::Mesh& mesh, float mass, const pod::Vector3f& offset ) {
|
||||
pod::PhysicsBody& uf::physics::impl::create( uf::Object& object, const uf::Mesh& mesh, float mass, const pod::Vector3f& offset, bool convex ) {
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& world = scene.getComponent<pod::World>();
|
||||
return create( world, object, mesh, mass, offset );
|
||||
return create( world, object, mesh, mass, offset, convex );
|
||||
}
|
||||
|
||||
void uf::physics::impl::destroy( uf::Object& object ) {
|
||||
@ -539,6 +549,7 @@ pod::RayQuery uf::physics::impl::rayCast( const pod::Ray& ray, const pod::World&
|
||||
case pod::ShapeType::PLANE: rayPlane( ray, *b, rayHit ); break;
|
||||
case pod::ShapeType::CAPSULE: rayCapsule( ray, *b, rayHit ); break;
|
||||
case pod::ShapeType::MESH: rayMesh( ray, *b, rayHit ); break;
|
||||
case pod::ShapeType::CONVEX_HULL: rayHull( ray, *b, rayHit ); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +69,10 @@ namespace {
|
||||
}
|
||||
|
||||
bool generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Manifold& manifold, float dt ) {
|
||||
if ( uf::physics::impl::settings.useGjk ) return generateContactsGjk( a, b, manifold, dt );
|
||||
bool useGjk = uf::physics::impl::settings.useGjk;
|
||||
if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) useGjk = false;
|
||||
//if ( a.collider.type == pod::ShapeType::PLANE || b.collider.type == pod::ShapeType::PLANE ) useGjk = false;
|
||||
if ( useGjk ) return generateContactsGjk( a, b, manifold, dt );
|
||||
::bindManifold( a, b, manifold, dt );
|
||||
|
||||
#define CHECK_CONTACT( A, B, fun )\
|
||||
@ -80,30 +83,44 @@ namespace {
|
||||
CHECK_CONTACT( AABB, PLANE, aabbPlane );
|
||||
CHECK_CONTACT( AABB, CAPSULE, aabbCapsule );
|
||||
CHECK_CONTACT( AABB, MESH, aabbMesh );
|
||||
CHECK_CONTACT( AABB, CONVEX_HULL, aabbHull );
|
||||
|
||||
CHECK_CONTACT( SPHERE, AABB, sphereAabb );
|
||||
CHECK_CONTACT( SPHERE, SPHERE, sphereSphere );
|
||||
CHECK_CONTACT( SPHERE, PLANE, spherePlane );
|
||||
CHECK_CONTACT( SPHERE, CAPSULE, sphereCapsule );
|
||||
CHECK_CONTACT( SPHERE, MESH, sphereMesh );
|
||||
CHECK_CONTACT( SPHERE, CONVEX_HULL, sphereHull );
|
||||
|
||||
CHECK_CONTACT( PLANE, AABB, planeAabb );
|
||||
CHECK_CONTACT( PLANE, SPHERE, planeSphere );
|
||||
CHECK_CONTACT( PLANE, PLANE, planePlane );
|
||||
CHECK_CONTACT( PLANE, CAPSULE, planeCapsule );
|
||||
CHECK_CONTACT( PLANE, MESH, planeMesh );
|
||||
CHECK_CONTACT( PLANE, CONVEX_HULL, planeHull );
|
||||
|
||||
CHECK_CONTACT( CAPSULE, AABB, capsuleAabb );
|
||||
CHECK_CONTACT( CAPSULE, SPHERE, capsuleSphere );
|
||||
CHECK_CONTACT( CAPSULE, PLANE, capsulePlane );
|
||||
CHECK_CONTACT( CAPSULE, CAPSULE, capsuleCapsule );
|
||||
CHECK_CONTACT( CAPSULE, MESH, capsuleMesh );
|
||||
CHECK_CONTACT( CAPSULE, CONVEX_HULL, capsuleHull );
|
||||
|
||||
CHECK_CONTACT( MESH, AABB, meshAabb );
|
||||
CHECK_CONTACT( MESH, SPHERE, meshSphere );
|
||||
CHECK_CONTACT( MESH, PLANE, meshPlane );
|
||||
CHECK_CONTACT( MESH, CAPSULE, meshCapsule );
|
||||
CHECK_CONTACT( MESH, MESH, meshMesh );
|
||||
CHECK_CONTACT( MESH, CONVEX_HULL, meshHull );
|
||||
|
||||
CHECK_CONTACT( CONVEX_HULL, AABB, hullAabb );
|
||||
CHECK_CONTACT( CONVEX_HULL, SPHERE, hullSphere );
|
||||
CHECK_CONTACT( CONVEX_HULL, PLANE, hullPlane );
|
||||
CHECK_CONTACT( CONVEX_HULL, CAPSULE, hullCapsule );
|
||||
CHECK_CONTACT( CONVEX_HULL, MESH, hullMesh );
|
||||
CHECK_CONTACT( CONVEX_HULL, CONVEX_HULL, hullHull );
|
||||
|
||||
UF_EXCEPTION("unregistered contact: {} vs {}", (int) a.collider.type, (int) b.collider.type );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,17 +1,3 @@
|
||||
/*
|
||||
// transform capsule line segments to local space
|
||||
p1 = uf::transform::applyInverse( mesh.transform, p1 );
|
||||
p2 = uf::transform::applyInverse( mesh.transform, p2 );
|
||||
|
||||
*/
|
||||
/*
|
||||
|
||||
contact = uf::transform::apply( mesh.transform, contact );
|
||||
normal = uf::quaternion::rotate( mesh.transform.orientation, normal );
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
namespace {
|
||||
bool meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( MESH, AABB );
|
||||
@ -113,7 +99,7 @@ namespace {
|
||||
|
||||
return hit;
|
||||
}
|
||||
// to-do
|
||||
|
||||
bool meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( MESH, MESH );
|
||||
|
||||
@ -123,29 +109,61 @@ namespace {
|
||||
const auto& bvhB = *b.collider.mesh.bvh;
|
||||
const auto& meshB = *b.collider.mesh.mesh;
|
||||
|
||||
auto tA = ::getTransform( a );
|
||||
auto tB = ::getTransform( b );
|
||||
auto relTransform = uf::transform::relative( tA, tB );
|
||||
|
||||
// compute overlaps between one BVH and another BVH
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
|
||||
|
||||
//UF_TIMER_MULTITRACE_START("Colliding {} ({} indices) <=> {} ({} indices)", a.object->getName(), bvhA.indices.size(), b.object->getName(), bvhB.indices.size());
|
||||
//UF_TIMER_MULTITRACE("Querying overlaps...");
|
||||
::queryOverlaps( bvhA, bvhB, pairs );
|
||||
//UF_TIMER_MULTITRACE("Queried overlaps.");
|
||||
::queryOverlaps( bvhA, bvhB, relTransform, pairs );
|
||||
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
//UF_TIMER_MULTITRACE("Colliding triangles (pairs={})...", pairs.size());
|
||||
for (auto [idA, idB] : pairs ) {
|
||||
auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space
|
||||
auto tB = ::fetchTriangle( meshB, idB, b );
|
||||
auto triA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space
|
||||
auto triB = ::fetchTriangle( meshB, idB, b );
|
||||
|
||||
bool collides = ::triangleTriangle( triA, triB, manifold, eps );
|
||||
if ( !collides ) continue;
|
||||
hit = true;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( MESH, CONVEX_HULL );
|
||||
|
||||
const auto& mesh = a;
|
||||
const auto& hull = b;
|
||||
|
||||
const auto& bvhA = *a.collider.mesh.bvh;
|
||||
const auto& meshA = *a.collider.mesh.mesh;
|
||||
|
||||
const auto& bvhB = *b.collider.convexHull.bvh;
|
||||
const auto& meshB = *b.collider.convexHull.mesh;
|
||||
|
||||
auto tA = ::getTransform( a );
|
||||
auto tB = ::getTransform( b );
|
||||
auto relTransform = uf::transform::relative( tA, tB );
|
||||
|
||||
// compute overlaps between one BVH and another BVH
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
::queryOverlaps( bvhA, bvhB, relTransform, pairs );
|
||||
|
||||
bool hit = false;
|
||||
// do collision per hull and triangle
|
||||
for (auto [ triID, hullID ] : pairs ) {
|
||||
auto triView = ::physicsBodyTriView( mesh, triID );
|
||||
auto hullView = ::physicsBodyHullView( hull, hullID );
|
||||
|
||||
bool collides = ::triangleHull( triView, hullView, manifold, eps );
|
||||
|
||||
bool collides = ::triangleTriangle( tA, tB, manifold, eps );
|
||||
if ( !collides ) continue;
|
||||
hit = true;
|
||||
}
|
||||
//UF_TIMER_MULTITRACE("Collided triangles.");
|
||||
//UF_TIMER_MULTITRACE_END("Collided mesh.");
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
@ -54,4 +54,8 @@ namespace {
|
||||
ASSERT_COLLIDER_TYPES( PLANE, MESH );
|
||||
REVERSE_COLLIDER( a, b, meshPlane );
|
||||
}
|
||||
bool planeHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( PLANE, CONVEX_HULL );
|
||||
REVERSE_COLLIDER( a, b, hullPlane );
|
||||
}
|
||||
}
|
||||
@ -229,4 +229,38 @@ namespace {
|
||||
|
||||
return rayHit.hit;
|
||||
}
|
||||
|
||||
bool rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQuery& rayHit ) {
|
||||
const auto& bvh = *body.collider.convexHull.bvh;
|
||||
const auto& meshData = *body.collider.convexHull.mesh;
|
||||
|
||||
const auto transform = ::getTransform( body );
|
||||
|
||||
pod::Ray ray;
|
||||
ray.origin = uf::transform::applyInverse( transform, r.origin );
|
||||
ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction );
|
||||
|
||||
thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
::queryBVH( bvh, ray, candidates );
|
||||
|
||||
for ( auto hullID : candidates ) {
|
||||
auto hullView = ::physicsBodyHullView( body, hullID );
|
||||
|
||||
float t;
|
||||
pod::Vector3f normal;
|
||||
|
||||
if ( !::gjk( ray, hullView, rayHit.contact.penetration, t, normal ) ) continue;
|
||||
if ( t >= rayHit.contact.penetration ) continue;
|
||||
|
||||
rayHit.hit = true;
|
||||
rayHit.body = &body;
|
||||
|
||||
rayHit.contact.point = uf::transform::apply( transform, ray.origin + ray.direction * t );
|
||||
rayHit.contact.normal = uf::quaternion::rotate( transform.orientation, normal );
|
||||
rayHit.contact.penetration = t;
|
||||
}
|
||||
|
||||
return rayHit.hit;
|
||||
}
|
||||
}
|
||||
@ -56,4 +56,8 @@ namespace {
|
||||
ASSERT_COLLIDER_TYPES( SPHERE, MESH );
|
||||
REVERSE_COLLIDER( a, b, meshSphere );
|
||||
}
|
||||
bool sphereHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( SPHERE, CONVEX_HULL );
|
||||
REVERSE_COLLIDER( a, b, hullSphere );
|
||||
}
|
||||
}
|
||||
@ -120,7 +120,7 @@ namespace {
|
||||
}
|
||||
|
||||
// if body is a mesh, apply its transform to the triangles, else reorient the normal with respect to the body
|
||||
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID, const pod::PhysicsBody& body, bool fast = false ) {
|
||||
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID, const pod::PhysicsBody& body, bool fast ) {
|
||||
auto tri = ::fetchTriangle( mesh, triID );
|
||||
|
||||
auto transform = ::getTransform( body );
|
||||
@ -566,4 +566,27 @@ namespace {
|
||||
ASSERT_COLLIDER_TYPES( TRIANGLE, CAPSULE );
|
||||
return ::triangleCapsule( a.collider.triangle, b, manifold, eps );
|
||||
}
|
||||
bool triangleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps ) {
|
||||
ASSERT_COLLIDER_TYPES( TRIANGLE, CONVEX_HULL );
|
||||
const auto& tri = a;
|
||||
const auto& hull = b;
|
||||
|
||||
pod::Simplex simplex;
|
||||
if ( !::gjk( tri, hull, simplex ) ) return false;
|
||||
|
||||
auto result = ::epa( tri, hull, simplex );
|
||||
|
||||
if ( !uf::vector::isValid(result.point) ) return false;
|
||||
|
||||
manifold.points.emplace_back( result );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool triangleHull( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps ) {
|
||||
pod::PhysicsBody triView = {};
|
||||
triView.collider.type = pod::ShapeType::TRIANGLE;
|
||||
triView.collider.triangle = tri;
|
||||
|
||||
return ::triangleHull( triView, body, manifold, eps );
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user