an almost perfect ragdoll (has issues when you grab it)

This commit is contained in:
ecker 2026-05-22 22:02:13 -05:00
parent 6deb8795c5
commit eec75781ba
10 changed files with 110 additions and 34 deletions

View File

@ -0,0 +1,6 @@
{
"name": "Ragdoll Limb",
"metadata": {
"holdable": true
}
}

View File

@ -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]

View File

@ -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 );

View File

@ -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 );

View File

@ -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<uf::ObjectBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
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<uf::ObjectBehavior::Metadata>().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 ) {

View File

@ -93,6 +93,11 @@ namespace binds {
static uf::Object null;
return null;
};
uf::Object& create( sol::optional<bool> init ) {
auto& entity = uf::instantiator::instantiate("Object");
if ( init.value_or(true) ) entity.initialize();
return entity;
};
uf::Object& currentScene() {
return uf::scene::getCurrentScene().as<uf::Object>();
};
@ -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<float> mass, sol::optional<pod::Vector3f> 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<sol::table>();
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));

View File

@ -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<bool> force ) {
self.update(force.value_or(true));
}
}

View File

@ -121,6 +121,11 @@ namespace binds {
if ( type == "Metadata" ) {
self.callHook( "object:Serialize.%UID%" );
auto& metadata = self.getComponent<uf::Serializer>();
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<bool> 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<uf::stl::string> filename, sol::optional<bool> init ) {
auto* pointer = self.loadChildPointer( filename.value_or(""), init.value_or(true) );
if ( pointer ) return pointer->as<uf::Object>();
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<float> 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<bool> init ){
if ( arg.is<uf::stl::string>() ) {
self.load( arg.as<uf::stl::string>() );
} else if ( arg.is<sol::table>() ) {
@ -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) ),

View File

@ -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<uf::Object*, float> 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<bool> 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<float> swingLimit, sol::optional<float> 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<bool> 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) ),

View File

@ -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<bool> 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<bool> 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 <uf/ext/lua/component.h>
@ -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