From 94396f00b55db293a3543d8476e15fb8dc5e0389 Mon Sep 17 00:00:00 2001 From: Tazer Date: Sun, 19 Apr 2026 08:01:30 +0200 Subject: [PATCH 1/4] add generic constraints --- .../GenericConstraintConfiguration.java | 30 ++++ .../generic/GenericConstraintHandle.java | 28 +++ .../sable/physics/impl/rapier/Rapier3D.java | 50 ++++++ .../impl/rapier/RapierPhysicsPipeline.java | 6 + .../RapierGenericConstraintHandle.java | 81 +++++++++ common/src/main/rust/rapier/src/joints.rs | 169 +++++++++++++++++- 6 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java create mode 100644 common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java create mode 100644 common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java diff --git a/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java new file mode 100644 index 00000000..80a0de1d --- /dev/null +++ b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java @@ -0,0 +1,30 @@ +package dev.ryanhcode.sable.api.physics.constraint.generic; + +import dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis; +import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintConfiguration; +import org.joml.Quaterniondc; +import org.joml.Vector3dc; + +import java.util.EnumSet; +import java.util.Set; + +/** + * A configuration for a generic constraint, with per-axis hard locks and re-anchorable local frames. + * @param pos1 the position in world space assumed to be inside the plot of the first sub-level (ex. a block position). + * @param pos2 the position in world space assumed to be inside the plot of the second sub-level (ex. a block position). + * @param orientation1 the local orientation of the joint frame on the first sub-level. + * @param orientation2 the local orientation of the joint frame on the second sub-level. + * @param lockedAxes the set of axes hard-locked by the solver; empty matches a free constraint. + */ +public record GenericConstraintConfiguration( + Vector3dc pos1, + Vector3dc pos2, + Quaterniondc orientation1, + Quaterniondc orientation2, + Set lockedAxes +) implements PhysicsConstraintConfiguration { + + public GenericConstraintConfiguration(final Vector3dc pos1, final Vector3dc pos2, final Quaterniondc orientation1, final Quaterniondc orientation2) { + this(pos1, pos2, orientation1, orientation2, EnumSet.noneOf(ConstraintJointAxis.class)); + } +} diff --git a/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java new file mode 100644 index 00000000..8499a59c --- /dev/null +++ b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java @@ -0,0 +1,28 @@ +package dev.ryanhcode.sable.api.physics.constraint.generic; + +import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintHandle; +import org.joml.Quaterniondc; +import org.joml.Vector3dc; + +/** + * A generic constraint between two bodies + */ +public interface GenericConstraintHandle extends PhysicsConstraintHandle { + + /** + * Sets the local frame on the first body. + * + * @param localPosition the local anchor position + * @param localRotation the local frame orientation + */ + void setFrame1(Vector3dc localPosition, Quaterniondc localRotation); + + /** + * Sets the local frame on the second body. + * + * @param localPosition the local anchor position + * @param localRotation the local frame orientation + */ + void setFrame2(Vector3dc localPosition, Quaterniondc localRotation); + +} diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java index b391a457..b79b865b 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java @@ -369,6 +369,56 @@ public static native long addFreeConstraint(final int dimensionID, double localOrientationZB, double localOrientationWB); + /** + * Adds a generic constraint between two objects. + * + * @param id the object ID + * @param otherId the other object ID + * @param localAnchorXA the local anchor X on the first object + * @param localAnchorYA the local anchor Y on the first object + * @param localAnchorZA the local anchor Z on the first object + * @param localOrientationXA the local orientation X of the first object + * @param localOrientationYA the local orientation Y of the first object + * @param localOrientationZA the local orientation Z of the first object + * @param localOrientationWA the local orientation W of the first object + * @param localAnchorXB the local anchor X on the second object + * @param localAnchorYB the local anchor Y on the second object + * @param localAnchorZB the local anchor Z on the second object + * @param localOrientationXB the local orientation X of the second object + * @param localOrientationYB the local orientation Y of the second object + * @param localOrientationZB the local orientation Z of the second object + * @param localOrientationWB the local orientation W of the second object + * @param lockedAxesMask bit mask of locked axes; bit {@code n} corresponds to {@link dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis#ordinal()} + */ + @ApiStatus.Internal + public static native long addGenericConstraint(final int dimensionID, + int id, + int otherId, + double localAnchorXA, + double localAnchorYA, + double localAnchorZA, + double localOrientationXA, + double localOrientationYA, + double localOrientationZA, + double localOrientationWA, + double localAnchorXB, + double localAnchorYB, + double localAnchorZB, + double localOrientationXB, + double localOrientationYB, + double localOrientationZB, + double localOrientationWB, + int lockedAxesMask); + + /** + * Rewrites the local frame on one side of a constraint. The frame is applied to the underlying joint each tick. + * + * @param handle the handle of the constraint + * @param side {@code 0} for the first body's frame, {@code 1} for the second body's frame + */ + @ApiStatus.Internal + public static native void setConstraintFrame(final int dimensionID, long handle, int side, double localPosX, double localPosY, double localPosZ, double localOrientationX, double localOrientationY, double localOrientationZ, double localOrientationW); + /** * Sets if contacts are enabled between the two bodies in the constraint * diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/RapierPhysicsPipeline.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/RapierPhysicsPipeline.java index 7613f8a5..011e0200 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/RapierPhysicsPipeline.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/RapierPhysicsPipeline.java @@ -7,6 +7,7 @@ import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintHandle; import dev.ryanhcode.sable.api.physics.constraint.fixed.FixedConstraintConfiguration; import dev.ryanhcode.sable.api.physics.constraint.free.FreeConstraintConfiguration; +import dev.ryanhcode.sable.api.physics.constraint.generic.GenericConstraintConfiguration; import dev.ryanhcode.sable.api.physics.constraint.rotary.RotaryConstraintConfiguration; import dev.ryanhcode.sable.api.physics.mass.MassTracker; import dev.ryanhcode.sable.api.physics.object.box.BoxHandle; @@ -23,6 +24,7 @@ import dev.ryanhcode.sable.physics.impl.rapier.collider.RapierVoxelColliderData; import dev.ryanhcode.sable.physics.impl.rapier.constraint.fixed.RapierFixedConstraintHandle; import dev.ryanhcode.sable.physics.impl.rapier.constraint.free.RapierFreeConstraintHandle; +import dev.ryanhcode.sable.physics.impl.rapier.constraint.generic.RapierGenericConstraintHandle; import dev.ryanhcode.sable.physics.impl.rapier.constraint.rotary.RapierRotaryConstraintHandle; import dev.ryanhcode.sable.physics.impl.rapier.rope.RapierRopeHandle; import dev.ryanhcode.sable.sublevel.ServerSubLevel; @@ -644,6 +646,10 @@ public T addConstraint(@Nullable final Serve return (T) RapierFreeConstraintHandle.create(this.level, sublevelA, sublevelB, config); } + if (configuration instanceof final GenericConstraintConfiguration config) { + return (T) RapierGenericConstraintHandle.create(this.level, sublevelA, sublevelB, config); + } + Sable.LOGGER.error("Unknown constraint configuration type: {}", configuration.getClass().getName()); return null; } diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java new file mode 100644 index 00000000..706aaf3b --- /dev/null +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java @@ -0,0 +1,81 @@ +package dev.ryanhcode.sable.physics.impl.rapier.constraint.generic; + +import dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis; +import dev.ryanhcode.sable.api.physics.constraint.generic.GenericConstraintConfiguration; +import dev.ryanhcode.sable.api.physics.constraint.generic.GenericConstraintHandle; +import dev.ryanhcode.sable.physics.impl.rapier.Rapier3D; +import dev.ryanhcode.sable.physics.impl.rapier.constraint.RapierConstraintHandle; +import dev.ryanhcode.sable.sublevel.ServerSubLevel; +import net.minecraft.server.level.ServerLevel; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaterniondc; +import org.joml.Vector3dc; + +public class RapierGenericConstraintHandle extends RapierConstraintHandle implements GenericConstraintHandle { + + private static final int FRAME_SIDE_FIRST = 0; + private static final int FRAME_SIDE_SECOND = 1; + + /** + * Creates a rapier constraint handle + */ + public static RapierGenericConstraintHandle create(final ServerLevel serverLevel, @Nullable final ServerSubLevel sublevelA, @Nullable final ServerSubLevel sublevelB, final GenericConstraintConfiguration config) { + final int sceneID = Rapier3D.getID(serverLevel); + + int lockedAxesMask = 0; + for (final ConstraintJointAxis axis : config.lockedAxes()) { + lockedAxesMask |= 1 << axis.ordinal(); + } + + final long handle = Rapier3D.addGenericConstraint( + sceneID, + sublevelA == null ? -1 : Rapier3D.getID(sublevelA), + sublevelB == null ? -1 : Rapier3D.getID(sublevelB), + config.pos1().x(), + config.pos1().y(), + config.pos1().z(), + config.orientation1().x(), + config.orientation1().y(), + config.orientation1().z(), + config.orientation1().w(), + config.pos2().x(), + config.pos2().y(), + config.pos2().z(), + config.orientation2().x(), + config.orientation2().y(), + config.orientation2().z(), + config.orientation2().w(), + lockedAxesMask + ); + + return new RapierGenericConstraintHandle(sceneID, handle); + } + + /** + * Creates a new constraint handle + * + * @param sceneID the scene ID that this constraint is in + * @param handle the handle from the physics engine + */ + public RapierGenericConstraintHandle(final int sceneID, final long handle) { + super(sceneID, handle); + } + + @Override + public void setFrame1(final Vector3dc localPosition, final Quaterniondc localRotation) { + Rapier3D.setConstraintFrame( + this.sceneID, this.handle, FRAME_SIDE_FIRST, + localPosition.x(), localPosition.y(), localPosition.z(), + localRotation.x(), localRotation.y(), localRotation.z(), localRotation.w() + ); + } + + @Override + public void setFrame2(final Vector3dc localPosition, final Quaterniondc localRotation) { + Rapier3D.setConstraintFrame( + this.sceneID, this.handle, FRAME_SIDE_SECOND, + localPosition.x(), localPosition.y(), localPosition.z(), + localRotation.x(), localRotation.y(), localRotation.z(), localRotation.w() + ); + } +} diff --git a/common/src/main/rust/rapier/src/joints.rs b/common/src/main/rust/rapier/src/joints.rs index 94b0637f..b3172823 100644 --- a/common/src/main/rust/rapier/src/joints.rs +++ b/common/src/main/rust/rapier/src/joints.rs @@ -26,6 +26,9 @@ struct SubLevelJoint { normal_a: Vector3, normal_b: Vector3, + rotation_a: Option, + rotation_b: Option, + handle: RapierJointHandle, fixed: bool, @@ -59,7 +62,7 @@ pub fn tick(scene_id: jint) { .get_mut(joint.handle, false) .unwrap(); impulse_joint.data.contacts_enabled = joint.contacts_enabled; - if !joint.fixed { + if !joint.fixed && joint.rotation_a.is_none() { impulse_joint.data.set_local_axis1(Vector::new( joint.normal_a.x as Real, joint.normal_a.y as Real, @@ -78,7 +81,7 @@ pub fn tick(scene_id: jint) { local_anchor_1.y as Real, local_anchor_1.z as Real, )); - if !joint.fixed { + if !joint.fixed && joint.rotation_b.is_none() { impulse_joint.data.set_local_axis2(Vector::new( joint.normal_b.x as Real, joint.normal_b.y as Real, @@ -97,6 +100,12 @@ pub fn tick(scene_id: jint) { local_anchor_2.y as Real, local_anchor_2.z as Real, )); + if let Some(rotation_a) = joint.rotation_a { + impulse_joint.data.local_frame1.rotation = rotation_a; + } + if let Some(rotation_b) = joint.rotation_b { + impulse_joint.data.local_frame2.rotation = rotation_b; + } } } @@ -297,6 +306,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add normal_a: Vector3::new(axis_x_a, axis_y_a, axis_z_a), normal_b: Vector3::new(axis_x_b, axis_y_b, axis_z_b), + rotation_a: None, + rotation_b: None, + handle, fixed: false, @@ -383,6 +395,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add normal_a: Vector3::new(0.0, 0.0, 0.0), normal_b: Vector3::new(0.0, 0.0, 0.0), + rotation_a: None, + rotation_b: None, + handle, fixed: true, @@ -466,6 +481,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add normal_a: Vector3::new(0.0, 0.0, 0.0), normal_b: Vector3::new(0.0, 0.0, 0.0), + rotation_a: None, + rotation_b: None, + handle, fixed: true, @@ -475,3 +493,150 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle_long } + +#[no_mangle] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addGenericConstraint< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id_a: jint, + id_b: jint, + local_x_a: jdouble, + local_y_a: jdouble, + local_z_a: jdouble, + local_q_x_a: jdouble, + local_q_y_a: jdouble, + local_q_z_a: jdouble, + local_q_w_a: jdouble, + local_x_b: jdouble, + local_y_b: jdouble, + local_z_b: jdouble, + local_q_x_b: jdouble, + local_q_y_b: jdouble, + local_q_z_b: jdouble, + local_q_w_b: jdouble, + locked_axes_mask: jint, +) -> SableJointHandle { + let scene = get_scene_mut_ref(scene_id); + + let rb_a = if id_a == -1 { + scene.ground_handle.unwrap() + } else { + scene.rigid_bodies[&(id_a as LevelColliderID)] + }; + + let rb_b = if id_b == -1 { + scene.ground_handle.unwrap() + } else { + scene.rigid_bodies[&(id_b as LevelColliderID)] + }; + + let locked_axes = JointAxesMask::from_bits_truncate(locked_axes_mask as u8); + + let rotation_a = Quat::from_xyzw( + local_q_x_a as Real, + local_q_y_a as Real, + local_q_z_a as Real, + local_q_w_a as Real, + ); + let rotation_b = Quat::from_xyzw( + local_q_x_b as Real, + local_q_y_b as Real, + local_q_z_b as Real, + local_q_w_b as Real, + ); + + let mut joint = GenericJointBuilder::new(locked_axes).softness(SpringCoefficients::new( + JOINT_SPRING_FREQUENCY, + JOINT_SPRING_DAMPING_RATIO, + )); + joint.0.local_frame1.rotation = rotation_a; + joint.0.local_frame2.rotation = rotation_b; + + let handle = scene + .impulse_joint_set + .insert(rb_a, rb_b, joint.build(), true); + + let (index, generation) = handle.0.into_raw_parts(); + let handle_long: SableJointHandle = index as jlong | (generation as jlong) << 32; + + scene.joint_set.joints.insert( + handle_long, + SubLevelJoint { + id_a: if id_a == -1 { + None + } else { + Some(id_a as LevelColliderID) + }, + id_b: if id_b == -1 { + None + } else { + Some(id_b as LevelColliderID) + }, + + pos_a: Vector3::new(local_x_a as f64, local_y_a as f64, local_z_a as f64), + pos_b: Vector3::new(local_x_b as f64, local_y_b as f64, local_z_b as f64), + + normal_a: Vector3::new(0.0, 0.0, 0.0), + normal_b: Vector3::new(0.0, 0.0, 0.0), + + rotation_a: Some(rotation_a), + rotation_b: Some(rotation_b), + + handle, + + fixed: false, + contacts_enabled: true, + }, + ); + + handle_long +} + +const FRAME_SIDE_FIRST: jint = 0; +const FRAME_SIDE_SECOND: jint = 1; + +#[no_mangle] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintFrame< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, + side: jint, + local_x: jdouble, + local_y: jdouble, + local_z: jdouble, + local_q_x: jdouble, + local_q_y: jdouble, + local_q_z: jdouble, + local_q_w: jdouble, +) { + let scene = get_scene_mut_ref(scene_id); + let Some(joint) = scene.joint_set.joints.get_mut(&joint_id) else { + return; + }; + + let position = Vector3::new(local_x as f64, local_y as f64, local_z as f64); + let rotation = Quat::from_xyzw( + local_q_x as Real, + local_q_y as Real, + local_q_z as Real, + local_q_w as Real, + ); + + match side { + FRAME_SIDE_FIRST => { + joint.pos_a = position; + joint.rotation_a = Some(rotation); + } + FRAME_SIDE_SECOND => { + joint.pos_b = position; + joint.rotation_b = Some(rotation); + } + _ => {} + } +} From ea8ce3fdc4253af38ff8e5885175aae7d269533d Mon Sep 17 00:00:00 2001 From: Tazer Date: Sun, 19 Apr 2026 08:09:09 +0200 Subject: [PATCH 2/4] small consistency changes --- .../ryanhcode/sable/physics/impl/rapier/Rapier3D.java | 6 +++--- common/src/main/rust/rapier/src/joints.rs | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java index b79b865b..15f6f44a 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java @@ -411,10 +411,10 @@ public static native long addGenericConstraint(final int dimensionID, int lockedAxesMask); /** - * Rewrites the local frame on one side of a constraint. The frame is applied to the underlying joint each tick. + * Sets the local frame on one side of a constraint. * - * @param handle the handle of the constraint - * @param side {@code 0} for the first body's frame, {@code 1} for the second body's frame + * @param handle the handle of the constraint + * @param side {@code 0} for the first body, {@code 1} for the second body */ @ApiStatus.Internal public static native void setConstraintFrame(final int dimensionID, long handle, int side, double localPosX, double localPosY, double localPosZ, double localOrientationX, double localOrientationY, double localOrientationZ, double localOrientationW); diff --git a/common/src/main/rust/rapier/src/joints.rs b/common/src/main/rust/rapier/src/joints.rs index b3172823..137a76d7 100644 --- a/common/src/main/rust/rapier/src/joints.rs +++ b/common/src/main/rust/rapier/src/joints.rs @@ -587,7 +587,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle, - fixed: false, + fixed: true, contacts_enabled: true, }, ); @@ -595,9 +595,6 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle_long } -const FRAME_SIDE_FIRST: jint = 0; -const FRAME_SIDE_SECOND: jint = 1; - #[no_mangle] pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintFrame< 'local, @@ -629,11 +626,11 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set ); match side { - FRAME_SIDE_FIRST => { + 0 => { joint.pos_a = position; joint.rotation_a = Some(rotation); } - FRAME_SIDE_SECOND => { + 1 => { joint.pos_b = position; joint.rotation_b = Some(rotation); } From b6daf601b8e5fe6e341fdb88a38c85195368e7b0 Mon Sep 17 00:00:00 2001 From: Ocelot Date: Sun, 19 Apr 2026 16:48:43 -0600 Subject: [PATCH 3/4] Formatting --- .../generic/GenericConstraintConfiguration.java | 8 +++++--- .../generic/GenericConstraintHandle.java | 5 +++-- .../constraint/RapierConstraintHandle.java | 16 ++++++++++------ .../generic/RapierGenericConstraintHandle.java | 2 ++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java index 80a0de1d..56150402 100644 --- a/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java +++ b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintConfiguration.java @@ -10,11 +10,13 @@ /** * A configuration for a generic constraint, with per-axis hard locks and re-anchorable local frames. - * @param pos1 the position in world space assumed to be inside the plot of the first sub-level (ex. a block position). - * @param pos2 the position in world space assumed to be inside the plot of the second sub-level (ex. a block position). + * + * @param pos1 the position in world space assumed to be inside the plot of the first sub-level (ex. a block position). + * @param pos2 the position in world space assumed to be inside the plot of the second sub-level (ex. a block position). * @param orientation1 the local orientation of the joint frame on the first sub-level. * @param orientation2 the local orientation of the joint frame on the second sub-level. - * @param lockedAxes the set of axes hard-locked by the solver; empty matches a free constraint. + * @param lockedAxes the set of axes hard-locked by the solver; empty matches a free constraint. + * @since 1.1.0 */ public record GenericConstraintConfiguration( Vector3dc pos1, diff --git a/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java index 8499a59c..5c714c8f 100644 --- a/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java +++ b/common/src/main/java/dev/ryanhcode/sable/api/physics/constraint/generic/GenericConstraintHandle.java @@ -5,7 +5,9 @@ import org.joml.Vector3dc; /** - * A generic constraint between two bodies + * A generic constraint between two bodies. + * + * @since 1.1.0 */ public interface GenericConstraintHandle extends PhysicsConstraintHandle { @@ -24,5 +26,4 @@ public interface GenericConstraintHandle extends PhysicsConstraintHandle { * @param localRotation the local frame orientation */ void setFrame2(Vector3dc localPosition, Quaterniondc localRotation); - } diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/RapierConstraintHandle.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/RapierConstraintHandle.java index 493581bd..7adfc13a 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/RapierConstraintHandle.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/RapierConstraintHandle.java @@ -3,9 +3,12 @@ import dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis; import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintHandle; import dev.ryanhcode.sable.physics.impl.rapier.Rapier3D; +import org.jetbrains.annotations.ApiStatus; import org.joml.Vector3d; +@ApiStatus.Internal public abstract class RapierConstraintHandle implements PhysicsConstraintHandle { + /** * The handle to use for {@link dev.ryanhcode.sable.physics.impl.rapier.Rapier3D} methods */ @@ -20,8 +23,9 @@ public abstract class RapierConstraintHandle implements PhysicsConstraintHandle /** * Creates a new constraint handle + * * @param sceneID the scene ID that this constraint is in - * @param handle the handle from the physics engine + * @param handle the handle from the physics engine */ protected RapierConstraintHandle(final int sceneID, final long handle) { this.sceneID = sceneID; @@ -50,12 +54,12 @@ public void getJointImpulses(final Vector3d linearImpulseDest, final Vector3d an /** * Adds / sets a motor on this joint * - * @param axis The axis on which the motor operates - * @param target The target position along that axis [m | rad] - * @param stiffness How stiff the motor should act, or P in the PD controller - * @param damping How much damping the motor should have, or D in the PD controller + * @param axis The axis on which the motor operates + * @param target The target position along that axis [m | rad] + * @param stiffness How stiff the motor should act, or P in the PD controller + * @param damping How much damping the motor should have, or D in the PD controller * @param hasForceLimit If the motor should have a force limit - * @param maxForce The maximum force the motor can apply + * @param maxForce The maximum force the motor can apply */ @Override public void setMotor(final ConstraintJointAxis axis, final double target, final double stiffness, final double damping, final boolean hasForceLimit, final double maxForce) { diff --git a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java index 706aaf3b..24160741 100644 --- a/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java +++ b/common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/generic/RapierGenericConstraintHandle.java @@ -7,10 +7,12 @@ import dev.ryanhcode.sable.physics.impl.rapier.constraint.RapierConstraintHandle; import dev.ryanhcode.sable.sublevel.ServerSubLevel; import net.minecraft.server.level.ServerLevel; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.joml.Quaterniondc; import org.joml.Vector3dc; +@ApiStatus.Internal public class RapierGenericConstraintHandle extends RapierConstraintHandle implements GenericConstraintHandle { private static final int FRAME_SIDE_FIRST = 0; From f943779416b2b070e684a37ea1551a76a03f8994 Mon Sep 17 00:00:00 2001 From: Ocelot Date: Sun, 19 Apr 2026 16:48:55 -0600 Subject: [PATCH 4/4] Panic for invalid constraint side --- common/src/main/rust/rapier/src/joints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/rust/rapier/src/joints.rs b/common/src/main/rust/rapier/src/joints.rs index 137a76d7..1b8c9ce1 100644 --- a/common/src/main/rust/rapier/src/joints.rs +++ b/common/src/main/rust/rapier/src/joints.rs @@ -634,6 +634,6 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set joint.pos_b = position; joint.rotation_b = Some(rotation); } - _ => {} + _ => panic!("Invalid constraint frame side: {}", side) } }