1010import com .github .stephengold .joltjni .enumerate .EConstraintSpace ;
1111import com .github .stephengold .joltjni .enumerate .EMotionType ;
1212import com .github .stephengold .joltjni .operator .Op ;
13- import com .github .stephengold .joltjni .readonly .RVec3Arg ;
1413import net .minecraft .world .InteractionHand ;
1514import net .minecraft .world .entity .player .Player ;
1615import net .minecraft .world .level .Level ;
16+ import net .timtaran .interactivemc .body .player .interaction .GrabInteraction ;
17+ import net .timtaran .interactivemc .body .player .physics .PlayerBodyPartGhostRigidBody ;
18+ import net .timtaran .interactivemc .body .player .physics .PlayerBodyPartRigidBody ;
19+ import net .timtaran .interactivemc .body .player .store .PlayerBodyDataStore ;
1720import net .timtaran .interactivemc .init .registry .BodyRegistry ;
18- import net .timtaran . interactivemc . init . InteractiveMC ;
21+ import net .xmx . velthoric . core . body . VxBody ;
1922import net .xmx .velthoric .core .body .VxRemovalReason ;
2023import net .xmx .velthoric .core .body .server .VxServerBodyManager ;
21- import net .xmx .velthoric .core .body .VxBody ;
22- import net .xmx .velthoric .core .constraint .VxConstraint ;
23- import net .xmx .velthoric .core .constraint .manager .VxConstraintManager ;
24- import net .xmx .velthoric .core .intersection .VxPhysicsIntersector ;
2524import net .xmx .velthoric .core .physics .VxJoltBridge ;
26- import net .xmx .velthoric .core .physics .VxPhysicsLayers ;
2725import net .xmx .velthoric .core .physics .world .VxPhysicsWorld ;
2826import net .xmx .velthoric .math .VxConversions ;
2927import net .xmx .velthoric .math .VxTransform ;
3028import org .jetbrains .annotations .Nullable ;
3129
3230import java .util .*;
33- import java .util .concurrent .ConcurrentHashMap ;
3431
3532/**
3633 * Manages the creation, tracking, and interaction of player bodies in the physics world.
4845 * @author timtaran
4946 */
5047public class PlayerBodyManager {
51- public static final float GRAB_RADIUS = 0.3f ;
52- private static final Vec3 SHAPE_SCALE = new Vec3 (1f , 1f , 1f );
53-
5448 private static final HashMap <VxPhysicsWorld , PlayerBodyManager > managers = new HashMap <>();
5549
56- private record PlayerBodyPartData (UUID bodyPartId , UUID ghostBodyPartId , @ Nullable UUID grabbedBodyId ,
57- @ Nullable UUID grabConstraintId ) {
58- }
59-
60- /**
61- * Contains all bodies associated with each player, indexed by their UUID.
62- */
63- private static final HashMap <UUID , EnumMap <PlayerBodyPart , PlayerBodyPartData >> playersBodies = new HashMap <>();
64-
65- /**
66- * Contains the Jolt body IDs of all player bodies for quick lookup during interactions.
67- */
68- private static final ConcurrentHashMap <UUID , List <Integer >> playersJoltBodies = new ConcurrentHashMap <>();
69-
7050 private final VxPhysicsWorld world ;
51+ private final GrabInteraction grabInteraction ;
7152
7253 private PlayerBodyManager (VxPhysicsWorld world ) {
7354 this .world = world ;
55+ this .grabInteraction = new GrabInteraction (world );
7456 }
7557
7658 /**
@@ -97,7 +79,7 @@ public static PlayerBodyManager get(VxPhysicsWorld world) {
9779 * Creates a body part for the given player and body part type.
9880 *
9981 * @param partType the type of body part (head, hands, etc.)
100- * @param player the player who owns this body part
82+ * @param player the player who owns this body part
10183 * @return data about the created body part, including the IDs of both the main and ghost bodies
10284 */
10385 private PlayerBodyPartData createBodyPart (PlayerBodyPart partType , Player player ) {
@@ -133,7 +115,6 @@ private PlayerBodyPartData createBodyPart(PlayerBodyPart partType, Player player
133115 VxJoltBridge .INSTANCE .getJoltBody (world , bodyPartGhost ).setMotionType (EMotionType .Kinematic );
134116 // Workaround until https://github.com/xI-Mx-Ix/Velthoric/issues/31 will be resolved
135117
136-
137118 try (SixDofConstraintSettings settings = new SixDofConstraintSettings ()) {
138119 settings .setSpace (EConstraintSpace .LocalToBodyCom );
139120
@@ -178,7 +159,7 @@ private PlayerBodyPartData createBodyPart(PlayerBodyPart partType, Player player
178159 * @param player the player to spawn bodies for
179160 */
180161 public void spawnPlayer (Player player ) {
181- if (playersBodies .containsKey (player .getUUID ())) {
162+ if (PlayerBodyDataStore . playersBodies .containsKey (player .getUUID ())) {
182163 removePlayer (player );
183164 }
184165
@@ -192,16 +173,16 @@ public void spawnPlayer(Player player) {
192173 playerBodies .put (partType , bodyPartData );
193174
194175 joltBodyIds .add (
195- bodyManager .getVxBody (bodyPartData .bodyPartId ).getBodyId ()
176+ bodyManager .getVxBody (bodyPartData .bodyPartId () ).getBodyId ()
196177 );
197178
198179 joltBodyIds .add (
199- bodyManager .getVxBody (bodyPartData .ghostBodyPartId ).getBodyId ()
180+ bodyManager .getVxBody (bodyPartData .ghostBodyPartId () ).getBodyId ()
200181 );
201182 }
202183
203- playersBodies .put (player .getUUID (), playerBodies );
204- playersJoltBodies .put (player .getUUID (), joltBodyIds );
184+ PlayerBodyDataStore . playersBodies .put (player .getUUID (), playerBodies );
185+ PlayerBodyDataStore . playersJoltBodies .put (player .getUUID (), joltBodyIds );
205186 }
206187
207188 /**
@@ -210,36 +191,33 @@ public void spawnPlayer(Player player) {
210191 * @param player the player to remove bodies for
211192 */
212193 public void removePlayer (Player player ) {
213- EnumMap <PlayerBodyPart , PlayerBodyPartData > playerBodies = playersBodies .remove (player .getUUID ());
194+ EnumMap <PlayerBodyPart , PlayerBodyPartData > playerBodies = PlayerBodyDataStore . playersBodies .remove (player .getUUID ());
214195 if (playerBodies == null ) return ;
215196
216- playersJoltBodies .remove (player .getUUID ());
197+ PlayerBodyDataStore . playersJoltBodies .remove (player .getUUID ());
217198
218199 for (PlayerBodyPartData bodyData : playerBodies .values ()) {
219- world .getBodyManager ().removeBody (bodyData .bodyPartId , VxRemovalReason .DISCARD );
220- world .getBodyManager ().removeBody (bodyData .ghostBodyPartId , VxRemovalReason .DISCARD );
200+ world .getBodyManager ().removeBody (bodyData .bodyPartId () , VxRemovalReason .DISCARD );
201+ world .getBodyManager ().removeBody (bodyData .ghostBodyPartId () , VxRemovalReason .DISCARD );
221202 // Constraints are being removed internally in removeBody, so we don't need to worry about them here.
222203 }
223204 }
224205
225206 /**
226207 * Attempts to grab an object using the specified player's hand.
227- * <p>
228- * This method performs a sphere cast from the grab point and tries to grab the closest
229- * non-player body within the grab radius.
230- * </p>
231208 *
232- * @param player the player attempting to grab
209+ * @param player the player attempting to grab
233210 * @param interactionHand the hand to use for grabbing (main or off-hand)
234211 * @return the body that was grabbed, or null if no body was grabbed
212+ * @see GrabInteraction#grab(Player, VxBody, PlayerBodyPart)
235213 */
236214 @ Nullable
237215 public VxBody grab (Player player , InteractionHand interactionHand ) {
238216 PlayerBodyPart playerBodyPart = PlayerBodyPart .fromInteractionHand (interactionHand );
239217 if (playerBodyPart == null )
240218 return null ;
241219
242- EnumMap <PlayerBodyPart , PlayerBodyPartData > playerBodies = playersBodies .get (player .getUUID ());
220+ EnumMap <PlayerBodyPart , PlayerBodyPartData > playerBodies = PlayerBodyDataStore . playersBodies .get (player .getUUID ());
243221 if (playerBodies == null )
244222 return null ;
245223
@@ -254,122 +232,40 @@ public VxBody grab(Player player, InteractionHand interactionHand) {
254232 if (playerBodyPartData .grabbedBodyId () != null )
255233 return null ; // already grabbing something
256234
257- VxBody body = world .getBodyManager ().getVxBody (playerBodyPartData .bodyPartId );
235+ VxBody body = world .getBodyManager ().getVxBody (playerBodyPartData .bodyPartId () );
258236 if (body == null ) {
259237 throw new IllegalStateException (
260238 "Body not found for body part " + playerBodyPart + " of player " + player .getUUID ()
261239 );
262240 }
263241
264- try (ObjectLayerFilter olFilter = new ObjectLayerFilter () {
265- @ Override
266- public boolean shouldCollide (int objectLayer ) {
267- return objectLayer != VxPhysicsLayers .NON_MOVING ;
268- }
269- };
270- BroadPhaseLayerFilter bplFilter = new BroadPhaseLayerFilter ();
271- BodyFilter bodyFilter = new BodyFilter (); // runtime checks works really strange so we will check body ids below
272- SphereShape shape = new SphereShape (GRAB_RADIUS )) {
273-
274- RVec3Arg base = new RVec3 (0.0f , 0.0f , 0.0f );
275-
276- VxTransform vxTransform = body .getTransform ();
277-
278- RVec3 worldGrabPoint = vxTransform .getTranslation ();
279- RVec3 localGrabPoint = playerBodyPart .getLocalGrabPoint ();
280-
281- // Rotate the local grab point by the body's rotation to get the correct world offset.
282- RVec3 localGrabPointRotated = new RVec3 (localGrabPoint );
283- localGrabPointRotated .rotateInPlace (vxTransform .getRotation ());
284-
285- // Add the rotated local grab point to the body's position to get the final grab point in world space.
286- worldGrabPoint .addInPlace (localGrabPointRotated .xx (), localGrabPointRotated .yy (), localGrabPointRotated .zz ());
287- RMat44 comTransform = new VxTransform (worldGrabPoint , vxTransform .getRotation ()).toRMat44 ();
242+ GrabInteraction .GrabResult grabResult = grabInteraction .grab (player , body , playerBodyPart );
288243
289- List <VxPhysicsIntersector .IntersectShapeResult > intersections = VxPhysicsIntersector .narrowIntersectShape (world , shape , SHAPE_SCALE , comTransform , base , bplFilter , olFilter , bodyFilter );
290-
291- intersections .sort (Comparator .comparingDouble (result -> { // sort by closest intersection point to base.
292- Vec3 p = result .bodyContactPoint ();
293-
294- double dx = p .getX () - vxTransform .getTranslation ().x ();
295- double dy = p .getY () - vxTransform .getTranslation ().y ();
296- double dz = p .getZ () - vxTransform .getTranslation ().z ();
297-
298- return dx * dx + dy * dy + dz * dz ;
299- }));
300-
301- VxConstraintManager constraintManager = world .getConstraintManager ();
302-
303- for (VxPhysicsIntersector .IntersectShapeResult intersection : intersections ) {
304- if (
305- !playersJoltBodies .get (player .getUUID ()).contains (intersection .bodyId ())
306-
307- ) {
308- Body grabbedJoltBody = VxJoltBridge .INSTANCE .getJoltBody (world , intersection .bodyId ());
309-
310- if (grabbedJoltBody .getObjectLayer () != VxPhysicsLayers .TERRAIN ) {
311- VxBody grabbedBody = world .getBodyManager ().getByJoltBodyId (intersection .bodyId ());
312-
313- if (grabbedBody == null ) {
314- InteractiveMC .LOGGER .warn ("vxBody1 is null for body ID: {}" , intersection .bodyId ());
315- continue ;
316- }
317-
318- if (grabbedJoltBody .getMotionType () == EMotionType .Dynamic ) {
319- VxTransform grabbedBodyTransform = grabbedBody .getTransform ();
320- // Calculate a new world-space position for the body so that the local contact point
321- // aligns exactly with the desired grab point in world space.
322- RVec3 worldGrabPointOnBody = new RVec3 (
323- worldGrabPoint .xx () - (intersection .bodyContactPoint ().getX () - grabbedBodyTransform .getTranslation ().xx ()),
324- worldGrabPoint .yy () - (intersection .bodyContactPoint ().getY () - grabbedBodyTransform .getTranslation ().yy ()),
325- worldGrabPoint .zz () - (intersection .bodyContactPoint ().getZ () - grabbedBodyTransform .getTranslation ().zz ())
326- );
327-
328- grabbedJoltBody .setPositionAndRotationInternal (worldGrabPointOnBody , grabbedBodyTransform .getRotation ());
329- }
330- else {
331- // todo move grabber body if grabbed body not meant to be moved by physics
332- }
333-
334- try (FixedConstraintSettings settings = new FixedConstraintSettings ()) {
335- settings .setSpace (EConstraintSpace .WorldSpace );
336- settings .setPoint1 (worldGrabPoint );
337- settings .setPoint2 (worldGrabPoint );
338-
339- // todo rework
340- //if (body instanceof Grabber grabber)
341- // grabbedJoltBody.setCollisionGroup(new CollisionGroup(GroupFilters.PLAYER_BODY_FILTER, GroupFilters.PLAYER_BODY_GROUP_ID, grabber.getSubGroupId()));
342-
343- VxConstraint constraint = constraintManager .createConstraint (settings , body .getPhysicsId (), grabbedBody .getPhysicsId ());
344- constraint .setPersistent (false );
244+ if (grabResult .grabbedBody () == null )
245+ return null ;
345246
346- playerBodies .put (playerBodyPart , new PlayerBodyPartData (playerBodyPartData .bodyPartId , playerBodyPartData .ghostBodyPartId , grabbedBody .getPhysicsId (), constraint .getConstraintId ()));
247+ playerBodies .put (playerBodyPart , new PlayerBodyPartData (
248+ playerBodyPartData .bodyPartId (), playerBodyPartData .ghostBodyPartId (),
249+ grabResult .grabbedBody ().getPhysicsId (),
250+ grabResult .grabConstraint () != null ? grabResult .grabConstraint ().getConstraintId () : null
251+ ));
347252
348- return grabbedBody ;
349- }
350- } // todo add terrain grab after implementing client-side prediction
351- }
352- }
353- }
354-
355- return null ;
253+ return grabResult .grabbedBody ();
356254 }
357255
358256 /**
359257 * Releases any object being grabbed by the specified player's hand.
360- * <p>
361- * This removes the grab constraint, allowing the grabbed body to move freely again.
362- * </p>
363258 *
364- * @param player the player releasing the grab
259+ * @param player the player releasing the grab
365260 * @param interactionHand the hand to release (main or off-hand)
261+ * @see GrabInteraction#release(PlayerBodyPartData)
366262 */
367263 public void release (Player player , InteractionHand interactionHand ) {
368264 PlayerBodyPart playerBodyPart = PlayerBodyPart .fromInteractionHand (interactionHand );
369265 if (playerBodyPart == null )
370266 return ;
371267
372- EnumMap <PlayerBodyPart , PlayerBodyPartData > playerBodies = playersBodies .get (player .getUUID ());
268+ EnumMap <PlayerBodyPart , PlayerBodyPartData > playerBodies = PlayerBodyDataStore . playersBodies .get (player .getUUID ());
373269 if (playerBodies == null )
374270 return ;
375271
@@ -384,8 +280,8 @@ public void release(Player player, InteractionHand interactionHand) {
384280 return ;
385281 }
386282
387- world . getConstraintManager (). removeConstraint ( playerBodyPartData . grabConstraintId );
283+ grabInteraction . release ( playerBodyPartData );
388284
389- playerBodies .put (playerBodyPart , new PlayerBodyPartData (playerBodyPartData .bodyPartId , playerBodyPartData .ghostBodyPartId , null , null ));
285+ playerBodies .put (playerBodyPart , new PlayerBodyPartData (playerBodyPartData .bodyPartId () , playerBodyPartData .ghostBodyPartId () , null , null ));
390286 }
391287}
0 commit comments