even more inane code cleanup, migrated immediate mode debug draw into its own file, added transform flattening pre/post physics step (for two reasons)

This commit is contained in:
ecker 2026-05-26 22:08:16 -05:00
parent 0a92fed14a
commit 94ce7be618
42 changed files with 670 additions and 295 deletions

View File

@ -350,8 +350,8 @@
"max": 0.01 // 0.2
},
"debug draw": {
"static": false,
"dynamic": false
"static": true,
"dynamic": true
},
"fixed step": true,
"substeps": 4

View File

@ -0,0 +1,55 @@
{
"type": "Object",
"name": "Cesium Man Model",
"ignore": false,
"import": "/model.json",
"assets": [
// "/cesiumMan/cesiumMan.glb"
{ "filename": "/cesiumMan/cesiumMan/graph.json" }
],
"behaviors": [],
"transform": {
"position": [ -10, 1, -50 ],
"rotation": {
"axis": [ 0, 1, 0 ],
"angle": 0
},
"scale": [ 5.0, 5.0, 5.0 ]
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"graph": {
"key": "CesiumMan",
"debug": {
"print": {
"animations": true
}
},
"exporter": {
"enabled": true,
"unwrap": false,
"optimize": false
},
"baking": {
"enabled": false
},
"lights": {
"lightmap": false
},
"renderer": {
"cull mode": "front",
"filter": "linear",
"flip textures": false,
"invert": false,
"skinned": true
},
"physics": {
"ragdoll": true
}
}
}
}

View File

@ -16,7 +16,7 @@
"events": {
"click": {
"name": "game:Scene.Load",
"payload": { "scene": "Sponza" },
"payload": { "scene": "SourceEngine" },
"delay": 0.125
}
},

View File

@ -3,8 +3,9 @@
"assets": [
// { "filename": "./models/mds_mcdonalds.glb" }
{ "filename": "./models/mds_mcdonalds/graph.json" }
,{ "filename": "/ragdoll.json", "delay": 1 }
// ,{ "filename": "/ragdoll.json", "delay": 1 }
,{ "filename": "/craeture.json", "delay": 2.0 }
// ,{ "filename": "/cesiumMan.json", "delay": 2.0 }
],
"metadata": {
"graph": {
@ -16,7 +17,7 @@
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
"prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
// "prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
// regex matches
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },

View File

@ -82,7 +82,7 @@
#endif
#if BARYCENTRIC
#ifndef BARYCENTRIC_CALCULATE
#define BARYCENTRIC_CALCULATE 1
#define BARYCENTRIC_CALCULATE 0
#endif
#ifndef BUFFER_REFERENCE
#define BUFFER_REFERENCE 1

View File

@ -0,0 +1,21 @@
#pragma once
#include <uf/config.h>
#include <uf/utils/math/shapes.h>
#include <uf/utils/math/vector.h>
#include <uf/utils/math/transform.h>
namespace uf {
namespace debug {
void UF_API drawLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 } );
void UF_API drawAabb( pod::AABB aabb, pod::Transform<> transform = {} );
void UF_API drawObb( pod::OBB obb, pod::Transform<> transform = {} );
void UF_API drawSphere( pod::Sphere sphere, pod::Transform<> transform = {} );
void UF_API drawCapsule( pod::Capsule capsule, pod::Transform<> transform = {} );
void UF_API drawPlane( pod::Plane plane, pod::Transform<> transform = {} );
void UF_API drawTriangle( pod::Triangle tri, pod::Transform<> transform = {} );
void UF_API addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 }, float ttl = 1.0f );
void UF_API draw( float dt = 0 );
}
}

View File

@ -4,6 +4,7 @@
namespace impl {
void solveBallSocketConstraint( pod::Constraint& constraint, float dt );
void drawBallSocket( const pod::Constraint& constraint );
}
namespace uf {

View File

@ -4,6 +4,7 @@
namespace impl {
void solveConeTwistConstraint( pod::Constraint& constraint, float dt );
void drawConeTwist( const pod::Constraint& constraint );
}
namespace uf {

View File

@ -4,6 +4,7 @@
namespace impl {
void solveDistanceConstraint( pod::Constraint& constraint, float dt );
void drawDistance( const pod::Constraint& contact );
}
namespace uf {

View File

@ -4,6 +4,7 @@
namespace impl {
void solveHingeConstraint( pod::Constraint& constraint, float dt );
void drawHinge( const pod::Constraint& constraint );
}
namespace uf {

View File

@ -4,6 +4,7 @@
namespace impl {
void solveSliderConstraint( pod::Constraint& constraint, float dt );
void drawSlider( const pod::Constraint& constraint );
}
namespace uf {

View File

@ -4,6 +4,7 @@
namespace impl {
void solveSpringConstraint( pod::Constraint& constraint, float dt );
void drawSpring( const pod::Constraint& constraint );
}
namespace uf {

View File

@ -4,6 +4,7 @@
namespace impl {
void solveWeldConstraint( pod::Constraint& constraint, float dt );
void drawWeld( const pod::Constraint& constraint );
}
namespace uf {

View File

@ -1,19 +1,11 @@
#pragma once
#include "structs.h"
#include <uf/utils/debug/draw.h>
namespace impl {
struct Vertex {
pod::Vector3f position;
pod::Vector4f color;
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
};
/*FORCE_INLINE*/ void addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 } );
/*FORCE_INLINE*/ void addTransientLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 }, const pod::PhysicsBody* a = NULL, const pod::PhysicsBody* b = NULL );
void drawManifold( const pod::Manifold& manifold );
void drawBody( const pod::PhysicsBody& body );
void drawConstraint( const pod::Constraint& constraint );
void draw( const pod::World& world, float dt = 0 );
}

View File

@ -447,6 +447,8 @@ namespace pod {
uint32_t broadphaseBvhCapacity = 1; // number of bodies per leaf node
uint32_t meshBvhCapacity = 1; // number of triangles per leaf node
bool flattenTransforms = true; // flatten transforms before a step, then unflattens it at end of step (to solve a problem involving hierarchies)
// additionally flattens a BVH for linear iteration, rather than a recursive / stack-based traversal
bool flattenBvhBodies = true;
bool flattenBvhMeshes = true;

View File

@ -1,6 +1,8 @@
#include <uf/engine/graph/graph.h>
#include <uf/ext/gltf/gltf.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/math/physics/constraints.h>
#include <uf/utils/math/physics/common.h>
#include <uf/utils/mesh/grid.h>
#include <uf/utils/thread/thread.h>
#include <uf/utils/string/base64.h>
@ -301,72 +303,12 @@ uf::stl::vector<pod::OBB> uf::graph::obbFromSkin( const pod::Graph& graph, const
for ( auto& box : bounds ) {
auto extent = (box.extent - box.center) * 0.5f;
auto center = (box.extent + box.center) * 0.5f;
box = pod::OBB{ center, extent * 0.5f };
box = pod::OBB{ center, extent };
}
return bounds;
}
void uf::graph::rigRagdoll( pod::Graph& graph, pod::Node& node ) {
auto& storage = ::getGraphStorage(uf::scene::getCurrentScene());
auto& name = graph.skins[node.skin];
auto& skin = storage.skins[name];
auto bounds = uf::graph::obbFromSkin( graph, node );
uf::stl::unordered_map<int32_t, pod::PhysicsBody*> bodies;
// create physics bodies
const float density = 1.0f;
for ( auto i = 0; i < skin.joints.size(); ++i ) {
auto& obb = bounds[i];
if ( obb.extent.x < 0 ) continue; // invalid bounds
auto jointID = skin.joints[i]; // gLTF standard guarantees a jointID is the nodeID
auto& node = graph.nodes[jointID];
auto& entity = *node.entity;
auto offset = obb.center;
/*
auto matrix = ::worldMatrix( graph, jointID );
auto offset = uf::matrix::multiply( matrix, obb.center );
*/
float volume = 8.0f * obb.extent.x * obb.extent.y * obb.extent.z;
float mass = 10.0f; // volume * density;
auto& body = uf::physics::create( entity, mass, offset );
uf::physics::initialize( body, pod::OBB{ pod::Vector3f{}, obb.extent } );
bodies[jointID] = &body;
}
// create constraints
for ( auto i = 0; i < skin.joints.size(); ++i ) {
int32_t jointID = skin.joints[i];
auto& node = graph.nodes[jointID];
// no body: cannot constrain
if ( bodies.count( jointID ) == 0 ) continue;
// no parent: cannot constrain
if ( bodies.count( node.parent ) == 0 ) continue;
auto* bodyA = bodies[node.parent];
auto* bodyB = bodies[jointID];
auto matrixA = ::worldMatrix( graph, node.parent );
auto matrixB = ::worldMatrix( graph, jointID );
auto pivotA = uf::matrix::extractTranslation( matrixA );
auto pivotB = uf::matrix::extractTranslation( matrixB );
auto pivot = pivotB; // pivot is where the bone starts
auto axis = uf::vector::normalize( pivotB - pivotA );
// fallback
if ( uf::vector::distanceSquared( pivotB, pivotA ) < EPS2 ) {
axis = uf::quaternion::rotate( uf::matrix::extractRotation(matrixB), pod::Vector3f{0, 1, 0} );
}
// auto& constraint = uf::physics::constrain( *bodyA, *bodyB );
// uf::physics::constrainConeTwist( constraint, pivot, axis );
}
// scrapped for now
}

View File

@ -1105,6 +1105,12 @@ void uf::graph::process( pod::Graph& graph ) {
UF_MSG_DEBUG("\tImage: {}", name);
}
}
if ( graphMetadataJson["debug"]["print"]["animations"].as<bool>() ) {
UF_MSG_DEBUG("Animations: {}", graph.animations.size());
for ( auto& name : graph.animations ) {
UF_MSG_DEBUG("\tAnimation: {}", name);
}
}
*/
UF_DEBUG_TIMER_MULTITRACE("Rigging ragdolls");
@ -1358,7 +1364,10 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
}
}
for ( auto index : node.children ) uf::graph::process( graph, index, entity );
for ( auto childIndex : node.children ) {
uf::graph::process( graph, childIndex, entity );
graph.nodes[childIndex].parent = index;
}
}
void uf::graph::destroy( pod::Graph& graph ) {
@ -1397,6 +1406,7 @@ void uf::graph::initialize( pod::Graph& graph ) {
if ( entity->getUid() == 0 ) entity->initialize();
});
auto& scene = uf::scene::getCurrentScene();
scene.invalidateGraph();
}

View File

@ -5,6 +5,7 @@
#include <uf/utils/camera/camera.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/renderer/renderer.h>
#include <uf/utils/debug/draw.h>
#include <uf/utils/io/fmt.h>
#include <uf/engine/ext.h>
#include <regex>
@ -294,6 +295,8 @@ void uf::scene::tick() {
}
uf::graph::tick( scene );
uf::debug::draw( uf::time::delta );
}
void uf::scene::render() {
if ( scenes.empty() ) return;

View File

@ -21,7 +21,10 @@
#if BARYCENTRIC
// 0 keeps a buffer for barycentric coordinates, 1 will reconstruct in the deferred pass
#ifndef BARYCENTRIC_CALCULATE
#define BARYCENTRIC_CALCULATE 1
// currently has issues with:
// * skinned meshes because I'm not applying joint transformations
// * this weird texture offsetting from movement despite having fixed this with the original camera buffers
#define BARYCENTRIC_CALCULATE 0
#endif
#endif

View File

@ -0,0 +1,254 @@
#include <uf/utils/debug/draw.h>
#include <uf/engine/scene/scene.h>
#include <uf/engine/graph/graph.h>
namespace impl {
struct Vertex {
pod::Vector3f position;
pod::Vector4f color;
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
};
struct Line {
pod::Vector3f start = {};
pod::Vector3f end = {};
pod::Vector4f color = { 1, 1, 1, 1 };
float ttl = 0;
};
float decayRate = 0.25f;
uf::stl::vector<impl::Vertex> lines;
uf::stl::unordered_map<size_t, impl::Line> transientLines;
size_t getHash( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color ) {
size_t hash = 0;
uf::hash(hash, start, end, color);
return hash;
}
}
UF_VERTEX_DESCRIPTOR(impl::Vertex,
UF_VERTEX_DESCRIPTION(impl::Vertex, R32G32B32_SFLOAT, position)
UF_VERTEX_DESCRIPTION(impl::Vertex, R32G32B32A32_SFLOAT, color)
)
void uf::debug::drawLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color ) {
impl::lines.emplace_back( impl::Vertex{ start, color } );
impl::lines.emplace_back( impl::Vertex{ end, color } );
}
void uf::debug::drawAabb( pod::AABB aabb, pod::Transform<> transform ) {
pod::Vector3f corners[8] = {
{aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
{aabb.min.x, aabb.min.y, aabb.max.z}, {aabb.max.x, aabb.min.y, aabb.max.z},
{aabb.max.x, aabb.max.y, aabb.max.z}, {aabb.min.x, aabb.max.y, aabb.max.z}
};
// bottom face
uf::debug::drawLine( corners[0], corners[1] ); uf::debug::drawLine( corners[1], corners[2] );
uf::debug::drawLine( corners[2], corners[3] ); uf::debug::drawLine( corners[3], corners[0] );
// top face
uf::debug::drawLine( corners[4], corners[5] ); uf::debug::drawLine( corners[5], corners[6] );
uf::debug::drawLine( corners[6], corners[7] ); uf::debug::drawLine( corners[7], corners[4] );
// vertical edges
uf::debug::drawLine( corners[0], corners[4] ); uf::debug::drawLine( corners[1], corners[5] );
uf::debug::drawLine( corners[2], corners[6] ); uf::debug::drawLine( corners[3], corners[7] );
}
void uf::debug::drawObb( pod::OBB obb, pod::Transform<> transform ) {
auto aabb = pod::AABB{
.min = obb.center - obb.extent,
.max = obb.center + obb.extent,
};
pod::Vector3f corners[8] = {
{aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
{aabb.min.x, aabb.min.y, aabb.max.z}, {aabb.max.x, aabb.min.y, aabb.max.z},
{aabb.max.x, aabb.max.y, aabb.max.z}, {aabb.min.x, aabb.max.y, aabb.max.z}
};
FOR_EACH( 8, {
corners[i] = uf::transform::apply(transform, corners[i]);
});
// bottom face
uf::debug::drawLine( corners[0], corners[1] ); uf::debug::drawLine( corners[1], corners[2] );
uf::debug::drawLine( corners[2], corners[3] ); uf::debug::drawLine( corners[3], corners[0] );
// top face
uf::debug::drawLine( corners[4], corners[5] ); uf::debug::drawLine( corners[5], corners[6] );
uf::debug::drawLine( corners[6], corners[7] ); uf::debug::drawLine( corners[7], corners[4] );
// vertical edges
uf::debug::drawLine( corners[0], corners[4] ); uf::debug::drawLine( corners[1], corners[5] );
uf::debug::drawLine( corners[2], corners[6] ); uf::debug::drawLine( corners[3], corners[7] );
}
void uf::debug::drawSphere( pod::Sphere sphere, pod::Transform<> transform ) {
const int segments = 16;
const float angleIncrement = (2.0f * M_PI) / segments;
for ( auto i = 0; i < segments; ++i ) {
float theta1 = i * angleIncrement;
float theta2 = (i + 1) * angleIncrement;
float c1 = std::cos(theta1) * sphere.radius;
float s1 = std::sin(theta1) * sphere.radius;
float c2 = std::cos(theta2) * sphere.radius;
float s2 = std::sin(theta2) * sphere.radius;
pod::Vector3f xy1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, s1, 0.0f});
pod::Vector3f xy2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, s2, 0.0f});
pod::Vector3f xz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, 0.0f, s1});
pod::Vector3f xz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, 0.0f, s2});
pod::Vector3f yz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c1, s1});
pod::Vector3f yz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c2, s2});
uf::debug::drawLine( transform.position + xy1, transform.position + xy2 );
uf::debug::drawLine( transform.position + xz1, transform.position + xz2 );
uf::debug::drawLine( transform.position + yz1, transform.position + yz2 );
}
}
// for some reason compiling these two and not even using them causes physics to break
/*
void uf::debug::drawCapsule( pod::Capsule capsule, pod::Transform<> transform ) {
const pod::Vector3f up = uf::quaternion::rotate( transform.orientation, pod::Vector3f{0,1,0} );
auto p1 = transform.position + up * capsule.halfHeight;
auto p2 = transform.position - up * capsule.halfHeight;
const int segments = 16;
const float angleIncrement = (2.0f * M_PI) / segments;
for ( auto i = 0; i < segments; ++i ) {
float theta1 = i * angleIncrement;
float theta2 = (i + 1) * angleIncrement;
float c1 = std::cos(theta1) * capsule.radius;
float s1 = std::sin(theta1) * capsule.radius;
float c2 = std::cos(theta2) * capsule.radius;
float s2 = std::sin(theta2) * capsule.radius;
pod::Vector3f xy1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, s1, 0.0f});
pod::Vector3f xy2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, s2, 0.0f});
pod::Vector3f xz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, 0.0f, s1});
pod::Vector3f xz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, 0.0f, s2});
pod::Vector3f yz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c1, s1});
pod::Vector3f yz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c2, s2});
uf::debug::drawLine( p1 + xy1, p1 + xy2 );
uf::debug::drawLine( p1 + xz1, p1 + xz2 );
uf::debug::drawLine( p1 + yz1, p1 + yz2 );
uf::debug::drawLine( p2 + xy1, p2 + xy2 );
uf::debug::drawLine( p2 + xz1, p2 + xz2 );
uf::debug::drawLine( p2 + yz1, p2 + yz2 );
}
pod::Vector3f rx = uf::quaternion::rotate(transform.orientation, pod::Vector3f{capsule.radius, 0.0f, 0.0f});
pod::Vector3f ry = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, capsule.radius, 0.0f});
pod::Vector3f rz = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, 0.0f, capsule.radius});
uf::debug::drawLine( p1 + rx, p2 + rx );
uf::debug::drawLine( p1 - rx, p2 - rx );
uf::debug::drawLine( p1 + ry, p2 + ry );
uf::debug::drawLine( p1 - ry, p2 - ry );
uf::debug::drawLine( p1 + rz, p2 + rz );
uf::debug::drawLine( p1 - rz, p2 - rz );
}
// to-do: properly implement this
void uf::debug::drawPlane( pod::Plane plane, pod::Transform<> transform ) {
pod::Vector3f right = uf::quaternion::rotate(transform.orientation, pod::Vector3f{1, 0, 0});
pod::Vector3f forward = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 0, 1});
float size = 10.0f;
pod::Vector3f p0 = transform.position + (right * size) + (forward * size);
pod::Vector3f p1 = transform.position - (right * size) + (forward * size);
pod::Vector3f p2 = transform.position - (right * size) - (forward * size);
pod::Vector3f p3 = transform.position + (right * size) - (forward * size);
uf::debug::drawLine( p0, p1 );
uf::debug::drawLine( p1, p2 );
uf::debug::drawLine( p2, p3 );
uf::debug::drawLine( p3, p0 );
uf::debug::drawLine( p0, p2 );
uf::debug::drawLine( p1, p3 );
}
*/
void uf::debug::drawTriangle( pod::Triangle tri, pod::Transform<> transform ) {
pod::Vector3f v0 = uf::transform::apply(transform, tri.points[0]);
pod::Vector3f v1 = uf::transform::apply(transform, tri.points[1]);
pod::Vector3f v2 = uf::transform::apply(transform, tri.points[2]);
uf::debug::drawLine( v0, v1 );
uf::debug::drawLine( v1, v2 );
uf::debug::drawLine( v2, v0 );
}
void uf::debug::addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color, float ttl ) {
impl::transientLines[impl::getHash( start, end, color )] = impl::Line{ start, end, color, ttl };
}
void uf::debug::draw( float dt ) {
for ( auto it = impl::transientLines.begin(); it != impl::transientLines.end(); ) {
auto& line = it->second;
if ( line.ttl <= 0 ) it = impl::transientLines.erase( it );
else {
uf::debug::drawLine( line.start, line.end, line.color * pod::Vector4f{ 1, 1, 1, CLAMP( line.ttl, 0, 1 ) } );
line.ttl -= dt * impl::decayRate;
++it;
}
}
if ( impl::lines.empty() ) return;
// to-do: make this static to avoid additional allocations
uf::Mesh mesh;
mesh.bind<impl::Vertex>();
mesh.insertVertices<impl::Vertex>(impl::lines);
auto& scene = uf::scene::getCurrentScene();
auto& graphics = scene.getComponent<uf::renderer::Graphics>();
auto& graphic = graphics["immediate"];
if ( !graphic.initialized ) {
graphic.device = &uf::renderer::device;
graphic.material.device = &uf::renderer::device;
// to-do: bin by descriptor instead of one global set
graphic.descriptor.depth.test = true;
graphic.descriptor.depth.write = false;
graphic.descriptor.renderTarget = 1; // "forward";
graphic.descriptor.topology = uf::renderer::enums::PrimitiveTopology::LINE_LIST;
graphic.descriptor.fill = uf::renderer::enums::PolygonMode::LINE;
graphic.descriptor.lineWidth = 2;
uf::stl::string vertexShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/line/vert.spv");
uf::stl::string fragmentShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/line/frag.spv");
graphic.material.metadata.autoInitializeUniformBuffers = false;
graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX);
graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT);
graphic.material.metadata.autoInitializeUniformBuffers = true;
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
// vertex shader
{
auto& shader = graphic.material.getShader("vertex");
shader.aliasBuffer( storage.buffers.camera );
}
graphic.initialize();
graphic.initializeMesh( mesh );
UF_MSG_DEBUG("Initialized graphic");
} else {
bool rebuild = graphic.updateMesh( mesh );
if ( rebuild ) uf::renderer::states::rebuild = true; // to-do: rebuild the defer mode only
}
impl::lines.clear();
}

View File

@ -66,7 +66,11 @@ void impl::updateActivity( pod::PhysicsBody& body, float dt ) {
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.reference = body.transform;
#endif
return uf::transform::flatten( t );
}
// get position of a body, uses bounds center or transform's position
@ -112,8 +116,9 @@ bool impl::shouldCollide( const pod::PhysicsBody& a, const pod::PhysicsBody& b )
pod::Matrix3f impl::computeWorldInverseInertia( const pod::PhysicsBody& b ) {
if ( b.inverseMass == 0.0f ) return pod::Matrix3f{};
auto t = impl::getTransform( b );
pod::Matrix3f invI_local = uf::matrix::diagonal( b.inverseInertiaTensor );
pod::Matrix3f R = uf::quaternion::matrix3(b.transform->orientation);
pod::Matrix3f R = uf::quaternion::matrix3( t.orientation );
#if 1
return R * invI_local * uf::matrix::transpose(R);

View File

@ -59,3 +59,26 @@ pod::Constraint& uf::physics::constrainBallSocket( pod::Constraint& constraint,
return 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 pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA);
auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB);
uf::debug::drawLine( tA.position, pA );
uf::debug::drawLine( tB.position, pB );
uf::debug::drawLine( pA, pB );
// crosshair
float size = 0.1f;
uf::debug::drawLine( pA - pod::Vector3f{size, 0.0f, 0.0f}, pA + pod::Vector3f{size, 0.0f, 0.0f} );
uf::debug::drawLine( pA - pod::Vector3f{0.0f, size, 0.0f}, pA + pod::Vector3f{0.0f, size, 0.0f} );
uf::debug::drawLine( pA - pod::Vector3f{0.0f, 0.0f, size}, pA + pod::Vector3f{0.0f, 0.0f, size} );
}

View File

@ -167,3 +167,31 @@ pod::Constraint& uf::physics::constrainConeTwist( pod::Constraint& constraint, c
return constraint;
}
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 pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA);
auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB);
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);
uf::debug::drawLine( pA, pB );
float axisLength = 0.5f;
uf::debug::drawLine( pA, pA + taA * axisLength );
uf::debug::drawLine( pB, pB + taB * axisLength );
float refLength = 0.25f;
uf::debug::drawLine( pA, pA + raA * refLength );
uf::debug::drawLine( pB, pB + raB * refLength );
}

View File

@ -54,3 +54,27 @@ pod::Constraint& uf::physics::constrainDistance( pod::Constraint& constraint, co
return constraint;
}
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 pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA);
auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB);
uf::debug::drawLine( tA.position, pA );
uf::debug::drawLine( tB.position, pB );
uf::debug::drawLine( pA, pB );
// crosshair
float size = 0.1f;
uf::debug::drawLine( pA - pod::Vector3f{size, 0.0f, 0.0f}, pA + pod::Vector3f{size, 0.0f, 0.0f} );
uf::debug::drawLine( pA - pod::Vector3f{0.0f, size, 0.0f}, pA + pod::Vector3f{0.0f, size, 0.0f} );
uf::debug::drawLine( pA - pod::Vector3f{0.0f, 0.0f, size}, pA + pod::Vector3f{0.0f, 0.0f, size} );
}

View File

@ -72,3 +72,24 @@ pod::Constraint& uf::physics::constrainHinge( pod::Constraint& constraint, const
return constraint;
}
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 pA = tA.position + uf::quaternion::rotate(tA.orientation, joint.localAnchorA);
auto pB = tB.position + uf::quaternion::rotate(tB.orientation, joint.localAnchorB);
auto aA = uf::quaternion::rotate(tA.orientation, joint.localAxisA);
auto aB = uf::quaternion::rotate(tB.orientation, joint.localAxisB);
uf::debug::drawLine( pA, pB );
float pin = 0.5f;
uf::debug::drawLine( pA - pod::Vector3f{aA.x * pin, aA.y * pin, aA.z * pin}, pA + pod::Vector3f{aA.x * pin, aA.y * pin, aA.z * pin} );
uf::debug::drawLine( pB - pod::Vector3f{aB.x * pin, aB.y * pin, aB.z * pin}, pB + pod::Vector3f{aB.x * pin, aB.y * pin, aB.z * pin} );
}

View File

@ -133,3 +133,8 @@ pod::Constraint& uf::physics::constrainSlider( pod::Constraint& constraint, cons
return constraint;
}
void impl::drawSlider( const pod::Constraint& constraint ) {
if ( !constraint.a || !constraint.b ) return;
}

View File

@ -59,3 +59,8 @@ pod::Constraint& uf::physics::constrainSpring( pod::Constraint& constraint, cons
return constraint;
}
void impl::drawSpring( const pod::Constraint& constraint ) {
if ( !constraint.a || !constraint.b ) return;
}

View File

@ -72,3 +72,8 @@ pod::Constraint& uf::physics::constrainWeld( pod::Constraint& constraint, const
return constraint;
}
void impl::drawWeld( const pod::Constraint& constraint ) {
if ( !constraint.a || !constraint.b ) return;
}

View File

@ -1,52 +1,17 @@
#include <uf/utils/math/physics/common.h>
#include <uf/utils/math/physics/narrowphase.h>
#include <uf/utils/math/physics/constraints.h>
#include <uf/utils/math/physics/draw.h>
#include <uf/engine/scene/scene.h>
#include <uf/engine/graph/graph.h>
namespace {
// define a struct for a line because I hate hate hate tuple syntax
struct Line {
pod::Vector3f start = {};
pod::Vector3f end = {};
pod::Vector4f color = { 1, 1, 1, 1 };
float ttl = 0;
};
uf::stl::vector<impl::Vertex> lines;
uf::stl::unordered_map<size_t, Line> transientLines;
size_t getHash( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color, const pod::PhysicsBody* a, const pod::PhysicsBody* b ) {
size_t hash = 0;
/*
int qx = static_cast<int>(start.x * 10.0f);
int qy = static_cast<int>(start.y * 10.0f);
int qz = static_cast<int>(start.z * 10.0f);
uf::hash(hash, a, b, qx, qy, qz);
*/
uf::hash(hash, start, end, color);
return hash;
}
}
UF_VERTEX_DESCRIPTOR(impl::Vertex,
UF_VERTEX_DESCRIPTION(impl::Vertex, R32G32B32_SFLOAT, position)
UF_VERTEX_DESCRIPTION(impl::Vertex, R32G32B32A32_SFLOAT, color)
)
void impl::addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color ) {
::lines.emplace_back( impl::Vertex{ start, color } );
::lines.emplace_back( impl::Vertex{ end, color } );
}
void impl::addTransientLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color, const pod::PhysicsBody* a, const pod::PhysicsBody* b ) {
::transientLines[::getHash( start, end, color, a, b )] = Line{ start, end, color, 1.0f };
}
void impl::drawManifold( const pod::Manifold& manifold ) {
for ( auto& contact : manifold.points ) {
auto& start = contact.point;
auto end = contact.point + (contact.normal * MIN(contact.penetration, 0.1f) * 2);
impl::addTransientLine( start, end, pod::Vector4f{ 1, 0, 0, 1 }, manifold.a, manifold.b );
uf::debug::addLine( start, end, pod::Vector4f{ 1, 0, 0, 1 } );
}
}
void impl::drawBody( const pod::PhysicsBody& body ) {
@ -78,65 +43,36 @@ void impl::drawBody( const pod::PhysicsBody& body ) {
break;
}
}
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;
void impl::draw( const pod::World& world, float dt ) {
if ( world.bodies.empty() ) return;
::lines.clear();
for ( auto* body : world.bodies ) impl::drawBody( *body );
for ( auto it = ::transientLines.begin(); it != ::transientLines.end(); ) {
auto& line = it->second;
if ( line.ttl <= 0 ) it = ::transientLines.erase( it );
else {
impl::addLine( line.start, line.end, line.color * pod::Vector4f{ 1, 1, 1, line.ttl } );
line.ttl -= dt * 0.25f;
++it;
}
}
if ( ::lines.empty() ) return;
uf::Mesh mesh;
mesh.bind<impl::Vertex>();
mesh.insertVertices<impl::Vertex>(::lines);
auto& scene = uf::scene::getCurrentScene();
auto& graphics = scene.getComponent<uf::renderer::Graphics>();
auto& graphic = graphics["physics"];
if ( !graphic.initialized ) {
graphic.device = &uf::renderer::device;
graphic.material.device = &uf::renderer::device;
//graphic.descriptor.depth.test = false;
graphic.descriptor.depth.write = false;
graphic.descriptor.renderTarget = 1; // "forward";
graphic.descriptor.topology = uf::renderer::enums::PrimitiveTopology::LINE_LIST;
graphic.descriptor.fill = uf::renderer::enums::PolygonMode::LINE;
graphic.descriptor.lineWidth = 2;
uf::stl::string vertexShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/line/vert.spv");
uf::stl::string fragmentShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/line/frag.spv");
graphic.material.metadata.autoInitializeUniformBuffers = false;
graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX);
graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT);
graphic.material.metadata.autoInitializeUniformBuffers = true;
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
// vertex shader
{
auto& shader = graphic.material.getShader("vertex");
shader.aliasBuffer( storage.buffers.camera );
}
graphic.initialize();
graphic.initializeMesh( mesh );
UF_MSG_DEBUG("Initialized graphic");
} else {
bool rebuild = graphic.updateMesh( mesh );
if ( rebuild ) uf::renderer::states::rebuild = true;
switch( constraint.type ) {
case pod::ConstraintType::BALL_AND_SOCKET:
impl::drawBallSocket( constraint );
break;
case pod::ConstraintType::HINGE:
impl::drawHinge( constraint );
break;
case pod::ConstraintType::CONE_TWIST:
impl::drawConeTwist( constraint );
break;
case pod::ConstraintType::SLIDER:
impl::drawSlider( constraint );
break;
case pod::ConstraintType::DISTANCE:
impl::drawDistance( constraint );
break;
case pod::ConstraintType::WELD:
impl::drawWeld( constraint );
break;
case pod::ConstraintType::SPRING:
impl::drawSpring( constraint );
break;
}
}
void impl::draw( const pod::World& world, float dt ) {
for ( auto* body : world.bodies ) impl::drawBody( *body );
for ( auto* constraint : world.constraints ) impl::drawConstraint( *constraint );
}

View File

@ -103,6 +103,31 @@ void uf::physics::step( pod::World& world, float dt ) {
++uf::physics::settings.frameCounter;
// flatten all transforms into a contiguous buffer
static thread_local uf::stl::vector<pod::Transform<>*> originalTransforms; // stores the pointer to the original transform
static thread_local uf::stl::vector<pod::Transform<>> flattenedTransforms; // stores the flattened transforms
static thread_local uf::stl::unordered_map<pod::Transform<>*, pod::Transform<>*> transformsMap; // maps the original transform to the flattened one (for querying)
if ( uf::physics::settings.flattenTransforms ) {
originalTransforms.resize( bodies.size() );
flattenedTransforms.resize( bodies.size() );
transformsMap.clear();
// copy all transforms, flatten them, and map the original to the reference
for ( auto i = 0; i < bodies.size(); ++i ) {
auto& body = *bodies[i];
//if ( body.inverseMass == 0.0f ) continue; // static bodies don't ever move, so the hierarchy problem doesn't affect it (right?)
auto& transform = *body.transform;
auto& flattenedTransform = flattenedTransforms[i];
auto& originalTransform = originalTransforms[i];
originalTransform = body.transform;
flattenedTransform = uf::transform::flatten( transform );
transformsMap[originalTransform] = &flattenedTransform;
body.transform = &flattenedTransform; // body now points to the flattened transform
}
}
for ( auto* body : bodies ) {
impl::integrate( *body, dt );
}
@ -232,6 +257,41 @@ void uf::physics::step( pod::World& world, float dt ) {
// snap velocities of bodies
for ( auto* b : bodies ) impl::snapVelocity( *b, dt );
// unflatten all transforms
if ( uf::physics::settings.flattenTransforms ) for ( auto i = 0; i < bodies.size(); ++i ) {
auto& body = *bodies[i];
//if ( body.inverseMass == 0.0f ) continue; // do not bother with static bodies
auto& originalTransform = originalTransforms[i];
auto& flattenedTransform = flattenedTransforms[i];
// now points back to original transform
body.transform = originalTransform;
auto& transform = *body.transform;
// solo transform: simply update
if ( !transform.reference ) {
// update position and orientation
transform.position = flattenedTransform.position;
transform.orientation = flattenedTransform.orientation;
// referential transform: convert new world space into local space (to original transform)
} else {
pod::Transform<> parentTransform;
// parent is not within the physics system, use original reference
if ( transformsMap.count( originalTransform->reference ) == 0 ) {
parentTransform = uf::transform::flatten( *originalTransform->reference );
// parent is within the physics system, use the already-flattened transform
} else {
parentTransform = *transformsMap[originalTransform->reference];
}
transform.position = uf::transform::applyInverse( parentTransform, flattenedTransform.position );
transform.orientation = uf::quaternion::multiply(
uf::quaternion::inverse( parentTransform.orientation ),
flattenedTransform.orientation
);
}
}
}
void uf::physics::setMass( pod::PhysicsBody& body, float mass ) {
@ -452,7 +512,7 @@ void uf::physics::applyAngularVelocity( pod::PhysicsBody& body, const pod::Quate
}
void uf::physics::applyRotation( pod::PhysicsBody& body, const pod::Quaternion<>& q ) {
impl::wakeBody( body );
uf::transform::rotate( *body.transform/*.reference*/, q );
uf::transform::rotate( *body.transform, q );
}
void uf::physics::applyRotation( pod::PhysicsBody& body, const pod::Vector3f& axis, float angle ) {
uf::physics::applyRotation( body, uf::quaternion::axisAngle( axis, angle ) );
@ -481,7 +541,10 @@ pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, fl
// initial initialization
body.world = &world;
body.object = &object;
body.transform/*.reference*/ = &object.getComponent<pod::Transform<>>();
body.transform = &object.getComponent<pod::Transform<>>();
#if UF_PHYSICS_SYNC_TRANSFORMS
body.flattenedTransform = uf::transform::flatten( *body.transform );
#endif
body.offset = offset;
body.inverseMass = mass == 0.0f ? 0.0f : 1.0f / mass;
body.collider.type = {};

View File

@ -186,6 +186,8 @@ void impl::integrate( pod::PhysicsBody& body, float dt ) {
if ( !body.activity.awake || body.inverseMass == 0.0f ) return;
auto& world = *body.world;
auto& transform = *body.transform;
auto fT = uf::transform::flatten( transform );
// linear integration
pod::Vector3f acceleration = body.forceAccumulator * body.inverseMass;
@ -194,26 +196,26 @@ void impl::integrate( pod::PhysicsBody& body, float dt ) {
// angular integration
{
pod::Matrix3f R = uf::quaternion::matrix3(body.transform->orientation);
pod::Matrix3f R = uf::quaternion::matrix3( fT.orientation );
pod::Vector3f localTorque = uf::matrix::multiply( uf::matrix::transpose(R), body.torqueAccumulator );
pod::Vector3f localAngAccel = localTorque * body.inverseInertiaTensor; // element-wise
body.angularVelocity += uf::matrix::multiply( R, localAngAccel ) * dt;
}
// update position
body.transform/*.reference*/->position += body.velocity * dt;
transform.position += body.velocity * dt;
// update orientation
float angularSpeed2 = uf::vector::magnitude( body.angularVelocity );
if ( angularSpeed2 > EPS2 ) {
float angularSpeed = std::sqrt( angularSpeed2 );
pod::Quaternion<> dq = uf::quaternion::axisAngle( body.angularVelocity / angularSpeed, angularSpeed * dt);
uf::transform::rotate( *body.transform/*.reference*/, dq );
uf::transform::rotate( transform, dq );
}
// pseudo-impulse position correction
if ( !uf::physics::settings.ngsPositionSolver ) {
body.transform->position += body.pseudoVelocity * dt;
transform.position += body.pseudoVelocity * dt;
float pseudoAngularSpeed2 = uf::vector::magnitude( body.pseudoAngularVelocity );
if ( pseudoAngularSpeed2 > EPS ) {
@ -222,7 +224,7 @@ void impl::integrate( pod::PhysicsBody& body, float dt ) {
float clampedSpeed = std::min(pseudoAngularSpeed, (2.0f * M_PI / 180.0f) / dt);
pod::Quaternion<> dq = uf::quaternion::axisAngle( axis, clampedSpeed * dt );
uf::transform::rotate( *body.transform, dq );
uf::transform::rotate( transform, dq );
}
// reset

View File

@ -79,24 +79,9 @@ bool impl::aabbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
}
void impl::drawAabb( const pod::PhysicsBody& body ) {
auto& aabb = body.bounds;
pod::Vector3f corners[8] = {
{aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
{aabb.min.x, aabb.min.y, aabb.max.z}, {aabb.max.x, aabb.min.y, aabb.max.z},
{aabb.max.x, aabb.max.y, aabb.max.z}, {aabb.min.x, aabb.max.y, aabb.max.z}
};
// bottom face
impl::addLine( corners[0], corners[1] ); impl::addLine( corners[1], corners[2] );
impl::addLine( corners[2], corners[3] ); impl::addLine( corners[3], corners[0] );
// top face
impl::addLine( corners[4], corners[5] ); impl::addLine( corners[5], corners[6] );
impl::addLine( corners[6], corners[7] ); impl::addLine( corners[7], corners[4] );
// vertical edges
impl::addLine( corners[0], corners[4] ); impl::addLine( corners[1], corners[5] );
impl::addLine( corners[2], corners[6] ); impl::addLine( corners[3], corners[7] );
const auto& aabb = body.bounds;
auto transform = impl::getTransform( body );
uf::debug::drawAabb( aabb, transform );
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::AABB& aabb ) {

View File

@ -107,26 +107,55 @@ bool impl::capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po
}
void impl::drawCapsule( const pod::PhysicsBody& body ) {
float radius = body.collider.capsule.radius;
const auto& capsule = body.collider.capsule;
auto transform = impl::getTransform(body);
#if 0
uf::debug::drawCapsule( capsule, transform );
#else
auto [p1, p2] = impl::getCapsuleSegment(body);
const int segments = 16;
const float angleIncrement = (2.0f * M_PI) / segments;
pod::Vector3f up = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 1, 0});
pod::Vector3f right = uf::vector::normalize(impl::computeTangent(up));
pod::Vector3f forward = uf::vector::cross(up, right);
for ( auto i = 0; i < segments; ++i ) {
float theta1 = i * angleIncrement;
float theta2 = (i + 1) * angleIncrement;
pod::Vector3f rightOffset = right * radius;
pod::Vector3f forwardOffset = forward * radius;
float c1 = std::cos(theta1) * capsule.radius;
float s1 = std::sin(theta1) * capsule.radius;
float c2 = std::cos(theta2) * capsule.radius;
float s2 = std::sin(theta2) * capsule.radius;
impl::addLine( p1 + rightOffset, p2 + rightOffset );
impl::addLine( p1 - rightOffset, p2 - rightOffset );
impl::addLine( p1 + forwardOffset, p2 + forwardOffset );
impl::addLine( p1 - forwardOffset, p2 - forwardOffset );
pod::Vector3f xy1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, s1, 0.0f});
pod::Vector3f xy2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, s2, 0.0f});
impl::addLine( p1 + rightOffset, p1 - rightOffset );
impl::addLine( p1 + forwardOffset, p1 - forwardOffset );
impl::addLine( p2 + rightOffset, p2 - rightOffset );
impl::addLine( p2 + forwardOffset, p2 - forwardOffset );
pod::Vector3f xz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, 0.0f, s1});
pod::Vector3f xz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, 0.0f, s2});
pod::Vector3f yz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c1, s1});
pod::Vector3f yz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c2, s2});
uf::debug::drawLine( p1 + xy1, p1 + xy2 );
uf::debug::drawLine( p1 + xz1, p1 + xz2 );
uf::debug::drawLine( p1 + yz1, p1 + yz2 );
uf::debug::drawLine( p2 + xy1, p2 + xy2 );
uf::debug::drawLine( p2 + xz1, p2 + xz2 );
uf::debug::drawLine( p2 + yz1, p2 + yz2 );
}
pod::Vector3f rx = uf::quaternion::rotate(transform.orientation, pod::Vector3f{capsule.radius, 0.0f, 0.0f});
pod::Vector3f ry = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, capsule.radius, 0.0f});
pod::Vector3f rz = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, 0.0f, capsule.radius});
uf::debug::drawLine( p1 + rx, p2 + rx );
uf::debug::drawLine( p1 - rx, p2 - rx );
uf::debug::drawLine( p1 + ry, p2 + ry );
uf::debug::drawLine( p1 - ry, p2 - ry );
uf::debug::drawLine( p1 + rz, p2 + rz );
uf::debug::drawLine( p1 - rz, p2 - rz );
#endif
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Capsule& capsule ) {

View File

@ -84,16 +84,14 @@ bool impl::hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
void impl::drawHull( const pod::PhysicsBody& body ) {
const uf::Mesh* meshData = body.collider.convexHull.mesh;
auto transform = impl::getTransform( body );
if ( !meshData ) return;
size_t totalTriangles = 0;
for ( const auto& view : meshData->buffer_views ) totalTriangles += view.index.count / 3;
for ( size_t i = 0; i < totalTriangles; ++i ) {
auto tri = impl::fetchTriangle(*meshData, i, body);
impl::addLine( tri.points[0], tri.points[1] );
impl::addLine( tri.points[1], tri.points[2] );
impl::addLine( tri.points[2], tri.points[0] );
auto tri = uf::mesh::fetchTriangle( *meshData, i );
uf::debug::drawTriangle( tri, transform );
}
}

View File

@ -189,18 +189,18 @@ bool impl::meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
}
void impl::drawMesh( const pod::PhysicsBody& body ) {
const uf::Mesh* meshData = body.collider.mesh.mesh;
auto transform = impl::getTransform( body );
if ( !meshData ) return;
if ( body.inverseMass == 0.0f ) return;
size_t totalTriangles = 0;
for ( const auto& view : meshData->buffer_views ) totalTriangles += view.index.count / 3;
for ( size_t i = 0; i < totalTriangles; ++i ) {
auto tri = impl::fetchTriangle(*meshData, i, body);
impl::addLine( tri.points[0], tri.points[1] );
impl::addLine( tri.points[1], tri.points[2] );
impl::addLine( tri.points[2], tri.points[0] );
auto tri = uf::mesh::fetchTriangle( *meshData, i );
uf::debug::drawTriangle( tri, transform );
}
}

View File

@ -299,28 +299,9 @@ bool impl::obbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::M
}
void impl::drawObb( const pod::PhysicsBody& body ) {
auto aabb = impl::obbToAabb( body.collider.obb );
pod::Vector3f corners[8] = {
{aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
{aabb.min.x, aabb.min.y, aabb.max.z}, {aabb.max.x, aabb.min.y, aabb.max.z},
{aabb.max.x, aabb.max.y, aabb.max.z}, {aabb.min.x, aabb.max.y, aabb.max.z}
};
const auto& obb = body.collider.obb;
auto transform = impl::getTransform(body);
FOR_EACH( 8, {
corners[i] = uf::transform::apply(transform, corners[i]);
});
// bottom face
impl::addLine( corners[0], corners[1] ); impl::addLine( corners[1], corners[2] );
impl::addLine( corners[2], corners[3] ); impl::addLine( corners[3], corners[0] );
// top face
impl::addLine( corners[4], corners[5] ); impl::addLine( corners[5], corners[6] );
impl::addLine( corners[6], corners[7] ); impl::addLine( corners[7], corners[4] );
// vertical edges
impl::addLine( corners[0], corners[4] ); impl::addLine( corners[1], corners[5] );
impl::addLine( corners[2], corners[6] ); impl::addLine( corners[3], corners[7] );
uf::debug::drawObb( obb, transform );
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::OBB& obb ) {

View File

@ -67,8 +67,12 @@ bool impl::planeHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:
}
void impl::drawPlane( const pod::PhysicsBody& body ) {
const auto& plane = body.collider.plane;
auto transform = impl::getTransform(body);
#if 0
uf::debug::drawPlane( plane, transform );
#else
pod::Vector3f right = uf::quaternion::rotate(transform.orientation, pod::Vector3f{1, 0, 0});
pod::Vector3f forward = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 0, 1});
@ -78,13 +82,14 @@ void impl::drawPlane( const pod::PhysicsBody& body ) {
pod::Vector3f p2 = transform.position - (right * size) - (forward * size);
pod::Vector3f p3 = transform.position + (right * size) - (forward * size);
impl::addLine( p0, p1 );
impl::addLine( p1, p2 );
impl::addLine( p2, p3 );
impl::addLine( p3, p0 );
uf::debug::drawLine( p0, p1 );
uf::debug::drawLine( p1, p2 );
uf::debug::drawLine( p2, p3 );
uf::debug::drawLine( p3, p0 );
impl::addLine( p0, p2 );
impl::addLine( p1, p3 );
uf::debug::drawLine( p0, p2 );
uf::debug::drawLine( p1, p3 );
#endif
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Plane& plane ) {

View File

@ -304,7 +304,7 @@ void impl::drawRay( const pod::Ray& ray, const pod::RayQuery& query ) {
auto& start = ray.origin;
auto end = ray.origin + ray.direction * query.contact.penetration;
impl::addTransientLine( start, end, query.hit ? pod::Vector4f{ 0, 1, 0, 1 } : pod::Vector4f{ 1, 0, 0, 1 }, query.invoker, query.body );
uf::debug::addLine( start, end, query.hit ? pod::Vector4f{ 0, 1, 0, 1 } : pod::Vector4f{ 1, 0, 0, 1 } );
}
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::PhysicsBody& body, float maxDistance ) {

View File

@ -69,35 +69,9 @@ bool impl::sphereHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod
}
void impl::drawSphere( const pod::PhysicsBody& body ) {
float radius = body.collider.sphere.radius;
const auto& sphere = body.collider.sphere;
auto transform = impl::getTransform(body);
const int segments = 16;
const float PI = 3.14159265359f;
const float angleIncrement = (2.0f * PI) / segments;
for ( auto i = 0; i < segments; ++i ) {
float theta1 = i * angleIncrement;
float theta2 = (i + 1) * angleIncrement;
float c1 = std::cos(theta1) * radius;
float s1 = std::sin(theta1) * radius;
float c2 = std::cos(theta2) * radius;
float s2 = std::sin(theta2) * radius;
pod::Vector3f xy1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, s1, 0.0f});
pod::Vector3f xy2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, s2, 0.0f});
pod::Vector3f xz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, 0.0f, s1});
pod::Vector3f xz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, 0.0f, s2});
pod::Vector3f yz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c1, s1});
pod::Vector3f yz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c2, s2});
impl::addLine( transform.position + xy1, transform.position + xy2 );
impl::addLine( transform.position + xz1, transform.position + xz2 );
impl::addLine( transform.position + yz1, transform.position + yz2 );
}
uf::debug::drawSphere( sphere, transform );
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Sphere& sphere ) {

View File

@ -358,14 +358,7 @@ bool impl::triangleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, p
void impl::drawTriangle( const pod::PhysicsBody& body ) {
const auto& tri = body.collider.triangle;
auto transform = impl::getTransform(body);
pod::Vector3f v0 = uf::transform::apply(transform, tri.points[0]);
pod::Vector3f v1 = uf::transform::apply(transform, tri.points[1]);
pod::Vector3f v2 = uf::transform::apply(transform, tri.points[2]);
impl::addLine( v0, v1 );
impl::addLine( v1, v2 );
impl::addLine( v2, v0 );
uf::debug::drawTriangle( tri, transform );
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::TriangleWithNormal& tri ) {

View File

@ -12,6 +12,9 @@ void impl::solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt,
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );
auto& aT = *a.transform;
auto& bT = *b.transform;
if ( a.inverseMass == 0.0f && b.inverseMass == 0.0f ) continue;
for ( auto& c : manifold.points ) {
@ -38,7 +41,7 @@ void impl::solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt,
// apply impulses directly
if ( ctxA.invM > 0.0f ) {
pod::Vector3f translation = P * ctxA.invM;
a.transform->position -= translation;
aT.position -= translation;
tA.position -= translation;
pod::Vector3f deltaAngleA = uf::matrix::multiply(ctxA.invI, uf::vector::cross(rA, -P));
@ -53,7 +56,7 @@ void impl::solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt,
if ( ctxB.invM > 0.0f ) {
pod::Vector3f translation = P * ctxB.invM;
b.transform->position += translation;
bT.position += translation;
tB.position += translation;
pod::Vector3f deltaAngleB = uf::matrix::multiply(ctxB.invI, uf::vector::cross(rB, P));

View File

@ -157,7 +157,7 @@ void ext::CraetureBehavior::initialize( uf::Object& self ) {
void ext::CraetureBehavior::tick( uf::Object& self ) {
auto& metadata = this->getComponent<ext::CraetureBehavior::Metadata>();
if ( this->hasComponent<pod::Graph>() ) {
if ( this->hasComponent<pod::Graph>() && metadata.animation != "" ) {
auto& graph = this->getComponent<pod::Graph>();
pod::payloads::QueueAnimationPayload payload;
payload.name = metadata.animation;