Skip to content

Commit 06d5e4f

Browse files
Add handle for rapier's generic constraints (#125)
Co-authored-by: Ocelot <FinntheRaider@gmail.com>
1 parent d38446f commit 06d5e4f

7 files changed

Lines changed: 374 additions & 8 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dev.ryanhcode.sable.api.physics.constraint.generic;
2+
3+
import dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis;
4+
import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintConfiguration;
5+
import org.joml.Quaterniondc;
6+
import org.joml.Vector3dc;
7+
8+
import java.util.EnumSet;
9+
import java.util.Set;
10+
11+
/**
12+
* A configuration for a generic constraint, with per-axis hard locks and re-anchorable local frames.
13+
*
14+
* @param pos1 the position in world space assumed to be inside the plot of the first sub-level (ex. a block position).
15+
* @param pos2 the position in world space assumed to be inside the plot of the second sub-level (ex. a block position).
16+
* @param orientation1 the local orientation of the joint frame on the first sub-level.
17+
* @param orientation2 the local orientation of the joint frame on the second sub-level.
18+
* @param lockedAxes the set of axes hard-locked by the solver; empty matches a free constraint.
19+
* @since 1.1.0
20+
*/
21+
public record GenericConstraintConfiguration(
22+
Vector3dc pos1,
23+
Vector3dc pos2,
24+
Quaterniondc orientation1,
25+
Quaterniondc orientation2,
26+
Set<ConstraintJointAxis> lockedAxes
27+
) implements PhysicsConstraintConfiguration<GenericConstraintHandle> {
28+
29+
public GenericConstraintConfiguration(final Vector3dc pos1, final Vector3dc pos2, final Quaterniondc orientation1, final Quaterniondc orientation2) {
30+
this(pos1, pos2, orientation1, orientation2, EnumSet.noneOf(ConstraintJointAxis.class));
31+
}
32+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dev.ryanhcode.sable.api.physics.constraint.generic;
2+
3+
import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintHandle;
4+
import org.joml.Quaterniondc;
5+
import org.joml.Vector3dc;
6+
7+
/**
8+
* A generic constraint between two bodies.
9+
*
10+
* @since 1.1.0
11+
*/
12+
public interface GenericConstraintHandle extends PhysicsConstraintHandle {
13+
14+
/**
15+
* Sets the local frame on the first body.
16+
*
17+
* @param localPosition the local anchor position
18+
* @param localRotation the local frame orientation
19+
*/
20+
void setFrame1(Vector3dc localPosition, Quaterniondc localRotation);
21+
22+
/**
23+
* Sets the local frame on the second body.
24+
*
25+
* @param localPosition the local anchor position
26+
* @param localRotation the local frame orientation
27+
*/
28+
void setFrame2(Vector3dc localPosition, Quaterniondc localRotation);
29+
}

common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/Rapier3D.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,56 @@ public static native long addFreeConstraint(final int dimensionID,
369369
double localOrientationZB,
370370
double localOrientationWB);
371371

372+
/**
373+
* Adds a generic constraint between two objects.
374+
*
375+
* @param id the object ID
376+
* @param otherId the other object ID
377+
* @param localAnchorXA the local anchor X on the first object
378+
* @param localAnchorYA the local anchor Y on the first object
379+
* @param localAnchorZA the local anchor Z on the first object
380+
* @param localOrientationXA the local orientation X of the first object
381+
* @param localOrientationYA the local orientation Y of the first object
382+
* @param localOrientationZA the local orientation Z of the first object
383+
* @param localOrientationWA the local orientation W of the first object
384+
* @param localAnchorXB the local anchor X on the second object
385+
* @param localAnchorYB the local anchor Y on the second object
386+
* @param localAnchorZB the local anchor Z on the second object
387+
* @param localOrientationXB the local orientation X of the second object
388+
* @param localOrientationYB the local orientation Y of the second object
389+
* @param localOrientationZB the local orientation Z of the second object
390+
* @param localOrientationWB the local orientation W of the second object
391+
* @param lockedAxesMask bit mask of locked axes; bit {@code n} corresponds to {@link dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis#ordinal()}
392+
*/
393+
@ApiStatus.Internal
394+
public static native long addGenericConstraint(final int dimensionID,
395+
int id,
396+
int otherId,
397+
double localAnchorXA,
398+
double localAnchorYA,
399+
double localAnchorZA,
400+
double localOrientationXA,
401+
double localOrientationYA,
402+
double localOrientationZA,
403+
double localOrientationWA,
404+
double localAnchorXB,
405+
double localAnchorYB,
406+
double localAnchorZB,
407+
double localOrientationXB,
408+
double localOrientationYB,
409+
double localOrientationZB,
410+
double localOrientationWB,
411+
int lockedAxesMask);
412+
413+
/**
414+
* Sets the local frame on one side of a constraint.
415+
*
416+
* @param handle the handle of the constraint
417+
* @param side {@code 0} for the first body, {@code 1} for the second body
418+
*/
419+
@ApiStatus.Internal
420+
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);
421+
372422
/**
373423
* Sets if contacts are enabled between the two bodies in the constraint
374424
*

common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/RapierPhysicsPipeline.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintHandle;
88
import dev.ryanhcode.sable.api.physics.constraint.fixed.FixedConstraintConfiguration;
99
import dev.ryanhcode.sable.api.physics.constraint.free.FreeConstraintConfiguration;
10+
import dev.ryanhcode.sable.api.physics.constraint.generic.GenericConstraintConfiguration;
1011
import dev.ryanhcode.sable.api.physics.constraint.rotary.RotaryConstraintConfiguration;
1112
import dev.ryanhcode.sable.api.physics.mass.MassTracker;
1213
import dev.ryanhcode.sable.api.physics.object.box.BoxHandle;
@@ -23,6 +24,7 @@
2324
import dev.ryanhcode.sable.physics.impl.rapier.collider.RapierVoxelColliderData;
2425
import dev.ryanhcode.sable.physics.impl.rapier.constraint.fixed.RapierFixedConstraintHandle;
2526
import dev.ryanhcode.sable.physics.impl.rapier.constraint.free.RapierFreeConstraintHandle;
27+
import dev.ryanhcode.sable.physics.impl.rapier.constraint.generic.RapierGenericConstraintHandle;
2628
import dev.ryanhcode.sable.physics.impl.rapier.constraint.rotary.RapierRotaryConstraintHandle;
2729
import dev.ryanhcode.sable.physics.impl.rapier.rope.RapierRopeHandle;
2830
import dev.ryanhcode.sable.sublevel.ServerSubLevel;
@@ -644,6 +646,10 @@ public <T extends PhysicsConstraintHandle> T addConstraint(@Nullable final Serve
644646
return (T) RapierFreeConstraintHandle.create(this.level, sublevelA, sublevelB, config);
645647
}
646648

649+
if (configuration instanceof final GenericConstraintConfiguration config) {
650+
return (T) RapierGenericConstraintHandle.create(this.level, sublevelA, sublevelB, config);
651+
}
652+
647653
Sable.LOGGER.error("Unknown constraint configuration type: {}", configuration.getClass().getName());
648654
return null;
649655
}

common/src/main/java/dev/ryanhcode/sable/physics/impl/rapier/constraint/RapierConstraintHandle.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis;
44
import dev.ryanhcode.sable.api.physics.constraint.PhysicsConstraintHandle;
55
import dev.ryanhcode.sable.physics.impl.rapier.Rapier3D;
6+
import org.jetbrains.annotations.ApiStatus;
67
import org.joml.Vector3d;
78

9+
@ApiStatus.Internal
810
public abstract class RapierConstraintHandle implements PhysicsConstraintHandle {
11+
912
/**
1013
* The handle to use for {@link dev.ryanhcode.sable.physics.impl.rapier.Rapier3D} methods
1114
*/
@@ -20,8 +23,9 @@ public abstract class RapierConstraintHandle implements PhysicsConstraintHandle
2023

2124
/**
2225
* Creates a new constraint handle
26+
*
2327
* @param sceneID the scene ID that this constraint is in
24-
* @param handle the handle from the physics engine
28+
* @param handle the handle from the physics engine
2529
*/
2630
protected RapierConstraintHandle(final int sceneID, final long handle) {
2731
this.sceneID = sceneID;
@@ -50,12 +54,12 @@ public void getJointImpulses(final Vector3d linearImpulseDest, final Vector3d an
5054
/**
5155
* Adds / sets a motor on this joint
5256
*
53-
* @param axis The axis on which the motor operates
54-
* @param target The target position along that axis [m | rad]
55-
* @param stiffness How stiff the motor should act, or P in the PD controller
56-
* @param damping How much damping the motor should have, or D in the PD controller
57+
* @param axis The axis on which the motor operates
58+
* @param target The target position along that axis [m | rad]
59+
* @param stiffness How stiff the motor should act, or P in the PD controller
60+
* @param damping How much damping the motor should have, or D in the PD controller
5761
* @param hasForceLimit If the motor should have a force limit
58-
* @param maxForce The maximum force the motor can apply
62+
* @param maxForce The maximum force the motor can apply
5963
*/
6064
@Override
6165
public void setMotor(final ConstraintJointAxis axis, final double target, final double stiffness, final double damping, final boolean hasForceLimit, final double maxForce) {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package dev.ryanhcode.sable.physics.impl.rapier.constraint.generic;
2+
3+
import dev.ryanhcode.sable.api.physics.constraint.ConstraintJointAxis;
4+
import dev.ryanhcode.sable.api.physics.constraint.generic.GenericConstraintConfiguration;
5+
import dev.ryanhcode.sable.api.physics.constraint.generic.GenericConstraintHandle;
6+
import dev.ryanhcode.sable.physics.impl.rapier.Rapier3D;
7+
import dev.ryanhcode.sable.physics.impl.rapier.constraint.RapierConstraintHandle;
8+
import dev.ryanhcode.sable.sublevel.ServerSubLevel;
9+
import net.minecraft.server.level.ServerLevel;
10+
import org.jetbrains.annotations.ApiStatus;
11+
import org.jetbrains.annotations.Nullable;
12+
import org.joml.Quaterniondc;
13+
import org.joml.Vector3dc;
14+
15+
@ApiStatus.Internal
16+
public class RapierGenericConstraintHandle extends RapierConstraintHandle implements GenericConstraintHandle {
17+
18+
private static final int FRAME_SIDE_FIRST = 0;
19+
private static final int FRAME_SIDE_SECOND = 1;
20+
21+
/**
22+
* Creates a rapier constraint handle
23+
*/
24+
public static RapierGenericConstraintHandle create(final ServerLevel serverLevel, @Nullable final ServerSubLevel sublevelA, @Nullable final ServerSubLevel sublevelB, final GenericConstraintConfiguration config) {
25+
final int sceneID = Rapier3D.getID(serverLevel);
26+
27+
int lockedAxesMask = 0;
28+
for (final ConstraintJointAxis axis : config.lockedAxes()) {
29+
lockedAxesMask |= 1 << axis.ordinal();
30+
}
31+
32+
final long handle = Rapier3D.addGenericConstraint(
33+
sceneID,
34+
sublevelA == null ? -1 : Rapier3D.getID(sublevelA),
35+
sublevelB == null ? -1 : Rapier3D.getID(sublevelB),
36+
config.pos1().x(),
37+
config.pos1().y(),
38+
config.pos1().z(),
39+
config.orientation1().x(),
40+
config.orientation1().y(),
41+
config.orientation1().z(),
42+
config.orientation1().w(),
43+
config.pos2().x(),
44+
config.pos2().y(),
45+
config.pos2().z(),
46+
config.orientation2().x(),
47+
config.orientation2().y(),
48+
config.orientation2().z(),
49+
config.orientation2().w(),
50+
lockedAxesMask
51+
);
52+
53+
return new RapierGenericConstraintHandle(sceneID, handle);
54+
}
55+
56+
/**
57+
* Creates a new constraint handle
58+
*
59+
* @param sceneID the scene ID that this constraint is in
60+
* @param handle the handle from the physics engine
61+
*/
62+
public RapierGenericConstraintHandle(final int sceneID, final long handle) {
63+
super(sceneID, handle);
64+
}
65+
66+
@Override
67+
public void setFrame1(final Vector3dc localPosition, final Quaterniondc localRotation) {
68+
Rapier3D.setConstraintFrame(
69+
this.sceneID, this.handle, FRAME_SIDE_FIRST,
70+
localPosition.x(), localPosition.y(), localPosition.z(),
71+
localRotation.x(), localRotation.y(), localRotation.z(), localRotation.w()
72+
);
73+
}
74+
75+
@Override
76+
public void setFrame2(final Vector3dc localPosition, final Quaterniondc localRotation) {
77+
Rapier3D.setConstraintFrame(
78+
this.sceneID, this.handle, FRAME_SIDE_SECOND,
79+
localPosition.x(), localPosition.y(), localPosition.z(),
80+
localRotation.x(), localRotation.y(), localRotation.z(), localRotation.w()
81+
);
82+
}
83+
}

0 commit comments

Comments
 (0)