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:
parent
3b5db2c32c
commit
0ce6b57867
6
Makefile
6
Makefile
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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 ] } } }*/ },
|
||||||
|
|||||||
@ -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": {
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
@ -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" }
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
opengl
|
vulkan
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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 );
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 != "" ) {
|
||||||
|
|||||||
@ -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 );
|
||||||
|
|
||||||
|
|||||||
@ -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 };
|
||||||
|
|||||||
@ -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());
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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{};
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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} ) {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user