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 effb5f9b9..e61fb0046 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 @@ -455,6 +455,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 97f75ef54..f31efd30b 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 @@ -1,5 +1,30 @@ package com.simibubi.create.content.contraptions.components.structureMovement; +import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isExtensionPole; +import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isPistonHead; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +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 org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; + import com.simibubi.create.AllBlocks; import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.content.contraptions.base.IRotate; @@ -32,9 +57,22 @@ 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.*; +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.*; + +import net.minecraft.block.AbstractButtonBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ChestBlock; +import net.minecraft.block.DoorBlock; +import net.minecraft.block.IWaterLoggable; +import net.minecraft.block.PressurePlateBlock; import net.minecraft.block.material.PushReaction; import net.minecraft.entity.Entity; import net.minecraft.fluid.FluidState; @@ -54,6 +92,9 @@ import net.minecraft.util.Direction.Axis; import net.minecraft.util.Rotation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; +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.math.vector.Vector3d; import net.minecraft.util.palette.HashMapPalette; import net.minecraft.village.PointOfInterestType; @@ -72,19 +113,10 @@ import net.minecraftforge.fluids.capability.templates.FluidTank; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.registries.GameData; -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; - -import javax.annotation.Nullable; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isExtensionPole; -import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isPistonHead; public abstract class Contraption { + public Optional> simplifiedEntityColliders; public AbstractContraptionEntity entity; public CombinedInvWrapper inventory; public CombinedTankWrapper fluidInventory; @@ -105,6 +137,8 @@ public abstract class Contraption { private Map initialPassengers; private List pendingSubContraptions; + private CompletableFuture simplifiedEntityColliderProvider; + // Client public Map presentTileEntities; public List maybeInstancedTileEntities; @@ -127,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; } @@ -160,6 +193,8 @@ 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; } @@ -223,6 +258,14 @@ public abstract class Contraption { .collect(Collectors.toList()); fluidInventory = new CombinedTankWrapper( Arrays.copyOf(fluidHandlers.toArray(), fluidHandlers.size(), IFluidHandler[].class)); + gatherBBsOffThread(); + } + + public void onEntityRemoved(AbstractContraptionEntity entity) { + if (simplifiedEntityColliderProvider != null) { + simplifiedEntityColliderProvider.cancel(false); + simplifiedEntityColliderProvider = null; + } } public void onEntityInitialize(World world, AbstractContraptionEntity contraptionEntity) { @@ -1136,6 +1179,28 @@ public abstract class Contraption { return new EmptyLighter(this); } + 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(world, localPos); + if (collisionShape.isEmpty()) + continue; + combinedShape = VoxelShapes.combine(combinedShape, + collisionShape.withOffset(localPos.getX(), localPos.getY(), localPos.getZ()), IBooleanFunction.OR); + } + return combinedShape.simplify() + .toBoundingBoxList(); + }) + .thenAccept(r -> { + simplifiedEntityColliders = Optional.of(r); + simplifiedEntityColliderProvider = null; + }); + } + 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 11443ae6e..b0554c42c 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 @@ -22,6 +22,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; @@ -36,6 +37,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; @@ -103,42 +106,56 @@ public class ContraptionCollider { AxisAlignedBB entityBounds = entity.getBoundingBox(); Vector3d motion = entity.getMotion(); float yawOffset = rotation.getYawOffset(); - Vector3d 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 + final Vector3d 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<>(Vector3d.ZERO); + MutableObject normal = new MutableObject<>(Vector3d.ZERO); + MutableObject location = new MutableObject<>(Vector3d.ZERO); MutableBoolean surfaceCollision = new MutableBoolean(false); MutableFloat temporalResponse = new MutableFloat(1); Vector3d 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) { Vector3d currentResponse = collisionResponse.getValue(); - obb.setCenter(obbCenter.add(currentResponse)); + Vector3d 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) @@ -147,15 +164,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; + Vector3d collidingNormal = intersect.getCollisionNormal(); + Vector3d collisionPosition = intersect.getCollisionPosition(); + + if (!isTemporal) { + Vector3d separation = intersect.asSeparationVec(entity.stepHeight); + if (separation != null && !separation.equals(Vector3d.ZERO)) { + collisionResponse.setValue(currentResponse.add(separation)); + timeOfImpact = 0; + } } - Vector3d separation = intersect.asSeparationVec(entity.stepHeight); - if (separation != null && !separation.equals(Vector3d.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) @@ -174,6 +204,9 @@ public class ContraptionCollider { // Resolve collision Vector3d entityMotion = entity.getMotion(); + Vector3d entityMotionNoTemporal = entityMotion; + Vector3d collisionNormal = normal.getValue(); + Vector3d collisionLocation = location.getValue(); Vector3d totalResponse = collisionResponse.getValue(); boolean hardCollision = !totalResponse.equals(Vector3d.ZERO); boolean temporalCollision = temporalResponse.getValue() != 1; @@ -186,8 +219,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(Vector3d.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(Vector3d.ZERO); + boolean anyCollision = hardCollision || temporalCollision; + + if (bounce > 0 && hasNormal && anyCollision) { + collisionNormal = collisionNormal.normalize(); + Vector3d 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) { @@ -215,6 +287,18 @@ public class ContraptionCollider { entityMotion = entityMotion.mul(1, 1, 0); } + if (bounce == 0 && slide > 0 && hasNormal && anyCollision && rotation.hasVerticalRotation()) { + collisionNormal = collisionNormal.normalize(); + Vector3d motionIn = entityMotionNoTemporal.mul(0, 1, 0) + .add(0, -.01f, 0); + Vector3d 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; @@ -228,10 +312,14 @@ public class ContraptionCollider { if (surfaceCollision.isTrue()) { entity.fallDistance = 0; - entity.setOnGround(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, @@ -253,6 +341,42 @@ public class ContraptionCollider { } + static boolean bounceEntity(Entity entity, Vector3d normal, AbstractContraptionEntity contraption, double factor) { + if (factor == 0) + return false; + if (entity.bypassesLandingEffects()) + return false; + if (normal.equals(Vector3d.ZERO)) + return false; + + Vector3d contraptionVec = Vector3d.ZERO; + Vector3d contactPointMotion = contraption.getContactPointMotion(entity.getPositionVec()); + Vector3d motion = entity.getMotion() + .subtract(contactPointMotion); + + Vector3d v2 = motion.crossProduct(normal) + .normalize(); + if (v2 != Vector3d.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(); + + Vector3d v3 = normal.crossProduct(v2); + motion = motion.subtract(contraptionVec); + Vector3d lr = new Vector3d(factor * motion.dotProduct(normal), -motion.dotProduct(v2), -motion.dotProduct(v3)); + + if (lr.dotProduct(lr) > 1 / 16f) { + Vector3d 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 Vector3d 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 509300274..0d7afc470 100644 --- a/src/main/java/com/simibubi/create/foundation/collision/ContinuousOBBCollider.java +++ b/src/main/java/com/simibubi/create/foundation/collision/ContinuousOBBCollider.java @@ -36,24 +36,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, Vector3d axis, double TL, double rA, double rB, - double projectedMotion) { + double projectedMotion, boolean axisOfObjA) { checkCount++; double distance = abs(TL); double diff = distance - (rA + rB); @@ -82,7 +83,12 @@ public class ContinuousOBBCollider extends OBBCollider { Vector3d 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) { @@ -98,45 +104,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(Vector3d.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(Vector3d.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) { -// Vector3d normalizedAxis = axis.normalize(); -// showDebugLine(Vector3d.ZERO, normalizedAxis.scale(projectedMotion), 0x111155, "motion", 5); -// showDebugLine(Vector3d.ZERO, normalizedAxis.scale(TL), 0xbb00bb, "tl", 4); -// showDebugLine(Vector3d.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; @@ -148,10 +128,14 @@ public class ContinuousOBBCollider extends OBBCollider { double latestCollisionEntryTime = UNDEFINED; double earliestCollisionExitTime = Double.MAX_VALUE; boolean isDiscreteCollision = true; + Vector3d collisionPosition; Vector3d stepSeparationAxis; double stepSeparation; + Vector3d normalAxis; + double normalSeparation; + public double getTimeOfImpact() { if (latestCollisionEntryTime == UNDEFINED) return UNDEFINED; @@ -164,9 +148,17 @@ public class ContinuousOBBCollider extends OBBCollider { return true; } + public Vector3d getCollisionNormal() { + return normalAxis == null ? null : createSeparationVec(normalSeparation, normalAxis); + } + + public Vector3d getCollisionPosition() { + return collisionPosition; + } + public Vector3d asSeparationVec(double obbStepHeight) { if (isDiscreteCollision) { - if (stepSeparation <= obbStepHeight) + if (stepSeparation <= obbStepHeight) return createSeparationVec(stepSeparation, stepSeparationAxis); return super.asSeparationVec(); } @@ -175,7 +167,7 @@ public class ContinuousOBBCollider extends OBBCollider { return null; return Vector3d.ZERO; } - + @Override public Vector3d asSeparationVec() { return asSeparationVec(0); 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 de6a50ee7..26a3ee18a 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.vector.Vector3d; 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,10 +31,12 @@ import java.util.stream.Collectors; @Mixin(Entity.class) public abstract class StepSoundMixin { + private final Entity self = (Entity) (Object) this; @Shadow public World world; + @Final @Shadow protected Random rand; @@ -55,21 +58,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(Vector3d anchorPos, TriConsumer action) { - Entity thi = (Entity) (Object) this; - getIntersectingContraptions(thi).forEach(cEntity -> { + getIntersectingContraptions().forEach(cEntity -> { Vector3d localPos = ContraptionCollider.getWorldToLocalTranslation(anchorPos, cEntity); localPos = anchorPos.add(localPos); @@ -87,15 +89,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, Vector3d movement, CallbackInfo ci) { - Entity thi = (Entity) (Object) this; World entityWorld = world; - Vector3d worldPos = thi.getPositionVec().add(0, -0.2, 0); + Vector3d worldPos = self.getPositionVec().add(0, -0.2, 0); AtomicBoolean stepped = new AtomicBoolean(false); forCollision(worldPos, (contraption, blockstate, blockPos) -> { @@ -110,19 +111,18 @@ public abstract class StepSoundMixin { world = entityWorld; } - @Inject(method = {"Lnet/minecraft/entity/Entity;spawnSprintingParticles()V"}, at = @At(value = "TAIL")) + @Inject(method = {"spawnSprintingParticles"}, at = @At(value = "TAIL")) private void createRunningParticlesMixin(CallbackInfo ci) { - Entity thi = (Entity) (Object) this; - Vector3d worldPos = thi.getPositionVec().add(0, -0.2, 0); + Vector3d worldPos = self.getPositionVec().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) { - Vector3d Vector3d = thi.getMotion(); + if (!blockstate.addRunningEffects(world, blockpos, self) && blockstate.getRenderType() != BlockRenderType.INVISIBLE) { + Vector3d 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(), - Vector3d.x * -4.0D, 1.5D, Vector3d.z * -4.0D); + 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/java/com/simibubi/create/foundation/utility/BlockHelper.java b/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java index 8b89fcf4b..badfc7ace 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.client.world.ClientWorld; @@ -260,6 +263,14 @@ 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; + } public static boolean hasBlockSolidSide(BlockState p_220056_0_, IBlockReader p_220056_1_, BlockPos p_220056_2_, Direction p_220056_3_) { return !p_220056_0_.isIn(BlockTags.LEAVES) && Block.doesSideFillSquare(p_220056_0_.getCollisionShape(p_220056_1_, p_220056_2_), p_220056_3_); 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