more fixes (to-do: fix BVH serialization/deserialization since I'm very sure it's wrong, but everything else should be plumbed right)

This commit is contained in:
ecker 2026-08-08 20:54:32 -05:00
parent 24d1d63516
commit 992556b054
16 changed files with 228 additions and 139 deletions

View File

@ -48,5 +48,6 @@ namespace uf {
size_t UF_API serialize( const pod::BVH& bvh, uf::stl::vector<uint8_t>& outBuffer, uint32_t offset = 0 );
bool UF_API deserialize( pod::BVH& bvh, const uf::stl::vector<uint8_t>& 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 );
}
}

View File

@ -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 );

View File

@ -200,7 +200,7 @@ namespace pod {
uf::stl::vector<pod::BVH::index_t> indices;
uf::stl::vector<pod::BVH::Node> nodes;
uf::stl::vector<pod::BVH::FlatNode> flattened;
uf::stl::vector<uint32_t> primitiveToNode;
uf::stl::vector<pod::BVH::index_t> indicesToNodes;
uf::stl::vector<pod::AABB> bounds;
uf::stl::vector<pod::AABB> flatBounds;

View File

@ -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<PendingImage>& 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<uint8_t>&& 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<uf::stl::string>();
size_t offset = json["offset"].as<size_t>();
@ -293,9 +287,14 @@ namespace {
bool deferred = graph.settings.stream.enabled;
if ( !deferred ) {
uf::stl::vector<uint8_t> 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<uint8_t>&& 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::stl::string>();
//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<uf::stl::string>();
// 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");

View File

@ -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<uint8_t> 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() );

View File

@ -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<pod::PhysicsBody>();
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<uf::renderer::Graphic>();
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<float>::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<size_t, size_t> 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] );
}
};

View File

@ -184,7 +184,10 @@ void impl::buildBroadphaseBVH( pod::BVH& bvh, const uf::stl::vector<pod::Physics
bvh.indices.clear();
bvh.nodes.clear();
bvh.bounds.clear();
bvh.indicesToNodes.clear();
bvh.bounds.reserve(bodies.size());
bvh.indices.reserve(bodies.size());
bvh.indicesToNodes.resize(bodies.size(), 0);
// stores bounds
uf::stl::vector<pod::AABB> 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<pod::Physics
if ( uf::physics::settings.useBvhSahBodies ) 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.flattenBvhBodies ) impl::flattenBVH( bvh, 0 );
if ( uf::physics::settings.flattenBvhBodies ) {
impl::flattenBVH( bvh, 0 );
// refitting code requires these to exist still
// bvh.nodes.clear();
// bvh.bounds.clear();
// bvh.nodes.shrink_to_fit();
// bvh.bounds.shrink_to_fit();
}
// mark as clean
bvh.dirty = false;
@ -214,7 +224,11 @@ void impl::buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t
bvh.indices.clear();
bvh.nodes.clear();
bvh.bounds.clear();
bvh.indicesToNodes.clear();
bvh.bounds.reserve( triangles );
bvh.indices.reserve( triangles );
bvh.indicesToNodes.resize( triangles, 0 );
// stores bounds
uf::stl::vector<pod::AABB> 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<pod::AABB> 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<uint8_t>& 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<uint8_t>& buffer
uf::stl::reader reader( buffer, offset, length > 0 ? length : buffer.size(), true, true );
const uint32_t* pNumIndices = reader.read<uint32_t>();
const uint32_t* pNumMap = reader.read<uint32_t>();
const uint32_t* pNumNodes = reader.read<uint32_t>();
const uint32_t* pNumFlat = reader.read<uint32_t>();
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<uint8_t>& 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;

View File

@ -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 );

View File

@ -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 };

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}