From 992556b05443df3a633f43663ee6c3645d1270c9 Mon Sep 17 00:00:00 2001 From: ecker Date: Sat, 8 Aug 2026 20:54:32 -0500 Subject: [PATCH] more fixes (to-do: fix BVH serialization/deserialization since I'm very sure it's wrong, but everything else should be plumbed right) --- .../uf/utils/math/physics/broadphase/bvh.h | 1 + engine/inc/uf/utils/math/physics/impl.h | 1 + engine/inc/uf/utils/mesh/mesh.h | 2 +- engine/src/engine/graph/decode.cpp | 64 +++++------ engine/src/engine/graph/encode.cpp | 43 +++++--- engine/src/engine/graph/graph.cpp | 97 +++++++++-------- .../src/utils/math/physics/broadphase/bvh.cpp | 100 ++++++++++++++---- engine/src/utils/math/physics/common.cpp | 7 +- engine/src/utils/math/physics/impl.cpp | 10 ++ .../utils/math/physics/narrowphase/aabb.cpp | 4 +- .../math/physics/narrowphase/capsule.cpp | 3 +- .../utils/math/physics/narrowphase/mesh.cpp | 22 +++- .../utils/math/physics/narrowphase/obb.cpp | 4 +- .../utils/math/physics/narrowphase/plane.cpp | 3 +- .../utils/math/physics/narrowphase/sphere.cpp | 3 +- .../math/physics/narrowphase/triangle.cpp | 3 +- 16 files changed, 228 insertions(+), 139 deletions(-) diff --git a/engine/inc/uf/utils/math/physics/broadphase/bvh.h b/engine/inc/uf/utils/math/physics/broadphase/bvh.h index 862d79ad..ef544347 100644 --- a/engine/inc/uf/utils/math/physics/broadphase/bvh.h +++ b/engine/inc/uf/utils/math/physics/broadphase/bvh.h @@ -48,5 +48,6 @@ namespace uf { size_t UF_API serialize( const pod::BVH& bvh, uf::stl::vector& outBuffer, uint32_t offset = 0 ); bool UF_API deserialize( pod::BVH& bvh, const uf::stl::vector& buffer, uint32_t offset = 0, uint32_t length = 0 ); void UF_API flagAsActive( pod::BVH& bvh, uint32_t index, bool active ); + void UF_API flagAsActive( pod::BVH& bvh, uint32_t index, uint32_t count, bool active ); } } \ No newline at end of file diff --git a/engine/inc/uf/utils/math/physics/impl.h b/engine/inc/uf/utils/math/physics/impl.h index 5d04b7ba..a44bd5d8 100644 --- a/engine/inc/uf/utils/math/physics/impl.h +++ b/engine/inc/uf/utils/math/physics/impl.h @@ -36,6 +36,7 @@ namespace uf { void UF_API setGravity( pod::PhysicsBody&, const pod::Vector3f& = { NAN, NAN, NAN } ); pod::Vector3f UF_API getGravity( pod::PhysicsBody& ); + void UF_API update( pod::PhysicsBody& body ); void UF_API updateInertia( pod::PhysicsBody& body ); void UF_API applyForce( pod::PhysicsBody& body, const pod::Vector3f& force ); diff --git a/engine/inc/uf/utils/mesh/mesh.h b/engine/inc/uf/utils/mesh/mesh.h index 28b81e72..e2896b07 100644 --- a/engine/inc/uf/utils/mesh/mesh.h +++ b/engine/inc/uf/utils/mesh/mesh.h @@ -200,7 +200,7 @@ namespace pod { uf::stl::vector indices; uf::stl::vector nodes; uf::stl::vector flattened; - uf::stl::vector primitiveToNode; + uf::stl::vector indicesToNodes; uf::stl::vector bounds; uf::stl::vector flatBounds; diff --git a/engine/src/engine/graph/decode.cpp b/engine/src/engine/graph/decode.cpp index 70b4bd85..d72e3f07 100644 --- a/engine/src/engine/graph/decode.cpp +++ b/engine/src/engine/graph/decode.cpp @@ -39,7 +39,7 @@ namespace { return 0; } - uf::Image decodeImage( ext::json::Value& json, pod::Graph& graph, const uf::stl::string& imageName, uf::stl::vector& pendingImages ) { + uf::Image decodeImage( ext::json::Value& json, pod::Graph& graph, const uf::stl::string& key ) { uf::Image image; uf::stl::string filename = ""; @@ -89,23 +89,17 @@ namespace { if ( graph.settings.stream.textures ) { auto& storage = uf::graph::getStorage(graph); - graph.streams.images[imageName] = { fullPath, offset, length }; + graph.streams.images[key] = { fullPath, offset, length }; } else { - pendingImages.push_back({ imageName, {}, extension, layers }); - auto& pending = pendingImages.back(); - size_t readLen = length > 0 ? length : uf::io::size( fullPath ); if ( readLen > 0 ) { - pending.buffer.resize(readLen); - uf::asset::read( fullPath, offset, readLen, pending.buffer.data()/*, [&graph, &pending]() { + uf::asset::read( fullPath, offset, readLen, [&graph, key, extension, layers]( uf::stl::vector&& buffer ) { auto& storage = uf::graph::getStorage(graph); - auto& image = storage.images[pending.name].data; + auto& image = storage.images[key].data; - uf::image::open( image, pending.buffer, pending.extension, false ); - uf::image::layers( image, pending.layers ); - - pending.buffer.clear(); - }*/ ); + uf::image::open( image, buffer, extension, false ); + uf::image::layers( image, layers ); + } ); } } @@ -279,10 +273,10 @@ namespace { return mesh; } - pod::BVH decodeBvh( ext::json::Value& json, pod::Graph& graph, const uf::stl::string& bvhName ) { + pod::BVH decodeBvh( ext::json::Value& json, pod::Graph& graph, const uf::stl::string& key ) { pod::BVH bvh; auto& storage = uf::graph::getStorage(graph); - auto& bvhStream = graph.streams.bvhs[bvhName]; + auto& bvhStream = graph.streams.bvhs[key]; uf::stl::string filename = json["filename"].as(); size_t offset = json["offset"].as(); @@ -293,9 +287,14 @@ namespace { bool deferred = graph.settings.stream.enabled; if ( !deferred ) { - uf::stl::vector tempBuffer(length); - uf::asset::read( fullPath, offset, length, tempBuffer.data() ); - uf::bvh::deserialize( bvh, tempBuffer, 0, length ); + size_t readLen = length > 0 ? length : uf::io::size( fullPath ); + if ( readLen > 0 ) { + uf::asset::read( fullPath, offset, readLen, [&graph, key]( uf::stl::vector&& buffer ) { + auto& storage = uf::graph::getStorage(graph); + auto& bvh = storage.bvhs[key]; + uf::bvh::deserialize( bvh, buffer ); + } ); + } } return bvh; @@ -534,14 +533,12 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const UF_DEBUG_TIMER_MULTITRACE("Reading images..."); graph.images.reserve( serializer["images"].size() ); - pendingImages.reserve( serializer["images"].size() ); - ext::json::forEach( serializer["images"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); //UF_DEBUG_TIMER_MULTITRACE("Reading image={}", name); storage.images[name] = { - .data = decodeImage( value, graph, name, pendingImages ), + .data = decodeImage( value, graph, name ), }; graph.images.emplace_back(name); }); @@ -568,11 +565,6 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const storage.meshes[name] = decodeMesh( json, graph, name ); graph.meshes.emplace_back(name); - // should probably be under serializer["bvhs"] - if ( value["bvh"].isObject() ) { - storage.bvhs[name] = decodeBvh( value["bvh"], graph, name ); - } - if ( preferMinified && !hasMinifiedAsset && !graph.settings.stream.enabled ) meshesToMinify.emplace_back( name ); }); @@ -580,6 +572,16 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const UF_DEBUG_TIMER_MULTITRACE("Read meshes"); }); + tasks.queue([&]{ + UF_DEBUG_TIMER_MULTITRACE("Reading BVHs..."); + ext::json::forEach( serializer["bvhs"], [&]( ext::json::Value& value ){ + auto name = key + value["name"].as(); + // storage.bvhs[name] = decodeBvh( value, graph, name ); + }); + + UF_DEBUG_TIMER_MULTITRACE("Read BVHs"); + }); + tasks.queue([&]{ UF_DEBUG_TIMER_MULTITRACE("Reading animation information..."); auto& animNode = serializer["animations"]; @@ -639,16 +641,6 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const UF_DEBUG_TIMER_MULTITRACE("Processing IO"); uf::asset::processIO(); - // process images - UF_DEBUG_TIMER_MULTITRACE("Processing pending images"); - for ( auto& pending : pendingImages ) { - auto& image = storage.images[pending.name].data; - - uf::image::open( image, pending.buffer, pending.extension, false ); - uf::image::layers( image, pending.layers ); - - pending.buffer.clear(); - } // process meshes that need to be minified because I can't easily tie it to the callback UF_DEBUG_TIMER_MULTITRACE("Processing meshes for minification"); diff --git a/engine/src/engine/graph/encode.cpp b/engine/src/engine/graph/encode.cpp index 0d8e2b43..4be4f7c7 100644 --- a/engine/src/engine/graph/encode.cpp +++ b/engine/src/engine/graph/encode.cpp @@ -352,20 +352,6 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string& json["min"] = encode( minMesh, settings, graph, minBuffer, minBinName ); } - // to-do: should probably be in its own file in the event the broadphase physics BVH gets saved too - // it seems that it does cause issues when writing to the mesh's buffer - if ( false ) { - pod::BVH bvh; - uf::bvh::build( bvh, mesh ); - - auto offset = meshesBuffer.size(); - auto length = uf::bvh::serialize( bvh, meshesBuffer ); - - json["bvh"]["filename"] = binName; - json["bvh"]["offset"] = offset; - json["bvh"]["length"] = length; - } - serializer["meshes"].emplace_back(json); } @@ -377,6 +363,35 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string& } }); + tasks.queue([&]{ + ext::json::reserve( serializer["bvhs"], graph.meshes.size() ); + + uf::stl::vector buffer; + uf::stl::string binName = "bvhs." + (settings.compression == "none" ? "bin" : settings.compression); + + auto offset = 0; + for ( auto& name : graph.meshes ) { + auto& mesh = storage.meshes.map.at(name); + pod::BVH bvh; + uf::bvh::build( bvh, mesh ); + + auto length = uf::bvh::serialize( bvh, buffer ); + + uf::Serializer json; + json["name"] = name; + json["filename"] = binName; + json["offset"] = offset; + json["length"] = length; + serializer["bvhs"].emplace_back(json); + + offset += length; + } + + if ( !buffer.empty() ) { + uf::io::write(directory + "/" + binName, buffer); + } + }); + tasks.queue([&]{ ext::json::reserve( serializer["animations"], graph.animations.size() ); diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index f80871b3..8932bb0a 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -2228,10 +2228,10 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { // update bounds if ( exists ) { - // body.bounds = impl::computeAABB( body ); - bvh = {}; + auto& body = entity.getComponent(); + uf::physics::update( body ); } - { + if ( !exists ) { // only need to initialize once, as the pointers will remain the same float mass = phyziks["mass"].as(0.0f); auto center = uf::vector::decode( phyziks["center"], pod::Vector3f{} ); @@ -2306,6 +2306,9 @@ void uf::graph::reload( pod::Graph& graph ) { bool needsGraphicBinding = !entity.hasComponent(); auto& work = pendingMeshes[node.mesh]; work.dependentNodes.push_back(node.index); + if ( work.queuedLODs.empty() && primitives.size() > 0 ) { + work.queuedLODs.resize( primitives.size(), -1 ); + } auto& bvh = storage.bvhs.map[meshName]; auto& bvhStream = graph.streams.bvhs[meshName]; @@ -2313,10 +2316,6 @@ void uf::graph::reload( pod::Graph& graph ) { work.needsBvhLoad = true; } - if ( work.queuedLODs.empty() && primitives.size() > 0 ) { - work.queuedLODs.resize( primitives.size(), -1 ); - } - if ( radius > 0 && mesh.indirect.count && mesh.indirect.count <= primitives.size() ) { float closestDistance = std::numeric_limits::max(); size_t closestDrawID = 0; @@ -2562,22 +2561,18 @@ void uf::graph::reload( pod::Graph& graph ) { uniqueIndexBuffers[attr.buffer] = attr.stride > 0 ? attr.stride : attr.descriptor.size; } - int actualReadsToPerform = 0; - - if ( work.needsBvhLoad ) { - actualReadsToPerform += 1; - } + int totalReads = work.needsBvhLoad ? 1 : 0; for ( auto& active : activeDraws ) { auto& lod = primitives[active.drawID].lod.levels[active.lodLevel]; if ( lod.indices > 0 ) { - actualReadsToPerform += uniqueIndexBuffers.size(); + totalReads += uniqueIndexBuffers.size(); for ( auto& [bufID, stride] : uniqueIndexBuffers ) { ctx->bufferSizes[bufID] += lod.indices * stride; } } if ( lod.vertices > 0 ) { - actualReadsToPerform += uniqueVertexBuffers.size(); + totalReads += uniqueVertexBuffers.size(); for ( auto& [bufID, stride] : uniqueVertexBuffers ) { ctx->bufferSizes[bufID] += lod.vertices * stride; } @@ -2590,46 +2585,60 @@ void uf::graph::reload( pod::Graph& graph ) { } } - ctx->pendingReads.store(actualReadsToPerform); + ctx->pendingReads.store(totalReads); auto finalizeStream = [ctx, graphPtr = &graph]() { - auto& currentGraph = *graphPtr; - auto& storage = uf::graph::getStorage(currentGraph); - auto& meshKey = currentGraph.meshes[ctx->meshID]; - auto& targetMesh = storage.meshes.map[meshKey]; - auto& targetPrimitives = storage.primitives.map[currentGraph.primitives[ctx->meshID]]; - auto& targetBvh = storage.bvhs.map[meshKey]; + auto& graph = *graphPtr; + auto& storage = uf::graph::getStorage(graph); + auto& meshKey = graph.meshes[ctx->meshID]; + auto& mesh = storage.meshes.map[meshKey]; + auto& primitives = storage.primitives.map[graph.primitives[ctx->meshID]]; + auto& bvh = storage.bvhs.map[meshKey]; + auto& bvhStream = graph.streams.bvhs[meshKey]; for ( auto& [b, buf] : ctx->completedBuffers ) { - std::swap(targetMesh.buffers[b], buf); + std::swap(mesh.buffers[b], buf); } - targetMesh.vertex.count = ctx->vertexCount; - targetMesh.index.count = ctx->indexCount; + mesh.vertex.count = ctx->vertexCount; + mesh.index.count = ctx->indexCount; + bool rebuildBvh = true;/* if ( !ctx->bvhRawBuffer.empty() ) { - uf::bvh::deserialize( targetBvh, ctx->bvhRawBuffer, 0, ctx->bvhRawBuffer.size() ); + if ( !uf::bvh::deserialize( bvh, ctx->bvhRawBuffer ) ) { + bvhStream.buffer.length = 0; + } ctx->bvhRawBuffer.clear(); ctx->bvhRawBuffer.shrink_to_fit(); } - bool bvhValid = !targetBvh.flattened.empty() || !targetBvh.nodes.empty(); + if ( bvhStream.buffer.length == 0 ) { + rebuildBvh = true; + }*/ + bool bvhValid = !bvh.flattened.empty() || !bvh.nodes.empty(); - auto& indirectAttr = targetMesh.indirect.attributes.front(); - pod::DrawCommand* targetDrawCommands = (pod::DrawCommand*) targetMesh.buffers[indirectAttr.buffer].data(); + auto& indirectAttr = mesh.indirect.attributes.front(); + pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.buffers[indirectAttr.buffer].data(); for ( auto& [drawID, cmd] : ctx->updatedCommands ) { - targetPrimitives[drawID].drawCommand = cmd; - targetDrawCommands[drawID] = cmd; + primitives[drawID].drawCommand = cmd; + drawCommands[drawID] = cmd; - if ( bvhValid ) { - uf::bvh::flagAsActive( targetBvh, drawID, (cmd.vertices > 0 && cmd.indices > 0) ); + if ( bvhValid && !rebuildBvh ) { + auto& lod = primitives[drawID].lod.levels[0]; + + uint32_t startTri = lod.indexID / 3; + uint32_t triCount = lod.indices / 3; + uf::bvh::flagAsActive( bvh, startTri, triCount, (cmd.vertices > 0 && cmd.indices > 0) ); } } - targetMesh.updateDescriptor(); + mesh.updateDescriptor(); + if ( rebuildBvh ) { + uf::bvh::build( bvh, mesh ); + } for ( auto nodeID : ctx->deps ) { - uf::graph::reload( currentGraph, currentGraph.nodes[nodeID] ); + uf::graph::reload( graph, graph.nodes[nodeID] ); } #if UF_USE_OPENGL @@ -2643,7 +2652,7 @@ void uf::graph::reload( pod::Graph& graph ) { } }; - if ( actualReadsToPerform > 0 ) { + if ( totalReads > 0 ) { uf::stl::unordered_map bufferWriteOffsets; if ( work.needsBvhLoad ) { @@ -2718,25 +2727,25 @@ void uf::graph::reload( pod::Graph& graph ) { ctx->pendingReads.store(totalReads); auto finalizeFullLoad = [ctx, graphPtr = &graph]() { - auto& currentGraph = *graphPtr; - auto& storage = uf::graph::getStorage(currentGraph); - auto& meshKey = currentGraph.meshes[ctx->meshID]; - auto& targetMesh = storage.meshes.map[meshKey]; - auto& targetBvh = storage.bvhs.map[meshKey]; + auto& graph = *graphPtr; + auto& storage = uf::graph::getStorage(graph); + auto& meshKey = graph.meshes[ctx->meshID]; + auto& mesh = storage.meshes.map[meshKey]; + auto& bvh = storage.bvhs.map[meshKey]; for ( auto& [b, buf] : ctx->newBuffers ) { - std::swap(targetMesh.buffers[b], buf); + std::swap(mesh.buffers[b], buf); } - targetMesh.updateDescriptor(); + mesh.updateDescriptor(); if ( !ctx->bvhRawBuffer.empty() ) { - uf::bvh::deserialize( targetBvh, ctx->bvhRawBuffer, 0, ctx->bvhRawBuffer.size() ); + uf::bvh::deserialize( bvh, ctx->bvhRawBuffer ); ctx->bvhRawBuffer.clear(); ctx->bvhRawBuffer.shrink_to_fit(); } for ( auto nodeID : ctx->deps ) { - uf::graph::reload( currentGraph, currentGraph.nodes[nodeID] ); + uf::graph::reload( graph, graph.nodes[nodeID] ); } }; diff --git a/engine/src/utils/math/physics/broadphase/bvh.cpp b/engine/src/utils/math/physics/broadphase/bvh.cpp index 7e941488..1fe170a9 100644 --- a/engine/src/utils/math/physics/broadphase/bvh.cpp +++ b/engine/src/utils/math/physics/broadphase/bvh.cpp @@ -184,7 +184,10 @@ void impl::buildBroadphaseBVH( pod::BVH& bvh, const uf::stl::vector bounds(bodies.size(), { {FLT_MAX, FLT_MAX, FLT_MAX}, {-FLT_MAX, -FLT_MAX, -FLT_MAX} }); @@ -203,7 +206,14 @@ void impl::buildBroadphaseBVH( pod::BVH& bvh, const uf::stl::vector bounds; @@ -249,7 +263,14 @@ void impl::buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t if ( uf::physics::settings.useBvhSahMeshes ) impl::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity ); else impl::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity ); // flatten if requested - if ( uf::physics::settings.flattenBvhMeshes ) impl::flattenBVH( bvh, 0 ); + if ( uf::physics::settings.flattenBvhMeshes ) { + impl::flattenBVH( bvh, 0 ); + // unstable at times + //bvh.nodes.clear(); + //bvh.bounds.clear(); + //bvh.nodes.shrink_to_fit(); + //bvh.bounds.shrink_to_fit(); + } // mark as clean bvh.dirty = false; @@ -263,7 +284,11 @@ void impl::buildConvexHullBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::in bvh.indices.clear(); bvh.nodes.clear(); + bvh.bounds.clear(); + bvh.indicesToNodes.clear(); + bvh.bounds.reserve( hullCount ); bvh.indices.reserve( hullCount ); + bvh.indicesToNodes.resize( hullCount, 0 ); // stores bounds uf::stl::vector bounds; @@ -285,7 +310,13 @@ void impl::buildConvexHullBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::in else impl::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity ); // flatten if requested - if ( uf::physics::settings.flattenBvhMeshes ) impl::flattenBVH( bvh, 0 ); + if ( uf::physics::settings.flattenBvhMeshes ) { + impl::flattenBVH( bvh, 0 ); + //bvh.nodes.clear(); + //bvh.bounds.clear(); + //bvh.nodes.shrink_to_fit(); + //bvh.bounds.shrink_to_fit(); + } // mark as clean bvh.dirty = false; @@ -464,11 +495,9 @@ pod::BVH::index_t impl::flattenBVH( pod::BVH& bvh, pod::BVH::index_t nodeID ) { if ( nodeID == 0 ) { bvh.flattened.clear(); bvh.flatBounds.clear(); - bvh.primitiveToNode.clear(); bvh.flattened.reserve(bvh.nodes.size()); bvh.flatBounds.reserve(bvh.bounds.size()); - bvh.primitiveToNode.resize(bvh.indices.size()); } const auto& node = bvh.nodes[nodeID]; @@ -491,7 +520,7 @@ pod::BVH::index_t impl::flattenBVH( pod::BVH& bvh, pod::BVH::index_t nodeID ) { bvh.flattened[flatID] = flat; for ( uint32_t i = 0; i < node.getCount(); ++i ) { - bvh.primitiveToNode[bvh.indices[node.start + i]] = flatID; + bvh.indicesToNodes[bvh.indices[node.start + i]] = flatID; } return flatID + 1; } @@ -997,8 +1026,8 @@ void impl::postprocessPairs( pod::BVH::pairs_t& pairs ) { } void uf::bvh::flagAsActive( pod::BVH& bvh, uint32_t index, bool active ) { - if ( index < bvh.primitiveToNode.size() ) { - uint32_t flatNodeID = bvh.primitiveToNode[index]; + if ( index < bvh.indicesToNodes.size() ) { + uint32_t flatNodeID = bvh.indicesToNodes[index]; if ( flatNodeID < bvh.flattened.size() ) { bvh.flattened[flatNodeID].setUnloaded(!active); } @@ -1019,16 +1048,42 @@ void uf::bvh::flagAsActive( pod::BVH& bvh, uint32_t index, bool active ) { } } +void uf::bvh::flagAsActive( pod::BVH& bvh, uint32_t index, uint32_t count, bool active ) { + if ( !bvh.indicesToNodes.empty() ) { + for ( uint32_t i = 0; i < count; ++i ) { + if ( index + i < bvh.indicesToNodes.size() ) { + uint32_t flatNodeID = bvh.indicesToNodes[index + i]; + if ( flatNodeID < bvh.flattened.size() ) { + bvh.flattened[flatNodeID].setUnloaded(!active); + } + } + } + } else if ( !bvh.nodes.empty() ) { + for ( auto& node : bvh.nodes ) { + if ( node.getCount() > 0 ) { + for ( uint32_t i = 0; i < node.getCount(); ++i ) { + uint32_t idx = bvh.indices[node.start + i]; + if ( idx >= index && idx < index + count ) { + node.setUnloaded(!active); + } + } + } + } + } +} + size_t uf::bvh::serialize( const pod::BVH& bvh, uf::stl::vector& outBuffer, uint32_t offset ) { uf::stl::writer writer( outBuffer, offset, true ); writer.write( (uint32_t)( bvh.indices.size() ) ); + writer.write( (uint32_t)( bvh.indicesToNodes.size() ) ); writer.write( (uint32_t)( bvh.nodes.size() ) ); writer.write( (uint32_t)( bvh.flattened.size() ) ); if ( !bvh.indices.empty() ) writer.write( bvh.indices ); + if ( !bvh.indicesToNodes.empty() ) writer.write( bvh.indicesToNodes ); if ( !bvh.nodes.empty() ) { writer.write( bvh.nodes ); writer.write( bvh.bounds); } - if ( !bvh.flattened.empty() ) { writer.write( bvh.flattened ); writer.write( bvh.flatBounds ); writer.write( bvh.primitiveToNode ); } + if ( !bvh.flattened.empty() ) { writer.write( bvh.flattened ); writer.write( bvh.flatBounds ); } return writer.offset() - offset; } @@ -1037,33 +1092,34 @@ bool uf::bvh::deserialize( pod::BVH& bvh, const uf::stl::vector& buffer uf::stl::reader reader( buffer, offset, length > 0 ? length : buffer.size(), true, true ); const uint32_t* pNumIndices = reader.read(); + const uint32_t* pNumMap = reader.read(); const uint32_t* pNumNodes = reader.read(); const uint32_t* pNumFlat = reader.read(); - if ( !pNumIndices || !pNumNodes || !pNumFlat ) return false; + if ( !pNumIndices || !pNumNodes || !pNumMap || !pNumFlat ) return false; uint32_t numIndices = *pNumIndices; + uint32_t numMap = *pNumMap; uint32_t numNodes = *pNumNodes; uint32_t numFlat = *pNumFlat; +// UF_MSG_DEBUG("Indices={}, Map={}, Nodes={}, Flat={}", numIndices, numMap, numNodes, numFlat); + if ( numIndices > 0 ) { if ( !reader.read( numIndices, bvh.indices ) ) return false; } else { bvh.indices.clear(); } + + if ( numMap ) { + if ( !reader.read( numMap, bvh.indicesToNodes ) ) return false; + } else { + bvh.indicesToNodes.clear(); + } if ( numNodes > 0 ) { - if ( numFlat > 0 ) { - reader.skip( numNodes * sizeof(pod::BVH::Node) ); - reader.skip( numNodes * sizeof(pod::AABB) ); - bvh.nodes.clear(); - bvh.nodes.shrink_to_fit(); - bvh.bounds.clear(); - bvh.bounds.shrink_to_fit(); - } else { - if ( !reader.read( numNodes, bvh.nodes ) ) return false; - if ( !reader.read( numNodes, bvh.bounds ) ) return false; - } + if ( !reader.read( numNodes, bvh.nodes ) ) return false; + if ( !reader.read( numNodes, bvh.bounds ) ) return false; } else { bvh.nodes.clear(); bvh.bounds.clear(); @@ -1072,11 +1128,9 @@ bool uf::bvh::deserialize( pod::BVH& bvh, const uf::stl::vector& buffer if ( numFlat > 0 ) { if ( !reader.read( numFlat, bvh.flattened ) ) return false; if ( !reader.read( numFlat, bvh.flatBounds ) ) return false; - if ( !reader.read( numIndices, bvh.primitiveToNode ) ) return false; } else { bvh.flattened.clear(); bvh.flatBounds.clear(); - bvh.primitiveToNode.clear(); } bvh.dirty = false; diff --git a/engine/src/utils/math/physics/common.cpp b/engine/src/utils/math/physics/common.cpp index 4423c38e..7ef71673 100644 --- a/engine/src/utils/math/physics/common.cpp +++ b/engine/src/utils/math/physics/common.cpp @@ -745,8 +745,11 @@ pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) { } break; case pod::ShapeType::MESH: case pod::ShapeType::CONVEX_HULL: { - if ( body.collider.mesh.bvh && !body.collider.mesh.bvh->bounds.empty() ) - return impl::transformAabbToWorld( body.collider.mesh.bvh->bounds[0], transform ); + if ( body.collider.mesh.bvh ) { + const auto& bvh = *body.collider.mesh.bvh; + if ( !bvh.flatBounds.empty() ) return impl::transformAabbToWorld( bvh.flatBounds[0], transform ); + if ( !bvh.bounds.empty() ) return impl::transformAabbToWorld( bvh.bounds[0], transform ); + } 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 ) impl::computeConvexHullAABB( view, view["position"], bounds ); diff --git a/engine/src/utils/math/physics/impl.cpp b/engine/src/utils/math/physics/impl.cpp index bff43d4d..e996e06d 100644 --- a/engine/src/utils/math/physics/impl.cpp +++ b/engine/src/utils/math/physics/impl.cpp @@ -446,6 +446,16 @@ pod::Vector3f uf::physics::getGravity( pod::PhysicsBody& body ) { return uf::vector::isValid( body.gravity ) ? body.gravity : body.world->gravity; } +void uf::physics::update( pod::PhysicsBody& body ) { + body.bounds = impl::computeAABB( body ); + uf::physics::updateInertia( body ); + + if ( body.inverseMass == 0.0f ) { + body.world->staticBvh.dirty = true; + } else { + body.world->dynamicBvh.dirty = true; + } +} void uf::physics::updateInertia( pod::PhysicsBody& body ) { if ( body.inverseMass == 0.0f ) { body.inverseInertiaTensor = { 0.0f, 0.0f, 0.0f }; diff --git a/engine/src/utils/math/physics/narrowphase/aabb.cpp b/engine/src/utils/math/physics/narrowphase/aabb.cpp index 7b87d2da..fb01a60c 100644 --- a/engine/src/utils/math/physics/narrowphase/aabb.cpp +++ b/engine/src/utils/math/physics/narrowphase/aabb.cpp @@ -101,8 +101,6 @@ void impl::drawAabb( const pod::PhysicsBody& body ) { pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::AABB& aabb ) { body.collider.type = pod::ShapeType::AABB; body.collider.aabb = aabb; - body.bounds = impl::computeAABB( body ); - - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/capsule.cpp b/engine/src/utils/math/physics/narrowphase/capsule.cpp index 0aad79ac..47d098ca 100644 --- a/engine/src/utils/math/physics/narrowphase/capsule.cpp +++ b/engine/src/utils/math/physics/narrowphase/capsule.cpp @@ -161,7 +161,6 @@ void impl::drawCapsule( const pod::PhysicsBody& body ) { pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Capsule& capsule ) { body.collider.type = pod::ShapeType::CAPSULE; body.collider.capsule = capsule; - body.bounds = impl::computeAABB( body ); - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/mesh.cpp b/engine/src/utils/math/physics/narrowphase/mesh.cpp index 25510b95..7df600b4 100644 --- a/engine/src/utils/math/physics/narrowphase/mesh.cpp +++ b/engine/src/utils/math/physics/narrowphase/mesh.cpp @@ -213,7 +213,18 @@ void impl::drawMesh( const pod::PhysicsBody& body ) { const uf::Mesh* meshData = body.collider.mesh.mesh; auto transform = impl::getTransform( body ); if ( !meshData ) return; - if ( body.inverseMass == 0.0f ) return; + // draw BVH instead + if ( body.inverseMass == 0.0f ) { + const auto& bvh = *body.collider.mesh.bvh; + if ( !bvh.flatBounds.empty() ) { + for ( const auto& bound : bvh.flatBounds ) uf::debug::drawShape( bound, transform ); + return; + } + if ( !bvh.bounds.empty() ) { + for ( const auto& bound : bvh.bounds ) uf::debug::drawShape( bound, transform ); + return; + } + } size_t totalTriangles = 0; for ( const auto& view : meshData->buffer_views ) totalTriangles += view.index.count / 3; @@ -237,24 +248,25 @@ void impl::drawMesh( const pod::PhysicsBody& body ) { pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const uf::Mesh& mesh, bool convex ) { if ( !body.collider.mesh.bvh ) { body.collider.mesh.bvh = new pod::BVH; - body.collider.mesh.ownsBvh = true; } else { *body.collider.mesh.bvh = {}; } - return uf::physics::initialize( body, mesh, *body.collider.mesh.bvh, convex ); + uf::physics::initialize( body, mesh, *body.collider.mesh.bvh, convex ); + body.collider.mesh.ownsBvh = true; + return body; } pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const uf::Mesh& mesh, pod::BVH& bvh, bool convex ) { body.collider.type = convex ? pod::ShapeType::CONVEX_HULL : pod::ShapeType::MESH; body.collider.mesh.mesh = &mesh; body.collider.mesh.bvh = &bvh; + body.collider.mesh.ownsBvh = false; // to-do: move this to the above initialize to allow for deferred BVH building? if ( bvh.nodes.empty() && bvh.flattened.empty() ) { impl::buildMeshBVH( bvh, mesh, uf::physics::settings.meshBvhCapacity ); } - body.bounds = impl::computeAABB( body ); - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/obb.cpp b/engine/src/utils/math/physics/narrowphase/obb.cpp index 39b8dbf7..0cb6c4dc 100644 --- a/engine/src/utils/math/physics/narrowphase/obb.cpp +++ b/engine/src/utils/math/physics/narrowphase/obb.cpp @@ -326,8 +326,6 @@ void impl::drawObb( const pod::PhysicsBody& body ) { pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::OBB& obb ) { body.collider.type = pod::ShapeType::OBB; body.collider.obb = obb; - body.bounds = impl::computeAABB( body ); - - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/plane.cpp b/engine/src/utils/math/physics/narrowphase/plane.cpp index dfd1354c..738576c8 100644 --- a/engine/src/utils/math/physics/narrowphase/plane.cpp +++ b/engine/src/utils/math/physics/narrowphase/plane.cpp @@ -95,7 +95,6 @@ void impl::drawPlane( const pod::PhysicsBody& body ) { pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Plane& plane ) { body.collider.type = pod::ShapeType::PLANE; body.collider.plane = plane; - body.bounds = impl::computeAABB( body ); - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/sphere.cpp b/engine/src/utils/math/physics/narrowphase/sphere.cpp index f7fc2d24..e25b94b1 100644 --- a/engine/src/utils/math/physics/narrowphase/sphere.cpp +++ b/engine/src/utils/math/physics/narrowphase/sphere.cpp @@ -104,7 +104,6 @@ void impl::drawSphere( const pod::PhysicsBody& body ) { pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Sphere& sphere ) { body.collider.type = pod::ShapeType::SPHERE; body.collider.sphere = sphere; - body.bounds = impl::computeAABB( body ); - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/triangle.cpp b/engine/src/utils/math/physics/narrowphase/triangle.cpp index c98ea3fc..c493c201 100644 --- a/engine/src/utils/math/physics/narrowphase/triangle.cpp +++ b/engine/src/utils/math/physics/narrowphase/triangle.cpp @@ -377,7 +377,6 @@ pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Tr if ( uf::vector::magnitude( body.collider.triangle.normal ) < 0.001f ) { body.collider.triangle.normal = impl::triangleNormal( (const pod::Triangle&) tri ); } - body.bounds = impl::computeAABB( body ); - uf::physics::updateInertia( body ); + uf::physics::update( body ); return body; } \ No newline at end of file