From 0bb18db4b60b05aa54696d5ad91ff61705369f08 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Wed, 31 Mar 2021 03:16:29 +0200 Subject: [PATCH 1/3] Oriented bouncing and sliding - Slime blocks on a moving Contraption are now bouncy - Ice blocks on moving Contraptions are now very slippery --- .../structureMovement/Contraption.java | 51 +++++- .../ContraptionCollider.java | 165 +++++++++++++++--- .../collision/ContinuousOBBCollider.java | 70 ++++---- .../foundation/utility/BlockHelper.java | 11 ++ 4 files changed, 227 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index b4dab5758..249377298 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -10,19 +10,18 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Queue; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.stream.Collectors; import javax.annotation.Nullable; -import com.simibubi.create.foundation.render.backend.instancing.IFlywheelWorld; -import com.simibubi.create.foundation.render.backend.light.GridAlignedBB; -import com.simibubi.create.foundation.utility.*; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -55,7 +54,15 @@ import com.simibubi.create.content.logistics.block.inventories.AdjustableCrateBl import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.fluid.CombinedTankWrapper; +import com.simibubi.create.foundation.render.backend.instancing.IFlywheelWorld; import com.simibubi.create.foundation.render.backend.light.EmptyLighter; +import com.simibubi.create.foundation.render.backend.light.GridAlignedBB; +import com.simibubi.create.foundation.utility.BlockFace; +import com.simibubi.create.foundation.utility.Coordinate; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.utility.NBTProcessors; +import com.simibubi.create.foundation.utility.UniqueLinkedList; import com.simibubi.create.foundation.utility.worldWrappers.WrappedWorld; import net.minecraft.block.AbstractButtonBlock; @@ -86,6 +93,9 @@ import net.minecraft.util.Rotation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.shapes.IBooleanFunction; +import net.minecraft.util.math.shapes.VoxelShape; +import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.util.palette.PaletteHashMap; import net.minecraft.village.PointOfInterestType; import net.minecraft.world.IWorld; @@ -106,6 +116,7 @@ import net.minecraftforge.registries.GameData; public abstract class Contraption { + public Optional> simplifiedEntityColliders; public AbstractContraptionEntity entity; public CombinedInvWrapper inventory; public CombinedTankWrapper fluidInventory; @@ -126,6 +137,8 @@ public abstract class Contraption { private Map initialPassengers; private List pendingSubContraptions; + private CompletableFuture> simplifiedEntityColliderProvider; + // Client public Map presentTileEntities; public List maybeInstancedTileEntities; @@ -148,13 +161,12 @@ public abstract class Contraption { specialRenderedTileEntities = new ArrayList<>(); pendingSubContraptions = new ArrayList<>(); stabilizedSubContraptions = new HashMap<>(); + simplifiedEntityColliders = Optional.empty(); } public ContraptionWorld getContraptionWorld() { - if (world == null) { + if (world == null) world = new ContraptionWorld(entity.world, this); - } - return world; } @@ -181,6 +193,7 @@ public abstract class Contraption { String type = nbt.getString("Type"); Contraption contraption = ContraptionType.fromType(type); contraption.readNBT(world, nbt, spawnData); + contraption.gatherBBsOffThread(); return contraption; } @@ -244,6 +257,7 @@ public abstract class Contraption { .collect(Collectors.toList()); fluidInventory = new CombinedTankWrapper( Arrays.copyOf(fluidHandlers.toArray(), fluidHandlers.size(), IFluidHandler[].class)); + gatherBBsOffThread(); } public void onEntityInitialize(World world, AbstractContraptionEntity contraptionEntity) { @@ -268,6 +282,15 @@ public abstract class Contraption { } public void onEntityTick(World world) { + if (simplifiedEntityColliderProvider != null && simplifiedEntityColliderProvider.isDone()) { + try { + simplifiedEntityColliders = Optional.of(simplifiedEntityColliderProvider.join()); + } catch (Exception e) { + e.printStackTrace(); + } + simplifiedEntityColliderProvider = null; + } + fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, world.isRemote)); } @@ -1158,6 +1181,22 @@ public abstract class Contraption { return new EmptyLighter(this); } + private void gatherBBsOffThread() { + simplifiedEntityColliderProvider = CompletableFuture.supplyAsync(() -> { + VoxelShape combinedShape = VoxelShapes.empty(); + for (Entry entry : blocks.entrySet()) { + BlockInfo info = entry.getValue(); + BlockPos localPos = entry.getKey(); + VoxelShape collisionShape = info.state.getCollisionShape(this.world, localPos); + if (collisionShape.isEmpty()) + continue; + combinedShape = VoxelShapes.combineAndSimplify(combinedShape, + collisionShape.withOffset(localPos.getX(), localPos.getY(), localPos.getZ()), IBooleanFunction.OR); + } + return combinedShape.toBoundingBoxList(); + }); + } + public static float getRadius(Set blocks, Direction.Axis axis) { switch (axis) { case X: diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java index 281cb1785..b95319e45 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java @@ -23,6 +23,7 @@ import com.simibubi.create.foundation.collision.ContinuousOBBCollider.Continuous import com.simibubi.create.foundation.collision.Matrix3d; import com.simibubi.create.foundation.collision.OrientedBB; import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.VecHelper; @@ -37,6 +38,8 @@ import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.ReuseableStream; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvents; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; @@ -104,40 +107,45 @@ public class ContraptionCollider { AxisAlignedBB entityBounds = entity.getBoundingBox(); Vec3d motion = entity.getMotion(); float yawOffset = rotation.getYawOffset(); - Vec3d position = getWorldToLocalTranslation(entity, anchorVec, rotationMatrix, yawOffset); - // Find all potential block shapes to collide with + // Prepare entity bounds AxisAlignedBB localBB = entityBounds.offset(position) .grow(1.0E-7D); - ReuseableStream potentialHits = - getPotentiallyCollidedShapes(world, contraption, localBB.expand(motion)); - if (potentialHits.createStream() - .count() == 0) - continue; - - // Prepare entity bounds OrientedBB obb = new OrientedBB(localBB); obb.setRotation(rotationMatrix); motion = motion.subtract(contraptionMotion); motion = rotationMatrix.transform(motion); + // Use simplified bbs when present + // TODO: is it worth filtering out far away bbs? + final Vec3d motionCopy = motion; + List collidableBBs = contraption.simplifiedEntityColliders.orElseGet(() -> { + + // Else find 'nearby' individual block shapes to collide with + List bbs = new ArrayList<>(); + ReuseableStream potentialHits = + getPotentiallyCollidedShapes(world, contraption, localBB.expand(motionCopy)); + potentialHits.createStream() + .forEach(shape -> shape.toBoundingBoxList() + .forEach(bbs::add)); + return bbs; + + }); + MutableObject collisionResponse = new MutableObject<>(Vec3d.ZERO); + MutableObject normal = new MutableObject<>(Vec3d.ZERO); + MutableObject location = new MutableObject<>(Vec3d.ZERO); MutableBoolean surfaceCollision = new MutableBoolean(false); MutableFloat temporalResponse = new MutableFloat(1); Vec3d obbCenter = obb.getCenter(); // Apply separation maths - List bbs = new ArrayList<>(); - potentialHits.createStream() - .forEach(shape -> shape.toBoundingBoxList() - .forEach(bbs::add)); - boolean doHorizontalPass = !rotation.hasVerticalRotation(); for (boolean horizontalPass : Iterate.trueAndFalse) { boolean verticalPass = !horizontalPass || !doHorizontalPass; - for (AxisAlignedBB bb : bbs) { + for (AxisAlignedBB bb : collidableBBs) { Vec3d currentResponse = collisionResponse.getValue(); obb.setCenter(obbCenter.add(currentResponse)); ContinuousSeparationManifold intersect = obb.intersect(bb, motion); @@ -148,15 +156,28 @@ public class ContraptionCollider { surfaceCollision.setValue(intersect.isSurfaceCollision()); double timeOfImpact = intersect.getTimeOfImpact(); - if (timeOfImpact > 0 && timeOfImpact < 1) { - if (temporalResponse.getValue() > timeOfImpact) - temporalResponse.setValue(timeOfImpact); - continue; + boolean isTemporal = timeOfImpact > 0 && timeOfImpact < 1; + Vec3d collidingNormal = intersect.getCollisionNormal(); + Vec3d collisionPosition = intersect.getCollisionPosition(); + + if (!isTemporal) { + Vec3d separation = intersect.asSeparationVec(entity.stepHeight); + if (separation != null && !separation.equals(Vec3d.ZERO)) { + collisionResponse.setValue(currentResponse.add(separation)); + timeOfImpact = 0; + } } - Vec3d separation = intersect.asSeparationVec(entity.stepHeight); - if (separation != null && !separation.equals(Vec3d.ZERO)) - collisionResponse.setValue(currentResponse.add(separation)); + boolean nearest = timeOfImpact >= 0 && temporalResponse.getValue() > timeOfImpact; + if (collidingNormal != null && nearest) + normal.setValue(collidingNormal); + if (collisionPosition != null && nearest) + location.setValue(collisionPosition); + + if (isTemporal) { + if (temporalResponse.getValue() > timeOfImpact) + temporalResponse.setValue(timeOfImpact); + } } if (verticalPass) @@ -175,6 +196,9 @@ public class ContraptionCollider { // Resolve collision Vec3d entityMotion = entity.getMotion(); + Vec3d entityMotionNoTemporal = entityMotion; + Vec3d collisionNormal = normal.getValue(); + Vec3d collisionLocation = location.getValue(); Vec3d totalResponse = collisionResponse.getValue(); boolean hardCollision = !totalResponse.equals(Vec3d.ZERO); boolean temporalCollision = temporalResponse.getValue() != 1; @@ -187,8 +211,47 @@ public class ContraptionCollider { .add(contraptionMotion); totalResponse = rotationMatrix.transform(totalResponse); totalResponse = VecHelper.rotate(totalResponse, yawOffset, Axis.Y); + collisionNormal = rotationMatrix.transform(collisionNormal); + collisionNormal = VecHelper.rotate(collisionNormal, yawOffset, Axis.Y); + collisionLocation = rotationMatrix.transform(collisionLocation); + collisionLocation = VecHelper.rotate(collisionLocation, yawOffset, Axis.Y); rotationMatrix.transpose(); + double bounce = 0; + double slide = 0; + + if (!collisionLocation.equals(Vec3d.ZERO)) { + collisionLocation = collisionLocation.add(entity.getPositionVec() + .add(entity.getBoundingBox() + .getCenter()) + .scale(.5f)); + if (temporalCollision) + collisionLocation = collisionLocation.add(0, motionResponse.y, 0); + BlockPos pos = new BlockPos(contraptionEntity.toLocalVector(collisionLocation, 0)); + if (contraption.getBlocks() + .containsKey(pos)) { + BlockState blockState = contraption.getBlocks() + .get(pos).state; + bounce = BlockHelper.getBounceMultiplier(blockState.getBlock()); + slide = Math.max(0, blockState.getSlipperiness(contraption.world, pos, entity) - .6f); + } + } + + boolean hasNormal = !collisionNormal.equals(Vec3d.ZERO); + boolean anyCollision = hardCollision || temporalCollision; + + if (bounce > 0 && hasNormal && anyCollision) { + collisionNormal = collisionNormal.normalize(); + Vec3d newNormal = collisionNormal.crossProduct(collisionNormal.crossProduct(entityMotionNoTemporal)) + .normalize(); + if (bounceEntity(entity, newNormal, contraptionEntity, bounce)) { + entity.world.playSound(playerType == PlayerType.CLIENT ? (PlayerEntity) entity : null, + entity.getX(), entity.getY(), entity.getZ(), SoundEvents.BLOCK_SLIME_BLOCK_FALL, + SoundCategory.BLOCKS, .5f, 1); + continue; + } + } + if (temporalCollision) { double idealVerticalMotion = motionResponse.y; if (idealVerticalMotion != entityMotion.y) { @@ -216,6 +279,18 @@ public class ContraptionCollider { entityMotion = entityMotion.mul(1, 1, 0); } + if (bounce == 0 && slide > 0 && hasNormal && anyCollision && rotation.hasVerticalRotation()) { + collisionNormal = collisionNormal.normalize(); + Vec3d motionIn = entityMotionNoTemporal.mul(0, 1, 0) + .add(0, -.01f, 0); + Vec3d slideNormal = collisionNormal.crossProduct(motionIn.crossProduct(collisionNormal)) + .normalize(); + entity.setMotion(entityMotion.mul(.8, 0, .8) + .add(slideNormal.scale((.2f + slide) * motionIn.length()) + .add(0, -0.1, 0))); + entityMotion = entity.getMotion(); + } + if (!hardCollision && surfaceCollision.isFalse()) continue; @@ -229,10 +304,14 @@ public class ContraptionCollider { if (surfaceCollision.isTrue()) { entity.fallDistance = 0; - entity.onGround = true; contraptionEntity.collidingEntities.put(entity, new MutableInt(0)); - if (entity instanceof ItemEntity) - entityMotion = entityMotion.mul(.5f, 1, .5f); + boolean canWalk = bounce != 0 || slide == 0; + if (canWalk || !rotation.hasVerticalRotation()) { + if (canWalk) + entity.onGround = true; + if (entity instanceof ItemEntity) + entityMotion = entityMotion.mul(.5f, 1, .5f); + } contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition); allowedMovement = getAllowedMovement(contactPointMotion, entity); entity.setPosition(entityPosition.x + allowedMovement.x, entityPosition.y, @@ -254,6 +333,42 @@ public class ContraptionCollider { } + static boolean bounceEntity(Entity entity, Vec3d normal, AbstractContraptionEntity contraption, double factor) { + if (factor == 0) + return false; + if (entity.bypassesLandingEffects()) + return false; + if (normal.equals(Vec3d.ZERO)) + return false; + + Vec3d contraptionVec = Vec3d.ZERO; + Vec3d contactPointMotion = contraption.getContactPointMotion(entity.getPositionVec()); + Vec3d motion = entity.getMotion() + .subtract(contactPointMotion); + + Vec3d v2 = motion.crossProduct(normal) + .normalize(); + if (v2 != Vec3d.ZERO) + contraptionVec = normal.scale(contraptionVec.dotProduct(normal)) + .add(v2.scale(contraptionVec.dotProduct(v2))); + else + v2 = normal.crossProduct(normal.add(Math.random(), Math.random(), Math.random())) + .normalize(); + + Vec3d v3 = normal.crossProduct(v2); + motion = motion.subtract(contraptionVec); + Vec3d lr = new Vec3d(factor * motion.dotProduct(normal), -motion.dotProduct(v2), -motion.dotProduct(v3)); + + if (lr.dotProduct(lr) > 1 / 16f) { + Vec3d newMot = contactPointMotion.add(normal.x * lr.x + v2.x * lr.y + v3.x * lr.z, + normal.y * lr.x + v2.y * lr.y + v3.y * lr.z, normal.z * lr.x + v2.z * lr.y + v3.z * lr.z); + entity.setMotion(newMot); + return true; + } + + return false; + } + public static Vec3d getWorldToLocalTranslation(Entity entity, AbstractContraptionEntity contraptionEntity) { return getWorldToLocalTranslation(entity, contraptionEntity.getAnchorVec(), contraptionEntity.getRotationState()); } diff --git a/src/main/java/com/simibubi/create/foundation/collision/ContinuousOBBCollider.java b/src/main/java/com/simibubi/create/foundation/collision/ContinuousOBBCollider.java index bc4015ac7..d5ed883fa 100644 --- a/src/main/java/com/simibubi/create/foundation/collision/ContinuousOBBCollider.java +++ b/src/main/java/com/simibubi/create/foundation/collision/ContinuousOBBCollider.java @@ -35,24 +35,25 @@ public class ContinuousOBBCollider extends OBBCollider { checkCount = 0; mf.stepSeparationAxis = uB1; mf.stepSeparation = Double.MAX_VALUE; + mf.normalSeparation = Double.MAX_VALUE; if ( // Separate along A's local axes (global XYZ) - !(separate(mf, uA0, diff.x, eA.x, a00 * eB.x + a01 * eB.y + a02 * eB.z, motion.x) - || separate(mf, uA1, diff.y, eA.y, a10 * eB.x + a11 * eB.y + a12 * eB.z, motion.y) - || separate(mf, uA2, diff.z, eA.z, a20 * eB.x + a21 * eB.y + a22 * eB.z, motion.z) + !(separate(mf, uA0, diff.x, eA.x, a00 * eB.x + a01 * eB.y + a02 * eB.z, motion.x, true) + || separate(mf, uA1, diff.y, eA.y, a10 * eB.x + a11 * eB.y + a12 * eB.z, motion.y, true) + || separate(mf, uA2, diff.z, eA.z, a20 * eB.x + a21 * eB.y + a22 * eB.z, motion.z, true) // Separate along B's local axes - || separate(mf, uB0, diff2.x, eA.x * a00 + eA.y * a10 + eA.z * a20, eB.x, motion2.x) - || separate(mf, uB1, diff2.y, eA.x * a01 + eA.y * a11 + eA.z * a21, eB.y, motion2.y) - || separate(mf, uB2, diff2.z, eA.x * a02 + eA.y * a12 + eA.z * a22, eB.z, motion2.z))) + || separate(mf, uB0, diff2.x, eA.x * a00 + eA.y * a10 + eA.z * a20, eB.x, motion2.x, false) + || separate(mf, uB1, diff2.y, eA.x * a01 + eA.y * a11 + eA.z * a21, eB.y, motion2.y, false) + || separate(mf, uB2, diff2.z, eA.x * a02 + eA.y * a12 + eA.z * a22, eB.z, motion2.z, false))) return mf; return null; } static boolean separate(ContinuousSeparationManifold mf, Vec3d axis, double TL, double rA, double rB, - double projectedMotion) { + double projectedMotion, boolean axisOfObjA) { checkCount++; double distance = abs(TL); double diff = distance - (rA + rB); @@ -81,7 +82,12 @@ public class ContinuousOBBCollider extends OBBCollider { Vec3d normalizedAxis = axis.normalize(); boolean isBestSeperation = distance != 0 && -(diff) <= abs(mf.separation); -// boolean isBestSeperation = discreteCollision && checkCount == 5; // Debug specific separations + // boolean isBestSeperation = discreteCollision && checkCount == 5; // Debug specific separations + + if (axisOfObjA && distance != 0 && -(diff) <= abs(mf.normalSeparation)) { + mf.normalAxis = normalizedAxis; + mf.normalSeparation = seperation; + } double dot = mf.stepSeparationAxis.dotProduct(axis); if (dot != 0 && discreteCollision) { @@ -97,45 +103,19 @@ public class ContinuousOBBCollider extends OBBCollider { stepSeparationVec = sepVec.subtract(axisPlane.scale(sepVec.dotProduct(stepPlane) / axisPlane.dotProduct(stepPlane))); stepSeparation = stepSeparationVec.length(); - - - if (abs(mf.stepSeparation) > abs(stepSeparation) && stepSeparation != 0) { -// CollisionDebugger.showDebugLine(Vec3d.ZERO, sepVec, 0x111155, "stepsep", -16); + if (abs(mf.stepSeparation) > abs(stepSeparation) && stepSeparation != 0) mf.stepSeparation = stepSeparation; - } } else { - if (abs(mf.stepSeparation) > stepSeparation) { + if (abs(mf.stepSeparation) > stepSeparation) mf.stepSeparation = stepSeparation; -// CollisionDebugger.showDebugLine(Vec3d.ZERO, stepSeparationVec, 0xff9999, "axis", -16); - } } - -// if (abs(mf.separation) < abs(stepSeparation) && stepSeparation != 0) } if (isBestSeperation) { - mf.axis = normalizedAxis; mf.separation = seperation; - - // Visualize values -// if (CollisionDebugger.AABB != null) { -// Vec3d normalizedAxis = axis.normalize(); -// showDebugLine(Vec3d.ZERO, normalizedAxis.scale(projectedMotion), 0x111155, "motion", 5); -// showDebugLine(Vec3d.ZERO, normalizedAxis.scale(TL), 0xbb00bb, "tl", 4); -// showDebugLine(Vec3d.ZERO, normalizedAxis.scale(sTL * rA), 0xff4444, "ra", 3); -// showDebugLine(normalizedAxis.scale(sTL * rA), -// normalizedAxis.scale(sTL * rA - entryTime * projectedMotion), 0x44ff44, "entry", 0); -// showDebugLine(normalizedAxis.scale(sTL * rA - entryTime * projectedMotion), -// normalizedAxis.scale(sTL * rA - entryTime * projectedMotion + exitTime * projectedMotion), 0x44ffff, -// "exit", -1); -// showDebugLine(normalizedAxis.scale(sTL * (distance - rB)), normalizedAxis.scale(TL), 0x4444ff, "rb", 2); -// showDebugLine(normalizedAxis.scale(sTL * (distance - rB)), -// normalizedAxis.scale(sTL * (distance - rB) + value), 0xff9966, "separation", 1); -//// System.out.println("TL:" + TL + ", rA: " + rA + ", rB: " + rB); -// } - + mf.collisionPosition = normalizedAxis.scale(signum(TL) * (axisOfObjA ? -rB : -rB) - signum(seperation) * .125f); } return false; @@ -147,10 +127,14 @@ public class ContinuousOBBCollider extends OBBCollider { double latestCollisionEntryTime = UNDEFINED; double earliestCollisionExitTime = Double.MAX_VALUE; boolean isDiscreteCollision = true; + Vec3d collisionPosition; Vec3d stepSeparationAxis; double stepSeparation; + Vec3d normalAxis; + double normalSeparation; + public double getTimeOfImpact() { if (latestCollisionEntryTime == UNDEFINED) return UNDEFINED; @@ -163,9 +147,17 @@ public class ContinuousOBBCollider extends OBBCollider { return true; } + public Vec3d getCollisionNormal() { + return normalAxis == null ? null : createSeparationVec(normalSeparation, normalAxis); + } + + public Vec3d getCollisionPosition() { + return collisionPosition; + } + public Vec3d asSeparationVec(double obbStepHeight) { if (isDiscreteCollision) { - if (stepSeparation <= obbStepHeight) + if (stepSeparation <= obbStepHeight) return createSeparationVec(stepSeparation, stepSeparationAxis); return super.asSeparationVec(); } @@ -174,7 +166,7 @@ public class ContinuousOBBCollider extends OBBCollider { return null; return Vec3d.ZERO; } - + @Override public Vec3d asSeparationVec() { return asSeparationVec(0); diff --git a/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java b/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java index bd2045f84..181f1177f 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java @@ -8,10 +8,13 @@ import org.apache.commons.lang3.mutable.MutableInt; import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.base.KineticTileEntity; +import com.simibubi.create.content.contraptions.components.actors.SeatBlock; +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.SlimeBlock; import net.minecraft.client.particle.DiggingParticle; import net.minecraft.client.particle.ParticleManager; import net.minecraft.entity.player.PlayerEntity; @@ -255,5 +258,13 @@ public class BlockHelper { } catch (Exception e) { } } + + public static double getBounceMultiplier(Block block) { + if (block instanceof SlimeBlock) + return 0.8D; + if (block instanceof BedBlock || block instanceof SeatBlock) + return 0.66 * 0.8D; + return 0; + } } From 65d21c374bfb7689f9b8c36f4940e49ce9bce729 Mon Sep 17 00:00:00 2001 From: JozsefA Date: Tue, 30 Mar 2021 18:50:07 -0700 Subject: [PATCH 2/3] Fix crash making step sounds on servers. --- .../foundation/mixin/StepSoundMixin.java | 32 +++++++++---------- src/main/resources/create.mixins.json | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/simibubi/create/foundation/mixin/StepSoundMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/StepSoundMixin.java index ba640af84..f7ecb40c2 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/StepSoundMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/StepSoundMixin.java @@ -16,6 +16,7 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.gen.feature.template.Template; import org.apache.logging.log4j.util.TriConsumer; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -30,6 +31,7 @@ import java.util.stream.Collectors; @Mixin(Entity.class) public abstract class StepSoundMixin { + private final Entity self = (Entity) (Object) this; @Shadow public boolean collided; @@ -37,6 +39,7 @@ public abstract class StepSoundMixin { @Shadow public World world; + @Final @Shadow protected Random rand; @@ -58,21 +61,20 @@ public abstract class StepSoundMixin { @Shadow protected abstract void playStepSound(BlockPos p_180429_1_, BlockState p_180429_2_); - private Set getIntersectingContraptions(Entity entity) { - Set contraptions = ContraptionHandler.loadedContraptions.get(entity.world) + private Set getIntersectingContraptions() { + Set contraptions = ContraptionHandler.loadedContraptions.get(this.world) .values() .stream() .map(Reference::get) - .filter(cEntity -> cEntity != null && cEntity.collidingEntities.containsKey(entity)) + .filter(cEntity -> cEntity != null && cEntity.collidingEntities.containsKey(self)) .collect(Collectors.toSet()); - contraptions.addAll(entity.world.getEntitiesWithinAABB(AbstractContraptionEntity.class, getBoundingBox().grow(1f))); + contraptions.addAll(this.world.getEntitiesWithinAABB(AbstractContraptionEntity.class, getBoundingBox().grow(1f))); return contraptions; } private void forCollision(Vec3d anchorPos, TriConsumer action) { - Entity thi = (Entity) (Object) this; - getIntersectingContraptions(thi).forEach(cEntity -> { + getIntersectingContraptions().forEach(cEntity -> { Vec3d localPos = ContraptionCollider.getWorldToLocalTranslation(anchorPos, cEntity); localPos = anchorPos.add(localPos); @@ -90,15 +92,14 @@ public abstract class StepSoundMixin { @Inject(at = @At( value = "JUMP", - opcode = 154, //IFNE + opcode = 154, // IFNE line 587 injecting before `!blockstate.isAir(this.world, blockpos)` ordinal = 4 ), method = "move" ) private void movementMixin(MoverType mover, Vec3d movement, CallbackInfo ci) { - Entity thi = (Entity) (Object) this; World entityWorld = world; - Vec3d worldPos = thi.getPositionVector().add(0, -0.2, 0); + Vec3d worldPos = self.getPositionVector().add(0, -0.2, 0); AtomicBoolean stepped = new AtomicBoolean(false); forCollision(worldPos, (contraption, blockstate, blockPos) -> { @@ -113,18 +114,17 @@ public abstract class StepSoundMixin { world = entityWorld; } - @Inject(method = {"Lnet/minecraft/entity/Entity;createRunningParticles()V"}, at = @At(value = "TAIL")) + @Inject(method = "createRunningParticles", at = @At("TAIL")) private void createRunningParticlesMixin(CallbackInfo ci) { - Entity thi = (Entity) (Object) this; - Vec3d worldPos = thi.getPositionVector().add(0, -0.2, 0); + Vec3d worldPos = self.getPositionVector().add(0, -0.2, 0); BlockPos pos = new BlockPos(worldPos); // pos where particles are spawned forCollision(worldPos, (contraption, blockstate, blockpos) -> { - if (!blockstate.addRunningEffects(world, blockpos, thi) && blockstate.getRenderType() != BlockRenderType.INVISIBLE) { - Vec3d vec3d = thi.getMotion(); + if (!blockstate.addRunningEffects(world, blockpos, self) && blockstate.getRenderType() != BlockRenderType.INVISIBLE) { + Vec3d vec3d = self.getMotion(); this.world.addParticle(new BlockParticleData(ParticleTypes.BLOCK, blockstate).setPos(pos), - thi.getX() + ((double) rand.nextFloat() - 0.5D) * (double) thi.getWidth(), - thi.getY() + 0.1D, thi.getZ() + ((double) rand.nextFloat() - 0.5D) * (double) thi.getWidth(), + self.getX() + ((double) rand.nextFloat() - 0.5D) * (double) self.getWidth(), + self.getY() + 0.1D, self.getZ() + ((double) rand.nextFloat() - 0.5D) * (double) self.getWidth(), vec3d.x * -4.0D, 1.5D, vec3d.z * -4.0D); } }); diff --git a/src/main/resources/create.mixins.json b/src/main/resources/create.mixins.json index 24c2db2a5..3ee1cb74c 100644 --- a/src/main/resources/create.mixins.json +++ b/src/main/resources/create.mixins.json @@ -4,7 +4,6 @@ "package": "com.simibubi.create.foundation.mixin", "compatibilityLevel": "JAVA_8", "refmap": "create.refmap.json", - "mixins": ["StepSoundMixin"], "client": [ "TileWorldHookMixin", "CancelTileEntityRenderMixin", @@ -13,7 +12,8 @@ "NetworkLightUpdateMixin", "RenderHooksMixin", "ShaderCloseMixin", - "TileRemoveMixin" + "TileRemoveMixin", + "StepSoundMixin" ], "injectors": { "defaultRequire": 1 From d61e387916cb911ab1221936581ecd91ed6a7553 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Wed, 31 Mar 2021 14:27:27 +0200 Subject: [PATCH 3/3] The least performant option - Fixed slow merging of collision shapes - Improved async handling of the collision shape merger - BBs far away from an entity now get skipped in the collision cycle --- .../AbstractContraptionEntity.java | 2 ++ .../structureMovement/Contraption.java | 33 +++++++++++-------- .../ContraptionCollider.java | 13 ++++++-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java index 867d8d685..d50d90658 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java @@ -461,6 +461,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit if (!ticking) contraption.stop(world); } + if (contraption != null) + contraption.onEntityRemoved(this); super.remove(keepData); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index 249377298..659c2f591 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -137,7 +137,7 @@ public abstract class Contraption { private Map initialPassengers; private List pendingSubContraptions; - private CompletableFuture> simplifiedEntityColliderProvider; + private CompletableFuture simplifiedEntityColliderProvider; // Client public Map presentTileEntities; @@ -193,6 +193,7 @@ public abstract class Contraption { String type = nbt.getString("Type"); Contraption contraption = ContraptionType.fromType(type); contraption.readNBT(world, nbt, spawnData); + contraption.world = new ContraptionWorld(world, contraption); contraption.gatherBBsOffThread(); return contraption; } @@ -260,6 +261,13 @@ public abstract class Contraption { gatherBBsOffThread(); } + public void onEntityRemoved(AbstractContraptionEntity entity) { + if (simplifiedEntityColliderProvider != null) { + simplifiedEntityColliderProvider.cancel(false); + simplifiedEntityColliderProvider = null; + } + } + public void onEntityInitialize(World world, AbstractContraptionEntity contraptionEntity) { if (world.isRemote) return; @@ -282,15 +290,6 @@ public abstract class Contraption { } public void onEntityTick(World world) { - if (simplifiedEntityColliderProvider != null && simplifiedEntityColliderProvider.isDone()) { - try { - simplifiedEntityColliders = Optional.of(simplifiedEntityColliderProvider.join()); - } catch (Exception e) { - e.printStackTrace(); - } - simplifiedEntityColliderProvider = null; - } - fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, world.isRemote)); } @@ -1182,19 +1181,25 @@ public abstract class Contraption { } private void gatherBBsOffThread() { + getContraptionWorld(); simplifiedEntityColliderProvider = CompletableFuture.supplyAsync(() -> { VoxelShape combinedShape = VoxelShapes.empty(); for (Entry entry : blocks.entrySet()) { BlockInfo info = entry.getValue(); BlockPos localPos = entry.getKey(); - VoxelShape collisionShape = info.state.getCollisionShape(this.world, localPos); + VoxelShape collisionShape = info.state.getCollisionShape(world, localPos); if (collisionShape.isEmpty()) continue; - combinedShape = VoxelShapes.combineAndSimplify(combinedShape, + combinedShape = VoxelShapes.combine(combinedShape, collisionShape.withOffset(localPos.getX(), localPos.getY(), localPos.getZ()), IBooleanFunction.OR); } - return combinedShape.toBoundingBoxList(); - }); + return combinedShape.simplify() + .toBoundingBoxList(); + }) + .thenAccept(r -> { + simplifiedEntityColliders = Optional.of(r); + simplifiedEntityColliderProvider = null; + }); } public static float getRadius(Set blocks, Direction.Axis axis) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java index b95319e45..706805838 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java @@ -118,7 +118,6 @@ public class ContraptionCollider { motion = rotationMatrix.transform(motion); // Use simplified bbs when present - // TODO: is it worth filtering out far away bbs? final Vec3d motionCopy = motion; List collidableBBs = contraption.simplifiedEntityColliders.orElseGet(() -> { @@ -147,7 +146,17 @@ public class ContraptionCollider { for (AxisAlignedBB bb : collidableBBs) { Vec3d currentResponse = collisionResponse.getValue(); - obb.setCenter(obbCenter.add(currentResponse)); + Vec3d currentCenter = obbCenter.add(currentResponse); + + if (Math.abs(currentCenter.x - bb.getCenter().x) - entityBounds.getXSize() - 1 > bb.getXSize() / 2) + continue; + if (Math.abs((currentCenter.y + motion.y) - bb.getCenter().y) - entityBounds.getYSize() + - 1 > bb.getYSize() / 2) + continue; + if (Math.abs(currentCenter.z - bb.getCenter().z) - entityBounds.getZSize() - 1 > bb.getZSize() / 2) + continue; + + obb.setCenter(currentCenter); ContinuousSeparationManifold intersect = obb.intersect(bb, motion); if (intersect == null)