From 5402aff2a8852a32b70bb636ae6e520196aec393 Mon Sep 17 00:00:00 2001 From: ecker Date: Wed, 27 May 2026 20:54:51 -0500 Subject: [PATCH] imagine my shock the week-long issue predicated on scaling issues (constraint solving is now scaling aware, automatic ragdoll rigging seems to work, but need to actually bind the transforms to joint matrices) --- bin/data/config.json | 7 +- bin/data/entities/cesiumMan.json | 5 +- bin/data/entities/craeture.json | 24 ++- bin/data/entities/craetureModel.json | 7 +- bin/data/entities/door.json | 5 +- bin/data/shaders/base/line/frag.glsl | 4 +- engine/inc/uf/engine/graph/graph.h | 1 + engine/inc/uf/engine/graph/pod.inl | 5 + engine/inc/uf/utils/math/physics/common.h | 4 +- engine/inc/uf/utils/math/physics/impl.h | 4 +- engine/inc/uf/utils/math/physics/structs.h | 11 +- engine/src/engine/ext/ext.cpp | 25 +-- engine/src/engine/graph/animation.cpp | 146 ++++++++++++++++-- engine/src/engine/graph/graph.cpp | 20 ++- engine/src/engine/object/behaviors/graph.cpp | 30 +++- engine/src/ext/lua/usertypes/physics.cpp | 3 +- engine/src/utils/debug/draw.cpp | 2 +- engine/src/utils/math/physics/common.cpp | 27 +++- .../math/physics/constraints/ballSocket.cpp | 16 +- .../math/physics/constraints/coneTwist.cpp | 26 ++-- .../math/physics/constraints/distance.cpp | 16 +- .../utils/math/physics/constraints/hinge.cpp | 13 +- .../utils/math/physics/constraints/slider.cpp | 7 +- .../utils/math/physics/constraints/spring.cpp | 7 +- engine/src/utils/math/physics/draw.cpp | 8 +- engine/src/utils/math/physics/impl.cpp | 16 +- .../utils/math/physics/narrowphase/epa.cpp | 29 +++- .../utils/math/physics/narrowphase/ray.cpp | 2 +- 28 files changed, 343 insertions(+), 127 deletions(-) diff --git a/bin/data/config.json b/bin/data/config.json index f4701eab..5dd42b3e 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -8,7 +8,7 @@ "useLightmaps": false, "max": 32, "shadows": { - "enabled": false, + "enabled": true, "update": 4, "max": 16, "samples": 2 @@ -351,7 +351,10 @@ }, "debug draw": { "static": true, - "dynamic": true + "dynamic": true, + "contacts": false, + "constraints": true, + "rays": false }, "fixed step": true, "substeps": 4 diff --git a/bin/data/entities/cesiumMan.json b/bin/data/entities/cesiumMan.json index c29fc805..2b862d6e 100644 --- a/bin/data/entities/cesiumMan.json +++ b/bin/data/entities/cesiumMan.json @@ -14,7 +14,7 @@ "axis": [ 0, 1, 0 ], "angle": 0 }, - "scale": [ 5.0, 5.0, 5.0 ] + "scale": [ 10.0, 10.0, 10.0 ] }, "system": { "hot reload": { @@ -27,6 +27,9 @@ "debug": { "print": { "animations": true + }, + "draw": { + "armature": true } }, "exporter": { diff --git a/bin/data/entities/craeture.json b/bin/data/entities/craeture.json index e976401c..accf527d 100644 --- a/bin/data/entities/craeture.json +++ b/bin/data/entities/craeture.json @@ -2,8 +2,8 @@ "type": "Object", "name": "Craeture", "ignore": false, + "import": "./craetureModel.json", "assets": [ - "./craetureModel.json", "./scripts/craeture.lua" ], "behaviors": [ @@ -11,13 +11,10 @@ "SoundEmitterBehavior" ], "transform": { + "position": [ -7.04676, 10.169565, -44.0822 ], //"position": [ 0, 1.5, 21 ], - "position": [ 16.3489, 1.37972, -68.1571 ], - "rotation": { - "axis": [ 0, 1, 0 ], - "angle": 0 - }, - "scale": [ 1, 1, 1 ] + //"position": [ 16.3489, 1.37972, -68.1571 ], + "scale": [ 0.3, 0.3, 0.3 ] }, "system": { "hot reload": { @@ -26,7 +23,17 @@ }, "metadata": { "name": "Craeture", - + "graph": { + "debug": { + "draw": { + "armature": true + } + }, + "physics": { + "ragdoll": true + } + } + /* "physics": { "gravity": [ 0, -9.81, 0 ], "inertia": [ 0, 0, 0 ], @@ -41,5 +48,6 @@ "shared": false } + */ } } \ No newline at end of file diff --git a/bin/data/entities/craetureModel.json b/bin/data/entities/craetureModel.json index 701144da..99964191 100644 --- a/bin/data/entities/craetureModel.json +++ b/bin/data/entities/craetureModel.json @@ -7,9 +7,7 @@ // "/player/bear.glb" { "filename": "/player/bear/graph.json" } ], - "behaviors": [ - "CraetureBehavior" - ], + "behaviors": [], "transform": { "position": [ 0, -2.5, 0 ], // "position": [ 12.5715, 3.53811, 7.6238 ], @@ -18,7 +16,7 @@ "axis": [ 0, 1, 0 ], "angle": 0 }, - "scale": [ 0.35, 0.35, 0.35 ], + // "scale": [ 0.35, 0.35, 0.35 ], "reference": "parent" }, "system": { @@ -50,6 +48,7 @@ "filter": "linear", "flip textures": false, "invert": false, + "render": false, "skinned": true }, "animations": { diff --git a/bin/data/entities/door.json b/bin/data/entities/door.json index 2d24eed5..efbf47eb 100644 --- a/bin/data/entities/door.json +++ b/bin/data/entities/door.json @@ -6,8 +6,9 @@ "metadata": { "physics": { "mass": 0, - "inertia": [0, 0, 0], - "type": "bounding box" + // "inertia": [0, 0, 0], + // "type": "bounding box" + "type": "mesh" } } } \ No newline at end of file diff --git a/bin/data/shaders/base/line/frag.glsl b/bin/data/shaders/base/line/frag.glsl index 46d0c4f2..3b0467e4 100644 --- a/bin/data/shaders/base/line/frag.glsl +++ b/bin/data/shaders/base/line/frag.glsl @@ -5,5 +5,7 @@ layout (location = 0) in vec4 inColor; layout (location = 0) out vec4 outColor; void main() { - outColor = inColor; + // needs to be specified like this for some unknown reason + outColor.rgb = inColor.rgb; + outColor.a = inColor.a; } \ No newline at end of file diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index b291a825..1098bd7e 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -150,6 +150,7 @@ namespace uf { void UF_API override( pod::Graph& ); void UF_API animate( pod::Graph&, const uf::stl::string&, float = 1, bool = true ); + uf::stl::vector collectBones( const pod::Graph& graph, const pod::Node& node ); uf::stl::vector obbFromSkin( const pod::Graph& graph, const pod::Node& node ); void rigRagdoll( pod::Graph& graph, pod::Node& node ); diff --git a/engine/inc/uf/engine/graph/pod.inl b/engine/inc/uf/engine/graph/pod.inl index ad016571..6f66a386 100644 --- a/engine/inc/uf/engine/graph/pod.inl +++ b/engine/inc/uf/engine/graph/pod.inl @@ -58,6 +58,11 @@ namespace pod { uf::stl::vector inverseBindMatrices; }; + struct UF_API Bone { + pod::Vector3f start; + pod::Vector3f end; + }; + struct UF_API Animation { struct Sampler { uf::stl::string interpolator; diff --git a/engine/inc/uf/utils/math/physics/common.h b/engine/inc/uf/utils/math/physics/common.h index 6a69a410..908db8c5 100644 --- a/engine/inc/uf/utils/math/physics/common.h +++ b/engine/inc/uf/utils/math/physics/common.h @@ -12,7 +12,9 @@ namespace impl { pod::Transform<> getTransform( const pod::PhysicsBody& body ); pod::Vector3f getPosition( const pod::PhysicsBody& body, bool useTransform = false ); - + pod::Vector3f apply( const pod::Transform<>& t, const pod::Vector3f& p ); + pod::Vector3f applyInverse( const pod::Transform<>& t, const pod::Vector3f& p ); + pod::PhysicsBody physicsBodyHullView( const pod::PhysicsBody& body, int32_t index = -1 ); pod::PhysicsBody physicsBodyTriView( const pod::TriangleWithNormal triangle, const pod::PhysicsBody& body = {} ); pod::PhysicsBody physicsBodyTriView( const pod::PhysicsBody& body, size_t triID ); diff --git a/engine/inc/uf/utils/math/physics/impl.h b/engine/inc/uf/utils/math/physics/impl.h index 0387a3bd..a8b5baa4 100644 --- a/engine/inc/uf/utils/math/physics/impl.h +++ b/engine/inc/uf/utils/math/physics/impl.h @@ -58,8 +58,8 @@ namespace uf { const pod::CollisionEvent::events_t& UF_API getCollisionEvents( const pod::World& world ); pod::CollisionEvent::events_t UF_API getCollisionEvents( const pod::PhysicsBody& body ); - pod::PhysicsBody& UF_API create( uf::Object&, float mass = 0.0f, const pod::Vector3f& = {} ); - pod::PhysicsBody& UF_API create( pod::World&, uf::Object&, float mass = 0.0f, const pod::Vector3f& = {} ); + pod::PhysicsBody& UF_API create( uf::Object&, float mass = 0.0f, const pod::Vector3f& = {}, const pod::Quaternion<>& = {0,0,0,1} ); + pod::PhysicsBody& UF_API create( pod::World&, uf::Object&, float mass = 0.0f, const pod::Vector3f& = {}, const pod::Quaternion<>& = {0,0,0,1} ); void UF_API destroy( uf::Object& ); void UF_API destroy( pod::PhysicsBody& ); } diff --git a/engine/inc/uf/utils/math/physics/structs.h b/engine/inc/uf/utils/math/physics/structs.h index cdf51c69..48c50145 100644 --- a/engine/inc/uf/utils/math/physics/structs.h +++ b/engine/inc/uf/utils/math/physics/structs.h @@ -407,7 +407,8 @@ namespace pod { float inverseMass = 0.0f; int32_t viewIndex = -1; // -1 means it's not an aliased view - pod::Vector3f offset = {}; + pod::Vector3f offsetPosition = {}; + pod::Quaternion<> offsetOrientation = { 0, 0, 0, 1 }; pod::Vector3f velocity = {}; pod::Vector3f forceAccumulator = {}; @@ -437,7 +438,6 @@ namespace pod { bool resolveBlockContact = true; // attempts to resolve an invalid BlockNxN solve with an BlockN-1xN-1 solve bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK - pod::CollisionMask debugDraw = pod::Collider::CATEGORY_NONE; // draws wireframe of collision bodies bool async = false; // dedicated thread for physics sim float timestep = 1.0f / 60.0f; // timestep for fixed step ticks bool fixedStep = true; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime @@ -485,6 +485,13 @@ namespace pod { }; float groundedThreshold = 0.7f; // threshold before marking a body as grounded + + struct { + pod::CollisionMask mask = pod::Collider::CATEGORY_NONE; // draws wireframe of collision bodies + bool contacts = false; + bool constraints = false; + bool rays = false; + } debugDraw; }; struct World { diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index 348314af..d9ebf522 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -223,17 +223,24 @@ void UF_API uf::load( ext::json::Value& json ) { } auto& configEnginePhysicsDebugDrawJson = configEnginePhysicsJson["debug draw"]; if ( ext::json::isObject( configEnginePhysicsDebugDrawJson ) ) { - if ( configEnginePhysicsDebugDrawJson["static"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_STATIC; - if ( configEnginePhysicsDebugDrawJson["dynamic"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_DYNAMIC; - if ( configEnginePhysicsDebugDrawJson["player"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_PLAYER; - if ( configEnginePhysicsDebugDrawJson["npc"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_NPC; - if ( configEnginePhysicsDebugDrawJson["trigger"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_TRIGGER; - if ( configEnginePhysicsDebugDrawJson["projectile"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_PROJECTILE; - if ( configEnginePhysicsDebugDrawJson["character"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_CHARACTER; - if ( configEnginePhysicsDebugDrawJson["all"].as() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_ALL; + if ( configEnginePhysicsDebugDrawJson["static"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_STATIC; + if ( configEnginePhysicsDebugDrawJson["dynamic"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_DYNAMIC; + if ( configEnginePhysicsDebugDrawJson["player"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_PLAYER; + if ( configEnginePhysicsDebugDrawJson["npc"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_NPC; + if ( configEnginePhysicsDebugDrawJson["trigger"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_TRIGGER; + if ( configEnginePhysicsDebugDrawJson["projectile"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_PROJECTILE; + if ( configEnginePhysicsDebugDrawJson["character"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_CHARACTER; + if ( configEnginePhysicsDebugDrawJson["all"].as() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_ALL; + + uf::physics::settings.debugDraw.contacts = configEnginePhysicsDebugDrawJson["contacts"].as( uf::physics::settings.debugDraw.contacts ); + uf::physics::settings.debugDraw.constraints = configEnginePhysicsDebugDrawJson["constraints"].as( uf::physics::settings.debugDraw.constraints ); + uf::physics::settings.debugDraw.rays = configEnginePhysicsDebugDrawJson["rays"].as( uf::physics::settings.debugDraw.rays ); } else if ( configEnginePhysicsDebugDrawJson.is() && configEnginePhysicsDebugDrawJson.as() ) { - uf::physics::settings.debugDraw = pod::Collider::CATEGORY_ALL; + uf::physics::settings.debugDraw.mask = pod::Collider::CATEGORY_ALL; + uf::physics::settings.debugDraw.contacts = true; + uf::physics::settings.debugDraw.constraints = true; + uf::physics::settings.debugDraw.rays = true; } } diff --git a/engine/src/engine/graph/animation.cpp b/engine/src/engine/graph/animation.cpp index dfc54085..7eb6e7d8 100644 --- a/engine/src/engine/graph/animation.cpp +++ b/engine/src/engine/graph/animation.cpp @@ -76,17 +76,18 @@ namespace { #endif } - pod::Matrix4f localMatrix( pod::Graph& graph, int32_t index ) { + pod::Matrix4f localMatrix( const pod::Graph& graph, int32_t index ) { auto& node = 0 < index && index <= graph.nodes.size() ? graph.nodes[index] : graph.root; + auto& transform = node.transform; return - uf::matrix::translate( uf::matrix::identity(), node.transform.position ) * - uf::quaternion::matrix(node.transform.orientation) * - uf::matrix::scale( uf::matrix::identity(), node.transform.scale ) * - node.transform.model; + uf::matrix::translate( uf::matrix::identity(), transform.position ) * + uf::quaternion::matrix(transform.orientation) * + uf::matrix::scale( uf::matrix::identity(), transform.scale ) * + transform.model; } - pod::Matrix4f worldMatrix( pod::Graph& graph, int32_t index ) { + pod::Matrix4f worldMatrix( const pod::Graph& graph, int32_t index ) { pod::Matrix4f matrix = ::localMatrix( graph, index ); - auto& node = *uf::graph::find( graph, index ); + auto& node = 0 < index && index <= graph.nodes.size() ? graph.nodes[index] : graph.root; int32_t parent = node.parent; while ( 0 < parent && parent <= graph.nodes.size() ) { matrix = ::localMatrix( graph, parent ) * matrix; @@ -261,6 +262,25 @@ void uf::graph::updateAnimation( pod::Graph& graph, pod::Node& node ) { } // separate function in the event something later might need it +uf::stl::vector uf::graph::collectBones( const pod::Graph& graph, const pod::Node& node ) { + auto& storage = ::getGraphStorage(uf::scene::getCurrentScene()); + auto& name = graph.skins[node.skin]; + auto& skin = storage.skins[name]; + + uf::stl::vector bones( graph.nodes.size() ); + for ( auto i = 0; i < skin.joints.size(); ++i ) { + auto nodeID = skin.joints[i]; + auto& node = graph.nodes[nodeID]; + if ( node.parent < 0 ) continue; + auto& parent = graph.nodes[node.parent]; + + auto tA = uf::transform::flatten( parent.transform ); + auto tB = uf::transform::flatten( node.transform ); + + bones[nodeID] = pod::Bone{ tA.position, tB.position }; + } + return bones; +} uf::stl::vector uf::graph::obbFromSkin( const pod::Graph& graph, const pod::Node& node ) { const float wThresold = 0.15f; @@ -290,11 +310,11 @@ uf::stl::vector uf::graph::obbFromSkin( const pod::Graph& graph, const for ( auto w = 0; w < 4; ++w ) { if ( weights[w] <= wThresold ) continue; - uint16_t boneID = joints[w]; - pod::Vector3f localPos = uf::matrix::multiply( skin.inverseBindMatrices[boneID], pos ); + uint16_t jointID = joints[w]; + pod::Vector3f localPos = uf::matrix::multiply( skin.inverseBindMatrices[jointID], pos ); - bounds[boneID].center = uf::vector::min( bounds[boneID].center, localPos ); - bounds[boneID].extent = uf::vector::max( bounds[boneID].extent, localPos ); + bounds[jointID].center = uf::vector::min( bounds[jointID].center, localPos ); + bounds[jointID].extent = uf::vector::max( bounds[jointID].extent, localPos ); } } } @@ -310,5 +330,107 @@ uf::stl::vector uf::graph::obbFromSkin( const pod::Graph& graph, const } void uf::graph::rigRagdoll( pod::Graph& graph, pod::Node& node ) { - // scrapped for now + auto& storage = ::getGraphStorage(uf::scene::getCurrentScene()); + auto& name = graph.skins[node.skin]; + auto& skin = storage.skins[name]; + + auto bounds = uf::graph::obbFromSkin( graph, node ); + auto bones = uf::graph::collectBones( graph, node ); + auto armatureTransform = uf::transform::flatten( graph.root.entity->getComponent>() ); + + // create physics bodies + uf::stl::vector bodies( graph.nodes.size(), NULL ); + + for ( auto i = 0; i < skin.joints.size(); ++i ) { + auto nodeID = skin.joints[i]; + auto& node = graph.nodes[nodeID]; + if ( node.parent < 0 ) continue; + auto& entity = *node.entity; + auto transform = uf::transform::flatten( entity.getComponent>() ); + + // this definitely could be done better...... + auto& bone = bones[nodeID]; + auto& obb = bounds[i]; + // if ( obb.extent.x < 0 ) continue; + // transform center into world space + auto boneCenter = uf::transform::apply( armatureTransform, (bone.end + bone.start) * 0.5f ); + // transform back into local space + auto offset = uf::transform::applyInverse( transform, boneCenter ); + auto orientation = uf::quaternion::identity(); + float length = uf::vector::norm( offset ) * 2.0f; + + float mass = 100.0f; + auto& body = uf::physics::create( entity, mass, offset, orientation ); + if ( obb.extent.x < 0 ) { + uf::physics::initialize( body, pod::Sphere{ 0.1f } ); + } else { + uf::physics::initialize( body, pod::OBB{ pod::Vector3f{}, obb.extent * armatureTransform.scale } ); + } + //uf::physics::initialize( body, pod::Capsule{ 0.5f, length * 0.5f } ); + + bodies[nodeID] = &body; + } + + // create constraints + for ( auto i = 0; i < skin.joints.size(); ++i ) { + auto nodeID = skin.joints[i]; + auto& node = graph.nodes[nodeID]; + if ( !bodies[node.parent] ) continue; // no parent body, skip constraint + if ( !bodies[nodeID] ) continue; // no node body, skip constraint + + auto& bodyA = *bodies[node.parent]; + auto& bodyB = *bodies[nodeID]; + + auto tA = uf::transform::flatten( *bodyA.transform ); // bone start + auto tB = uf::transform::flatten( *bodyB.transform ); // bone end + + auto pivot = tA.position; + auto axis = uf::vector::normalize( tB.position - tA.position ); + + auto& constraint = uf::physics::constrain( bodyA, bodyB ); + uf::physics::constrainConeTwist( constraint, pivot, axis ); + } + + // works only pre-init and for no scaling +#if 0 + for ( auto i = 0; i < skin.joints.size(); ++i ) { + auto nodeID = skin.joints[i]; + auto& node = graph.nodes[nodeID]; + if ( node.parent < 0 ) continue; + auto& entity = *node.entity; + + auto boneStart = uf::transform::applyInverse( node.transform, pod::Vector3f{0, 0, 0} ); + auto offset = uf::vector::lerp( boneStart, pod::Vector3f{}, 0.5f ); + auto orientation = uf::quaternion::identity(); + // float length = ... + + float mass = 0.0f; + auto& body = uf::physics::create( entity, mass, offset, orientation ); + uf::physics::initialize( body, pod::Sphere{ 0.5f } ); + + bodies[nodeID] = &body; + } + + // create constraints + for ( auto i = 0; i < skin.joints.size(); ++i ) { + auto nodeID = skin.joints[i]; + auto& node = graph.nodes[nodeID]; + if ( node.parent < 0 ) continue; + auto& parent = graph.nodes[node.parent]; + if ( bodies.count( node.parent ) == 0 ) continue; + if ( bodies.count( nodeID ) == 0 ) continue; + + auto& bodyA = bodies[node.parent]; + auto& bodyB = bodies[nodeID]; + + auto tA = uf::transform::flatten( parent.transform ); + auto tB = uf::transform::flatten( node.transform ); + + auto pivot = tA.position; + auto axis = uf::vector::normalize( tB.position - tA.position ); + + auto& constraint = uf::physics::constrain( *bodyA, *bodyB ); + uf::physics::constrainConeTwist( constraint, pivot, axis ); + } +#endif } \ No newline at end of file diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index f7508442..31265832 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -1113,17 +1113,6 @@ void uf::graph::process( pod::Graph& graph ) { } */ - UF_DEBUG_TIMER_MULTITRACE("Rigging ragdolls"); - for ( auto& node : graph.nodes ) { - if ( node.skin < 0 || node.mesh < 0 ) continue; - ext::json::Value tag = ext::json::find( node.name, graphMetadataJson["tags"] ); - if ( ext::json::isNull( tag ) ) tag["physics"] = graphMetadataJson["physics"]; - if ( tag["physics"]["ragdoll"].as(false) ) { - uf::graph::rigRagdoll( graph, node ); - } - } - UF_DEBUG_TIMER_MULTITRACE_END("Rigged ragdolls."); - UF_DEBUG_TIMER_MULTITRACE("Updating master graph"); #if UF_GRAPH_EXTENDED uf::graph::reload( graph ); @@ -1406,6 +1395,15 @@ void uf::graph::initialize( pod::Graph& graph ) { if ( entity->getUid() == 0 ) entity->initialize(); }); + auto& graphMetadataJson = graph.metadata; + for ( auto& node : graph.nodes ) { + if ( node.skin < 0 || node.mesh < 0 ) continue; + ext::json::Value tag = ext::json::find( node.name, graphMetadataJson["tags"] ); + if ( ext::json::isNull( tag ) ) tag["physics"] = graphMetadataJson["physics"]; + if ( tag["physics"]["ragdoll"].as(false) ) { + uf::graph::rigRagdoll( graph, node ); + } + } auto& scene = uf::scene::getCurrentScene(); scene.invalidateGraph(); diff --git a/engine/src/engine/object/behaviors/graph.cpp b/engine/src/engine/object/behaviors/graph.cpp index 0cdb45d8..dfd0e8be 100644 --- a/engine/src/engine/object/behaviors/graph.cpp +++ b/engine/src/engine/object/behaviors/graph.cpp @@ -13,6 +13,7 @@ #include #include #include +#include UF_BEHAVIOR_REGISTER_CPP(uf::GraphBehavior) UF_BEHAVIOR_TRAITS_CPP(uf::GraphBehavior, ticks = true, renders = false, thread = "") @@ -68,13 +69,32 @@ void uf::GraphBehavior::initialize( uf::Object& self ) { } void uf::GraphBehavior::destroy( uf::Object& self ) {} void uf::GraphBehavior::tick( uf::Object& self ) { - /* Test */ { - TIMER(1, uf::inputs::kbm::states::T ) { - UF_MSG_DEBUG("Regenerating graph graphics..."); - auto& graph = this->getComponent(); - uf::graph::reload( graph ); + if ( !this->hasComponent() ) return; + auto& graph = this->getComponent(); + if ( !graph.metadata["debug"]["draw"]["armature"].as(false) ) return; + auto& transform = this->getComponent>(); + for ( auto& node : graph.nodes ) { + if ( node.skin < 0 || node.mesh < 0 ) continue; + auto bones = uf::graph::collectBones( graph, node ); + for ( auto& bone : bones ) { + bone.start = uf::transform::apply( transform, bone.start ); + bone.end = uf::transform::apply( transform, bone.end ); + + uf::debug::drawLine( bone.start, bone.end, pod::Vector4f{ 0, 1, 0, 1 } ); } } + + static bool hid = false; + if ( hid ) return; + hid = true; + this->process([&](uf::Entity* entity){ + if ( !entity->hasComponent() ) return; + auto& graphic = entity->getComponent(); + if ( !graphic.initialized ) return; + auto& descriptorSet = graphic.getDescriptorSet(); + descriptorSet.metadata.process = false; + uf::renderer::states::rebuild = true; + }); } void uf::GraphBehavior::render( uf::Object& self ) {} void uf::GraphBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ) {} diff --git a/engine/src/ext/lua/usertypes/physics.cpp b/engine/src/ext/lua/usertypes/physics.cpp index b6621342..667ca13f 100644 --- a/engine/src/ext/lua/usertypes/physics.cpp +++ b/engine/src/ext/lua/usertypes/physics.cpp @@ -31,7 +31,8 @@ namespace binds { pod::Transform<> getTransform( pod::PhysicsBody& body ) { pod::Transform<> t; - t.position = body.offset; + t.position = body.offsetPosition; + t.orientation = body.offsetOrientation; t.reference = body.transform; return uf::transform::flatten( t ); } diff --git a/engine/src/utils/debug/draw.cpp b/engine/src/utils/debug/draw.cpp index c7515946..d6d36d63 100644 --- a/engine/src/utils/debug/draw.cpp +++ b/engine/src/utils/debug/draw.cpp @@ -219,7 +219,7 @@ void uf::debug::draw( float dt ) { graphic.material.device = &uf::renderer::device; // to-do: bin by descriptor instead of one global set - graphic.descriptor.depth.test = true; + graphic.descriptor.depth.test = false; graphic.descriptor.depth.write = false; graphic.descriptor.renderTarget = 1; // "forward"; graphic.descriptor.topology = uf::renderer::enums::PrimitiveTopology::LINE_LIST; diff --git a/engine/src/utils/math/physics/common.cpp b/engine/src/utils/math/physics/common.cpp index aa8463bc..94ca8829 100644 --- a/engine/src/utils/math/physics/common.cpp +++ b/engine/src/utils/math/physics/common.cpp @@ -65,12 +65,9 @@ void impl::updateActivity( pod::PhysicsBody& body, float dt ) { // to-do: find a succinct way to explain this madness pod::Transform<> impl::getTransform( const pod::PhysicsBody& body ) { pod::Transform<> t; - t.position = body.offset; -#if UF_PHYSICS_SYNC_TRANSFORMS - t.reference = const_cast*>( &body.flattenedTransform ); -#else + t.position = body.offsetPosition; + t.orientation = body.offsetOrientation; t.reference = body.transform; -#endif return uf::transform::flatten( t ); } // get position of a body, uses bounds center or transform's position @@ -78,6 +75,23 @@ pod::Vector3f impl::getPosition( const pod::PhysicsBody& body, bool useTransform if ( !useTransform ) return impl::aabbCenter( body.bounds ); return impl::getTransform( body ).position; } +// applies a transform +pod::Vector3f impl::apply( const pod::Transform<>& t, const pod::Vector3f& p ) { + return uf::transform::apply( t, p ); +} +// applies an inverse transform +pod::Vector3f impl::applyInverse( const pod::Transform<>& t, const pod::Vector3f& p ) { + return uf::transform::applyInverse( t, p ); +} +/* +// these isometrically applies a transform +pod::Vector3f impl::apply( const pod::Transform<>& t, const pod::Vector3f& p ) { + return uf::quaternion::rotate( t.orientation, p + t.position ); +} +pod::Vector3f impl::applyInverse( const pod::Transform<>& t, const pod::Vector3f& p ) { + return uf::quaternion::rotate( uf::quaternion::inverse( t.orientation ), p - t.position ); +} +*/ // creates a view of a hull body pod::PhysicsBody impl::physicsBodyHullView( const pod::PhysicsBody& body, int32_t index ) { pod::PhysicsBody view = body; @@ -94,7 +108,8 @@ pod::PhysicsBody impl::physicsBodyTriView( const pod::TriangleWithNormal triangl view.collider.triangle.normal = impl::triangleNormal( (const pod::Triangle&) triangle ); } // assume triangle is already transformed - view.offset = {}; + view.offsetPosition = {}; + view.offsetOrientation = {0,0,0,1}; view.transform = NULL; return view; } diff --git a/engine/src/utils/math/physics/constraints/ballSocket.cpp b/engine/src/utils/math/physics/constraints/ballSocket.cpp index b8d3cc40..fc8553f0 100644 --- a/engine/src/utils/math/physics/constraints/ballSocket.cpp +++ b/engine/src/utils/math/physics/constraints/ballSocket.cpp @@ -14,8 +14,11 @@ void impl::solveBallSocketConstraint( pod::Constraint& constraint, float dt ) { auto tA = impl::getTransform( a ); auto tB = impl::getTransform( b ); - auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA ); - auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB ); + auto anchorA = joint.localAnchorA * tA.scale; + auto anchorB = joint.localAnchorB * tB.scale; + + auto rA = uf::quaternion::rotate( tA.orientation, anchorA ); + auto rB = uf::quaternion::rotate( tB.orientation, anchorB ); pod::Matrix3f K = {}; pod::Vector3f axes[3] = { {1,0,0}, {0,1,0}, {0,0,1} }; @@ -63,13 +66,12 @@ pod::Constraint& uf::physics::constrainBallSocket( pod::Constraint& constraint, void impl::drawBallSocket( const pod::Constraint& constraint ) { if ( !constraint.a || !constraint.b ) return; - auto tA = impl::getTransform(*constraint.a); - auto tB = impl::getTransform(*constraint.b); - const auto& joint = constraint.ballSocket; + auto tA = impl::getTransform( *constraint.a ); + auto tB = impl::getTransform( *constraint.b ); - auto pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA); - auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB); + auto pA = uf::transform::apply( tA, joint.localAnchorA ); + auto pB = uf::transform::apply( tB, joint.localAnchorB ); uf::debug::drawLine( tA.position, pA ); uf::debug::drawLine( tB.position, pB ); diff --git a/engine/src/utils/math/physics/constraints/coneTwist.cpp b/engine/src/utils/math/physics/constraints/coneTwist.cpp index ec6395b3..0159f53c 100644 --- a/engine/src/utils/math/physics/constraints/coneTwist.cpp +++ b/engine/src/utils/math/physics/constraints/coneTwist.cpp @@ -172,26 +172,26 @@ pod::Constraint& uf::physics::constrainConeTwist( pod::Constraint& constraint, c void impl::drawConeTwist( const pod::Constraint& constraint ) { if ( !constraint.a || !constraint.b ) return; - auto tA = impl::getTransform(*constraint.a); - auto tB = impl::getTransform(*constraint.b); const auto& joint = constraint.coneTwist; + auto tA = impl::getTransform( *constraint.a ); + auto tB = impl::getTransform( *constraint.b ); - auto pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA); - auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB); + auto pA = uf::transform::apply( tA, joint.localAnchorA ); + auto pB = uf::transform::apply( tB, joint.localAnchorB ); - auto taA = uf::quaternion::rotate(tA.orientation, joint.localTwistAxisA); - auto taB = uf::quaternion::rotate(tB.orientation, joint.localTwistAxisB); + auto taA = uf::quaternion::rotate( tA.orientation, joint.localTwistAxisA ); + auto taB = uf::quaternion::rotate( tB.orientation, joint.localTwistAxisB ); - auto raA = uf::quaternion::rotate(tA.orientation, joint.localReferenceAxisA); - auto raB = uf::quaternion::rotate(tB.orientation, joint.localReferenceAxisB); + auto raA = uf::quaternion::rotate( tA.orientation, joint.localReferenceAxisA ); + auto raB = uf::quaternion::rotate( tB.orientation, joint.localReferenceAxisB ); - uf::debug::drawLine( pA, pB ); + uf::debug::drawLine( pA, pB, pod::Vector4f{ 1, 1, 0, 1 } ); // yellow float axisLength = 0.5f; - uf::debug::drawLine( pA, pA + taA * axisLength ); - uf::debug::drawLine( pB, pB + taB * axisLength ); + uf::debug::drawLine( pA, pA + taA * axisLength, pod::Vector4f{ 1, 0, 1, 1 } ); // pink + uf::debug::drawLine( pB, pB + taB * axisLength, pod::Vector4f{ 1, 0, 1, 1 } ); float refLength = 0.25f; - uf::debug::drawLine( pA, pA + raA * refLength ); - uf::debug::drawLine( pB, pB + raB * refLength ); + uf::debug::drawLine( pA, pA + raA * refLength, pod::Vector4f{ 0, 1, 1, 1 } ); // cyan + uf::debug::drawLine( pB, pB + raB * refLength, pod::Vector4f{ 0, 1, 1, 1 } ); } \ No newline at end of file diff --git a/engine/src/utils/math/physics/constraints/distance.cpp b/engine/src/utils/math/physics/constraints/distance.cpp index 93698369..8b09405d 100644 --- a/engine/src/utils/math/physics/constraints/distance.cpp +++ b/engine/src/utils/math/physics/constraints/distance.cpp @@ -11,8 +11,11 @@ void impl::solveDistanceConstraint( pod::Constraint& constraint, float dt ) { auto tA = impl::getTransform( a ); auto tB = impl::getTransform( b ); - auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA ); - auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB ); + auto anchorA = joint.localAnchorA * tA.scale; + auto anchorB = joint.localAnchorB * tB.scale; + + auto rA = uf::quaternion::rotate( tA.orientation, anchorA ); + auto rB = uf::quaternion::rotate( tB.orientation, anchorB ); auto pA = tA.position + rA; auto pB = tB.position + rB; @@ -58,13 +61,12 @@ pod::Constraint& uf::physics::constrainDistance( pod::Constraint& constraint, co void impl::drawDistance( const pod::Constraint& constraint ) { if ( !constraint.a || !constraint.b ) return; - auto tA = impl::getTransform(*constraint.a); - auto tB = impl::getTransform(*constraint.b); - const auto& joint = constraint.distance; + auto tA = impl::getTransform( *constraint.a ); + auto tB = impl::getTransform( *constraint.b ); - auto pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA); - auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB); + auto pA = uf::transform::apply( tA, joint.localAnchorA ); + auto pB = uf::transform::apply( tB, joint.localAnchorB ); uf::debug::drawLine( tA.position, pA ); uf::debug::drawLine( tB.position, pB ); diff --git a/engine/src/utils/math/physics/constraints/hinge.cpp b/engine/src/utils/math/physics/constraints/hinge.cpp index 9f692038..09c0fe0e 100644 --- a/engine/src/utils/math/physics/constraints/hinge.cpp +++ b/engine/src/utils/math/physics/constraints/hinge.cpp @@ -76,16 +76,15 @@ pod::Constraint& uf::physics::constrainHinge( pod::Constraint& constraint, const void impl::drawHinge( const pod::Constraint& constraint ) { if ( !constraint.a || !constraint.b ) return; - auto tA = impl::getTransform(*constraint.a); - auto tB = impl::getTransform(*constraint.b); - const auto& joint = constraint.hinge; + auto tA = impl::getTransform( *constraint.a ); + auto tB = impl::getTransform( *constraint.b ); - auto pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA); - auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB); + auto pA = uf::transform::apply( tA, joint.localAnchorA ); + auto pB = uf::transform::apply( tB, joint.localAnchorB ); - auto aA = uf::quaternion::rotate(tA.orientation, joint.localAxisA); - auto aB = uf::quaternion::rotate(tB.orientation, joint.localAxisB); + auto aA = uf::quaternion::rotate( tA.orientation, joint.localAxisA ); + auto aB = uf::quaternion::rotate( tB.orientation, joint.localAxisB ); uf::debug::drawLine( pA, pB ); diff --git a/engine/src/utils/math/physics/constraints/slider.cpp b/engine/src/utils/math/physics/constraints/slider.cpp index 0fbfb8c9..f9ced31b 100644 --- a/engine/src/utils/math/physics/constraints/slider.cpp +++ b/engine/src/utils/math/physics/constraints/slider.cpp @@ -12,8 +12,11 @@ void impl::solveSliderConstraint( pod::Constraint& constraint, float dt ) { auto tA = impl::getTransform( a ); auto tB = impl::getTransform( b ); - auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA ); - auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB ); + auto anchorA = joint.localAnchorA * tA.scale; + auto anchorB = joint.localAnchorB * tB.scale; + + auto rA = uf::quaternion::rotate( tA.orientation, anchorA ); + auto rB = uf::quaternion::rotate( tB.orientation, anchorB ); auto pA = tA.position + rA; auto pB = tB.position + rB; diff --git a/engine/src/utils/math/physics/constraints/spring.cpp b/engine/src/utils/math/physics/constraints/spring.cpp index c44389b5..15155661 100644 --- a/engine/src/utils/math/physics/constraints/spring.cpp +++ b/engine/src/utils/math/physics/constraints/spring.cpp @@ -11,8 +11,11 @@ void impl::solveSpringConstraint( pod::Constraint& constraint, float dt ) { auto tA = impl::getTransform( a ); auto tB = impl::getTransform( b ); - auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA ); - auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB ); + auto anchorA = joint.localAnchorA * tA.scale; + auto anchorB = joint.localAnchorB * tB.scale; + + auto rA = uf::quaternion::rotate( tA.orientation, anchorA ); + auto rB = uf::quaternion::rotate( tB.orientation, anchorB ); auto worldAnchorA = tA.position + rA; auto worldAnchorB = tB.position + rB; diff --git a/engine/src/utils/math/physics/draw.cpp b/engine/src/utils/math/physics/draw.cpp index 050eb9c1..3a197784 100644 --- a/engine/src/utils/math/physics/draw.cpp +++ b/engine/src/utils/math/physics/draw.cpp @@ -7,6 +7,7 @@ void impl::drawManifold( const pod::Manifold& manifold ) { + if ( !uf::physics::settings.debugDraw.contacts ) return; for ( auto& contact : manifold.points ) { auto& start = contact.point; auto end = contact.point + (contact.normal * MIN(contact.penetration, 0.1f) * 2); @@ -15,7 +16,7 @@ void impl::drawManifold( const pod::Manifold& manifold ) { } } void impl::drawBody( const pod::PhysicsBody& body ) { - if ( !(body.collider.category & uf::physics::settings.debugDraw) ) return; + if ( !(body.collider.category & uf::physics::settings.debugDraw.mask) ) return; switch( body.collider.type ) { case pod::ShapeType::AABB: impl::drawAabb( body ); @@ -44,8 +45,9 @@ void impl::drawBody( const pod::PhysicsBody& body ) { } } void impl::drawConstraint( const pod::Constraint& constraint ) { - if ( !(constraint.a->collider.category & uf::physics::settings.debugDraw) ) return; - if ( !(constraint.b->collider.category & uf::physics::settings.debugDraw) ) return; + if ( !uf::physics::settings.debugDraw.constraints ) return; + if ( !(constraint.a->collider.category & uf::physics::settings.debugDraw.mask) ) return; + if ( !(constraint.b->collider.category & uf::physics::settings.debugDraw.mask) ) return; switch( constraint.type ) { case pod::ConstraintType::BALL_AND_SOCKET: diff --git a/engine/src/utils/math/physics/impl.cpp b/engine/src/utils/math/physics/impl.cpp index 39343f26..927a189e 100644 --- a/engine/src/utils/math/physics/impl.cpp +++ b/engine/src/utils/math/physics/impl.cpp @@ -66,7 +66,7 @@ void uf::physics::tick( pod::World& world, float dt ) { accumulator -= timestep; } - if ( uf::physics::settings.debugDraw != pod::Collider::CATEGORY_NONE ) impl::draw( world, dt ); + if ( uf::physics::settings.debugDraw.mask != pod::Collider::CATEGORY_NONE ) impl::draw( world, dt ); } void uf::physics::terminate() { uf::physics::terminate( uf::scene::getCurrentScene() ); @@ -241,7 +241,7 @@ void uf::physics::step( pod::World& world, float dt ) { // dispatch collision events impl::dispatchManifold( manifold, collisionEvents, activeCollisions, previousCollisions ); // draw collision events - if ( uf::physics::settings.debugDraw ) impl::drawManifold( manifold ); + if ( uf::physics::settings.debugDraw.contacts ) impl::drawManifold( manifold ); } // dispatch exiting collisions for ( auto& [ key, pair ] : previousCollisions ) { @@ -534,7 +534,7 @@ pod::CollisionEvent::events_t uf::physics::getCollisionEvents( const pod::Physic return res; } // body creation -pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, float mass, const pod::Vector3f& offset ) { +pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, float mass, const pod::Vector3f& positionOffset, const pod::Quaternion<>& orientationOffset ) { auto& root = object.getComponent(); auto isRoot = !root.world; auto& body = isRoot ? root : *(new pod::PhysicsBody); @@ -542,10 +542,8 @@ pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, fl body.world = &world; body.object = &object; body.transform = &object.getComponent>(); -#if UF_PHYSICS_SYNC_TRANSFORMS - body.flattenedTransform = uf::transform::flatten( *body.transform ); -#endif - body.offset = offset; + body.offsetPosition = positionOffset; + body.offsetOrientation = orientationOffset; body.inverseMass = mass == 0.0f ? 0.0f : 1.0f / mass; body.collider.type = {}; @@ -578,8 +576,8 @@ pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, fl return body; } -pod::PhysicsBody& uf::physics::create( uf::Object& object, float mass, const pod::Vector3f& offset ) { - return create( getWorld(), object, mass, offset ); +pod::PhysicsBody& uf::physics::create( uf::Object& object, float mass, const pod::Vector3f& positionOffset, const pod::Quaternion<>& orientationOffset ) { + return create( getWorld(), object, mass, positionOffset, orientationOffset ); } void uf::physics::destroy( uf::Object& object ) { diff --git a/engine/src/utils/math/physics/narrowphase/epa.cpp b/engine/src/utils/math/physics/narrowphase/epa.cpp index 61e97a22..48b592cb 100644 --- a/engine/src/utils/math/physics/narrowphase/epa.cpp +++ b/engine/src/utils/math/physics/narrowphase/epa.cpp @@ -178,7 +178,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di bool impl::generateClippingManifold( const pod::PhysicsBody& a, const pod::PhysicsBody& b, const pod::Contact& contact, pod::Manifold& manifold ) { if ( !uf::vector::isValid(contact.point) ) return false; - auto& normal = contact.normal; + auto normal = contact.normal; pod::Vector3f polyA[4]; pod::Vector3f polyB[4]; @@ -192,18 +192,31 @@ bool impl::generateClippingManifold( const pod::PhysicsBody& a, const pod::Physi return true; } - // to-do: reference face is the most perpendicular face - pod::Vector3f* refPoly = polyA; - pod::Vector3f* incPoly = polyB; - int refCount = countA; - int incCount = countB; + pod::Vector3f normalA = uf::vector::normalize(uf::vector::cross(polyA[1] - polyA[0], polyA[2] - polyA[0])); + pod::Vector3f normalB = uf::vector::normalize(uf::vector::cross(polyB[1] - polyB[0], polyB[2] - polyB[0])); + + float dotA = uf::vector::dot(normalA, normal); + float dotB = uf::vector::dot(normalB, -normal); + + pod::Vector3f* refPoly; + pod::Vector3f* incPoly; + int refCount, incCount; + pod::Vector3f refNormal; + + if ( dotA > dotB ) { + refPoly = polyA; refCount = countA; refNormal = normalA; + incPoly = polyB; incCount = countB; + } else { + refPoly = polyB; refCount = countB; refNormal = normalB; + incPoly = polyA; incCount = countA; + + normal = -normal; + } pod::Vector3f clipBuffer[8]; int clipCount = incCount; for ( auto i = 0; i < incCount; ++i ) clipBuffer[i] = incPoly[i]; - pod::Vector3f refNormal = uf::vector::normalize(uf::vector::cross(refPoly[1] - refPoly[0], refPoly[2] - refPoly[0])); - for ( auto i = 0; i < refCount; ++i ) { pod::Vector3f edgeStart = refPoly[i]; pod::Vector3f edgeEnd = refPoly[(i + 1) % refCount]; diff --git a/engine/src/utils/math/physics/narrowphase/ray.cpp b/engine/src/utils/math/physics/narrowphase/ray.cpp index 370f8028..9ce1da6f 100644 --- a/engine/src/utils/math/physics/narrowphase/ray.cpp +++ b/engine/src/utils/math/physics/narrowphase/ray.cpp @@ -342,7 +342,7 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world } } - if ( uf::physics::settings.debugDraw ) impl::drawRay( ray, rayHit ); + if ( uf::physics::settings.debugDraw.rays ) impl::drawRay( ray, rayHit ); return rayHit; } \ No newline at end of file