diff --git a/leenkx/Sources/leenkx/trait/physics/bullet/RigidBody.hx b/leenkx/Sources/leenkx/trait/physics/bullet/RigidBody.hx index a84d758..d4912bc 100644 --- a/leenkx/Sources/leenkx/trait/physics/bullet/RigidBody.hx +++ b/leenkx/Sources/leenkx/trait/physics/bullet/RigidBody.hx @@ -36,6 +36,18 @@ class RigidBody extends iron.Trait { var useDeactivation: Bool; var deactivationParams: Array; var ccd = false; // Continuous collision detection + // New velocity limiting properties + var linearVelocityMin: Float; + var linearVelocityMax: Float; + var angularVelocityMin: Float; + var angularVelocityMax: Float; + // New lock properties + var lockTranslationX: Bool; + var lockTranslationY: Bool; + var lockTranslationZ: Bool; + var lockRotationX: Bool; + var lockRotationY: Bool; + var lockRotationZ: Bool; public var group = 1; public var mask = 1; var trigger = false; @@ -120,7 +132,17 @@ class RigidBody extends iron.Trait { collisionMargin: 0.0, linearDeactivationThreshold: 0.0, angularDeactivationThrshold: 0.0, - deactivationTime: 0.0 + deactivationTime: 0.0, + linearVelocityMin: 0.0, + linearVelocityMax: 0.0, + angularVelocityMin: 0.0, + angularVelocityMax: 0.0, + lockTranslationX: false, + lockTranslationY: false, + lockTranslationZ: false, + lockRotationX: false, + lockRotationY: false, + lockRotationZ: false }; if (flags == null) flags = { @@ -139,6 +161,18 @@ class RigidBody extends iron.Trait { this.angularFactors = [params.angularFactorsX, params.angularFactorsY, params.angularFactorsZ]; this.collisionMargin = params.collisionMargin; this.deactivationParams = [params.linearDeactivationThreshold, params.angularDeactivationThrshold, params.deactivationTime]; + // New velocity limiting properties + this.linearVelocityMin = params.linearVelocityMin; + this.linearVelocityMax = params.linearVelocityMax; + this.angularVelocityMin = params.angularVelocityMin; + this.angularVelocityMax = params.angularVelocityMax; + // New lock properties + this.lockTranslationX = params.lockTranslationX; + this.lockTranslationY = params.lockTranslationY; + this.lockTranslationZ = params.lockTranslationZ; + this.lockRotationX = params.lockRotationX; + this.lockRotationY = params.lockRotationY; + this.lockRotationZ = params.lockRotationZ; this.animated = flags.animated; this.trigger = flags.trigger; this.ccd = flags.ccd; @@ -291,11 +325,25 @@ class RigidBody extends iron.Trait { } if (linearFactors != null) { - setLinearFactor(linearFactors[0], linearFactors[1], linearFactors[2]); + // Apply lock properties by overriding factors + var lx = linearFactors[0]; + var ly = linearFactors[1]; + var lz = linearFactors[2]; + if (lockTranslationX) lx = 0.0; + if (lockTranslationY) ly = 0.0; + if (lockTranslationZ) lz = 0.0; + setLinearFactor(lx, ly, lz); } if (angularFactors != null) { - setAngularFactor(angularFactors[0], angularFactors[1], angularFactors[2]); + // Apply lock properties by overriding factors + var ax = angularFactors[0]; + var ay = angularFactors[1]; + var az = angularFactors[2]; + if (lockRotationX) ax = 0.0; + if (lockRotationY) ay = 0.0; + if (lockRotationZ) az = 0.0; + setAngularFactor(ax, ay, az); } if (trigger) bodyColl.setCollisionFlags(bodyColl.getCollisionFlags() | CF_NO_CONTACT_RESPONSE); @@ -411,6 +459,55 @@ class RigidBody extends iron.Trait { var rbs = physics.getContacts(this); if (rbs != null) for (rb in rbs) for (f in onContact) f(rb); } + + // Apply velocity limiting if enabled + if (!animated && !staticObj) { + applyVelocityLimits(); + } + } + + function applyVelocityLimits() { + if (!ready) return; + + // Check linear velocity limits + if (linearVelocityMin > 0.0 || linearVelocityMax > 0.0) { + var velocity = getLinearVelocity(); + var speed = velocity.length(); + + if (linearVelocityMin > 0.0 && speed < linearVelocityMin) { + // Increase velocity to minimum + if (speed > 0.0) { + velocity.normalize(); + velocity.mult(linearVelocityMin); + setLinearVelocity(velocity.x, velocity.y, velocity.z); + } + } else if (linearVelocityMax > 0.0 && speed > linearVelocityMax) { + // Clamp velocity to maximum + velocity.normalize(); + velocity.mult(linearVelocityMax); + setLinearVelocity(velocity.x, velocity.y, velocity.z); + } + } + + // Check angular velocity limits + if (angularVelocityMin > 0.0 || angularVelocityMax > 0.0) { + var angularVel = getAngularVelocity(); + var angularSpeed = angularVel.length(); + + if (angularVelocityMin > 0.0 && angularSpeed < angularVelocityMin) { + // Increase angular velocity to minimum + if (angularSpeed > 0.0) { + angularVel.normalize(); + angularVel.mult(angularVelocityMin); + setAngularVelocity(angularVel.x, angularVel.y, angularVel.z); + } + } else if (angularVelocityMax > 0.0 && angularSpeed > angularVelocityMax) { + // Clamp angular velocity to maximum + angularVel.normalize(); + angularVel.mult(angularVelocityMax); + setAngularVelocity(angularVel.x, angularVel.y, angularVel.z); + } + } } public function disableCollision() { @@ -745,6 +842,16 @@ typedef RigidBodyParams = { var linearDeactivationThreshold: Float; var angularDeactivationThrshold: Float; var deactivationTime: Float; + var linearVelocityMin: Float; + var linearVelocityMax: Float; + var angularVelocityMin: Float; + var angularVelocityMax: Float; + var lockTranslationX: Bool; + var lockTranslationY: Bool; + var lockTranslationZ: Bool; + var lockRotationX: Bool; + var lockRotationY: Bool; + var lockRotationZ: Bool; } typedef RigidBodyFlags = { diff --git a/leenkx/blender/lnx/exporter.py b/leenkx/blender/lnx/exporter.py index 5be20d2..4fd6fec 100644 --- a/leenkx/blender/lnx/exporter.py +++ b/leenkx/blender/lnx/exporter.py @@ -2830,8 +2830,8 @@ class LeenkxExporter: deact_av = 0.0 deact_time = 0.0 body_params = {} - body_params['linearDamping'] = rb.linear_damping - body_params['angularDamping'] = rb.angular_damping + body_params['linearDamping'] = bobject.lnx_rb_linear_damping + body_params['angularDamping'] = bobject.lnx_rb_angular_damping body_params['linearFactorsX'] = lx body_params['linearFactorsY'] = ly body_params['linearFactorsZ'] = lz @@ -2843,6 +2843,18 @@ class LeenkxExporter: body_params['linearDeactivationThreshold'] = deact_lv body_params['angularDeactivationThrshold'] = deact_av body_params['deactivationTime'] = deact_time + # New velocity limit properties + body_params['linearVelocityMin'] = bobject.lnx_rb_linear_velocity_min + body_params['linearVelocityMax'] = bobject.lnx_rb_linear_velocity_max + body_params['angularVelocityMin'] = bobject.lnx_rb_angular_velocity_min + body_params['angularVelocityMax'] = bobject.lnx_rb_angular_velocity_max + # New lock properties + body_params['lockTranslationX'] = bobject.lnx_rb_lock_translation_x + body_params['lockTranslationY'] = bobject.lnx_rb_lock_translation_y + body_params['lockTranslationZ'] = bobject.lnx_rb_lock_translation_z + body_params['lockRotationX'] = bobject.lnx_rb_lock_rotation_x + body_params['lockRotationY'] = bobject.lnx_rb_lock_rotation_y + body_params['lockRotationZ'] = bobject.lnx_rb_lock_rotation_z body_flags = {} body_flags['animated'] = rb.kinematic body_flags['trigger'] = bobject.lnx_rb_trigger diff --git a/leenkx/blender/lnx/props.py b/leenkx/blender/lnx/props.py index 04cd05c..ce6d643 100644 --- a/leenkx/blender/lnx/props.py +++ b/leenkx/blender/lnx/props.py @@ -368,6 +368,29 @@ def init_properties(): default=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False), size=20, subtype='LAYER') + + # Linear velocity limits + bpy.types.Object.lnx_rb_linear_velocity_min = FloatProperty(name="Linear Velocity Min", description="Minimum linear velocity", default=0.0, min=0.0) + bpy.types.Object.lnx_rb_linear_velocity_max = FloatProperty(name="Linear Velocity Max", description="Maximum linear velocity", default=0.0, min=0.0) + + # Angular velocity limits + bpy.types.Object.lnx_rb_angular_velocity_min = FloatProperty(name="Angular Velocity Min", description="Minimum angular velocity", default=0.0, min=0.0) + bpy.types.Object.lnx_rb_angular_velocity_max = FloatProperty(name="Angular Velocity Max", description="Maximum angular velocity", default=0.0, min=0.0) + + # Damping controls + bpy.types.Object.lnx_rb_linear_damping = FloatProperty(name="Linear Damping", description="Linear damping factor", default=0.04, min=0.0, max=1.0) + bpy.types.Object.lnx_rb_angular_damping = FloatProperty(name="Angular Damping", description="Angular damping factor", default=0.1, min=0.0, max=1.0) + + # Lock translation axes + bpy.types.Object.lnx_rb_lock_translation_x = BoolProperty(name="Lock Translation X", description="Lock movement along X axis", default=False) + bpy.types.Object.lnx_rb_lock_translation_y = BoolProperty(name="Lock Translation Y", description="Lock movement along Y axis", default=False) + bpy.types.Object.lnx_rb_lock_translation_z = BoolProperty(name="Lock Translation Z", description="Lock movement along Z axis", default=False) + + # Lock rotation axes + bpy.types.Object.lnx_rb_lock_rotation_x = BoolProperty(name="Lock Rotation X", description="Lock rotation around X axis", default=False) + bpy.types.Object.lnx_rb_lock_rotation_y = BoolProperty(name="Lock Rotation Y", description="Lock rotation around Y axis", default=False) + bpy.types.Object.lnx_rb_lock_rotation_z = BoolProperty(name="Lock Rotation Z", description="Lock rotation around Z axis", default=False) + bpy.types.Object.lnx_relative_physics_constraint = BoolProperty(name="Relative Physics Constraint", description="Add physics constraint relative to the parent object or collection when spawned", default=False) bpy.types.Object.lnx_animation_enabled = BoolProperty(name="Animation", description="Enable skinning & timeline animation", default=True) bpy.types.Object.lnx_tilesheet = StringProperty(name="Tilesheet", description="Set tilesheet animation", default='') diff --git a/leenkx/blender/lnx/props_ui.py b/leenkx/blender/lnx/props_ui.py index 33b0a8c..6ff3211 100644 --- a/leenkx/blender/lnx/props_ui.py +++ b/leenkx/blender/lnx/props_ui.py @@ -240,6 +240,41 @@ class LNX_PT_PhysicsPropsPanel(bpy.types.Panel): layout.prop(obj, 'lnx_rb_linear_factor') layout.prop(obj, 'lnx_rb_angular_factor') layout.prop(obj, 'lnx_rb_angular_friction') + + # Linear Velocity section + layout.separator() + layout.label(text="Linear Velocity:") + layout.prop(obj, 'lnx_rb_linear_velocity_min') + layout.prop(obj, 'lnx_rb_linear_velocity_max') + + # Angular Velocity section + layout.separator() + layout.label(text="Angular Velocity:") + layout.prop(obj, 'lnx_rb_angular_velocity_min') + layout.prop(obj, 'lnx_rb_angular_velocity_max') + + # Damping section + layout.separator() + layout.label(text="Damping:") + layout.prop(obj, 'lnx_rb_linear_damping') + layout.prop(obj, 'lnx_rb_angular_damping') + + # Lock Translation section + layout.separator() + layout.label(text="Lock Translation:") + row = layout.row(align=True) + row.prop(obj, 'lnx_rb_lock_translation_x', text="X") + row.prop(obj, 'lnx_rb_lock_translation_y', text="Y") + row.prop(obj, 'lnx_rb_lock_translation_z', text="Z") + + # Lock Rotation section + layout.separator() + layout.label(text="Lock Rotation:") + row = layout.row(align=True) + row.prop(obj, 'lnx_rb_lock_rotation_x', text="X") + row.prop(obj, 'lnx_rb_lock_rotation_y', text="Y") + row.prop(obj, 'lnx_rb_lock_rotation_z', text="Z") + layout.prop(obj, 'lnx_rb_trigger') layout.prop(obj, 'lnx_rb_ccd') layout.prop(obj, 'lnx_rb_interpolate')