fixes because i neglected to test the uf::graph changes on other scenes (just to realize i really need to optimize the internal physics system...)

This commit is contained in:
ecker 2025-09-01 20:21:32 -05:00
parent 3b5db2c32c
commit 0ce6b57867
22 changed files with 304 additions and 148 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 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 FLAGS += -march=native -g # -flto # -g
endif endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit simd ctti gltf imgui fmt freetype openal ogg wav # reactphysics REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit reactphysics simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN
DEPS += -lgdi32 -ldwmapi DEPS += -lgdi32 -ldwmapi
LINKS += #-Wl,-subsystem,windows 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 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 FLAGS += -march=native -g # -flto # -g
endif endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit simd ctti gltf imgui fmt freetype openal ogg wav # reactphysics REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit reactphysics simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_LINUX -fPIC FLAGS += -DUF_ENV_LINUX -fPIC
DEPS += -pthread -ldl -lX11 -lXrandr DEPS += -pthread -ldl -lX11 -lXrandr
INCS := -I./dep/master/include $(INCS) INCS := -I./dep/master/include $(INCS)
else ifneq (,$(findstring dreamcast,$(ARCH))) else ifneq (,$(findstring dreamcast,$(ARCH)))
FLAGS += -DUF_ENV_DREAMCAST # -DUF_LEAN_AND_MEAN # this apparently crashes FLAGS += -DUF_ENV_DREAMCAST # -DUF_LEAN_AND_MEAN # this apparently crashes
REQ_DEPS += opengl gldc json:nlohmann zlib lua simd ctti fmt freetype openal aldc ogg wav png # imgui # reactphysics REQ_DEPS += opengl gldc json:nlohmann zlib lua reactphysics simd ctti fmt freetype openal aldc ogg wav png # imgui
INCS := -I./dep/dreamcast/include $(INCS) INCS := -I./dep/dreamcast/include $(INCS)
endif endif

View File

@ -6,12 +6,7 @@
"metadata": { "metadata": {
"holdable": true, "holdable": true,
"physics": { "physics": {
// "gravity": [ 0, -9.81, 0 ], "type": "bounding box"
// "inertia": [10, 10, 10],
// "mass": 10,
"type": "bounding box",
"recenter": false
} }
} }
} }

View File

@ -36,7 +36,7 @@
// "/^light_[^e]/": { "ignore": true }, // "/^light_[^e]/": { "ignore": true },
// regexp matches // regexp matches
"/^worldspawn_/": { "physics": { "type": "mesh", "static": true } }, // "/^worldspawn_/": { "physics": { "type": "mesh", "static": true } },
"/^func_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } }, "/^func_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
"/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } }, "/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
"/^prop_static/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ }, "/^prop_static/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },

View File

@ -2,8 +2,8 @@
"import": "./base_sourceengine.json", "import": "./base_sourceengine.json",
"assets": [ "assets": [
// { "filename": "./models/mds_mcdonalds.glb" }, // { "filename": "./models/mds_mcdonalds.glb" },
{ "filename": "./models/mds_mcdonalds/graph.json" }, { "filename": "./models/mds_mcdonalds/graph.json" }
{ "filename": "/burger.json", "delay": 1 } // { "filename": "/burger.json", "delay": 1 }
], ],
"metadata": { "metadata": {
"graph": { "graph": {

View File

@ -1,9 +1,9 @@
{ {
// "import": "./rp_downtown_v2.json" // "import": "./rp_downtown_v2.json"
"import": "./ss2_medsci1.json" // "import": "./ss2_medsci1.json"
// "import": "./test_grid.json" // "import": "./test_grid.json"
// "import": "./sh2_mcdonalds.json" // "import": "./sh2_mcdonalds.json"
// "import": "./animal_crossing.json" // "import": "./animal_crossing.json"
// "import": "./mds_mcdonalds.json" "import": "./mds_mcdonalds.json"
// "import": "./gm_construct.json" // "import": "./gm_construct.json"
} }

View File

@ -2,9 +2,9 @@
"import": "./base_sourceengine.json", "import": "./base_sourceengine.json",
"assets": [ "assets": [
// { "filename": "./models/ss2_medsci1.glb" } // { "filename": "./models/ss2_medsci1.glb" }
// { "filename": "./models/ss2_medsci1/graph.json" } { "filename": "./models/ss2_medsci1/graph.json" }
// { "filename": "./models/ss2_medsci1_small.glb" } // { "filename": "./models/ss2_medsci1_small.glb" }
{ "filename": "./models/ss2_medsci1_small/graph.json" } // { "filename": "./models/ss2_medsci1_small/graph.json" }
// { "filename": "./models/ss2_medsci1_smallish.glb" } // { "filename": "./models/ss2_medsci1_smallish.glb" }
// { "filename": "./models/ss2_medsci1_smallish/graph.json" } // { "filename": "./models/ss2_medsci1_smallish/graph.json" }
], ],

View File

@ -1 +1 @@
opengl vulkan

View File

@ -29,6 +29,8 @@ namespace pod {
// Render information // Render information
uf::stl::vector<uf::stl::string> primitives; // uf::stl::vector<uf::stl::string> primitives; //
uf::stl::vector<uf::stl::string> instances; // ugh
uf::stl::vector<uf::stl::string> drawCommands; // ugh
uf::stl::vector<uf::stl::string> meshes; // uf::stl::vector<uf::stl::string> meshes; //
uf::stl::vector<uf::stl::string> images; // uf::stl::vector<uf::stl::string> images; //
@ -79,8 +81,10 @@ namespace pod {
// Local storage, used for save/load // Local storage, used for save/load
struct Storage { struct Storage {
uf::stl::KeyMap<pod::Instance> instances; // should be unused but is a counter for global instance IDs because I cannot for the life of me figure out a working way to remap otherwise
uf::stl::KeyMap<pod::Instance::Addresses> instanceAddresses; uf::stl::KeyMap<pod::Instance::Addresses> instanceAddresses;
uf::stl::KeyMap<uf::stl::vector<pod::Primitive>> primitives; uf::stl::KeyMap<uf::stl::vector<pod::Primitive>> primitives;
uf::stl::KeyMap<uf::stl::vector<pod::DrawCommand>> drawCommands; // unused
uf::stl::KeyMap<uf::Mesh> meshes; uf::stl::KeyMap<uf::Mesh> meshes;
uf::stl::KeyMap<uf::Image> images; uf::stl::KeyMap<uf::Image> images;

View File

@ -62,9 +62,36 @@ namespace pod {
typedef uint32_t CollisionMask; typedef uint32_t CollisionMask;
struct Collider { struct Collider {
enum CategoryMask : uint32_t {
CATEGORY_NONE = 0,
CATEGORY_STATIC = 1 << 0,
CATEGORY_DYNAMIC = 1 << 1,
CATEGORY_PLAYER = 1 << 2,
CATEGORY_NPC = 1 << 3,
CATEGORY_TRIGGER = 1 << 4,
CATEGORY_PROJECTILE = 1 << 5,
CATEGORY_CHARACTER = CATEGORY_PLAYER | CATEGORY_NPC,
CATEGORY_ALL = 0xFFFFFFFF
};
enum CollisionMask : uint32_t {
MASK_NONE = 0,
MASK_STATIC = CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC | CATEGORY_PROJECTILE,
MASK_DYNAMIC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC,
MASK_PLAYER = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_NPC | CATEGORY_PROJECTILE,
MASK_NPC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_PROJECTILE,
MASK_TRIGGER = CATEGORY_PLAYER | CATEGORY_NPC,
MASK_PROJECTILE = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC,
MASK_CHARACTER = MASK_PLAYER | MASK_NPC,
MASK_ALL = 0xFFFFFFFF
};
pod::ShapeType type; pod::ShapeType type;
pod::CollisionMask mask = 0xFFFFFFFF; pod::CollisionMask category = Collider::CATEGORY_ALL;
pod::CollisionMask mask = Collider::MASK_ALL;
union { union {
pod::Sphere sphere; pod::Sphere sphere;
pod::AABB aabb; pod::AABB aabb;
@ -105,6 +132,8 @@ namespace pod {
pod::Vector3f inertiaTensor = { 1, 1, 1 }; pod::Vector3f inertiaTensor = { 1, 1, 1 };
pod::Vector3f inverseInertiaTensor = { 1, 1, 1 }; pod::Vector3f inverseInertiaTensor = { 1, 1, 1 };
pod::Vector3f gravity = { NAN, NAN, NAN }; // an invalid gravity will fallback to world gravity
pod::AABB bounds; pod::AABB bounds;
pod::Collider collider; pod::Collider collider;
pod::PhysicsMaterial material; pod::PhysicsMaterial material;
@ -169,7 +198,15 @@ namespace uf {
void UF_API substep( pod::World&, float dt, int substeps ); void UF_API substep( pod::World&, float dt, int substeps );
void UF_API setMass( pod::RigidBody& body, float mass = 0.0f ); void UF_API setMass( pod::RigidBody& body, float mass = 0.0f );
void UF_API setInertia( pod::RigidBody& body ); void UF_API setColliderCategory( pod::RigidBody&, uint32_t category = pod::Collider::CATEGORY_ALL );
void UF_API setColliderCategory( pod::RigidBody&, const uf::stl::string& );
void UF_API setColliderMask( pod::RigidBody&, uint32_t mask = pod::Collider::MASK_ALL );
void UF_API setColliderMask( pod::RigidBody&, const uf::stl::string& );
void UF_API setGravity( pod::RigidBody&, const pod::Vector3f& = { NAN, NAN, NAN } );
pod::Vector3f UF_API getGravity( pod::RigidBody& );
void UF_API updateInertia( pod::RigidBody& body );
void UF_API applyForce( pod::RigidBody& body, const pod::Vector3f& force ); void UF_API applyForce( pod::RigidBody& body, const pod::Vector3f& force );
void UF_API applyForceAtPoint( pod::RigidBody body, const pod::Vector3f& force, const pod::Vector3f& point ); void UF_API applyForceAtPoint( pod::RigidBody body, const pod::Vector3f& force, const pod::Vector3f& point );
void UF_API applyImpulse( pod::RigidBody& body, const pod::Vector3f& impulse ); void UF_API applyImpulse( pod::RigidBody& body, const pod::Vector3f& impulse );

View File

@ -297,7 +297,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
if ( query.hit ) { if ( query.hit ) {
if ( metadata.movement.floored.print ) UF_MSG_DEBUG("{}: {} | {}", query.contact.penetration, uf::string::toString(*query.body->object), uf::vector::toString(physics.velocity)); if ( metadata.movement.floored.print ) UF_MSG_DEBUG("{}: {} | {}", query.contact.penetration, uf::string::toString(*query.body->object), uf::vector::toString(physics.velocity));
stats.floored = true; stats.floored = true;
if ( physics.velocity.y < 0.0f ) physics.velocity.y = 0.0f; //if ( physics.velocity.y < 0.0f ) physics.velocity.y = 0.0f;
} }
#endif #endif
} }
@ -348,6 +348,8 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
#if UF_USE_REACTPHYSICS #if UF_USE_REACTPHYSICS
if ( collider.stats.gravity == pod::Vector3f{0,0,0} ) stats.noclipped = true; if ( collider.stats.gravity == pod::Vector3f{0,0,0} ) stats.noclipped = true;
#else
if ( physics.gravity == pod::Vector3f{0,0,0} ) stats.noclipped = true;
#endif #endif
{ {
@ -429,6 +431,11 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
translator.forward.y += cameraTransform.forward.y; translator.forward.y += cameraTransform.forward.y;
translator.forward = uf::vector::normalize( translator.forward ); translator.forward = uf::vector::normalize( translator.forward );
} }
#else
else if ( stats.noclipped || physics.gravity == pod::Vector3f{0,0,0} ){
translator.forward.y += cameraTransform.forward.y;
translator.forward = uf::vector::normalize( translator.forward );
}
#endif #endif
if ( metadata.system.control ) { if ( metadata.system.control ) {
@ -441,6 +448,16 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
collider.body->enableGravity(!state); collider.body->enableGravity(!state);
uf::physics::impl::activateCollision(collider, !state); uf::physics::impl::activateCollision(collider, !state);
} }
#else
if ( !state ) {
uf::physics::impl::setGravity( physics );
uf::physics::impl::setColliderCategory( physics, "ALL");
uf::physics::impl::setColliderMask( physics, "ALL");
} else {
uf::physics::impl::setGravity( physics, pod::Vector3f{0,0,0});
uf::physics::impl::setColliderCategory( physics, "NONE");
uf::physics::impl::setColliderMask( physics, "NONE");
}
#endif #endif
stats.noclipped = state; stats.noclipped = state;

View File

@ -8,6 +8,7 @@
#include <uf/utils/math/physics.h> #include <uf/utils/math/physics.h>
#include <uf/utils/camera/camera.h> #include <uf/utils/camera/camera.h>
#include <uf/ext/xatlas/xatlas.h> #include <uf/ext/xatlas/xatlas.h>
#include <uf/utils/io/fmt.h>
// it's too unstable right now to do multithreaded loading, perhaps there's a better way // it's too unstable right now to do multithreaded loading, perhaps there's a better way
#if UF_USE_OPENGL #if UF_USE_OPENGL
@ -414,6 +415,17 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const
graph.settings.stream.hash = graph.metadata["stream"]["hash"].as(graph.settings.stream.hash); graph.settings.stream.hash = graph.metadata["stream"]["hash"].as(graph.settings.stream.hash);
graph.settings.stream.lastUpdate = graph.metadata["stream"]["lastUpdate"].as(graph.settings.stream.lastUpdate); graph.settings.stream.lastUpdate = graph.metadata["stream"]["lastUpdate"].as(graph.settings.stream.lastUpdate);
} }
// store offsets
{
size_t instances = 0;
for ( auto& key : storage.primitives.keys ) {
instances += storage.primitives.map[key].size();
}
graph.metadata["offsets"]["instances"] = instances;
graph.metadata["offsets"]["materials"] = storage.materials.keys.size();
graph.metadata["offsets"]["joints"] = storage.joints.keys.size();
}
uf::stl::string key = graph.metadata["key"].as<uf::stl::string>(""); uf::stl::string key = graph.metadata["key"].as<uf::stl::string>("");
if ( key != "" ) { if ( key != "" ) {

View File

@ -1043,14 +1043,6 @@ void uf::graph::process( pod::Graph& graph ) {
auto& drawCommand = primitive.drawCommand; auto& drawCommand = primitive.drawCommand;
auto& instance = primitive.instance; auto& instance = primitive.instance;
// remap instance ID
if ( 0 <= drawCommand.instanceID && drawCommand.instanceID < graph.primitives.size() ) {
for ( auto key : storage.primitives.keys ) {
if ( key == name ) break;
drawCommand.instanceID += storage.primitives[key].size();
}
}
if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) { if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) {
auto& keys = storage.materials.keys; auto& keys = storage.materials.keys;
auto& indices = storage.materials.indices; auto& indices = storage.materials.indices;
@ -1291,18 +1283,19 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
} }
// what a mess // what a mess
node.object = storage.entities.keys.size();
auto objectKeyName = ::objectKey( node );
storage.entities[objectKeyName] = &entity;
auto& objectData = storage.objects[objectKeyName];
auto model = uf::transform::model( transform );
objectData.model = model;
objectData.previous = model;
// //
if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) { if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) {
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]]; auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
node.object = storage.entities.keys.size();
auto objectKeyName = ::objectKey( node );
storage.entities[objectKeyName] = &entity;
auto& objectData = storage.objects[objectKeyName];
auto model = uf::transform::model( transform );
objectData.model = model;
objectData.previous = model;
pod::Instance::Bounds bounds; pod::Instance::Bounds bounds;
pod::DrawCommand* drawCommands = NULL; pod::DrawCommand* drawCommands = NULL;
@ -1316,11 +1309,19 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
auto& primitive = primitives[primitiveID]; auto& primitive = primitives[primitiveID];
auto& instance = primitive.instance; auto& instance = primitive.instance;
auto& drawCommand = primitive.drawCommand; auto& drawCommand = primitive.drawCommand;
auto instanceID = primitiveID;
instance.objectID = node.object; instance.objectID = node.object;
instance.jointID = graphMetadataJson["renderer"]["skinned"].as<bool>() ? 0 : -1; instance.jointID = graphMetadataJson["renderer"]["skinned"].as<bool>() ? 0 : -1;
// to-do: fix needing this to fix graphs
#if 0
auto instanceID = primitiveID;
#else
size_t instanceID = storage.instances.keys.size();
auto instanceKeyName = graph.instances.emplace_back(std::to_string(instanceID));
storage.instances[instanceKeyName] = instance;
#endif
bounds.min = uf::vector::min( bounds.min, instance.bounds.min ); bounds.min = uf::vector::min( bounds.min, instance.bounds.min );
bounds.max = uf::vector::max( bounds.max, instance.bounds.max ); bounds.max = uf::vector::max( bounds.max, instance.bounds.max );

View File

@ -156,8 +156,12 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
#else #else
auto type = metadataJsonPhysics["type"].as<uf::stl::string>(); auto type = metadataJsonPhysics["type"].as<uf::stl::string>();
float mass = metadataJsonPhysics["mass"].as<float>(); float mass = metadataJsonPhysics["mass"].as<float>();
auto category = metadataJsonPhysics["category"].as<uf::stl::string>("ALL");
auto mask = metadataJsonPhysics["mask"].as<uf::stl::string>("ALL");
pod::Vector3f offset = uf::vector::decode( metadataJsonPhysics["offset"], pod::Vector3f{} ); pod::Vector3f offset = uf::vector::decode( metadataJsonPhysics["offset"], pod::Vector3f{} );
pod::Vector3f gravity = uf::vector::decode( metadataJsonPhysics["gravity"], pod::Vector3f{ NAN, NAN, NAN } );
if ( type == "bounding box" || type == "aabb" ) { if ( type == "bounding box" || type == "aabb" ) {
pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} ); pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} );
@ -175,13 +179,17 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
uf::physics::impl::create( self, pod::Sphere{ radius }, mass, offset ); uf::physics::impl::create( self, pod::Sphere{ radius }, mass, offset );
} else if ( type == "capsule" ) { } else if ( type == "capsule" ) {
float radius = metadataJsonPhysics["radius"].as<float>(); float radius = metadataJsonPhysics["radius"].as<float>();
float height = metadataJsonPhysics["height"].as<float>(); float halfHeight = metadataJsonPhysics["height"].as<float>() * 0.5f;
uf::physics::impl::create( self, pod::Capsule{ radius, height * 0.5f }, mass, offset ); uf::physics::impl::create( self, pod::Capsule{ radius, halfHeight }, mass, offset );
} }
if ( this->hasComponent<pod::RigidBody>() ) { if ( this->hasComponent<pod::RigidBody>() ) {
auto& body = this->getComponent<pod::RigidBody>(); auto& body = this->getComponent<pod::RigidBody>();
uf::physics::impl::setColliderCategory( body, category );
uf::physics::impl::setColliderMask( body, mask );
uf::physics::impl::setGravity( body, gravity );
if ( this->getName() == "Player" ) { if ( this->getName() == "Player" ) {
body.inertiaTensor = { FLT_MAX, FLT_MAX, FLT_MAX }; body.inertiaTensor = { FLT_MAX, FLT_MAX, FLT_MAX };

View File

@ -253,7 +253,8 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
} }
// load meshes // load meshes
{ {
size_t masterAuxID = 0; size_t masterInstanceID = 0;
graph.meshes.reserve(model.meshes.size()); graph.meshes.reserve(model.meshes.size());
storage.meshes.reserve(model.meshes.size()); storage.meshes.reserve(model.meshes.size());

View File

@ -383,18 +383,15 @@ if ( meshopt.should ) {
primitives.reserve( meshlets.size() ); primitives.reserve( meshlets.size() );
for ( auto& meshlet : meshlets ) { for ( auto& meshlet : meshlets ) {
// to-do: check against the meshlet's actual primitive information meshlet.primitive.drawCommand.instances = 1;
auto& drawCommand = drawCommands.emplace_back(pod::DrawCommand{ meshlet.primitive.drawCommand.instanceID = ++masterInstanceID; // this doesn't matter......
.indices = meshlet.indices.size(), meshlet.primitive.drawCommand.indexID = indexID;
.instances = 1, meshlet.primitive.drawCommand.indices = meshlet.indices.size();
.indexID = indexID, meshlet.primitive.drawCommand.vertexID = vertexID;
.vertexID = vertexID, meshlet.primitive.drawCommand.vertices = meshlet.vertices.size();
.instanceID = meshlet.primitive.drawCommand.instanceID,
.auxID = meshlet.primitive.drawCommand.auxID, drawCommands.emplace_back(meshlet.primitive.drawCommand);
.materialID = meshlet.primitive.drawCommand.materialID,
.vertices = meshlet.vertices.size(),
});
primitives.emplace_back( meshlet.primitive ); primitives.emplace_back( meshlet.primitive );
indexID += meshlet.indices.size(); indexID += meshlet.indices.size();

View File

@ -62,53 +62,79 @@ size_t ext::xatlas::unwrapExperimental( pod::Graph& graph ) {
source = mesh; source = mesh;
source.updateDescriptor(); source.updateDescriptor();
for (auto& view : source.makeViews({"position", "uv", "st", "index"})) { uf::Mesh::Input vertexInput = mesh.vertex;
auto pos = view["position"];
auto uv = view["uv"];
auto st = view["st"];
auto idx = view["index"];
// Must have positions + indices uf::Mesh::Attribute positionAttribute;
if (!pos.valid() || !idx.valid()) continue; uf::Mesh::Attribute uvAttribute;
if (view.vertex.count == 0 || view.index.count == 0) continue; uf::Mesh::Attribute stAttribute;
for ( auto& attribute : mesh.vertex.attributes ) {
if ( attribute.descriptor.name == "position" ) positionAttribute = attribute;
else if ( attribute.descriptor.name == "uv" ) uvAttribute = attribute;
else if ( attribute.descriptor.name == "st" ) stAttribute = attribute;
}
UF_ASSERT( positionAttribute.descriptor.name == "position" && uvAttribute.descriptor.name == "uv" && stAttribute.descriptor.name == "st" );
if ( mesh.index.count ) {
uf::Mesh::Input indexInput = mesh.index;
uf::Mesh::Attribute indexAttribute = mesh.index.attributes.front();
// Choose xatlas index format
::xatlas::IndexFormat indexType = ::xatlas::IndexFormat::UInt32; ::xatlas::IndexFormat indexType = ::xatlas::IndexFormat::UInt32;
switch (idx.attribute.descriptor.size) { switch ( mesh.index.size ) {
case sizeof(uint16_t): indexType = ::xatlas::IndexFormat::UInt16; break; case sizeof(uint16_t): indexType = ::xatlas::IndexFormat::UInt16; break;
case sizeof(uint32_t): indexType = ::xatlas::IndexFormat::UInt32; break; case sizeof(uint32_t): indexType = ::xatlas::IndexFormat::UInt32; break;
default: UF_EXCEPTION("unsupported index type"); break; default: UF_EXCEPTION("unsupported index type"); break;
} }
// Get atlas target from indirect auxID or default 0 if ( mesh.indirect.count ) {
size_t atlasID = 0; auto& primitives = /*graph.storage*/storage.primitives[name];
if ( 0 <= view.indirectIndex ) { pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
auto* drawCommand = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
atlasID = drawCommand[view.indirectIndex].auxID; for ( auto i = 0; i < mesh.indirect.count; ++i ) {
size_t atlasID = drawCommands[i].auxID;
vertexInput = mesh.remapVertexInput( i );
indexInput = mesh.remapIndexInput( i );
auto& atlas = atlases[atlasID];
auto& entry = atlas.entries.emplace_back();
entry.index = index;
entry.commandID = i;
auto& decl = entry.decl;
decl.vertexPositionData = static_cast<uint8_t*>(positionAttribute.pointer) + positionAttribute.stride * vertexInput.first;
decl.vertexPositionStride = positionAttribute.stride;
decl.vertexUvData = static_cast<uint8_t*>(uvAttribute.pointer) + uvAttribute.stride * vertexInput.first;
decl.vertexUvStride = uvAttribute.stride;
decl.vertexCount = vertexInput.count;
decl.indexCount = indexInput.count;
decl.indexData = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
decl.indexFormat = indexType;
}
} else {
size_t atlasID = 0;
auto& atlas = atlases[atlasID];
auto& entry = atlas.entries.emplace_back();
entry.index = index;
auto& decl = entry.decl;
decl.vertexPositionData = static_cast<uint8_t*>(positionAttribute.pointer) + positionAttribute.stride * vertexInput.first;
decl.vertexPositionStride = positionAttribute.stride;
decl.vertexUvData = static_cast<uint8_t*>(uvAttribute.pointer) + uvAttribute.stride * vertexInput.first;
decl.vertexUvStride = uvAttribute.stride;
decl.vertexCount = vertexInput.count;
decl.indexCount = indexInput.count;
decl.indexData = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
decl.indexFormat = indexType;
} }
} else UF_EXCEPTION("to-do: not require indices for meshes");
auto& atlas = atlases[atlasID];
auto& entry = atlas.entries.emplace_back();
entry.index = index;
auto& decl = entry.decl;
decl.vertexPositionData = pos.data(view.vertex.first);
decl.vertexPositionStride = pos.stride();
if ( uv.valid() ) {
decl.vertexUvData = uv.data(view.vertex.first);
decl.vertexUvStride = uv.stride();
} else if ( st.valid() ) {
decl.vertexUvData = st.data(view.vertex.first);
decl.vertexUvStride = st.stride();
}
decl.vertexCount = view.vertex.count;
decl.indexCount = view.index.count;
decl.indexData = idx.data(view.index.first);
decl.indexFormat = indexType;
}
} }
::xatlas::ChartOptions chartOptions{}; ::xatlas::ChartOptions chartOptions{};

View File

@ -1,50 +1,54 @@
// BVH // BVH
namespace { namespace {
// collects a list of nodes that are overlapping with each other // collects a list of nodes that are overlapping with each other
void traverseNodePair( const pod::BVH& bvh, int leftID, int rightID, pod::BVH::pair_t& pairs ) { void traverseNodePair( const pod::BVH& bvh, int indexA, int indexB, pod::BVH::pair_t& pairs ) {
const auto& left = bvh.nodes[leftID]; const auto& nodeA = bvh.nodes[indexA];
const auto& right = bvh.nodes[rightID]; const auto& nodeB = bvh.nodes[indexB];
if ( !::aabbOverlap( left.bounds, right.bounds ) ) return; if ( !::aabbOverlap( nodeA.bounds, nodeB.bounds ) ) return;
if ( left.count > 0 && right.count > 0 ) { if ( nodeA.count > 0 && nodeB.count > 0 ) {
for ( auto i = 0; i < left.count; ++i ) for ( auto i = 0; i < nodeA.count; ++i )
for ( auto j = 0; j < right.count; ++j ) { for ( auto j = 0; j < nodeB.count; ++j ) {
int a = bvh.indices[left.start + i]; int indexA = bvh.indices[nodeA.start + i];
int b = bvh.indices[right.start + j]; int indexB = bvh.indices[nodeB.start + j];
pairs.emplace_back(std::pair{a, b}); pairs.emplace_back(std::pair{indexA, indexB});
} }
return; return;
} }
if ( left.count == 0 ) { if ( nodeA.count == 0 ) {
::traverseNodePair( bvh, left.left, rightID, pairs ); ::traverseNodePair( bvh, nodeA.left, indexB, pairs );
::traverseNodePair( bvh, left.right, rightID, pairs ); ::traverseNodePair( bvh, nodeA.right, indexB, pairs );
} else if ( right.count == 0 ) { } else if ( nodeB.count == 0 ) {
::traverseNodePair( bvh, leftID, right.left, pairs ); ::traverseNodePair( bvh, indexA, nodeB.left, pairs );
::traverseNodePair( bvh, leftID, right.right, pairs ); ::traverseNodePair( bvh, indexA, nodeB.right, pairs );
} }
} }
// collects a list of nodes from each BVH that are overlapping with each other (for mesh v mesh) // collects a list of nodes from each BVH that are overlapping with each other (for mesh v mesh)
void traverseNodePair( const pod::BVH& a, int nodeA, const pod::BVH& b, int nodeB, pod::BVH::pair_t& pairs ) { void traverseNodePair( const pod::BVH& bvhA, int indexA, const pod::BVH& bvhB, int indexB, pod::BVH::pair_t& pairs ) {
const auto& nA = a.nodes[nodeA]; const auto& nodeA = bvhA.nodes[indexA];
const auto& nB = b.nodes[nodeB]; const auto& nodeB = bvhB.nodes[indexB];
if ( !::aabbOverlap( nA.bounds, nB.bounds ) ) return; if ( !::aabbOverlap( nodeA.bounds, nodeB.bounds ) ) return;
if ( nA.count > 0 && nB.count > 0 ) { if ( nodeA.count > 0 && nodeB.count > 0 ) {
for ( auto i = 0; i < nA.count; i++ ) for ( auto i = 0; i < nodeA.count; i++ ) {
for ( auto j = 0; j < nB.count; j++ ) for ( auto j = 0; j < nodeB.count; j++ ) {
pairs.emplace_back(a.indices[nA.start+i], b.indices[nB.start+j]); auto indexA = bvhA.indices[nodeA.start+i];
auto indexB = bvhB.indices[nodeB.start+j];
pairs.emplace_back(std::pair{indexA, indexB});
}
}
return; return;
} }
if ( nA.count == 0 ) { if ( nodeA.count == 0 ) {
::traverseNodePair( a, nA.left, b , nodeB, pairs ); ::traverseNodePair( bvhA, nodeA.left, bvhB , indexB, pairs );
::traverseNodePair( a, nA.right, b , nodeB, pairs ); ::traverseNodePair( bvhA, nodeA.right, bvhB , indexB, pairs );
} else { } else if ( nodeB.count == 0 ) {
::traverseNodePair( a, nodeA, b, nB.left, pairs ); ::traverseNodePair( bvhA, indexA, bvhB, nodeB.left, pairs );
::traverseNodePair( a, nodeA, b, nB.right, pairs ); ::traverseNodePair( bvhA, indexA, bvhB, nodeB.right, pairs );
} }
} }
@ -54,9 +58,14 @@ namespace {
if ( node.count > 0 ) { if ( node.count > 0 ) {
for ( auto i = 0; i < node.count; ++i ) { for ( auto i = 0; i < node.count; ++i ) {
for ( auto j = i + 1; j < node.count; ++j ) { for ( auto j = i + 1; j < node.count; ++j ) {
int a = bvh.indices[node.start + i]; int indexA = bvh.indices[node.start + i];
int b = bvh.indices[node.start + j]; int indexB = bvh.indices[node.start + j];
pairs.emplace_back(std::pair{a, b});
if ( !::aabbOverlap( bvh.nodes[indexA].bounds, bvh.nodes[indexB].bounds ) ) {
continue;
}
pairs.emplace_back(std::pair{indexA, indexB});
} }
} }
return; return;

View File

@ -74,6 +74,13 @@ namespace {
return ::getTransform( body ).position; return ::getTransform( body ).position;
} }
bool shouldCollide( const pod::Collider& a, const pod::Collider& b ) {
return ( a.category & b.mask ) && ( b.category & a.mask );
}
bool shouldCollide( const pod::RigidBody& a, const pod::RigidBody& b ) {
return ::shouldCollide( a.collider, b.collider );
}
// normalizes the delta between two bodies / contacts by the distance (as it was already computed) if non-zero // normalizes the delta between two bodies / contacts by the distance (as it was already computed) if non-zero
// a lot of collider v colliders use this semantic // a lot of collider v colliders use this semantic
pod::Vector3f normalizeDelta( const pod::Vector3f& delta, float dist, float eps = EPS(1.0e-6), const pod::Vector3f& fallback = pod::Vector3f{0,1,0} ) { pod::Vector3f normalizeDelta( const pod::Vector3f& delta, float dist, float eps = EPS(1.0e-6), const pod::Vector3f& fallback = pod::Vector3f{0,1,0} ) {

View File

@ -6,14 +6,18 @@
namespace { namespace {
bool warmupSolver = true; bool warmupSolver = true;
bool blockContactSolver = false; bool blockContactSolver = false; // blockNxN solver is flawed
bool psgContactSolver = true; bool psgContactSolver = true; // iterative solver is flawed
bool useGjk = false; bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
bool fixedStep = true; bool fixedStep = true;
int substeps = 0; int substeps = 4;
int solverIterations = 10; // increasing these make things lag
float baumgarteCorrectionPercent = 0.005f; int broadphaseBvhCapacity = 1;
int meshBvhCapacity = 1;
int solverIterations = 5;
float baumgarteCorrectionPercent = 0.005f; // needs to be very small or the correction is too large
float baumgarteCorrectionSlop = 0.001f; float baumgarteCorrectionSlop = 0.001f;
uf::stl::unordered_map<size_t, pod::Manifold> manifoldsCache; uf::stl::unordered_map<size_t, pod::Manifold> manifoldsCache;
} }
@ -106,7 +110,7 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
uf::stl::vector<std::pair<int,int>> pairs; uf::stl::vector<std::pair<int,int>> pairs;
// create BVH // create BVH
::buildBroadphaseBVH(bvh, bodies); ::buildBroadphaseBVH( bvh, bodies, ::broadphaseBvhCapacity );
// query for overlaps // query for overlaps
::queryOverlaps( bvh, pairs ); ::queryOverlaps( bvh, pairs );
// iterate overlaps // iterate overlaps
@ -114,6 +118,9 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
auto& a = *bodies[ia]; auto& a = *bodies[ia];
auto& b = *bodies[ib]; auto& b = *bodies[ib];
// could be also pruned in the broadphase, but traversal needs to be agnostic between a BVH for bodies or a BVH for triangles
if ( !::shouldCollide( a, b ) ) continue;
pod::Manifold manifold; pod::Manifold manifold;
if ( ::generateContacts( a, b, manifold, dt ) ) { if ( ::generateContacts( a, b, manifold, dt ) ) {
// bodies with meshes already reorient the normal to the triangle's center // bodies with meshes already reorient the normal to the triangle's center
@ -169,9 +176,47 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
void uf::physics::impl::setMass( pod::RigidBody& body, float mass ) { void uf::physics::impl::setMass( pod::RigidBody& body, float mass ) {
body.mass = mass; body.mass = mass;
body.inverseMass = 1.0f / mass; body.inverseMass = 1.0f / mass;
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( body );
} }
void uf::physics::impl::setInertia( pod::RigidBody& body ) {
void uf::physics::impl::setColliderCategory( pod::RigidBody& body, uint32_t category ) {
body.collider.category = category;
}
void uf::physics::impl::setColliderCategory( pod::RigidBody& body, const uf::stl::string& category ) {
auto c = uf::string::uppercase( category );
if ( c == "NONE" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_NONE );
if ( c == "STATIC" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_STATIC );
if ( c == "DYNAMIC" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_DYNAMIC );
if ( c == "PLAYER" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_PLAYER );
if ( c == "NPC" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_NPC );
if ( c == "TRIGGER" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_TRIGGER );
if ( c == "PROJECTILE" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_PROJECTILE );
if ( c == "CHARACTER" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_CHARACTER );
if ( c == "ALL" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::CATEGORY_ALL );
}
void uf::physics::impl::setColliderMask( pod::RigidBody& body, uint32_t mask ) {
body.collider.mask = mask;
}
void uf::physics::impl::setColliderMask( pod::RigidBody& body, const uf::stl::string& mask ) {
auto m = uf::string::uppercase( mask );
if ( m == "NONE" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_NONE );
if ( m == "STATIC" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_STATIC );
if ( m == "DYNAMIC" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_DYNAMIC );
if ( m == "PLAYER" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_PLAYER );
if ( m == "NPC" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_NPC );
if ( m == "TRIGGER" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_TRIGGER );
if ( m == "PROJECTILE" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_PROJECTILE );
if ( m == "CHARACTER" ) return uf::physics::impl::setColliderCategory( body, pod::Collider::MASK_CHARACTER );
if ( m == "ALL" ) return uf::physics::impl::setColliderMask( body, pod::Collider::MASK_ALL );
}
void uf::physics::impl::setGravity( pod::RigidBody& body, const pod::Vector3f& gravity ) {
body.gravity = gravity;
}
pod::Vector3f uf::physics::impl::getGravity( pod::RigidBody& body ) {
return uf::vector::isValid( body.gravity ) ? body.gravity : body.world->gravity;
}
void uf::physics::impl::updateInertia( pod::RigidBody& body ) {
if ( body.isStatic || body.mass <= 0 ) { if ( body.isStatic || body.mass <= 0 ) {
body.inverseInertiaTensor = {}; body.inverseInertiaTensor = {};
return; return;
@ -254,6 +299,11 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.inverseMass = mass == 0.0f ? 0.0f : 1.0f / mass; body.inverseMass = mass == 0.0f ? 0.0f : 1.0f / mass;
body.isStatic = mass == 0.0f; body.isStatic = mass == 0.0f;
if ( body.isStatic ) {
uf::physics::impl::setColliderCategory(body, "STATIC");
uf::physics::impl::setColliderMask(body, "STATIC");
}
// insert into world // insert into world
world.bodies.emplace_back(&body); world.bodies.emplace_back(&body);
@ -264,7 +314,7 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.collider.type = pod::ShapeType::AABB; body.collider.type = pod::ShapeType::AABB;
body.collider.u.aabb = aabb; body.collider.u.aabb = aabb;
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( body );
UF_MSG_DEBUG("Creating body of type: {}, mass={}, min={}, max={}", "AABB", mass, uf::vector::toString(aabb.min), uf::vector::toString(aabb.max) ); UF_MSG_DEBUG("Creating body of type: {}, mass={}, min={}, max={}", "AABB", mass, uf::vector::toString(aabb.min), uf::vector::toString(aabb.max) );
return body; return body;
} }
@ -273,7 +323,7 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.collider.type = pod::ShapeType::SPHERE; body.collider.type = pod::ShapeType::SPHERE;
body.collider.u.sphere = sphere; body.collider.u.sphere = sphere;
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( body );
UF_MSG_DEBUG("Creating body of type={}, mass={}, radius={}", "SPHERE", mass, sphere.radius ); UF_MSG_DEBUG("Creating body of type={}, mass={}, radius={}", "SPHERE", mass, sphere.radius );
return body; return body;
} }
@ -282,7 +332,7 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.collider.type = pod::ShapeType::PLANE; body.collider.type = pod::ShapeType::PLANE;
body.collider.u.plane = plane; body.collider.u.plane = plane;
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( body );
UF_MSG_DEBUG("Creating body of type={}, mass={}, normal={}, offset={}", "PLANE", mass, uf::vector::toString( plane.normal ), plane.offset ); UF_MSG_DEBUG("Creating body of type={}, mass={}, normal={}, offset={}", "PLANE", mass, uf::vector::toString( plane.normal ), plane.offset );
return body; return body;
} }
@ -291,7 +341,7 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.collider.type = pod::ShapeType::CAPSULE; body.collider.type = pod::ShapeType::CAPSULE;
body.collider.u.capsule = capsule; body.collider.u.capsule = capsule;
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( body );
UF_MSG_DEBUG("Creating body of type={}, mass={}, radius={}, height={}", "CAPSULE", mass, capsule.radius, capsule.halfHeight * 2.0f ); UF_MSG_DEBUG("Creating body of type={}, mass={}, radius={}, height={}", "CAPSULE", mass, capsule.radius, capsule.halfHeight * 2.0f );
return body; return body;
} }
@ -300,7 +350,7 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.collider.type = pod::ShapeType::TRIANGLE; body.collider.type = pod::ShapeType::TRIANGLE;
body.collider.u.triangle = tri; body.collider.u.triangle = tri;
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( 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] ) ); 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; return body;
} }
@ -310,10 +360,10 @@ pod::RigidBody& uf::physics::impl::create( pod::World& world, uf::Object& object
body.collider.u.mesh.mesh = &mesh; body.collider.u.mesh.mesh = &mesh;
body.collider.u.mesh.bvh = new pod::BVH; body.collider.u.mesh.bvh = new pod::BVH;
::buildMeshBVH( *body.collider.u.mesh.bvh, mesh ); ::buildMeshBVH( *body.collider.u.mesh.bvh, mesh, ::meshBvhCapacity );
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::setInertia( body ); uf::physics::impl::updateInertia( body );
UF_MSG_DEBUG("Creating body of type={}, mass={}, nodes={} indices={} ", "MESH", mass, body.collider.u.mesh.bvh->nodes.size(), body.collider.u.mesh.bvh->indices.size()); UF_MSG_DEBUG("Creating body of type={}, mass={}, nodes={} indices={} ", "MESH", mass, body.collider.u.mesh.bvh->nodes.size(), body.collider.u.mesh.bvh->indices.size());
return body; return body;
} }

View File

@ -214,7 +214,7 @@ namespace {
// linear integration // linear integration
pod::Vector3f acceleration = body.forceAccumulator * body.inverseMass; pod::Vector3f acceleration = body.forceAccumulator * body.inverseMass;
acceleration += world.gravity; // apply gravity acceleration += uf::physics::impl::getGravity( body ); // apply gravity
body.velocity += acceleration * dt; body.velocity += acceleration * dt;
body.transform/*.reference*/->position += body.velocity * dt; body.transform/*.reference*/->position += body.velocity * dt;

View File

@ -123,16 +123,12 @@ namespace {
pod::BVH::pair_t pairs; pod::BVH::pair_t pairs;
::traverseNodePair(bvhA, 0, bvhB, 0, pairs); ::traverseNodePair(bvhA, 0, bvhB, 0, pairs);
UF_MSG_DEBUG("candidates={}", pairs.size());
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, idA, a ); // transform triangles to world space
auto tB = ::fetchTriangle( meshB, idB, b ); auto tB = ::fetchTriangle( meshB, idB, b );
UF_MSG_DEBUG("triA={}, triB={}", idA, idB );
if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue; if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue;
hit = true; hit = true;
} }

View File

@ -87,8 +87,6 @@ namespace {
float disc = b*b - 4*a*c; float disc = b*b - 4*a*c;
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; if ( disc < 0 ) return false;
float sqrtDisc = std::sqrt(disc); float sqrtDisc = std::sqrt(disc);
@ -97,8 +95,6 @@ namespace {
float t = ( t0 >= 0 ) ? t0 : t1; float t = ( t0 >= 0 ) ? t0 : t1;
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 if ( t < 0 ) return false; // both behind ray
// compare against current best hit // compare against current best hit