proper pre-computing of mesh views (with equally horrid code)

This commit is contained in:
ecker 2025-09-15 18:05:15 -05:00
parent 16062f8135
commit 58dee945f8
8 changed files with 46 additions and 43 deletions

View File

@ -6,7 +6,7 @@
"metadata": { "metadata": {
"holdable": true, "holdable": true,
"physics": { "physics": {
"mass": 5, "mass": 10000,
"type": "bounding box" "type": "bounding box"
// "type": "mesh" // "type": "mesh"
} }

View File

@ -11,12 +11,9 @@
#include <uf/utils/memory/unordered_set.h> #include <uf/utils/memory/unordered_set.h>
#include <uf/engine/object/object.h> #include <uf/engine/object/object.h>
#include <uf/utils/mesh/mesh.h>
#include <cfloat> #include <cfloat>
namespace uf {
class Mesh;
}
namespace pod { namespace pod {
enum class ShapeType { enum class ShapeType {
AABB, AABB,
@ -107,6 +104,7 @@ namespace pod {
struct MeshBVH { struct MeshBVH {
pod::BVH* bvh; pod::BVH* bvh;
const uf::Mesh* mesh; const uf::Mesh* mesh;
const uf::Mesh::views_t* views;
}; };
typedef uint32_t CollisionMask; typedef uint32_t CollisionMask;

View File

@ -163,19 +163,19 @@ namespace uf {
struct AttributeView { struct AttributeView {
Attribute attribute; Attribute attribute;
const void* data(size_t first = 0) const { const void* data(size_t first = 0) const {
if ( !valid() ) return NULL; if ( !valid() ) return NULL;
return static_cast<const uint8_t*>(attribute.pointer) + attribute.stride * first; return static_cast<const uint8_t*>(attribute.pointer) + attribute.stride * first;
} }
template<typename T> template<typename T>
const T* get(size_t first = 0) const { const T* get(size_t first = 0) const {
return reinterpret_cast<const T*>(data(first)); return reinterpret_cast<const T*>(data(first));
} }
bool valid() const { return attribute.pointer != NULL; } bool valid() const { return attribute.pointer != NULL; }
size_t stride() const { return attribute.stride; } size_t stride() const { return attribute.stride; }
size_t components() const { return attribute.descriptor.components; } size_t components() const { return attribute.descriptor.components; }
}; };
struct View { struct View {
@ -186,11 +186,12 @@ namespace uf {
uf::stl::unordered_map<uf::stl::string, uf::Mesh::AttributeView> attributes; uf::stl::unordered_map<uf::stl::string, uf::Mesh::AttributeView> attributes;
const AttributeView& operator[](const uf::stl::string& name) const { const AttributeView& operator[](const uf::stl::string& name) const {
static AttributeView null{}; static AttributeView null{};
if ( auto it = attributes.find(name); it != attributes.end() ) return it->second; if ( auto it = attributes.find(name); it != attributes.end() ) return it->second;
return null; return null;
} }
}; };
typedef uf::stl::vector<uf::Mesh::View> views_t;
uf::stl::vector<buffer_t> buffers; uf::stl::vector<buffer_t> buffers;

View File

@ -212,7 +212,7 @@ namespace {
bvh.dirty = false; bvh.dirty = false;
} }
void buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t capacity = 4 ) { void buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, const uf::Mesh::views_t& views, pod::BVH::index_t capacity = 4 ) {
uint32_t triangles = mesh.index.count / 3; uint32_t triangles = mesh.index.count / 3;
bvh.indices.clear(); bvh.indices.clear();
@ -223,7 +223,6 @@ namespace {
uf::stl::vector<pod::AABB> bounds; uf::stl::vector<pod::AABB> bounds;
bounds.reserve( triangles ); bounds.reserve( triangles );
auto views = mesh.makeViews({"position"});
UF_ASSERT( !views.empty() ); UF_ASSERT( !views.empty() );
// populate initial indices and bounds // populate initial indices and bounds
@ -376,13 +375,12 @@ namespace {
} }
} }
void refitBVH( pod::BVH& bvh, const uf::Mesh& mesh ) { void refitBVH( pod::BVH& bvh, const uf::Mesh& mesh, const uf::Mesh::views_t& views ) {
uint32_t triangles = mesh.index.count / 3; uint32_t triangles = mesh.index.count / 3;
uf::stl::vector<pod::AABB> bounds; uf::stl::vector<pod::AABB> bounds;
bounds.reserve( triangles ); bounds.reserve( triangles );
auto views = mesh.makeViews({"position"});
UF_ASSERT( !views.empty() ); UF_ASSERT( !views.empty() );
// populate initial indices and bounds // populate initial indices and bounds

View File

@ -9,8 +9,8 @@ namespace {
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold) bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
bool psgContactSolver = true; // use PSG contact solver 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 = false; // 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 bool fixedStep = false; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
uint32_t substeps = 0; // 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 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 // increasing these make things lag for reasons I can imagine why
@ -22,7 +22,7 @@ namespace {
bool flattenBvhMeshes = true; bool flattenBvhMeshes = true;
// use surface area heuristics for building the BVH, rather than naive splits // use surface area heuristics for building the BVH, rather than naive splits
bool useBvhSahBodies = true; // it actually seems slower to use these...... bool useBvhSahBodies = false; // it actually seems slower to use these......
bool useBvhSahMeshes = true; bool useBvhSahMeshes = true;
bool useSplitBvhs = true; // creates separate BVHs for static / dynamic objects bool useSplitBvhs = true; // creates separate BVHs for static / dynamic objects
@ -396,10 +396,11 @@ pod::PhysicsBody& uf::physics::impl::create( pod::World& world, uf::Object& obje
auto& body = uf::physics::impl::create( world, object, mass, offset ); auto& body = uf::physics::impl::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::MESH; body.collider.type = pod::ShapeType::MESH;
body.collider.mesh.mesh = &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; body.collider.mesh.bvh = new pod::BVH;
auto& bvh = *body.collider.mesh.bvh; auto& bvh = *body.collider.mesh.bvh;
::buildMeshBVH( bvh, mesh, ::meshBvhCapacity ); ::buildMeshBVH( bvh, mesh, views, ::meshBvhCapacity );
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::updateInertia( body ); uf::physics::impl::updateInertia( body );
@ -458,6 +459,7 @@ void uf::physics::impl::destroy( pod::PhysicsBody& body ) {
// remove any pointered collider data // remove any pointered collider data
if ( body.collider.type == pod::ShapeType::MESH ) { if ( body.collider.type == pod::ShapeType::MESH ) {
if ( body.collider.mesh.bvh ) delete body.collider.mesh.bvh; if ( body.collider.mesh.bvh ) delete body.collider.mesh.bvh;
if ( body.collider.mesh.views ) delete body.collider.mesh.views;
} }
} }

View File

@ -21,6 +21,7 @@ namespace {
const auto& bvh = *mesh.collider.mesh.bvh; const auto& bvh = *mesh.collider.mesh.bvh;
const auto& meshData = *mesh.collider.mesh.mesh; const auto& meshData = *mesh.collider.mesh.mesh;
const auto& meshViews = *mesh.collider.mesh.views;
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( aabb.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( aabb.bounds, ::getTransform( mesh ) );
@ -31,7 +32,7 @@ namespace {
bool hit = false; bool hit = false;
// do collision per triangle // do collision per triangle
for ( auto triID : candidates ) { for ( auto triID : candidates ) {
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
if ( !::triangleAabb( tri, aabb, manifold, eps ) ) continue; if ( !::triangleAabb( tri, aabb, manifold, eps ) ) continue;
hit = true; hit = true;
} }
@ -45,6 +46,7 @@ namespace {
const auto& bvh = *mesh.collider.mesh.bvh; const auto& bvh = *mesh.collider.mesh.bvh;
const auto& meshData = *mesh.collider.mesh.mesh; const auto& meshData = *mesh.collider.mesh.mesh;
const auto& meshViews = *mesh.collider.mesh.views;
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( sphere.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( sphere.bounds, ::getTransform( mesh ) );
@ -55,7 +57,7 @@ namespace {
bool hit = false; bool hit = false;
// do collision per triangle // do collision per triangle
for ( auto triID : candidates ) { for ( auto triID : candidates ) {
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
if ( !::triangleSphere( tri, sphere, manifold, eps ) ) continue; if ( !::triangleSphere( tri, sphere, manifold, eps ) ) continue;
hit = true; hit = true;
} }
@ -71,6 +73,7 @@ namespace {
const auto& bvh = *mesh.collider.mesh.bvh; const auto& bvh = *mesh.collider.mesh.bvh;
const auto& meshData = *mesh.collider.mesh.mesh; const auto& meshData = *mesh.collider.mesh.mesh;
const auto& meshViews = *mesh.collider.mesh.views;
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( plane.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( plane.bounds, ::getTransform( mesh ) );
@ -81,7 +84,7 @@ namespace {
bool hit = false; bool hit = false;
// do collision per triangle // do collision per triangle
for ( auto triID : candidates ) { for ( auto triID : candidates ) {
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
if ( !::trianglePlane( tri, plane, manifold, eps ) ) continue; if ( !::trianglePlane( tri, plane, manifold, eps ) ) continue;
hit = true; hit = true;
} }
@ -96,6 +99,7 @@ namespace {
const auto& bvh = *mesh.collider.mesh.bvh; const auto& bvh = *mesh.collider.mesh.bvh;
const auto& meshData = *mesh.collider.mesh.mesh; const auto& meshData = *mesh.collider.mesh.mesh;
const auto& meshViews = *mesh.collider.mesh.views;
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( capsule.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( capsule.bounds, ::getTransform( mesh ) );
@ -106,7 +110,7 @@ namespace {
bool hit = false; bool hit = false;
// do collision per triangle // do collision per triangle
for ( auto triID : candidates ) { for ( auto triID : candidates ) {
auto tri = ::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space auto tri = ::fetchTriangle( meshData, meshViews, triID, mesh ); // transform triangle to world space
if ( !::triangleCapsule( tri, capsule, manifold, eps ) ) continue; if ( !::triangleCapsule( tri, capsule, manifold, eps ) ) continue;
hit = true; hit = true;
} }
@ -119,9 +123,11 @@ namespace {
const auto& bvhA = *a.collider.mesh.bvh; const auto& bvhA = *a.collider.mesh.bvh;
const auto& meshA = *a.collider.mesh.mesh; const auto& meshA = *a.collider.mesh.mesh;
const auto& viewsA = *a.collider.mesh.views;
const auto& meshB = *b.collider.mesh.mesh;
const auto& bvhB = *b.collider.mesh.bvh; 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 // compute overlaps between one BVH and another BVH
thread_local pod::BVH::pairs_t pairs; thread_local pod::BVH::pairs_t pairs;
@ -131,8 +137,8 @@ namespace {
bool hit = false; bool hit = false;
// do collision per triangle // do collision per triangle
for (auto [ idA, idB] : pairs ) { for (auto [ idA, idB] : pairs ) {
auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space auto tA = ::fetchTriangle( meshA, viewsA, idA, a ); // transform triangles to world space
auto tB = ::fetchTriangle( meshB, idB, b ); auto tB = ::fetchTriangle( meshB, viewsB, idB, b );
if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue; if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue;
hit = true; hit = true;

View File

@ -193,8 +193,9 @@ namespace {
} }
bool rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQuery& rayHit ) { bool rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQuery& rayHit ) {
const auto& meshData = *body.collider.mesh.mesh;
const auto& bvh = *body.collider.mesh.bvh; 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 ); const auto transform = ::getTransform( body );
@ -207,7 +208,7 @@ namespace {
::queryBVH( bvh, ray, candidates ); ::queryBVH( bvh, ray, candidates );
for ( auto triID : candidates ) { for ( auto triID : candidates ) {
auto tri = ::fetchTriangle( meshData, triID ); auto tri = ::fetchTriangle( meshData, meshViews, triID );
float t, u, v; float t, u, v;
if ( !::rayTriangleIntersect( ray, tri, t, u, v ) ) continue; if ( !::rayTriangleIntersect( ray, tri, t, u, v ) ) continue;

View File

@ -91,10 +91,7 @@ namespace {
return tri; return tri;
} }
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID ) { pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, const uf::Mesh::views_t& views, size_t triID ) {
static thread_local uf::stl::unordered_map<const uf::Mesh*, uf::stl::vector<uf::Mesh::View>> cachedViews;
if ( cachedViews.count(&mesh) == 0 ) cachedViews[&mesh] = mesh.makeViews({"position"});
auto& views = cachedViews[&mesh];
UF_ASSERT(!views.empty()); UF_ASSERT(!views.empty());
// find which view contains this triangle index. // find which view contains this triangle index.
@ -121,8 +118,8 @@ namespace {
} }
// if body is a mesh, apply its transform to the triangles, else reorient the normal with respect to the body // 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, const uf::Mesh::views_t& views, size_t triID, const pod::PhysicsBody& body, bool fast = false ) {
auto tri = ::fetchTriangle( mesh, triID ); auto tri = ::fetchTriangle( mesh, views, triID );
auto transform = ::getTransform( body ); auto transform = ::getTransform( body );