77import dev .manifold .network .packets .BreakInConstructC2SPacket ;
88import dev .manifold .network .packets .ConstructSectionDataS2CPacket ;
99import dev .manifold .network .packets .RemoveConstructS2CPacket ;
10- import dev .manifold .physics .collision .ConstructCollisionManager ; // NEW API (local-space OBBs)
11- import dev .manifold .physics .collision .VoxelSDF ;
12- import dev .manifold .physics .core .OBB ;
13- import dev .manifold .physics .core .RigidState ;
14- import dev .manifold .physics .math .M3 ;
15- import dev .manifold .physics .math .V3 ;
10+ import dev .manifold .physics .collision .ConstructCollisionManager ;
1611import io .netty .buffer .Unpooled ;
1712import it .unimi .dsi .fastutil .longs .Long2ObjectMap ;
1813import net .fabricmc .fabric .api .networking .v1 .ServerPlayNetworking ;
@@ -57,72 +52,12 @@ public ConstructManager(ServerLevel simDimension) {
5752 this .simDimension = simDimension ;
5853 }
5954
60- public static VoxelSDF buildLocalSdfFromSimArea (ServerLevel sim ,
61- BlockPos simOrigin ,
62- BlockPos neg , BlockPos pos ) {
63- ArrayList <V3 > solids = new ArrayList <>();
64- for (int x = neg .getX (); x <= pos .getX (); x ++) {
65- for (int y = neg .getY (); y <= pos .getY (); y ++) {
66- for (int z = neg .getZ (); z <= pos .getZ (); z ++) {
67- BlockPos abs = simOrigin .offset (x , y , z );
68- if (!sim .getBlockState (abs ).isAir ()) {
69- solids .add (new V3 (x + 0.5 , y + 0.5 , z + 0.5 )); // block centers
70- }
71- }
72- }
73- }
74-
75- // grid spans [neg..pos] plus 1-cell shell, but **at cell centers**
76- int nx = (pos .getX () - neg .getX () + 1 ) + 1 ;
77- int ny = (pos .getY () - neg .getY () + 1 ) + 1 ;
78- int nz = (pos .getZ () - neg .getZ () + 1 ) + 1 ;
79-
80- // IMPORTANT: origin is the cell-center at neg corner
81- V3 origin = new V3 (neg .getX () + 0.5 , neg .getY () + 0.5 , neg .getZ () + 0.5 );
82- float [][][] phi = new float [nx ][ny ][nz ];
83-
84- V3 e = new V3 (0.5 , 0.5 , 0.5 );
85- for (int i = 0 ; i < nx ; i ++) {
86- double px = origin .x + i ; // centers, not corners
87- for (int j = 0 ; j < ny ; j ++) {
88- double py = origin .y + j ;
89- for (int k = 0 ; k < nz ; k ++) {
90- double pz = origin .z + k ;
91-
92- double best = Double .POSITIVE_INFINITY ;
93- for (int s = 0 ; s < solids .size (); s ++) {
94- V3 c = solids .get (s );
95- double d = sdfBox (px , py , pz , c , e );
96- if (d < best ) best = d ;
97- }
98- if (best == Double .POSITIVE_INFINITY ) best = 1e3 ;
99- phi [i ][j ][k ] = (float ) best ;
100- }
101- }
102- }
103- return new VoxelSDF (phi , origin );
104- }
105-
106- /** Exact signed distance to an axis-aligned box centered at c with half-extents e. */
107- private static double sdfBox (double px , double py , double pz , V3 c , V3 e ) {
108- double dx = Math .abs (px - c .x ) - e .x ;
109- double dy = Math .abs (py - c .y ) - e .y ;
110- double dz = Math .abs (pz - c .z ) - e .z ;
111- double ax = Math .max (dx , 0.0 );
112- double ay = Math .max (dy , 0.0 );
113- double az = Math .max (dz , 0.0 );
114- double outside = Math .sqrt (ax *ax + ay *ay + az *az );
115- double inside = Math .min (Math .max (dx , Math .max (dy , dz )), 0.0 );
116- return outside + inside ; // positive outside, negative inside
117- }
118-
11955 public void loadFromSave (ConstructSaveData saveData ) {
12056 for (DynamicConstruct construct : saveData .getConstructs ().values ()) {
12157 constructs .put (construct .getId (), construct );
12258 regionOwners .put (getRegionIndex (construct .getSimOrigin ()), construct .getId ());
12359
124- // NEW: rebuild local-space OBBs + upsert to collision manager
125- rebuildCollisionForConstruct (construct );
60+ ConstructCollisionManager .rebuild (construct , simDimension );
12661 }
12762 }
12863
@@ -142,8 +77,7 @@ public UUID createConstruct(BlockState state, ServerLevel level) {
14277 constructs .put (uuid , construct );
14378 regionOwners .put (region , uuid );
14479
145- // NEW: initial OBBs + upsert
146- rebuildCollisionForConstruct (construct );
80+ ConstructCollisionManager .rebuild (construct , simDimension );
14781
14882 return uuid ;
14983 }
@@ -256,8 +190,7 @@ public void placeBlockInConstruct(UUID uuid, BlockPos rel, BlockState state) {
256190 // -- Connectivity graph --
257191 construct .getConnectivityGraph ().addBlock (rel );
258192
259- // NEW: OBBs are local; COM changed ⇒ update collision upsert (new localOrigin)
260- rebuildCollisionForConstruct (construct );
193+ ConstructCollisionManager .rebuild (construct , simDimension );
261194 }
262195
263196 public void breakBlockInConstruct (BreakInConstructC2SPacket packet , ServerPlayNetworking .Context context ) {
@@ -306,8 +239,25 @@ public void breakBlockInConstruct(BreakInConstructC2SPacket packet, ServerPlayNe
306239 // Connectivity graph
307240 construct .getConnectivityGraph ().removeBlock (rel );
308241
309- // NEW: COM changed ⇒ refresh collision upsert
310- rebuildCollisionForConstruct (construct );
242+ ConstructCollisionManager .rebuild (construct , simDimension );
243+ }
244+ }
245+
246+ public Optional <BlockPos > getNegativeBounds (UUID id ) {
247+ DynamicConstruct construct = constructs .get (id );
248+ if (construct == null ) {
249+ return Optional .empty ();
250+ } else {
251+ return Optional .of (construct .getNegativeBounds ());
252+ }
253+ }
254+
255+ public Optional <BlockPos > getPositiveBounds (UUID id ) {
256+ DynamicConstruct construct = constructs .get (id );
257+ if (construct == null ) {
258+ return Optional .empty ();
259+ } else {
260+ return Optional .of (construct .getPositiveBounds ());
311261 }
312262 }
313263
@@ -329,17 +279,15 @@ public void expandBounds(UUID id, BlockPos rel) {
329279 construct .setNegativeBounds (new BlockPos (newNegX , newNegY , newNegZ ));
330280 construct .setPositiveBounds (new BlockPos (newPosX , newPosY , newPosZ ));
331281
332- // NEW: bounds changed ⇒ rebuild local OBBs + upsert
333- rebuildCollisionForConstruct (construct );
282+ ConstructCollisionManager .rebuild (construct , simDimension );
334283 }
335284
336285 public void tick (MinecraftServer server ) {
337286 for (DynamicConstruct construct : constructs .values ()) {
338287 // physics update
339288 construct .physicsTick ();
340289
341- // NEW: push updated pose/vel to collision system (no OBB rebuild needed here)
342- ConstructCollisionManager .updateState (construct .getId (), rigidFromConstruct (construct ));
290+ //fixme maybe do update collision system here too?
343291
344292 // compute sim chunks to keep loaded (in sim dimension)
345293 AABB box = construct .getBoundingBox ();
@@ -545,8 +493,7 @@ public void updateConstructCOMS(MassManager.ChangedItem changedItem) {
545493 construct .setCenterOfMass (newCOM );
546494 construct .setMass (newConstructMass );
547495
548- // NEW: COM changed ⇒ refresh collision upsert
549- rebuildCollisionForConstruct (construct );
496+ ConstructCollisionManager .rebuild (construct , simDimension );
550497 }
551498 }
552499 }
@@ -710,148 +657,6 @@ private void updateConstructBounds(DynamicConstruct construct) {
710657 construct .setNegativeBounds (new BlockPos (minX - 1 , minY - 1 , minZ - 1 ));
711658 construct .setPositiveBounds (new BlockPos (maxX + 1 , maxY + 1 , maxZ + 1 ));
712659
713- // NEW: rebuild OBBs + upsert to collision manager
714- rebuildCollisionForConstruct (construct );
715- }
716-
717- // =========================
718- // ===== NEW HELPERS =======
719- // =========================
720-
721- /** Rebuilds local-space OBBs for the given construct and upsserts into collision manager with current state. */
722- private void rebuildCollisionForConstruct (DynamicConstruct construct ) {
723- // 1) Local OBBs from the sim area (per solid block)
724- List <OBB > localObbs = buildLocalObbsFromSimArea (
725- simDimension ,
726- construct .getSimOrigin (),
727- construct .getNegativeBounds (),
728- construct .getPositiveBounds (),
729- /* default μ */ 0.6
730- );
731-
732- // 2) Local SDF from same block set (signed distance to union of unit cubes)
733- VoxelSDF sdf = buildLocalSdfFromSimArea (
734- simDimension ,
735- construct .getSimOrigin (),
736- construct .getNegativeBounds (),
737- construct .getPositiveBounds ()
738- );
739-
740- // 3) Upsert with localOrigin = COM (world = pos + rot * (local - COM))
741- V3 localOrigin = new V3 (
742- construct .getCenterOfMass ().x ,
743- construct .getCenterOfMass ().y ,
744- construct .getCenterOfMass ().z
745- );
746-
747- ConstructCollisionManager .upsertConstruct (
748- construct .getId (),
749- localObbs ,
750- rigidFromConstruct (construct ),
751- localOrigin ,
752- sdf
753- );
754- }
755-
756- public Optional <BlockPos > getNegativeBounds (UUID id ) {
757- DynamicConstruct construct = constructs .get (id );
758- if (construct == null ) {
759- return Optional .empty ();
760- } else {
761- return Optional .of (construct .getNegativeBounds ());
762- }
763- }
764-
765- public Optional <BlockPos > getPositiveBounds (UUID id ) {
766- DynamicConstruct construct = constructs .get (id );
767- if (construct == null ) {
768- return Optional .empty ();
769- } else {
770- return Optional .of (construct .getPositiveBounds ());
771- }
772- }
773-
774- /** Builds simple per-block local OBBs for all non-air blocks in [neg..pos] around simOrigin. */
775- public static List <OBB > buildLocalObbsFromSimArea (
776- ServerLevel sim ,
777- BlockPos simOrigin ,
778- BlockPos neg ,
779- BlockPos pos ,
780- double defaultMu
781- ) {
782- ArrayList <OBB > out = new ArrayList <>();
783- int id = 0 ;
784-
785- for (int x = neg .getX (); x <= pos .getX (); x ++) {
786- for (int y = neg .getY (); y <= pos .getY (); y ++) {
787- for (int z = neg .getZ (); z <= pos .getZ (); z ++) {
788- BlockPos rel = new BlockPos (x , y , z );
789- BlockPos abs = simOrigin .offset (rel );
790- BlockState bs = sim .getBlockState (abs );
791- if (bs .isAir ()) continue ;
792-
793- // Estimate friction μ from the block (ice < dirt < stone etc.)
794- double mu = estimateMu (sim , abs , bs , defaultMu );
795-
796- OBB o = new OBB ();
797- o .c = new V3 (x + 0.5 , y + 0.5 , z + 0.5 ); // local center in construct-space
798- o .e = new V3 (0.5 , 0.5 , 0.5 ); // half extents of a cube voxel
799- o .R = M3 .identity (); // axis-aligned in LOCAL space; world rotation comes from the construct
800- o .mu = mu ;
801- o .id = id ++;
802- out .add (o );
803- }
804- }
805- }
806-
807- if (out .isEmpty ()) {
808- // One tiny sentinel so BVH has a valid node; will be culled by broadphase anyway.
809- out .add (new OBB (new V3 (0 , 0 , 0 ), new V3 (1e-4 , 1e-4 , 1e-4 ), M3 .identity (), defaultMu , -1 ));
810- }
811- return out ;
812- }
813-
814- /** Roughly map Minecraft block "slipperiness/friction" into a Coulomb μ for sliding logic. */
815- private static double estimateMu (ServerLevel sim , BlockPos pos , BlockState state , double fallback ) {
816- try {
817- float f = state .getBlock ().getFriction ();
818- double mu = Math .max (0.02 , Math .min (1.2 , 1.4 - (double ) f * 1.2 ));
819- return mu ;
820- } catch (Throwable ignored ) { }
821-
822- // Last resort
823- return fallback ;
824- }
825-
826- /** Build a RigidState from DynamicConstruct’s pose + velocities. */
827- public static RigidState rigidFromConstruct (DynamicConstruct c ) {
828- // position (world)
829- V3 pos = new V3 (c .getPosition ().x , c .getPosition ().y , c .getPosition ().z );
830- // rotation (world)
831- M3 rot = fromQuaternion (c .getRotation ());
832- // linear velocity (world)
833- Vec3 v = c .getVelocity (); // assuming you have it; else Vec3.ZERO
834- V3 vLin = new V3 (v .x , v .y , v .z );
835- // angular velocity (world): if stored as Quaternion delta per tick, convert; otherwise 0.
836- V3 vAng = new V3 (0 , 0 , 0 ); // TODO: map your angular-velocity representation
837-
838- // Kinematic constructs for now (invMass=0)
839- RigidState rs = new RigidState ();
840- rs .set (pos , rot , vLin , vAng , /*invMass*/ 0.0 , M3 .identity ());
841- return rs ;
842- }
843-
844- /** Convert JOML Quaternionf to our M3 rotation (columns are basis vectors). */
845- private static M3 fromQuaternion (Quaternionf q ) {
846- // JOML stores unit quaternion; build 3x3 matrix
847- float xx = q .x * q .x , yy = q .y * q .y , zz = q .z * q .z ;
848- float xy = q .x * q .y , xz = q .x * q .z , yz = q .y * q .z ;
849- float wx = q .w * q .x , wy = q .w * q .y , wz = q .w * q .z ;
850-
851- V3 x = new V3 (1f - 2f *(yy + zz ), 2f *(xy + wz ), 2f *(xz - wy ));
852- V3 y = new V3 (2f *(xy - wz ), 1f - 2f *(xx + zz ), 2f *(yz + wx ));
853- V3 z = new V3 (2f *(xz + wy ), 2f *(yz - wx ), 1f - 2f *(xx + yy ));
854- M3 m = new M3 (); m .set (x , y , z );
855- return m ;
660+ ConstructCollisionManager .rebuild (construct , simDimension );
856661 }
857662}
0 commit comments