this is saner (although selectively querying for specific mesh attributes on view construction, just to have an interface to access them by name seems extremely silly), also triangleAabbSAT for stability (it's marginally more stable)
This commit is contained in:
parent
58dee945f8
commit
3ac3022f88
@ -6,9 +6,9 @@
|
||||
"metadata": {
|
||||
"holdable": true,
|
||||
"physics": {
|
||||
"mass": 10000,
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
"mass": 0,
|
||||
// "type": "bounding box"
|
||||
"type": "mesh"
|
||||
}
|
||||
}
|
||||
}
|
@ -104,7 +104,6 @@ namespace pod {
|
||||
struct MeshBVH {
|
||||
pod::BVH* bvh;
|
||||
const uf::Mesh* mesh;
|
||||
const uf::Mesh::views_t* views;
|
||||
};
|
||||
|
||||
typedef uint32_t CollisionMask;
|
||||
|
@ -197,10 +197,13 @@ namespace uf {
|
||||
|
||||
// crunge, but it's better this way for streaming in mesh data
|
||||
uf::stl::vector<uf::stl::string> buffer_paths;
|
||||
// mega cringe, but i'd like to have a way to cache it
|
||||
uf::stl::vector<uf::Mesh::View> buffer_views;
|
||||
protected:
|
||||
void _destroy( uf::Mesh::Input& input );
|
||||
void _bind( bool interleaved = uf::Mesh::defaultInterleaved );
|
||||
void _updateDescriptor( uf::Mesh::Input& input );
|
||||
void _updateViews();
|
||||
uf::Mesh::Attribute _remapAttribute( const uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute, size_t i = 0 ) const;
|
||||
|
||||
bool _hasV( const uf::Mesh::Input& input, const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) const;
|
||||
|
@ -212,7 +212,7 @@ namespace {
|
||||
bvh.dirty = false;
|
||||
}
|
||||
|
||||
void buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, const uf::Mesh::views_t& views, pod::BVH::index_t capacity = 4 ) {
|
||||
void buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t capacity = 4 ) {
|
||||
uint32_t triangles = mesh.index.count / 3;
|
||||
|
||||
bvh.indices.clear();
|
||||
@ -223,6 +223,7 @@ namespace {
|
||||
uf::stl::vector<pod::AABB> bounds;
|
||||
bounds.reserve( triangles );
|
||||
|
||||
const auto& views = mesh.buffer_views;
|
||||
UF_ASSERT( !views.empty() );
|
||||
|
||||
// populate initial indices and bounds
|
||||
@ -375,12 +376,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
void refitBVH( pod::BVH& bvh, const uf::Mesh& mesh, const uf::Mesh::views_t& views ) {
|
||||
void refitBVH( pod::BVH& bvh, const uf::Mesh& mesh ) {
|
||||
uint32_t triangles = mesh.index.count / 3;
|
||||
|
||||
uf::stl::vector<pod::AABB> bounds;
|
||||
bounds.reserve( triangles );
|
||||
|
||||
const auto& views = mesh.buffer_views;
|
||||
UF_ASSERT( !views.empty() );
|
||||
|
||||
// populate initial indices and bounds
|
||||
|
@ -14,15 +14,15 @@ namespace {
|
||||
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
|
||||
uint32_t broadphaseBvhCapacity = 1; // number of bodies per leaf node
|
||||
uint32_t meshBvhCapacity = 1; // number of triangles per leaf node
|
||||
uint32_t broadphaseBvhCapacity = 4; // number of bodies per leaf node
|
||||
uint32_t meshBvhCapacity = 4; // number of triangles per leaf node
|
||||
|
||||
// additionally flattens a BVH for linear iteration, rather than a recursive / stack-based traversal
|
||||
bool flattenBvhBodies = true;
|
||||
bool flattenBvhMeshes = true;
|
||||
|
||||
// use surface area heuristics for building the BVH, rather than naive splits
|
||||
bool useBvhSahBodies = false; // it actually seems slower to use these......
|
||||
bool useBvhSahBodies = true; // it actually seems slower to use these......
|
||||
bool useBvhSahMeshes = true;
|
||||
|
||||
bool useSplitBvhs = true; // creates separate BVHs for static / dynamic objects
|
||||
@ -396,11 +396,10 @@ pod::PhysicsBody& uf::physics::impl::create( pod::World& world, uf::Object& obje
|
||||
auto& body = uf::physics::impl::create( world, object, mass, offset );
|
||||
body.collider.type = pod::ShapeType::MESH;
|
||||
body.collider.mesh.mesh = &mesh;
|
||||
auto& views = *(body.collider.mesh.views = new uf::Mesh::views_t(mesh.makeViews({"position", "normal"})));
|
||||
|
||||
body.collider.mesh.bvh = new pod::BVH;
|
||||
auto& bvh = *body.collider.mesh.bvh;
|
||||
::buildMeshBVH( bvh, mesh, views, ::meshBvhCapacity );
|
||||
::buildMeshBVH( bvh, mesh, ::meshBvhCapacity );
|
||||
|
||||
body.bounds = ::computeAABB( body );
|
||||
uf::physics::impl::updateInertia( body );
|
||||
@ -459,7 +458,6 @@ void uf::physics::impl::destroy( pod::PhysicsBody& body ) {
|
||||
// remove any pointered collider data
|
||||
if ( body.collider.type == pod::ShapeType::MESH ) {
|
||||
if ( body.collider.mesh.bvh ) delete body.collider.mesh.bvh;
|
||||
if ( body.collider.mesh.views ) delete body.collider.mesh.views;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ namespace {
|
||||
|
||||
const auto& bvh = *mesh.collider.mesh.bvh;
|
||||
const auto& meshData = *mesh.collider.mesh.mesh;
|
||||
const auto& meshViews = *mesh.collider.mesh.views;
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = ::transformAabbToLocal( aabb.bounds, ::getTransform( mesh ) );
|
||||
@ -32,7 +31,7 @@ namespace {
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
for ( auto triID : candidates ) {
|
||||
auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
|
||||
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space
|
||||
if ( !::triangleAabb( tri, aabb, manifold, eps ) ) continue;
|
||||
hit = true;
|
||||
}
|
||||
@ -46,7 +45,6 @@ namespace {
|
||||
|
||||
const auto& bvh = *mesh.collider.mesh.bvh;
|
||||
const auto& meshData = *mesh.collider.mesh.mesh;
|
||||
const auto& meshViews = *mesh.collider.mesh.views;
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = ::transformAabbToLocal( sphere.bounds, ::getTransform( mesh ) );
|
||||
@ -57,7 +55,7 @@ namespace {
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
for ( auto triID : candidates ) {
|
||||
auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
|
||||
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space
|
||||
if ( !::triangleSphere( tri, sphere, manifold, eps ) ) continue;
|
||||
hit = true;
|
||||
}
|
||||
@ -73,7 +71,6 @@ namespace {
|
||||
|
||||
const auto& bvh = *mesh.collider.mesh.bvh;
|
||||
const auto& meshData = *mesh.collider.mesh.mesh;
|
||||
const auto& meshViews = *mesh.collider.mesh.views;
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = ::transformAabbToLocal( plane.bounds, ::getTransform( mesh ) );
|
||||
@ -84,7 +81,7 @@ namespace {
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
for ( auto triID : candidates ) {
|
||||
auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
|
||||
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space
|
||||
if ( !::trianglePlane( tri, plane, manifold, eps ) ) continue;
|
||||
hit = true;
|
||||
}
|
||||
@ -99,7 +96,6 @@ namespace {
|
||||
|
||||
const auto& bvh = *mesh.collider.mesh.bvh;
|
||||
const auto& meshData = *mesh.collider.mesh.mesh;
|
||||
const auto& meshViews = *mesh.collider.mesh.views;
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = ::transformAabbToLocal( capsule.bounds, ::getTransform( mesh ) );
|
||||
@ -110,7 +106,7 @@ namespace {
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
for ( auto triID : candidates ) {
|
||||
auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
|
||||
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space
|
||||
if ( !::triangleCapsule( tri, capsule, manifold, eps ) ) continue;
|
||||
hit = true;
|
||||
}
|
||||
@ -123,11 +119,9 @@ namespace {
|
||||
|
||||
const auto& bvhA = *a.collider.mesh.bvh;
|
||||
const auto& meshA = *a.collider.mesh.mesh;
|
||||
const auto& viewsA = *a.collider.mesh.views;
|
||||
|
||||
const auto& bvhB = *b.collider.mesh.bvh;
|
||||
const auto& meshB = *b.collider.mesh.mesh;
|
||||
const auto& viewsB = *b.collider.mesh.views;
|
||||
|
||||
// compute overlaps between one BVH and another BVH
|
||||
thread_local pod::BVH::pairs_t pairs;
|
||||
@ -137,8 +131,8 @@ namespace {
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
for (auto [ idA, idB] : pairs ) {
|
||||
auto tA = ::fetchTriangle( meshA, viewsA, idA, a ); // transform triangles to world space
|
||||
auto tB = ::fetchTriangle( meshB, viewsB, idB, b );
|
||||
auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space
|
||||
auto tB = ::fetchTriangle( meshB, idB, b );
|
||||
|
||||
if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue;
|
||||
hit = true;
|
||||
|
@ -195,7 +195,6 @@ namespace {
|
||||
bool rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQuery& rayHit ) {
|
||||
const auto& bvh = *body.collider.mesh.bvh;
|
||||
const auto& meshData = *body.collider.mesh.mesh;
|
||||
const auto& meshViews = *body.collider.mesh.views;
|
||||
|
||||
const auto transform = ::getTransform( body );
|
||||
|
||||
@ -208,7 +207,7 @@ namespace {
|
||||
::queryBVH( bvh, ray, candidates );
|
||||
|
||||
for ( auto triID : candidates ) {
|
||||
auto tri = ::fetchTriangle( meshData, meshViews, triID );
|
||||
auto tri = ::fetchTriangle( meshData, triID );
|
||||
|
||||
float t, u, v;
|
||||
if ( !::rayTriangleIntersect( ray, tri, t, u, v ) ) continue;
|
||||
|
@ -91,7 +91,8 @@ namespace {
|
||||
return tri;
|
||||
}
|
||||
|
||||
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, const uf::Mesh::views_t& views, size_t triID ) {
|
||||
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID ) {
|
||||
const auto& views = mesh.buffer_views;
|
||||
UF_ASSERT(!views.empty());
|
||||
|
||||
// find which view contains this triangle index.
|
||||
@ -118,8 +119,8 @@ 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, const uf::Mesh::views_t& views, size_t triID, const pod::PhysicsBody& body, bool fast = false ) {
|
||||
auto tri = ::fetchTriangle( mesh, views, triID );
|
||||
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID, const pod::PhysicsBody& body, bool fast = false ) {
|
||||
auto tri = ::fetchTriangle( mesh, triID );
|
||||
|
||||
auto transform = ::getTransform( body );
|
||||
|
||||
@ -230,10 +231,82 @@ namespace {
|
||||
|
||||
return { std::min({ p0, p1, p2 }), std::max({ p0, p1, p2 }) };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// triangle colliders
|
||||
namespace {
|
||||
// i feel like it'd just be better to treat an AABB as a 12-triangle mesh and do a triangleTriangle collision instead of 3 + 1 + 3 * 3 axis tests
|
||||
// but what do i know
|
||||
bool triangleAabbSAT( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = 1e-6f ) {
|
||||
const auto& aabb = body.bounds;
|
||||
|
||||
// box center and half extents
|
||||
pod::Vector3f boxCenter = ::aabbCenter( aabb );
|
||||
pod::Vector3f boxHalf = ::aabbExtent( aabb );
|
||||
|
||||
// move triangle into box-local space
|
||||
pod::Vector3f v0 = tri.points[0] - boxCenter;
|
||||
pod::Vector3f v1 = tri.points[1] - boxCenter;
|
||||
pod::Vector3f v2 = tri.points[2] - boxCenter;
|
||||
|
||||
// triangle edges
|
||||
pod::Vector3f e0 = v1 - v0;
|
||||
pod::Vector3f e1 = v2 - v1;
|
||||
pod::Vector3f e2 = v0 - v2;
|
||||
|
||||
float minOverlap = FLT_MAX;
|
||||
pod::Vector3f bestAxis;
|
||||
|
||||
auto testAxis = [&](const pod::Vector3f& axis) -> bool {
|
||||
if ( uf::vector::magnitude(axis) < eps ) return true; // skip degenerate
|
||||
|
||||
pod::Vector3f n = uf::vector::normalize(axis);
|
||||
|
||||
// project triangle
|
||||
float t0 = uf::vector::dot(v0, n);
|
||||
float t1 = uf::vector::dot(v1, n);
|
||||
float t2 = uf::vector::dot(v2, n);
|
||||
float triMin = std::min({t0, t1, t2});
|
||||
float triMax = std::max({t0, t1, t2});
|
||||
|
||||
// project box (radius along axis)
|
||||
float r = boxHalf.x * fabs(n.x) + boxHalf.y * fabs(n.y) + boxHalf.z * fabs(n.z); // to-do: use boxHalf + uf::vector::abs( n ) or something
|
||||
|
||||
// overlap test
|
||||
if ( triMin > r || triMax < -r ) return false; // separating axis
|
||||
|
||||
// compute overlap depth
|
||||
float overlap = std::min( triMin - r, triMax + r );
|
||||
if ( overlap < minOverlap ) {
|
||||
minOverlap = overlap;
|
||||
bestAxis = n;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if ( !testAxis({1,0,0}) ) return false;
|
||||
if ( !testAxis({0,1,0}) ) return false;
|
||||
if ( !testAxis({0,0,1}) ) return false;
|
||||
if ( !testAxis(uf::vector::cross(e0, e1)) ) return false;
|
||||
|
||||
pod::Vector3f boxAxes[3] = { {1,0,0}, {0,1,0}, {0,0,1} };
|
||||
pod::Vector3f edges[3] = { e0, e1, e2 };
|
||||
for ( auto& edge : edges ) {
|
||||
for ( auto& axis : boxAxes ) if ( !testAxis(uf::vector::cross(edge, axis)) ) return false;
|
||||
}
|
||||
|
||||
pod::Vector3f triNormal = uf::vector::normalize(uf::vector::cross(e0, e1));
|
||||
float d = uf::vector::dot(triNormal, v0);
|
||||
float dist = uf::vector::dot(triNormal, -boxCenter) - d;
|
||||
pod::Vector3f contact = boxCenter - triNormal * dist;
|
||||
|
||||
if ( uf::vector::dot(bestAxis, triNormal) < 0.0f ) bestAxis = -bestAxis;
|
||||
|
||||
manifold.points.emplace_back( pod::Contact{ contact, bestAxis, minOverlap } );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold, float eps ) {
|
||||
// if ( !::triangleTriangleIntersect( a, b ) ) return false;
|
||||
|
||||
@ -283,6 +356,9 @@ namespace {
|
||||
bool triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps ) {
|
||||
const auto& aabb = body;
|
||||
|
||||
#if 1
|
||||
return ::triangleAabbSAT( tri, body, manifold, eps );
|
||||
#else
|
||||
auto closest = ::closestPointOnTriangle( ::getPosition( aabb ), tri );
|
||||
auto closestAabb = ::closestPointOnAABB( closest, aabb.bounds );
|
||||
|
||||
@ -306,6 +382,7 @@ namespace {
|
||||
|
||||
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
bool triangleSphere( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps ) {
|
||||
const auto& sphere = body;
|
||||
|
@ -137,6 +137,7 @@ void uf::Mesh::updateDescriptor() {
|
||||
_updateDescriptor(index);
|
||||
_updateDescriptor(instance);
|
||||
_updateDescriptor(indirect);
|
||||
_updateViews();
|
||||
}
|
||||
void uf::Mesh::bind( const uf::Mesh& mesh ) { return bind( mesh, isInterleaved() ); }
|
||||
void uf::Mesh::bind( const uf::Mesh& mesh, bool interleaved ) {
|
||||
@ -490,6 +491,9 @@ void uf::Mesh::_updateDescriptor( uf::Mesh::Input& input ) {
|
||||
attribute.stride = isInterleaved(input.interleaved) ? input.size : attribute.descriptor.size;
|
||||
}
|
||||
}
|
||||
void uf::Mesh::_updateViews() {
|
||||
buffer_views = makeViews();
|
||||
}
|
||||
uf::Mesh::Attribute uf::Mesh::_remapAttribute( const uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute, size_t i ) const {
|
||||
UF_ASSERT( i < indirect.count );
|
||||
UF_ASSERT( &input == &vertex || &input == &index );
|
||||
|
Loading…
Reference in New Issue
Block a user