From eec75781bab108f0ab38bef67505aeb94553556d Mon Sep 17 00:00:00 2001 From: ecker Date: Fri, 22 May 2026 22:02:13 -0500 Subject: [PATCH] an almost perfect ragdoll (has issues when you grab it) --- bin/data/entities/ragdollLimb.json | 6 +++++ bin/data/entities/scripts/ragdoll.lua | 27 +++++++++++++------ engine/inc/uf/engine/entity/entity.h | 2 ++ engine/inc/uf/engine/object/object.h | 2 ++ engine/src/engine/object/object.cpp | 19 +++++++------ engine/src/ext/lua/lua.cpp | 10 +++++-- engine/src/ext/lua/usertypes/camera.cpp | 4 +-- engine/src/ext/lua/usertypes/object.cpp | 23 +++++++++++----- engine/src/ext/lua/usertypes/physics.cpp | 20 +++++++++----- engine/src/ext/lua/usertypes/transform.cpp | 31 +++++++++++++++++++++- 10 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 bin/data/entities/ragdollLimb.json diff --git a/bin/data/entities/ragdollLimb.json b/bin/data/entities/ragdollLimb.json new file mode 100644 index 00000000..40ac8b39 --- /dev/null +++ b/bin/data/entities/ragdollLimb.json @@ -0,0 +1,6 @@ +{ + "name": "Ragdoll Limb", + "metadata": { + "holdable": true + } +} \ No newline at end of file diff --git a/bin/data/entities/scripts/ragdoll.lua b/bin/data/entities/scripts/ragdoll.lua index f8181a6e..4b1031c4 100644 --- a/bin/data/entities/scripts/ragdoll.lua +++ b/bin/data/entities/scripts/ragdoll.lua @@ -11,6 +11,9 @@ local transform = ent:getComponent("Transform") local physicsBody = ent:getComponent("PhysicsBody") local metadata = ent:getComponent("Metadata") + +--transform.scale = Vector3f( 5, 5, 5 ) +local children = {} local bodies = { torso = { Vector3f(0, 1.0, 0), Vector3f(0.2, 0.3, 0.15), 20.0 }, head = { Vector3f(0, 1.6, 0), Vector3f(0.15, 0.15, 0.15), 5.0 }, @@ -43,22 +46,30 @@ local constraints = { for k, _ in pairs( bodies ) do position = bodies[k][1] - box = OBB( position, bodies[k][2] ) + extent = bodies[k][2] mass = bodies[k][3] + + child = ent:loadChild("./ragdollLimb.json", true) + childTransform = child:getComponent("Transform") + childTransform.position = position + childTransform:setReference( transform ) - body = physics.create( ent, mass, Vector3f() ) - body:asObb( box ) + body = physics.create( child, mass ) + body:asObb( OBB( Vector3f(), extent ) ) body:setGravity( Vector3f( 0, 0, 0 ) ) + children[k] = child bodies[k] = body end -for k, _ in pairs( constraints ) do - bodyA = bodies[constraints[k][1]] - bodyB = bodies[constraints[k][2]] - joint = constraints[k][3] - axis = constraints[k][4] +for k, _ in pairs( constraints ) do + bodyA = bodies[ constraints[k][1] ] + bodyB = bodies[ constraints[k][2] ] + + joint = transform:apply( constraints[k][3] ) + axis = transform.orientation:rotate( constraints[k][4] ) + swingLimit = constraints[k][5] twistLimit = constraints[k][6] diff --git a/engine/inc/uf/engine/entity/entity.h b/engine/inc/uf/engine/entity/entity.h index de02b3ca..a3037c74 100644 --- a/engine/inc/uf/engine/entity/entity.h +++ b/engine/inc/uf/engine/entity/entity.h @@ -100,6 +100,8 @@ namespace uf { void loadAssets( const uf::Serializer& ); + uf::Entity& createChild( bool = true ); + uf::Entity& loadChild( const uf::Serializer&, bool = true ); uf::Entity* loadChildPointer( const uf::Serializer&, bool = true ); std::size_t loadChildUid( const uf::Serializer&, bool = true ); diff --git a/engine/inc/uf/engine/object/object.h b/engine/inc/uf/engine/object/object.h index feacaf9e..e84d8ba3 100644 --- a/engine/inc/uf/engine/object/object.h +++ b/engine/inc/uf/engine/object/object.h @@ -25,6 +25,8 @@ namespace uf { void loadAssets( const uf::Serializer& ); + uf::Object& createChild( bool = true ); + uf::Object& loadChild( const uf::Serializer&, bool = true ); uf::Object* loadChildPointer( const uf::Serializer&, bool = true ); std::size_t loadChildUid( const uf::Serializer&, bool = true ); diff --git a/engine/src/engine/object/object.cpp b/engine/src/engine/object/object.cpp index 83a41125..6e18b065 100644 --- a/engine/src/engine/object/object.cpp +++ b/engine/src/engine/object/object.cpp @@ -444,27 +444,30 @@ bool uf::Object::load( const uf::Serializer& _json ) { #endif return true; } +uf::Object& uf::Object::createChild( bool initialize ) { + auto& entity = uf::instantiator::instantiate("Object"); + this->addChild( entity ); + return entity; +} + uf::Object& uf::Object::loadChild( const uf::stl::string& f, bool initialize ) { auto& metadata = this->getComponent(); auto& metadataJson = this->getComponent(); uf::Serializer json; uf::stl::string filename = uf::io::resolveURI( f, metadata.system.root ); - if ( !json.readFromFile(filename) ) { + if ( json.readFromFile(filename) ) { + json["source"] = filename; + json["root"] = uf::io::directory(filename); + json["hot reload"]["mtime"] = uf::io::mtime(filename) + 10; + } else { if ( !uf::Object::assertionLoad ) { UF_MSG_ERROR("assertionLoad is unset, loading empty entity"); - auto& entity = uf::instantiator::instantiate("Object"); - entity.getComponent().system.invalid = true; - this->addChild(entity); } else { UF_EXCEPTION("Failed to load file: {}", filename); } } - json["source"] = filename; - json["root"] = uf::io::directory(filename); - json["hot reload"]["mtime"] = uf::io::mtime(filename) + 10; - return this->loadChild(json, initialize); } uf::Object& uf::Object::loadChild( const uf::Serializer& _json, bool initialize ) { diff --git a/engine/src/ext/lua/lua.cpp b/engine/src/ext/lua/lua.cpp index f46a7f5f..d1ada5a9 100644 --- a/engine/src/ext/lua/lua.cpp +++ b/engine/src/ext/lua/lua.cpp @@ -93,6 +93,11 @@ namespace binds { static uf::Object null; return null; }; + uf::Object& create( sol::optional init ) { + auto& entity = uf::instantiator::instantiate("Object"); + if ( init.value_or(true) ) entity.initialize(); + return entity; + }; uf::Object& currentScene() { return uf::scene::getCurrentScene().as(); }; @@ -157,8 +162,8 @@ namespace binds { double delta(){ return uf::physics::time::delta; }; } namespace physics { - pod::PhysicsBody& create( uf::Object& object, float mass = 0.0f, const pod::Vector3f& center = {} ) { - return uf::physics::create( object, mass, center ); + pod::PhysicsBody& create( uf::Object& object, sol::optional mass, sol::optional center ) { + return uf::physics::create( object, mass.value_or(0.0f), center.value_or(pod::Vector3f{}) ); } } namespace json { @@ -228,6 +233,7 @@ void ext::lua::initialize() { { auto entities = state["entities"].get_or_create(); entities.set("get", UF_LUA_C_FUN(::binds::entities::get)); + entities.set("create", UF_LUA_C_FUN(::binds::entities::create)); entities.set("currentScene", UF_LUA_C_FUN(::binds::entities::currentScene)); entities.set("controller", UF_LUA_C_FUN(::binds::entities::controller)); entities.set("destroy", UF_LUA_C_FUN(::binds::entities::destroy)); diff --git a/engine/src/ext/lua/usertypes/camera.cpp b/engine/src/ext/lua/usertypes/camera.cpp index 5e3652e8..3cbf8a24 100644 --- a/engine/src/ext/lua/usertypes/camera.cpp +++ b/engine/src/ext/lua/usertypes/camera.cpp @@ -40,8 +40,8 @@ namespace binds { self.setProjection(matrix); } } - void update( uf::Camera& self, bool force = true ) { - self.update(force); + void update( uf::Camera& self, sol::optional force ) { + self.update(force.value_or(true)); } } diff --git a/engine/src/ext/lua/usertypes/object.cpp b/engine/src/ext/lua/usertypes/object.cpp index 530bd086..39efa213 100644 --- a/engine/src/ext/lua/usertypes/object.cpp +++ b/engine/src/ext/lua/usertypes/object.cpp @@ -121,6 +121,11 @@ namespace binds { if ( type == "Metadata" ) { self.callHook( "object:Serialize.%UID%" ); auto& metadata = self.getComponent(); + if ( !ext::json::isObject( metadata ) ) { + sol::table table( ext::lua::state, sol::create ); + return sol::make_object( ext::lua::state, table ); + } + auto decoded = ext::lua::decode( metadata ); if ( decoded ) { sol::table table = decoded.value(); @@ -194,6 +199,9 @@ namespace binds { static uf::Object null; return null; } + uf::Object& createChild( uf::Object& self, sol::optional init ) { + return self.createChild( init.value_or( true ) ); + } uf::Object& addChild( uf::Object& self, uf::Object& child ) { self.addChild( child ); return self; @@ -202,8 +210,8 @@ namespace binds { self.removeChild( child ); return self; } - uf::Object& loadChild( uf::Object& self, const uf::stl::string& filename, bool init = true ) { - auto* pointer = self.loadChildPointer( filename, init ); + uf::Object& loadChild( uf::Object& self, sol::optional filename, sol::optional init ) { + auto* pointer = self.loadChildPointer( filename.value_or(""), init.value_or(true) ); if ( pointer ) return pointer->as(); static uf::Object null; return null; @@ -255,12 +263,12 @@ namespace binds { ext::json::Value payload = uf::Serializer(table); self.lazyCallHook( name, payload ); } - void queueHook( uf::Object& self, const uf::stl::string& name, sol::table table, float delay = 0.0f ) { + void queueHook( uf::Object& self, const uf::stl::string& name, sol::table table, sol::optional delay ) { ext::json::Value payload = uf::Serializer(table); - self.queueHook( name, payload, delay ); + self.queueHook( name, payload, delay.value_or(0.0f) ); } uf::stl::string toString( uf::Object& self ) { - return self.getName() + ": " + std::to_string( self.getUid() ); + return uf::string::toString( self ); } size_t getUid( const uf::Object& o ) { return o.getUid(); } @@ -270,7 +278,7 @@ namespace binds { UF_LUA_REGISTER_USERTYPE(uf::Object, sol::call_constructor, sol::initializers( - []( uf::Object& self, sol::object arg, bool init = true ){ + []( uf::Object& self, sol::object arg, sol::optional init ){ if ( arg.is() ) { self.load( arg.as() ); } else if ( arg.is() ) { @@ -280,7 +288,7 @@ UF_LUA_REGISTER_USERTYPE(uf::Object, self.load(json); } } - if ( init ) self.initialize(); + if ( init.value_or(true) ) self.initialize(); } ), UF_LUA_REGISTER_USERTYPE_DEFINE( uid, UF_LUA_C_FUN(::binds::getUid) ), @@ -295,6 +303,7 @@ UF_LUA_REGISTER_USERTYPE(uf::Object, UF_LUA_REGISTER_USERTYPE_DEFINE( findByName, UF_LUA_C_FUN(::binds::findByName) ), UF_LUA_REGISTER_USERTYPE_DEFINE( globalFindByUid, UF_LUA_C_FUN(::binds::globalFindByUid) ), UF_LUA_REGISTER_USERTYPE_DEFINE( globalFindByName, UF_LUA_C_FUN(::binds::globalFindByName) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( createChild, UF_LUA_C_FUN(::binds::createChild) ), UF_LUA_REGISTER_USERTYPE_DEFINE( addChild, UF_LUA_C_FUN(::binds::addChild) ), UF_LUA_REGISTER_USERTYPE_DEFINE( removeChild, UF_LUA_C_FUN(::binds::removeChild) ), UF_LUA_REGISTER_USERTYPE_DEFINE( loadChild, UF_LUA_C_FUN(::binds::loadChild) ), diff --git a/engine/src/ext/lua/usertypes/physics.cpp b/engine/src/ext/lua/usertypes/physics.cpp index a4197759..474cdfd7 100644 --- a/engine/src/ext/lua/usertypes/physics.cpp +++ b/engine/src/ext/lua/usertypes/physics.cpp @@ -29,6 +29,13 @@ namespace binds { return uf::physics::applyRotation( self, q ); } + pod::Transform<> getTransform( pod::PhysicsBody& body ) { + pod::Transform<> t; + t.position = body.offset; + t.reference = body.transform; + return uf::transform::flatten( t ); + } + std::tuple rayCast( pod::PhysicsBody& self, const pod::Vector3f& center, const pod::Vector3f& direction ) { pod::RayQuery query = uf::physics::rayCast( pod::Ray{center, uf::vector::normalize( direction )}, self, uf::vector::norm( direction ) ); uf::Object* object = query.hit ? query.body->object : NULL; @@ -51,8 +58,8 @@ namespace binds { pod::PhysicsBody& asCapsule( pod::PhysicsBody& self, const pod::Capsule& shape ) { return uf::physics::initialize( self, shape ); } - pod::PhysicsBody& asMesh( pod::PhysicsBody& self, const uf::Mesh& shape, bool convex = false ) { - return uf::physics::initialize( self, shape, convex ); + pod::PhysicsBody& asMesh( pod::PhysicsBody& self, const uf::Mesh& shape, sol::optional convex ) { + return uf::physics::initialize( self, shape, convex.value_or(false) ); } pod::Constraint& constrain( pod::PhysicsBody& a, pod::PhysicsBody& b ) { @@ -69,11 +76,11 @@ namespace binds { pod::Constraint& asBallSocket( pod::Constraint& self, const pod::Vector3f& joint ) { return uf::physics::constrainBallSocket( self, joint ); } - pod::Constraint& asConeTwist( pod::Constraint& self, const pod::Vector3f& joint, const pod::Vector3f& axis, float swingLimit = M_PI / 4.0f, float twistLimit = M_PI / 8.0f ) { - return uf::physics::constrainConeTwist( self, joint, axis, swingLimit, twistLimit ); + pod::Constraint& asConeTwist( pod::Constraint& self, const pod::Vector3f& joint, const pod::Vector3f& axis, sol::optional swingLimit, sol::optional twistLimit ) { + return uf::physics::constrainConeTwist( self, joint, axis, swingLimit.value_or(M_PI / 4.0f), twistLimit.value_or(M_PI / 8.0f) ); } - pod::Constraint& asDistance( pod::Constraint& self, const pod::Vector3f& pA, const pod::Vector3f& pB, bool isRope = false ) { - return uf::physics::constrainDistance( self, pA, pB, isRope ); + pod::Constraint& asDistance( pod::Constraint& self, const pod::Vector3f& pA, const pod::Vector3f& pB, sol::optional isRope ) { + return uf::physics::constrainDistance( self, pA, pB, isRope.value_or(false) ); } pod::Constraint& asHinge( pod::Constraint& self, const pod::Vector3f& joint, const pod::Vector3f& axis ) { return uf::physics::constrainHinge( self, joint, axis ); @@ -198,6 +205,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::PhysicsBody, UF_LUA_REGISTER_USERTYPE_DEFINE( applyImpulse, UF_LUA_C_FUN(uf::physics::applyImpulse) ), UF_LUA_REGISTER_USERTYPE_DEFINE( applyRotation, UF_LUA_C_FUN(::binds::body::applyRotation) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( getTransform, UF_LUA_C_FUN(::binds::body::getTransform) ), UF_LUA_REGISTER_USERTYPE_DEFINE( setGravity, UF_LUA_C_FUN(::binds::body::setGravity) ), UF_LUA_REGISTER_USERTYPE_DEFINE( enableGravity, UF_LUA_C_FUN(::binds::body::enableGravity) ), UF_LUA_REGISTER_USERTYPE_DEFINE( rayCast, UF_LUA_C_FUN(::binds::body::rayCast) ), diff --git a/engine/src/ext/lua/usertypes/transform.cpp b/engine/src/ext/lua/usertypes/transform.cpp index f6a6f727..0868d43c 100644 --- a/engine/src/ext/lua/usertypes/transform.cpp +++ b/engine/src/ext/lua/usertypes/transform.cpp @@ -47,6 +47,28 @@ namespace binds { pod::Matrix4f getModel( const pod::Transform<>& t ) { return uf::transform::model( t ); } + + pod::Transform<> fromMatrix( const pod::Matrix4f& matrix ) { + return uf::transform::fromMatrix( matrix ); + } + pod::Transform<>& reference( pod::Transform<>& transform, const pod::Transform<>& parent, sol::optional reorient ) { + return uf::transform::reference( transform, parent, reorient.value_or(true) ); + } + pod::Transform<> interpolate( const pod::Transform<>& from, const pod::Transform<>& to, float factor, sol::optional reorient ) { + return uf::transform::interpolate( from, to, factor, reorient.value_or(true) ); + } + pod::Transform<> inverse(const pod::Transform<>& t) { + return uf::transform::inverse( t ); + } + pod::Vector3f apply( const pod::Transform<>& transform, const pod::Vector3f& point ) { + return uf::transform::apply( transform, point ); + } + pod::Vector3f applyInverse(const pod::Transform<>& t, const pod::Vector3f& worldPoint) { + return uf::transform::applyInverse( t, worldPoint ); + } + pod::Transform<> relative(const pod::Transform<>& a, const pod::Transform<>& b) { + return uf::transform::relative( a, b ); + } } #include @@ -67,6 +89,13 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Transform<>, UF_LUA_REGISTER_USERTYPE_DEFINE(setReference, UF_LUA_C_FUN(::binds::setReference)), UF_LUA_REGISTER_USERTYPE_DEFINE(unreference, UF_LUA_C_FUN(::binds::unreference)), UF_LUA_REGISTER_USERTYPE_DEFINE(lookAt, UF_LUA_C_FUN(::binds::lookAt)), - UF_LUA_REGISTER_USERTYPE_DEFINE(getModel, UF_LUA_C_FUN(::binds::getModel)) + UF_LUA_REGISTER_USERTYPE_DEFINE(getModel, UF_LUA_C_FUN(::binds::getModel)), + UF_LUA_REGISTER_USERTYPE_DEFINE(fromMatrix, UF_LUA_C_FUN(::binds::fromMatrix)), + UF_LUA_REGISTER_USERTYPE_DEFINE(reference, UF_LUA_C_FUN(::binds::reference)), + UF_LUA_REGISTER_USERTYPE_DEFINE(interpolate, UF_LUA_C_FUN(::binds::interpolate)), + UF_LUA_REGISTER_USERTYPE_DEFINE(inverse, UF_LUA_C_FUN(::binds::inverse)), + UF_LUA_REGISTER_USERTYPE_DEFINE(apply, UF_LUA_C_FUN(::binds::apply)), + UF_LUA_REGISTER_USERTYPE_DEFINE(applyInverse, UF_LUA_C_FUN(::binds::applyInverse)), + UF_LUA_REGISTER_USERTYPE_DEFINE(relative, UF_LUA_C_FUN(::binds::relative)) ) #endif \ No newline at end of file