Skip to content

Commit 99af7a4

Browse files
committed
Ended up going all the way back to normal collisions. Not super smooth, and still possible to penetrate.
1 parent e098f96 commit 99af7a4

18 files changed

Lines changed: 392 additions & 1707 deletions

src/main/java/dev/manifold/ConstructManager.java

Lines changed: 27 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@
77
import dev.manifold.network.packets.BreakInConstructC2SPacket;
88
import dev.manifold.network.packets.ConstructSectionDataS2CPacket;
99
import 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;
1611
import io.netty.buffer.Unpooled;
1712
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
1813
import 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
}

src/main/java/dev/manifold/mixin/EntityConstructCollisionMixin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public abstract class EntityConstructCollisionMixin {
3030
final Entity self = (Entity) (Object) this;
3131
final AABB startBB = this.getBoundingBox();
3232
final float stepHeight = Math.max(0.0f, this.maxUpStep());
33-
return ConstructCollisionEngine.resolveCollisions(vanillaResolved, self, startBB, stepHeight);
33+
34+
return ConstructCollisionEngine.resolveMotion(self, startBB, vanillaResolved, stepHeight);
3435
}
3536
}

0 commit comments

Comments
 (0)