finally it works (to-do: support for reading from fp16/bf16/quantized vertex data, some method of emitting contact information to the colliding bodies, collision masking, per-body gravity, fix sinking occuring when using the collider offset, some other things)

This commit is contained in:
ecker 2025-09-01 14:51:46 -05:00
parent 461894741f
commit 3b5db2c32c
13 changed files with 962 additions and 456 deletions

View File

@ -66,7 +66,7 @@ ifneq (,$(findstring win64,$(ARCH)))
REQ_DEPS += meshoptimizer toml xatlas curl ffx:fsr dc:texconv # vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit reactphysics simd ctti gltf imgui fmt freetype openal ogg wav
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit simd ctti gltf imgui fmt freetype openal ogg wav # reactphysics
FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN
DEPS += -lgdi32 -ldwmapi
LINKS += #-Wl,-subsystem,windows
@ -76,13 +76,13 @@ else ifneq (,$(findstring linux,$(ARCH)))
REQ_DEPS += toml xatlas curl dc:texconv # meshoptimizer ffx:fsr cpptrace vall_e # ncurses openvr draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit reactphysics simd ctti gltf imgui fmt freetype openal ogg wav
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit simd ctti gltf imgui fmt freetype openal ogg wav # reactphysics
FLAGS += -DUF_ENV_LINUX -fPIC
DEPS += -pthread -ldl -lX11 -lXrandr
INCS := -I./dep/master/include $(INCS)
else ifneq (,$(findstring dreamcast,$(ARCH)))
FLAGS += -DUF_ENV_DREAMCAST # -DUF_LEAN_AND_MEAN # this apparently crashes
REQ_DEPS += opengl gldc json:nlohmann zlib lua reactphysics simd ctti fmt freetype openal aldc ogg wav png # imgui
REQ_DEPS += opengl gldc json:nlohmann zlib lua simd ctti fmt freetype openal aldc ogg wav png # imgui # reactphysics
INCS := -I./dep/dreamcast/include $(INCS)
endif

View File

@ -77,18 +77,16 @@
"gravity": [ 0, -9.81, 0 ],
"inertia": [ 0, 0, 0 ],
"type": "sphere",
"radius": 2,
"offset": [ 0, 0, 0 ],
// "type": "capsule",
// "radius": 1,
// "height": 2,
// "type": "bounding box",
// "min": [ -1.0, -2.0, -1.0 ],
// "max": [ 1.0, 0.0, 1.0 ],
// "type": "sphere",
// "radius": 2,
"type": "capsule",
"radius": 1,
"height": 2,
"mass": 1,
"mass": 100,
"friction": 0.95,
"restitution": 0.0,

View File

@ -179,19 +179,20 @@ namespace uf {
void UF_API applyRotation( pod::RigidBody& body, const pod::Quaternion<>& q );
void UF_API applyRotation( pod::RigidBody& body, const pod::Vector3f& axis, float angle );
pod::RigidBody& UF_API create( uf::Object&, float mass = 0.0f );
pod::RigidBody& UF_API create( uf::Object& object, const pod::AABB& aabb, float mass = 0 );
pod::RigidBody& UF_API create( uf::Object& object, const pod::Sphere& sphere, float mass = 0 );
pod::RigidBody& UF_API create( uf::Object& object, const pod::Plane& plane, float mass = 0 );
pod::RigidBody& UF_API create( uf::Object& object, const pod::Capsule& capsule, float mass = 0 );
pod::RigidBody& UF_API create( uf::Object&, const uf::Mesh& mesh, float mass = 0 );
pod::RigidBody& UF_API create( uf::Object&, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( uf::Object& object, const pod::AABB& aabb, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( uf::Object& object, const pod::Sphere& sphere, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( uf::Object& object, const pod::Plane& plane, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( uf::Object& object, const pod::Capsule& capsule, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( uf::Object&, const uf::Mesh& mesh, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object&, float mass = 0.0f );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::AABB& aabb, float mass = 0 );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::Sphere& sphere, float mass = 0 );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::Plane& plane, float mass = 0 );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::Capsule& capsule, float mass = 0 );
pod::RigidBody& UF_API create( pod::World&, uf::Object&, const uf::Mesh& mesh, float mass = 0 );
pod::RigidBody& UF_API create( pod::World&, uf::Object&, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::AABB& aabb, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::Sphere& sphere, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::Plane& plane, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::Capsule& capsule, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object& object, const pod::TriangleWithNormal& tri, float mass = 0.0f, const pod::Vector3f& = {} );
pod::RigidBody& UF_API create( pod::World&, uf::Object&, const uf::Mesh& mesh, float mass = 0.0f, const pod::Vector3f& = {} );
void UF_API destroy( uf::Object& );
void UF_API destroy( pod::RigidBody& );

View File

@ -163,21 +163,21 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} );
pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} );
uf::physics::impl::create( self, pod::AABB{ .min = min, .max = max }, mass );
uf::physics::impl::create( self, pod::AABB{ .min = min, .max = max }, mass, offset );
} else if ( type == "plane" ) {
pod::Vector3f direction = uf::vector::decode( metadataJsonPhysics["direction"], pod::Vector3f{} );
float offset = metadataJsonPhysics["offset"].as<float>();
float o = metadataJsonPhysics["offset"].as<float>();
uf::physics::impl::create( self, pod::Plane{ direction, offset }, mass );
uf::physics::impl::create( self, pod::Plane{ direction, o }, mass, offset );
} else if ( type == "sphere" ) {
float radius = metadataJsonPhysics["radius"].as<float>();
uf::physics::impl::create( self, pod::Sphere{ radius }, mass );
uf::physics::impl::create( self, pod::Sphere{ radius }, mass, offset );
} else if ( type == "capsule" ) {
float radius = metadataJsonPhysics["radius"].as<float>();
float height = metadataJsonPhysics["height"].as<float>();
uf::physics::impl::create( self, pod::Capsule{ radius, height * 0.5f }, mass );
uf::physics::impl::create( self, pod::Capsule{ radius, height * 0.5f }, mass, offset );
}
if ( this->hasComponent<pod::RigidBody>() ) {
@ -187,9 +187,6 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
body.inertiaTensor = { FLT_MAX, FLT_MAX, FLT_MAX };
body.inverseInertiaTensor = { 0.0f, 0.0f, 0.0f };
}
// body.transform.position = offset;
body.offset = offset;
}
#endif
}

View File

@ -1,8 +1,15 @@
namespace {
bool aabbOverlap( const pod::AABB& a, const pod::AABB& b, float eps ) {
return (a.min.x < b.max.x - eps && a.max.x > b.min.x + eps) &&
(a.min.y < b.max.y - eps && a.max.y > b.min.y + eps) &&
(a.min.z < b.max.z - eps && a.max.z > b.min.z + eps);
for (int axis=0; axis<3; ++axis) {
if (a.max[axis] + eps < b.min[axis] - eps) return false;
if (a.min[axis] - eps > b.max[axis] + eps) return false;
}
return true;
/*
return !(a.max.x < b.min.x + eps || a.min.x > b.max.x - eps ||
a.max.y < b.min.y + eps || a.min.y > b.max.y - eps ||
a.max.z < b.min.z + eps || a.min.z > b.max.z - eps);
*/
}
pod::AABB computeSegmentAABB( const pod::Vector3f& p1, const pod::Vector3f p2, float r ) {

View File

@ -223,49 +223,122 @@ namespace {
return ::interpolateWithBarycentric( bary, points[0], points[1], points[2] );
}
bool pointInTriangle( const pod::Vector3f& p, const pod::Vector3f& a, const pod::Vector3f& b, const pod::Vector3f& c, float eps = EPS(1.0e-6f) ) {
auto bary = ::computeBarycentric( p, a, b, c, false );
return ( bary.x >= -eps && bary.y >= -eps && bary.z >= -eps );
}
bool pointInTriangle( const pod::Vector3f& p, const pod::Triangle& tri, float eps = EPS(1.0e-6f) ) {
auto bary = ::computeBarycentric( p, tri, false );
return ( bary.x >= -eps && bary.y >= -eps && bary.z >= -eps );
}
pod::Vector3f closestPointOnTriangle( const pod::Vector3f& p, const pod::Vector3f& a, const pod::Vector3f& b, const pod::Vector3f& c ) {
auto bary = ::computeBarycentric( p, a, b, c );
return ::interpolateWithBarycentric( bary, a, b, c );
// Check if P in vertex region outside A
pod::Vector3f ab = b - a;
pod::Vector3f ac = c - a;
pod::Vector3f ap = p - a;
float d1 = uf::vector::dot(ab, ap);
float d2 = uf::vector::dot(ac, ap);
if (d1 <= 0 && d2 <= 0) return a;
// Check if P in vertex region outside B
pod::Vector3f bp = p - b;
float d3 = uf::vector::dot(ab, bp);
float d4 = uf::vector::dot(ac, bp);
if (d3 >= 0 && d4 <= d3) return b;
// Check if P in edge region of AB, if so return projection on AB
float vc = d1 * d4 - d3 * d2;
if (vc <= 0 && d1 >= 0 && d3 <= 0) {
float v = d1 / (d1 - d3);
return a + ab * v;
}
// Check vertex region outside C
pod::Vector3f cp = p - c;
float d5 = uf::vector::dot(ab, cp);
float d6 = uf::vector::dot(ac, cp);
if (d6 >= 0 && d5 <= d6) return c;
// Check edge region of AC
float vb = d5 * d2 - d1 * d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
float w = d2 / (d2 - d6);
return a + ac * w;
}
// Check edge region of BC
float va = d3 * d6 - d5 * d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
return b + (c - b) * w;
}
// P inside face region. Return projection onto face
float denom = 1.0f / (va + vb + vc);
float v = vb * denom;
float w = vc * denom;
return a + ab * v + ac * w;
}
pod::Vector3f closestPointOnTriangle( const pod::Vector3f& p, const pod::Triangle& tri ) {
return ::closestPointOnTriangle( p, tri.points[0], tri.points[1], tri.points[2] );
}
pod::Vector3f orientNormalToAB( const pod::RigidBody& a, const pod::RigidBody& b, pod::Vector3f n ) {
return uf::vector::normalize( uf::vector::dot( n, ::getPosition( b ) - ::getPosition( a ) ) < 0.0f ? -n : n );
}
float segmentTriangleDistanceSq( const pod::Vector3f& p0, const pod::Vector3f& p1, const pod::Triangle& tri, pod::Vector3f& outSeg, pod::Vector3f& outTri ) {
// segment vs plane
auto n = uf::vector::normalize( uf::vector::cross( tri.points[1] - tri.points[0], tri.points[2]-tri.points[0] ) );
float d0 = uf::vector::dot( p0 - tri.points[0], n );
float d1 = uf::vector::dot( p1 - tri.points[0], n );
// intersects plane
if ( ( d0 * d1 ) <= 0 ) {
float t = d0 / ( d0-d1 );
auto p = p0 + ( p1 - p0 ) * t;
auto q = ::closestPointOnTriangle( p, tri );
outSeg = p; outTri = q;
return uf::vector::magnitude( p - q );
float segmentTriangleDistanceSq( const pod::Vector3f& p0, const pod::Vector3f& p1, const pod::Triangle& tri, pod::Vector3f& outSeg, pod::Vector3f& outTri, float eps = EPS(1.0e-6f) ) {
float best = std::numeric_limits<float>::max();
auto n = uf::vector::cross( tri.points[1]-tri.points[0], tri.points[2]-tri.points[0] );
float denom = uf::vector::dot( n, n );
if ( denom > eps ) {
n /= std::sqrt( denom );
float d0 = uf::vector::dot( p0 - tri.points[0], n );
float d1 = uf::vector::dot( p1 - tri.points[0], n );
if ( (d0 * d1) <= 0.0f ) {
float t = d0 / (d0 - d1);
auto q = p0 + (p1 - p0) * t;
if ( ::pointInTriangle( q, tri ) ) {
outSeg = q;
outTri = q;
return 0.0f;
}
}
}
// otherwise check endpoints against triangle
auto q0 = ::closestPointOnTriangle( p0, tri );
auto q1 = ::closestPointOnTriangle( p1, tri );
float d0sq = uf::vector::magnitude( p0 - q0 );
float d1sq = uf::vector::magnitude( p1 - q1 );
// segment endpoints to triangle
for ( auto p : { p0, p1 } ) {
auto q = ::closestPointOnTriangle( p, tri );
float d = uf::vector::distanceSquared( p, q );
if ( d < best ) {
best = d;
outSeg = p;
outTri = q;
}
}
if ( d0sq < d1sq ) { outSeg = p0; outTri = q0; return d0sq; }
else { outSeg = p1; outTri = q1; return d1sq; }
// segment edges to tri edges
for ( auto i = 0; i < 3; i++ ) {
auto j = ( i + 1 ) % 3;
auto [s,e] = ::closestSegmentSegment( p0, p1, tri.points[i], tri.points[j] );
float d = uf::vector::distanceSquared( s, e );
if ( d < best ) {
best = d;
outSeg = s;
outTri = e;
}
}
return best;
}
bool triangleTriangleIntersect( const pod::Triangle& a, const pod::Triangle& b, float eps = EPS(1e-6f) ) {
auto boxA = ::computeTriangleAABB( a );
auto boxB = ::computeTriangleAABB( b );
if ( !aabbOverlap( boxA, boxB ) ) return false;
if ( !::aabbOverlap( boxA, boxB ) ) return false;
// check vertices of a inside b or vice versa
for ( auto i = 0; i < 3; i++ ) {

View File

@ -242,13 +242,14 @@ void uf::physics::impl::applyRotation( pod::RigidBody& body, const pod::Vector3f
}
// body creation
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, float mass ) {
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, float mass, const pod::Vector3f& offset ) {
// bind to component
pod::RigidBody& body = object.getComponent<pod::RigidBody>();
// initial initialization
body.world = &world;
body.object = &object;
body.transform/*.reference*/ = &object.getComponent<pod::Transform<>>();
body.offset = offset;
body.mass = mass;
body.inverseMass = mass == 0.0f ? 0.0f : 1.0f / mass;
body.isStatic = mass == 0.0f;
@ -258,8 +259,8 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
return body;
}
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::AABB& aabb, float mass ) {
auto& body = uf::physics::impl::create( world, object, mass );
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::AABB& aabb, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::AABB;
body.collider.u.aabb = aabb;
body.bounds = ::computeAABB( body );
@ -267,8 +268,8 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
UF_MSG_DEBUG("Creating body of type: {}, mass={}, min={}, max={}", "AABB", mass, uf::vector::toString(aabb.min), uf::vector::toString(aabb.max) );
return body;
}
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::Sphere& sphere, float mass ) {
auto& body = uf::physics::impl::create( world, object, mass );
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::Sphere& sphere, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::SPHERE;
body.collider.u.sphere = sphere;
body.bounds = ::computeAABB( body );
@ -276,8 +277,8 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
UF_MSG_DEBUG("Creating body of type={}, mass={}, radius={}", "SPHERE", mass, sphere.radius );
return body;
}
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::Plane& plane, float mass ) {
auto& body = uf::physics::impl::create( world, object, mass );
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::Plane& plane, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::PLANE;
body.collider.u.plane = plane;
body.bounds = ::computeAABB( body );
@ -285,18 +286,26 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
UF_MSG_DEBUG("Creating body of type={}, mass={}, normal={}, offset={}", "PLANE", mass, uf::vector::toString( plane.normal ), plane.offset );
return body;
}
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::Capsule& capsule, float mass ) {
auto& body = uf::physics::impl::create( world, object, mass );
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::Capsule& capsule, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::CAPSULE;
body.collider.u.capsule = capsule;
body.material.restitution = 0.001f;
body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body );
UF_MSG_DEBUG("Creating body of type={}, mass={}, radius={}, height={}", "CAPSULE", mass, capsule.radius, capsule.halfHeight * 2.0f );
return body;
}
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const uf::Mesh& mesh, float mass ) {
auto& body = uf::physics::impl::create( world, object, mass );
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const pod::TriangleWithNormal& tri, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::TRIANGLE;
body.collider.u.triangle = tri;
body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body );
UF_MSG_DEBUG("Creating body of type={}, mass={}, point[0]={}, point[1]={}, point[2]={}", "TRIANGLE", mass, uf::vector::toString( tri.points[0] ), uf::vector::toString( tri.points[1] ), uf::vector::toString( tri.points[2] ) );
return body;
}
pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object, const uf::Mesh& mesh, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::MESH;
body.collider.u.mesh.mesh = &mesh;
body.collider.u.mesh.bvh = new pod::BVH;
@ -309,39 +318,39 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
return body;
}
pod::RigidBody& uf::physics::impl::create( uf::Object& object, float mass ) {
pod::RigidBody& uf::physics::impl::create( uf::Object& object, float mass, const pod::Vector3f& offset ) {
// bind to scene
// auto& root = object.getRootParent<>(); // in the event a scene is being initialized that is not the root scene, use the root parent instead
// auto& world = root.getComponent<pod::World>();
auto& scene = uf::scene::getCurrentScene();
auto& world = scene.getComponent<pod::World>();
return create( world, object, mass );
return create( world, object, mass, offset );
}
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::AABB& aabb, float mass ) {
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::AABB& aabb, float mass, const pod::Vector3f& offset ) {
auto& scene = uf::scene::getCurrentScene();
auto& world = scene.getComponent<pod::World>();
return create( world, object, aabb, mass );
return create( world, object, aabb, mass, offset );
}
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::Sphere& sphere, float mass ) {
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::Sphere& sphere, float mass, const pod::Vector3f& offset ) {
auto& scene = uf::scene::getCurrentScene();
auto& world = scene.getComponent<pod::World>();
return create( world, object, sphere, mass );
return create( world, object, sphere, mass, offset );
}
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::Plane& plane, float mass ) {
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::Plane& plane, float mass, const pod::Vector3f& offset ) {
auto& scene = uf::scene::getCurrentScene();
auto& world = scene.getComponent<pod::World>();
return create( world, object, plane, mass );
return create( world, object, plane, mass, offset );
}
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::Capsule& capsule, float mass ) {
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const pod::Capsule& capsule, float mass, const pod::Vector3f& offset ) {
auto& scene = uf::scene::getCurrentScene();
auto& world = scene.getComponent<pod::World>();
return create( world, object, capsule, mass );
return create( world, object, capsule, mass, offset );
}
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const uf::Mesh& mesh, float mass ) {
pod::RigidBody& uf::physics::impl::create( uf::Object& object, const uf::Mesh& mesh, float mass, const pod::Vector3f& offset ) {
auto& scene = uf::scene::getCurrentScene();
auto& world = scene.getComponent<pod::World>();
return create( world, object, mesh, mass );
return create( world, object, mesh, mass, offset );
}
void uf::physics::impl::destroy( uf::Object& object ) {

View File

@ -227,16 +227,6 @@ namespace {
uf::transform::rotate( *body.transform/*.reference*/, dq );
}
/*
UF_MSG_DEBUG("acceleration={}, velocity={}, forceAccumulator={}, position={}, center={}",
uf::vector::toString( acceleration ),
uf::vector::toString( body.velocity ),
uf::vector::toString( body.forceAccumulator ),
uf::vector::toString( body.transform->position ),
uf::vector::toString( ::getPosition( body ) )
);
*/
// update AABB
body.bounds = ::computeAABB( body );

View File

@ -99,8 +99,6 @@ namespace {
uf::stl::vector<int> candidates;
::queryBVH( bvh, bounds, candidates );
UF_MSG_DEBUG("candidates={}", candidates.size());
bool hit = false;
// do collision per triangle
for ( auto triID : candidates ) {
@ -125,11 +123,16 @@ namespace {
pod::BVH::pair_t pairs;
::traverseNodePair(bvhA, 0, bvhB, 0, pairs);
UF_MSG_DEBUG("candidates={}", pairs.size());
bool hit = false;
// do collision per triangle
for (auto [ idA, idB] : pairs ) {
auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space
auto tB = ::fetchTriangle( meshB, idB, b );
UF_MSG_DEBUG("triA={}, triB={}", idA, idB );
if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue;
hit = true;
}

View File

@ -30,14 +30,14 @@ namespace {
auto& normal = plane.collider.u.plane.normal;
float offset = plane.collider.u.plane.offset;
auto center = ::getPosition( a );
auto center = ::getPosition( sphere );
float r = sphere.collider.u.sphere.radius;
float dist = uf::vector::dot( normal, center ) - offset;
if ( dist > r ) return false;
auto contact = center - normal * dist - normal * r;
float penetration = r - dist;
auto contact = center - normal * dist - normal * penetration;
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;

View File

@ -75,42 +75,39 @@ namespace {
return true;
}
bool raySphere( const pod::Ray& ray, const pod::RigidBody& body, pod::RayQuery& rayHit ) {
auto center = ::getPosition( body );
auto center = ::getPosition(body);
float r = body.collider.u.sphere.radius;
// vector from sphere center to ray origin
auto oc = ray.origin - center;
// ray starts inside
if ( uf::vector::magnitude( oc ) < r * r ) {
rayHit.hit = true;
rayHit.body = &body;
rayHit.contact.point = ray.origin;
rayHit.contact.normal = uf::vector::normalize(ray.origin - center);
rayHit.contact.penetration = 0.0f;
return true;
}
float a = uf::vector::dot( ray.direction, ray.direction );
float b = 2.0f * uf::vector::dot( oc, ray.direction );
float c = uf::vector::dot( oc, oc ) - r * r;
float c = uf::vector::dot( oc, oc ) - r*r;
float discriminant = b*b - 4*a*c;
if ( discriminant < 0.0f ) return false;
float disc = b*b - 4*a*c;
float sqrtDisc = std::sqrt(discriminant);
UF_MSG_DEBUG( "center={}, r={}, oc={}, a, b, c, disc", uf::vector::toString( center ), r, uf::vector::toString( oc ), a, b, c, disc );
if ( disc < 0 ) return false;
float sqrtDisc = std::sqrt(disc);
float t0 = (-b - sqrtDisc) / (2*a);
float t1 = (-b + sqrtDisc) / (2*a);
// take nearest valid hit
float t = t0;
if ( t < 0.0f ) t = t1;
if ( t < 0.0f ) return false;
float t = ( t0 >= 0 ) ? t0 : t1;
if ( t >= rayHit.contact.penetration ) return false; // further than current closest
UF_MSG_DEBUG( "sqrtDisc={}, t0={}, t1={}, t={}, rayHit.contact.penetration={}", sqrtDisc, t0, t1, t, rayHit.contact.penetration );
if ( t < 0 ) return false; // both behind ray
// compare against current best hit
if ( t >= rayHit.contact.penetration ) return false;
// record hit
rayHit.hit = true;
rayHit.body = &body;
rayHit.contact.point = ray.origin + ray.direction * t;
rayHit.contact.point = ray.origin + ray.direction*t;
rayHit.contact.normal = uf::vector::normalize( rayHit.contact.point - center );
rayHit.contact.penetration = t;
return true;

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ namespace {
return uf::vector::normalize(uf::vector::cross(tri.points[1] - tri.points[0], tri.points[2] - tri.points[0]));
}
pod::Vector3f triangleNormal( const pod::TriangleWithNormal& tri ) {
return uf::vector::normalize(( tri.normals[0] + tri.normals[1] + tri.normals[2] ) / 3.0f);
return uf::vector::normalize( tri.normals[0] + tri.normals[1] + tri.normals[2] );
}
pod::AABB computeTriangleAABB( const void* vertices, size_t vertexStride, const void* indexData, size_t indexSize, size_t triID ) {
@ -126,28 +126,135 @@ namespace {
return tri;
}
bool computeTriangleTriangleSegment( const pod::TriangleWithNormal& A, const pod::TriangleWithNormal& B, pod::Vector3f& p0, pod::Vector3f& p1, float eps = EPS(1e-6f) ) {
uf::stl::vector<pod::Vector3f> intersections;
auto checkAndPush = [&]( const pod::Vector3f& pt ) {
// avoid duplicates
for ( auto& v : intersections ) {
if ( uf::vector::distanceSquared( v, pt ) < eps*eps ) return;
}
intersections.emplace_back(pt);
};
// segment-plane intersection
auto intersectSegmentPlane = [&](const pod::Vector3f& a, const pod::Vector3f& b, const pod::Vector3f& n, float d, pod::Vector3f& out)->bool {
pod::Vector3f ab = b - a;
float denom = uf::vector::dot( n, ab );
if (fabs(denom) < eps) return false; // parallel
float t = (d - uf::vector::dot( n, a )) / denom;
if ( t < -eps || t > 1.0f + eps ) return false;
out = a + ab * t;
return true;
};
// planes
auto nA = ::triangleNormal( A );
auto nB = ::triangleNormal( B );
float dA = uf::vector::dot( nA, A.points[0] );
float dB = uf::vector::dot( nB, B.points[0] );
// clip edges of A against plane of B
const pod::Vector3f At[3] = { A.points[0], A.points[1], A.points[2] };
for ( auto i = 0; i < 3; ++i ) {
int j = ( i + 1 ) % 3;
pod::Vector3f p;
if ( intersectSegmentPlane( At[i], At[j], nB, dB, p ) ) {
// check if intersection lies inside triangle B
if ( ::pointInTriangle( p, B ) ) checkAndPush(p);
}
}
// clip edges of B against plane of A
const pod::Vector3f Bt[3] = { B.points[0], B.points[1], B.points[2] };
for ( auto i = 0; i < 3; ++i ) {
int j = ( i + 1 ) % 3;
pod::Vector3f p;
if ( intersectSegmentPlane( Bt[i], Bt[j], nA, dA, p ) ) {
if ( ::pointInTriangle( p, A ) ) checkAndPush(p);
}
}
if ( intersections.empty() ) return false;
// degenerate intersection
if ( intersections.size() == 1 ) {
p0 = p1 = intersections[0];
return true;
}
// find two furthest apart points for intersection segment
float maxDist2 = -1.0f;
for ( auto i = 0 ; i < intersections.size(); i++ ) {
for ( auto j = i + 1 ; j<intersections.size(); j++ ) {
float d2 = uf::vector::distanceSquared( intersections[i], intersections[j] );
if ( d2 > maxDist2 ) {
maxDist2 = d2;
p0 = intersections[i];
p1 = intersections[j];
}
}
}
return maxDist2 >= 0.0f;
}
pod::Vector2f projectTriangleOntoAxis( const pod::TriangleWithNormal& tri, const pod::Vector3f& axis ) {
pod::Vector3f normal = uf::vector::normalize( axis );
float p0 = uf::vector::dot( tri.points[0], normal );
float p1 = uf::vector::dot( tri.points[1], normal );
float p2 = uf::vector::dot( tri.points[2], normal );
return { std::min({ p0, p1, p2 }), std::max({ p0, p1, p2 }) };
}
}
/*
#if REORIENT_NORMALS_ON_CONTACT
if ( uf::vector::dot(normal, delta) < 0.0f ) normal = -normal;
#endif
// uf::vector::normalize( ::interpolateWithBarycentric( ::computeBarycentric( contact, tri ), tri.normals ) );
*/
// triangle colliders
namespace {
bool triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold, float eps ) {
if ( !::triangleTriangleIntersect( a, b ) ) return false;
// if ( !::triangleTriangleIntersect( a, b ) ) return false;
uf::stl::vector<pod::Vector3f> axes = { ::triangleNormal( a ), ::triangleNormal( b ) };
pod::Vector3f p0 = {}, p1 = {};
if ( !::computeTriangleTriangleSegment(a, b, p0, p1) ) {
auto contact = ( p0 + p1 ) * 0.5f;
auto normal = uf::vector::normalize( axes[0] + axes[1] );
manifold.points.emplace_back(pod::Contact{ contact, normal, eps });
return true;
}
auto contact = ( p0 + p1 ) * 0.5f;
float penetration = std::numeric_limits<float>::max();
pod::Vector3f normal;
// check edge cross-products
for ( auto i = 0; i < 3; i++ ) {
auto ea = a.points[( i + 1 ) % 3] - a.points[i];
for ( auto j = 0; j < 3; j++ ) {
auto eb = b.points[( j + 1 ) % 3] - b.points[j];
auto axis = uf::vector::cross(ea, eb);
if ( uf::vector::magnitude( axis ) > eps*eps ) axes.emplace_back( axis );
}
}
// project onto each axis
for ( auto axis : axes ) {
axis = uf::vector::normalize( axis );
pod::Vector2f aP = ::projectTriangleOntoAxis( a, axis );
pod::Vector2f bP = ::projectTriangleOntoAxis( b, axis );
float overlap = std::min( aP.x, bP.x ) - std::max( aP.y, bP.y );
if ( overlap < 0) return false; // separating axis
if ( overlap < penetration ) {
penetration = overlap;
normal = axis;
}
}
// to-do: properly derive the contact information
auto contact = ( a.points[0] + b.points[0] ) * 0.5f; // center point
auto normal = ::triangleNormal( a );
float penetration = 0.001f;
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;
}
@ -168,7 +275,7 @@ namespace {
float dist = std::sqrt( dist2 );
// to-do: properly derive the contact information
auto contact = closest;
auto contact = closest; // ( closest + closestAabb ) * 0.5f;
auto normal = ( dist > eps ) ? ( delta / dist ) : ::triangleNormal( tri );
float penetration = tolerance - dist;
@ -184,17 +291,17 @@ namespace {
float r = sphere.collider.u.sphere.radius;
auto center = ::getPosition( sphere );
auto closest = ::closestPointOnTriangle( center, tri );
auto closest = ::closestPointOnTriangle( center, tri.points[0], tri.points[1], tri.points[2] );
if ( !uf::vector::isValid( closest ) ) return false;
// to-do: derive proper delta
auto delta = center - closest;
float dist2 = uf::vector::dot(delta, delta);
float dist2 = uf::vector::dot( delta, delta );
if ( dist2 > r * r ) return false;
float dist = std::sqrt(dist2);
auto contact = closest;
auto contact = ( center + closest ) * 0.5f;
auto normal = ( dist > eps ) ? ( delta / dist ) : ::triangleNormal( tri );
float penetration = r - dist;
@ -205,9 +312,50 @@ namespace {
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;
}
// to-do
// to-do: implement
bool trianglePlane( const pod::TriangleWithNormal& tri, const pod::RigidBody& body, pod::Manifold& manifold, float eps ) {
return false;
const auto& plane = body;
auto normal = plane.collider.u.plane.normal;
float d = plane.collider.u.plane.offset;
bool hit = false;
pod::Vector3f dist;
for ( auto i = 0; i < 3; i++ ) dist[i] = uf::vector::dot(normal, tri.points[i] ) - d;
// completely on one side
bool allAbove = ( dist.x > eps && dist.y > eps && dist.z > eps );
bool allBelow = ( dist.x < -eps && dist.y < -eps && dist.z < -eps );
if ( allAbove )
return hit;
if ( allBelow ) {
hit = true;
for ( auto i = 0; i < 3; i++ ) {
float penetration = -dist[i];
manifold.points.emplace_back(pod::Contact{tri.points[i], normal, penetration});
}
return hit;
}
// points touching plane
for ( auto i = 0; i < 3; i++ )
if ( fabs( dist[i] ) <= eps ) {
hit = true;
manifold.points.emplace_back(pod::Contact{ tri.points[i], normal, 0.0f });
}
// edges that cross plane
for ( auto i = 0; i < 3; i++ ) {
auto j = (i + 1) % 3;
if ( ( dist[i] > 0 && dist[j] < 0 ) || ( dist[i] < 0 && dist[j] > 0 ) ) {
hit = true;
float t = dist[i] / ( dist[i] - dist[j] );
auto contact = tri.points[i] + ( tri.points[j] - tri.points[i] ) * t;
float penetration = -std::min( dist[i], dist[j] );
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
}
}
return hit;
}
bool triangleCapsule( const pod::TriangleWithNormal& tri, const pod::RigidBody& body, pod::Manifold& manifold, float eps ) {
const auto& capsule = body;
@ -226,7 +374,7 @@ namespace {
auto delta = ( closestSeg - closest );
// to-do: properly derive the contact information
auto contact = closest;
auto contact = closest; // ( closestSeg + closest ) * 0.5f;
auto normal = ( dist > eps ) ? ( delta / dist ) : ::triangleNormal( tri );
float penetration = r - dist;
@ -237,4 +385,25 @@ namespace {
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;
}
bool triangleTriangle( const pod::RigidBody& a, const pod::RigidBody& b, pod::Manifold& manifold, float eps ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, TRIANGLE );
return ::triangleTriangle( a.collider.u.triangle, b.collider.u.triangle, manifold, eps );
}
bool triangleAabb( const pod::RigidBody& a, const pod::RigidBody& b, pod::Manifold& manifold, float eps ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, AABB );
return ::triangleAabb( a.collider.u.triangle, b, manifold, eps );
}
bool triangleSphere( const pod::RigidBody& a, const pod::RigidBody& b, pod::Manifold& manifold, float eps ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, SPHERE );
return ::triangleSphere( a.collider.u.triangle, b, manifold, eps );
}
bool trianglePlane( const pod::RigidBody& a, const pod::RigidBody& b, pod::Manifold& manifold, float eps ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, PLANE );
return ::trianglePlane( a.collider.u.triangle, b, manifold, eps );
}
bool triangleCapsule( const pod::RigidBody& a, const pod::RigidBody& b, pod::Manifold& manifold, float eps ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, CAPSULE );
return ::triangleCapsule( a.collider.u.triangle, b, manifold, eps );
}
}