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)
This commit is contained in:
parent
94ce7be618
commit
5402aff2a8
@ -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
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@ -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": {
|
||||
|
||||
@ -6,8 +6,9 @@
|
||||
"metadata": {
|
||||
"physics": {
|
||||
"mass": 0,
|
||||
"inertia": [0, 0, 0],
|
||||
"type": "bounding box"
|
||||
// "inertia": [0, 0, 0],
|
||||
// "type": "bounding box"
|
||||
"type": "mesh"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<pod::Bone> collectBones( const pod::Graph& graph, const pod::Node& node );
|
||||
uf::stl::vector<pod::OBB> obbFromSkin( const pod::Graph& graph, const pod::Node& node );
|
||||
void rigRagdoll( pod::Graph& graph, pod::Node& node );
|
||||
|
||||
|
||||
@ -58,6 +58,11 @@ namespace pod {
|
||||
uf::stl::vector<pod::Matrix4f> inverseBindMatrices;
|
||||
};
|
||||
|
||||
struct UF_API Bone {
|
||||
pod::Vector3f start;
|
||||
pod::Vector3f end;
|
||||
};
|
||||
|
||||
struct UF_API Animation {
|
||||
struct Sampler {
|
||||
uf::stl::string interpolator;
|
||||
|
||||
@ -12,6 +12,8 @@ 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 = {} );
|
||||
|
||||
@ -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& );
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_STATIC;
|
||||
if ( configEnginePhysicsDebugDrawJson["dynamic"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_DYNAMIC;
|
||||
if ( configEnginePhysicsDebugDrawJson["player"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_PLAYER;
|
||||
if ( configEnginePhysicsDebugDrawJson["npc"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_NPC;
|
||||
if ( configEnginePhysicsDebugDrawJson["trigger"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_TRIGGER;
|
||||
if ( configEnginePhysicsDebugDrawJson["projectile"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_PROJECTILE;
|
||||
if ( configEnginePhysicsDebugDrawJson["character"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_CHARACTER;
|
||||
if ( configEnginePhysicsDebugDrawJson["all"].as<bool>() ) uf::physics::settings.debugDraw |= pod::Collider::CATEGORY_ALL;
|
||||
if ( configEnginePhysicsDebugDrawJson["static"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_STATIC;
|
||||
if ( configEnginePhysicsDebugDrawJson["dynamic"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_DYNAMIC;
|
||||
if ( configEnginePhysicsDebugDrawJson["player"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_PLAYER;
|
||||
if ( configEnginePhysicsDebugDrawJson["npc"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_NPC;
|
||||
if ( configEnginePhysicsDebugDrawJson["trigger"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_TRIGGER;
|
||||
if ( configEnginePhysicsDebugDrawJson["projectile"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_PROJECTILE;
|
||||
if ( configEnginePhysicsDebugDrawJson["character"].as<bool>() ) uf::physics::settings.debugDraw.mask |= pod::Collider::CATEGORY_CHARACTER;
|
||||
if ( configEnginePhysicsDebugDrawJson["all"].as<bool>() ) 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<bool>() && configEnginePhysicsDebugDrawJson.as<bool>() ) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<pod::Bone> 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<pod::Bone> 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<pod::OBB> uf::graph::obbFromSkin( const pod::Graph& graph, const pod::Node& node ) {
|
||||
const float wThresold = 0.15f;
|
||||
|
||||
@ -290,11 +310,11 @@ uf::stl::vector<pod::OBB> 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<pod::OBB> 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<pod::Transform<>>() );
|
||||
|
||||
// create physics bodies
|
||||
uf::stl::vector<pod::PhysicsBody*> 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<pod::Transform<>>() );
|
||||
|
||||
// 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
|
||||
}
|
||||
@ -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<bool>(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<bool>(false) ) {
|
||||
uf::graph::rigRagdoll( graph, node );
|
||||
}
|
||||
}
|
||||
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
scene.invalidateGraph();
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include <uf/utils/mesh/mesh.h>
|
||||
#include <uf/utils/string/hash.h>
|
||||
#include <uf/utils/io/inputs.h>
|
||||
#include <uf/utils/debug/draw.h>
|
||||
|
||||
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...");
|
||||
if ( !this->hasComponent<pod::Graph>() ) return;
|
||||
auto& graph = this->getComponent<pod::Graph>();
|
||||
uf::graph::reload( graph );
|
||||
if ( !graph.metadata["debug"]["draw"]["armature"].as<bool>(false) ) return;
|
||||
auto& transform = this->getComponent<pod::Transform<>>();
|
||||
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<uf::Graphic>() ) return;
|
||||
auto& graphic = entity->getComponent<uf::Graphic>();
|
||||
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 ) {}
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<pod::Transform<>*>( &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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
const auto& joint = constraint.ballSocket;
|
||||
auto tA = impl::getTransform( *constraint.a );
|
||||
auto tB = impl::getTransform( *constraint.b );
|
||||
|
||||
const auto& joint = constraint.ballSocket;
|
||||
|
||||
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 );
|
||||
|
||||
@ -172,12 +172,12 @@ pod::Constraint& uf::physics::constrainConeTwist( pod::Constraint& constraint, c
|
||||
void impl::drawConeTwist( const pod::Constraint& constraint ) {
|
||||
if ( !constraint.a || !constraint.b ) return;
|
||||
|
||||
const auto& joint = constraint.coneTwist;
|
||||
auto tA = impl::getTransform( *constraint.a );
|
||||
auto tB = impl::getTransform( *constraint.b );
|
||||
const auto& joint = constraint.coneTwist;
|
||||
|
||||
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 );
|
||||
@ -185,13 +185,13 @@ void impl::drawConeTwist( const pod::Constraint& constraint ) {
|
||||
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 } );
|
||||
}
|
||||
@ -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;
|
||||
|
||||
const auto& joint = constraint.distance;
|
||||
auto tA = impl::getTransform( *constraint.a );
|
||||
auto tB = impl::getTransform( *constraint.b );
|
||||
|
||||
const auto& joint = constraint.distance;
|
||||
|
||||
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 );
|
||||
|
||||
@ -76,13 +76,12 @@ pod::Constraint& uf::physics::constrainHinge( pod::Constraint& constraint, const
|
||||
void impl::drawHinge( const pod::Constraint& constraint ) {
|
||||
if ( !constraint.a || !constraint.b ) return;
|
||||
|
||||
const auto& joint = constraint.hinge;
|
||||
auto tA = impl::getTransform( *constraint.a );
|
||||
auto tB = impl::getTransform( *constraint.b );
|
||||
|
||||
const auto& joint = constraint.hinge;
|
||||
|
||||
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 );
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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<pod::PhysicsBody>();
|
||||
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<pod::Transform<>>();
|
||||
#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 ) {
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user