Merge branch 'mc1.15/dev' into mc1.16/dev

This commit is contained in:
simibubi 2021-03-31 14:50:16 +02:00
commit 2b4ef88fc1
7 changed files with 293 additions and 99 deletions

View file

@ -455,6 +455,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
if (!ticking) if (!ticking)
contraption.stop(world); contraption.stop(world);
} }
if (contraption != null)
contraption.onEntityRemoved(this);
super.remove(keepData); super.remove(keepData);
} }

View file

@ -1,5 +1,30 @@
package com.simibubi.create.content.contraptions.components.structureMovement; 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.AllBlocks;
import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.base.IRotate; 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.instancing.IFlywheelWorld;
import com.simibubi.create.foundation.render.backend.light.EmptyLighter; import com.simibubi.create.foundation.render.backend.light.EmptyLighter;
import com.simibubi.create.foundation.render.backend.light.GridAlignedBB; 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 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.block.material.PushReaction;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.fluid.FluidState; import net.minecraft.fluid.FluidState;
@ -54,6 +92,9 @@ import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Rotation; import net.minecraft.util.Rotation;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; 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.math.vector.Vector3d;
import net.minecraft.util.palette.HashMapPalette; import net.minecraft.util.palette.HashMapPalette;
import net.minecraft.village.PointOfInterestType; 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.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.items.wrapper.CombinedInvWrapper;
import net.minecraftforge.registries.GameData; 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 abstract class Contraption {
public Optional<List<AxisAlignedBB>> simplifiedEntityColliders;
public AbstractContraptionEntity entity; public AbstractContraptionEntity entity;
public CombinedInvWrapper inventory; public CombinedInvWrapper inventory;
public CombinedTankWrapper fluidInventory; public CombinedTankWrapper fluidInventory;
@ -105,6 +137,8 @@ public abstract class Contraption {
private Map<BlockPos, Entity> initialPassengers; private Map<BlockPos, Entity> initialPassengers;
private List<BlockFace> pendingSubContraptions; private List<BlockFace> pendingSubContraptions;
private CompletableFuture<Void> simplifiedEntityColliderProvider;
// Client // Client
public Map<BlockPos, TileEntity> presentTileEntities; public Map<BlockPos, TileEntity> presentTileEntities;
public List<TileEntity> maybeInstancedTileEntities; public List<TileEntity> maybeInstancedTileEntities;
@ -127,13 +161,12 @@ public abstract class Contraption {
specialRenderedTileEntities = new ArrayList<>(); specialRenderedTileEntities = new ArrayList<>();
pendingSubContraptions = new ArrayList<>(); pendingSubContraptions = new ArrayList<>();
stabilizedSubContraptions = new HashMap<>(); stabilizedSubContraptions = new HashMap<>();
simplifiedEntityColliders = Optional.empty();
} }
public ContraptionWorld getContraptionWorld() { public ContraptionWorld getContraptionWorld() {
if (world == null) { if (world == null)
world = new ContraptionWorld(entity.world, this); world = new ContraptionWorld(entity.world, this);
}
return world; return world;
} }
@ -160,6 +193,8 @@ public abstract class Contraption {
String type = nbt.getString("Type"); String type = nbt.getString("Type");
Contraption contraption = ContraptionType.fromType(type); Contraption contraption = ContraptionType.fromType(type);
contraption.readNBT(world, nbt, spawnData); contraption.readNBT(world, nbt, spawnData);
contraption.world = new ContraptionWorld(world, contraption);
contraption.gatherBBsOffThread();
return contraption; return contraption;
} }
@ -223,6 +258,14 @@ public abstract class Contraption {
.collect(Collectors.toList()); .collect(Collectors.toList());
fluidInventory = new CombinedTankWrapper( fluidInventory = new CombinedTankWrapper(
Arrays.copyOf(fluidHandlers.toArray(), fluidHandlers.size(), IFluidHandler[].class)); 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) { public void onEntityInitialize(World world, AbstractContraptionEntity contraptionEntity) {
@ -1136,6 +1179,28 @@ public abstract class Contraption {
return new EmptyLighter(this); return new EmptyLighter(this);
} }
private void gatherBBsOffThread() {
getContraptionWorld();
simplifiedEntityColliderProvider = CompletableFuture.supplyAsync(() -> {
VoxelShape combinedShape = VoxelShapes.empty();
for (Entry<BlockPos, BlockInfo> 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<BlockPos> blocks, Direction.Axis axis) { public static float getRadius(Set<BlockPos> blocks, Direction.Axis axis) {
switch (axis) { switch (axis) {
case X: case X:

View file

@ -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.Matrix3d;
import com.simibubi.create.foundation.collision.OrientedBB; import com.simibubi.create.foundation.collision.OrientedBB;
import com.simibubi.create.foundation.networking.AllPackets; 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.Iterate;
import com.simibubi.create.foundation.utility.VecHelper; 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.Axis;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.ReuseableStream; 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.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -103,42 +106,56 @@ public class ContraptionCollider {
AxisAlignedBB entityBounds = entity.getBoundingBox(); AxisAlignedBB entityBounds = entity.getBoundingBox();
Vector3d motion = entity.getMotion(); Vector3d motion = entity.getMotion();
float yawOffset = rotation.getYawOffset(); float yawOffset = rotation.getYawOffset();
Vector3d position = getWorldToLocalTranslation(entity, anchorVec, rotationMatrix, yawOffset); Vector3d position = getWorldToLocalTranslation(entity, anchorVec, rotationMatrix, yawOffset);
// Find all potential block shapes to collide with // Prepare entity bounds
AxisAlignedBB localBB = entityBounds.offset(position) AxisAlignedBB localBB = entityBounds.offset(position)
.grow(1.0E-7D); .grow(1.0E-7D);
ReuseableStream<VoxelShape> potentialHits =
getPotentiallyCollidedShapes(world, contraption, localBB.expand(motion));
if (potentialHits.createStream()
.count() == 0)
continue;
// Prepare entity bounds
OrientedBB obb = new OrientedBB(localBB); OrientedBB obb = new OrientedBB(localBB);
obb.setRotation(rotationMatrix); obb.setRotation(rotationMatrix);
motion = motion.subtract(contraptionMotion); motion = motion.subtract(contraptionMotion);
motion = rotationMatrix.transform(motion); motion = rotationMatrix.transform(motion);
// Use simplified bbs when present
final Vector3d motionCopy = motion;
List<AxisAlignedBB> collidableBBs = contraption.simplifiedEntityColliders.orElseGet(() -> {
// Else find 'nearby' individual block shapes to collide with
List<AxisAlignedBB> bbs = new ArrayList<>();
ReuseableStream<VoxelShape> potentialHits =
getPotentiallyCollidedShapes(world, contraption, localBB.expand(motionCopy));
potentialHits.createStream()
.forEach(shape -> shape.toBoundingBoxList()
.forEach(bbs::add));
return bbs;
});
MutableObject<Vector3d> collisionResponse = new MutableObject<>(Vector3d.ZERO); MutableObject<Vector3d> collisionResponse = new MutableObject<>(Vector3d.ZERO);
MutableObject<Vector3d> normal = new MutableObject<>(Vector3d.ZERO);
MutableObject<Vector3d> location = new MutableObject<>(Vector3d.ZERO);
MutableBoolean surfaceCollision = new MutableBoolean(false); MutableBoolean surfaceCollision = new MutableBoolean(false);
MutableFloat temporalResponse = new MutableFloat(1); MutableFloat temporalResponse = new MutableFloat(1);
Vector3d obbCenter = obb.getCenter(); Vector3d obbCenter = obb.getCenter();
// Apply separation maths // Apply separation maths
List<AxisAlignedBB> bbs = new ArrayList<>();
potentialHits.createStream()
.forEach(shape -> shape.toBoundingBoxList()
.forEach(bbs::add));
boolean doHorizontalPass = !rotation.hasVerticalRotation(); boolean doHorizontalPass = !rotation.hasVerticalRotation();
for (boolean horizontalPass : Iterate.trueAndFalse) { for (boolean horizontalPass : Iterate.trueAndFalse) {
boolean verticalPass = !horizontalPass || !doHorizontalPass; boolean verticalPass = !horizontalPass || !doHorizontalPass;
for (AxisAlignedBB bb : bbs) { for (AxisAlignedBB bb : collidableBBs) {
Vector3d currentResponse = collisionResponse.getValue(); 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); ContinuousSeparationManifold intersect = obb.intersect(bb, motion);
if (intersect == null) if (intersect == null)
@ -147,15 +164,28 @@ public class ContraptionCollider {
surfaceCollision.setValue(intersect.isSurfaceCollision()); surfaceCollision.setValue(intersect.isSurfaceCollision());
double timeOfImpact = intersect.getTimeOfImpact(); double timeOfImpact = intersect.getTimeOfImpact();
if (timeOfImpact > 0 && timeOfImpact < 1) { boolean isTemporal = timeOfImpact > 0 && timeOfImpact < 1;
if (temporalResponse.getValue() > timeOfImpact) Vector3d collidingNormal = intersect.getCollisionNormal();
temporalResponse.setValue(timeOfImpact); Vector3d collisionPosition = intersect.getCollisionPosition();
continue;
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); boolean nearest = timeOfImpact >= 0 && temporalResponse.getValue() > timeOfImpact;
if (separation != null && !separation.equals(Vector3d.ZERO)) if (collidingNormal != null && nearest)
collisionResponse.setValue(currentResponse.add(separation)); normal.setValue(collidingNormal);
if (collisionPosition != null && nearest)
location.setValue(collisionPosition);
if (isTemporal) {
if (temporalResponse.getValue() > timeOfImpact)
temporalResponse.setValue(timeOfImpact);
}
} }
if (verticalPass) if (verticalPass)
@ -174,6 +204,9 @@ public class ContraptionCollider {
// Resolve collision // Resolve collision
Vector3d entityMotion = entity.getMotion(); Vector3d entityMotion = entity.getMotion();
Vector3d entityMotionNoTemporal = entityMotion;
Vector3d collisionNormal = normal.getValue();
Vector3d collisionLocation = location.getValue();
Vector3d totalResponse = collisionResponse.getValue(); Vector3d totalResponse = collisionResponse.getValue();
boolean hardCollision = !totalResponse.equals(Vector3d.ZERO); boolean hardCollision = !totalResponse.equals(Vector3d.ZERO);
boolean temporalCollision = temporalResponse.getValue() != 1; boolean temporalCollision = temporalResponse.getValue() != 1;
@ -186,8 +219,47 @@ public class ContraptionCollider {
.add(contraptionMotion); .add(contraptionMotion);
totalResponse = rotationMatrix.transform(totalResponse); totalResponse = rotationMatrix.transform(totalResponse);
totalResponse = VecHelper.rotate(totalResponse, yawOffset, Axis.Y); 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(); 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) { if (temporalCollision) {
double idealVerticalMotion = motionResponse.y; double idealVerticalMotion = motionResponse.y;
if (idealVerticalMotion != entityMotion.y) { if (idealVerticalMotion != entityMotion.y) {
@ -215,6 +287,18 @@ public class ContraptionCollider {
entityMotion = entityMotion.mul(1, 1, 0); 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()) if (!hardCollision && surfaceCollision.isFalse())
continue; continue;
@ -228,10 +312,14 @@ public class ContraptionCollider {
if (surfaceCollision.isTrue()) { if (surfaceCollision.isTrue()) {
entity.fallDistance = 0; entity.fallDistance = 0;
entity.setOnGround(true);
contraptionEntity.collidingEntities.put(entity, new MutableInt(0)); contraptionEntity.collidingEntities.put(entity, new MutableInt(0));
if (entity instanceof ItemEntity) boolean canWalk = bounce != 0 || slide == 0;
entityMotion = entityMotion.mul(.5f, 1, .5f); if (canWalk || !rotation.hasVerticalRotation()) {
if (canWalk)
entity.onGround = true;
if (entity instanceof ItemEntity)
entityMotion = entityMotion.mul(.5f, 1, .5f);
}
contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition); contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition);
allowedMovement = getAllowedMovement(contactPointMotion, entity); allowedMovement = getAllowedMovement(contactPointMotion, entity);
entity.setPosition(entityPosition.x + allowedMovement.x, entityPosition.y, 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) { public static Vector3d getWorldToLocalTranslation(Entity entity, AbstractContraptionEntity contraptionEntity) {
return getWorldToLocalTranslation(entity, contraptionEntity.getAnchorVec(), contraptionEntity.getRotationState()); return getWorldToLocalTranslation(entity, contraptionEntity.getAnchorVec(), contraptionEntity.getRotationState());
} }

View file

@ -36,24 +36,25 @@ public class ContinuousOBBCollider extends OBBCollider {
checkCount = 0; checkCount = 0;
mf.stepSeparationAxis = uB1; mf.stepSeparationAxis = uB1;
mf.stepSeparation = Double.MAX_VALUE; mf.stepSeparation = Double.MAX_VALUE;
mf.normalSeparation = Double.MAX_VALUE;
if ( if (
// Separate along A's local axes (global XYZ) // 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, 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) || 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) || 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 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, 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) || 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))) || separate(mf, uB2, diff2.z, eA.x * a02 + eA.y * a12 + eA.z * a22, eB.z, motion2.z, false)))
return mf; return mf;
return null; return null;
} }
static boolean separate(ContinuousSeparationManifold mf, Vector3d axis, double TL, double rA, double rB, static boolean separate(ContinuousSeparationManifold mf, Vector3d axis, double TL, double rA, double rB,
double projectedMotion) { double projectedMotion, boolean axisOfObjA) {
checkCount++; checkCount++;
double distance = abs(TL); double distance = abs(TL);
double diff = distance - (rA + rB); double diff = distance - (rA + rB);
@ -82,7 +83,12 @@ public class ContinuousOBBCollider extends OBBCollider {
Vector3d normalizedAxis = axis.normalize(); Vector3d normalizedAxis = axis.normalize();
boolean isBestSeperation = distance != 0 && -(diff) <= abs(mf.separation); 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); double dot = mf.stepSeparationAxis.dotProduct(axis);
if (dot != 0 && discreteCollision) { if (dot != 0 && discreteCollision) {
@ -98,45 +104,19 @@ public class ContinuousOBBCollider extends OBBCollider {
stepSeparationVec = stepSeparationVec =
sepVec.subtract(axisPlane.scale(sepVec.dotProduct(stepPlane) / axisPlane.dotProduct(stepPlane))); sepVec.subtract(axisPlane.scale(sepVec.dotProduct(stepPlane) / axisPlane.dotProduct(stepPlane)));
stepSeparation = stepSeparationVec.length(); stepSeparation = stepSeparationVec.length();
if (abs(mf.stepSeparation) > abs(stepSeparation) && stepSeparation != 0)
if (abs(mf.stepSeparation) > abs(stepSeparation) && stepSeparation != 0) {
// CollisionDebugger.showDebugLine(Vector3d.ZERO, sepVec, 0x111155, "stepsep", -16);
mf.stepSeparation = stepSeparation; mf.stepSeparation = stepSeparation;
}
} else { } else {
if (abs(mf.stepSeparation) > stepSeparation) { if (abs(mf.stepSeparation) > stepSeparation)
mf.stepSeparation = stepSeparation; mf.stepSeparation = stepSeparation;
// CollisionDebugger.showDebugLine(Vector3d.ZERO, stepSeparationVec, 0xff9999, "axis", -16);
}
} }
// if (abs(mf.separation) < abs(stepSeparation) && stepSeparation != 0)
} }
if (isBestSeperation) { if (isBestSeperation) {
mf.axis = normalizedAxis; mf.axis = normalizedAxis;
mf.separation = seperation; mf.separation = seperation;
mf.collisionPosition = normalizedAxis.scale(signum(TL) * (axisOfObjA ? -rB : -rB) - signum(seperation) * .125f);
// 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);
// }
} }
return false; return false;
@ -148,10 +128,14 @@ public class ContinuousOBBCollider extends OBBCollider {
double latestCollisionEntryTime = UNDEFINED; double latestCollisionEntryTime = UNDEFINED;
double earliestCollisionExitTime = Double.MAX_VALUE; double earliestCollisionExitTime = Double.MAX_VALUE;
boolean isDiscreteCollision = true; boolean isDiscreteCollision = true;
Vector3d collisionPosition;
Vector3d stepSeparationAxis; Vector3d stepSeparationAxis;
double stepSeparation; double stepSeparation;
Vector3d normalAxis;
double normalSeparation;
public double getTimeOfImpact() { public double getTimeOfImpact() {
if (latestCollisionEntryTime == UNDEFINED) if (latestCollisionEntryTime == UNDEFINED)
return UNDEFINED; return UNDEFINED;
@ -164,6 +148,14 @@ public class ContinuousOBBCollider extends OBBCollider {
return true; return true;
} }
public Vector3d getCollisionNormal() {
return normalAxis == null ? null : createSeparationVec(normalSeparation, normalAxis);
}
public Vector3d getCollisionPosition() {
return collisionPosition;
}
public Vector3d asSeparationVec(double obbStepHeight) { public Vector3d asSeparationVec(double obbStepHeight) {
if (isDiscreteCollision) { if (isDiscreteCollision) {
if (stepSeparation <= obbStepHeight) if (stepSeparation <= obbStepHeight)

View file

@ -16,6 +16,7 @@ import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template; import net.minecraft.world.gen.feature.template.Template;
import org.apache.logging.log4j.util.TriConsumer; import org.apache.logging.log4j.util.TriConsumer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -30,10 +31,12 @@ import java.util.stream.Collectors;
@Mixin(Entity.class) @Mixin(Entity.class)
public abstract class StepSoundMixin { public abstract class StepSoundMixin {
private final Entity self = (Entity) (Object) this;
@Shadow @Shadow
public World world; public World world;
@Final
@Shadow @Shadow
protected Random rand; protected Random rand;
@ -55,21 +58,20 @@ public abstract class StepSoundMixin {
@Shadow @Shadow
protected abstract void playStepSound(BlockPos p_180429_1_, BlockState p_180429_2_); protected abstract void playStepSound(BlockPos p_180429_1_, BlockState p_180429_2_);
private Set<AbstractContraptionEntity> getIntersectingContraptions(Entity entity) { private Set<AbstractContraptionEntity> getIntersectingContraptions() {
Set<AbstractContraptionEntity> contraptions = ContraptionHandler.loadedContraptions.get(entity.world) Set<AbstractContraptionEntity> contraptions = ContraptionHandler.loadedContraptions.get(this.world)
.values() .values()
.stream() .stream()
.map(Reference::get) .map(Reference::get)
.filter(cEntity -> cEntity != null && cEntity.collidingEntities.containsKey(entity)) .filter(cEntity -> cEntity != null && cEntity.collidingEntities.containsKey(self))
.collect(Collectors.toSet()); .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; return contraptions;
} }
private void forCollision(Vector3d anchorPos, TriConsumer<Contraption, BlockState, BlockPos> action) { private void forCollision(Vector3d anchorPos, TriConsumer<Contraption, BlockState, BlockPos> action) {
Entity thi = (Entity) (Object) this; getIntersectingContraptions().forEach(cEntity -> {
getIntersectingContraptions(thi).forEach(cEntity -> {
Vector3d localPos = ContraptionCollider.getWorldToLocalTranslation(anchorPos, cEntity); Vector3d localPos = ContraptionCollider.getWorldToLocalTranslation(anchorPos, cEntity);
localPos = anchorPos.add(localPos); localPos = anchorPos.add(localPos);
@ -87,15 +89,14 @@ public abstract class StepSoundMixin {
@Inject(at = @At( @Inject(at = @At(
value = "JUMP", value = "JUMP",
opcode = 154, //IFNE opcode = 154, // IFNE line 587 injecting before `!blockstate.isAir(this.world, blockpos)`
ordinal = 4 ordinal = 4
), ),
method = "move" method = "move"
) )
private void movementMixin(MoverType mover, Vector3d movement, CallbackInfo ci) { private void movementMixin(MoverType mover, Vector3d movement, CallbackInfo ci) {
Entity thi = (Entity) (Object) this;
World entityWorld = world; 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); AtomicBoolean stepped = new AtomicBoolean(false);
forCollision(worldPos, (contraption, blockstate, blockPos) -> { forCollision(worldPos, (contraption, blockstate, blockPos) -> {
@ -110,19 +111,18 @@ public abstract class StepSoundMixin {
world = entityWorld; 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) { private void createRunningParticlesMixin(CallbackInfo ci) {
Entity thi = (Entity) (Object) this; Vector3d worldPos = self.getPositionVec().add(0, -0.2, 0);
Vector3d worldPos = thi.getPositionVec().add(0, -0.2, 0);
BlockPos pos = new BlockPos(worldPos); // pos where particles are spawned BlockPos pos = new BlockPos(worldPos); // pos where particles are spawned
forCollision(worldPos, (contraption, blockstate, blockpos) -> { forCollision(worldPos, (contraption, blockstate, blockpos) -> {
if (!blockstate.addRunningEffects(world, blockpos, thi) && blockstate.getRenderType() != BlockRenderType.INVISIBLE) { if (!blockstate.addRunningEffects(world, blockpos, self) && blockstate.getRenderType() != BlockRenderType.INVISIBLE) {
Vector3d Vector3d = thi.getMotion(); Vector3d vec3d = self.getMotion();
this.world.addParticle(new BlockParticleData(ParticleTypes.BLOCK, blockstate).setPos(pos), this.world.addParticle(new BlockParticleData(ParticleTypes.BLOCK, blockstate).setPos(pos),
thi.getX() + ((double) rand.nextFloat() - 0.5D) * (double) thi.getWidth(), self.getX() + ((double) rand.nextFloat() - 0.5D) * (double) self.getWidth(),
thi.getY() + 0.1D, thi.getZ() + ((double) rand.nextFloat() - 0.5D) * (double) thi.getWidth(), self.getY() + 0.1D, self.getZ() + ((double) rand.nextFloat() - 0.5D) * (double) self.getWidth(),
Vector3d.x * -4.0D, 1.5D, Vector3d.z * -4.0D); vec3d.x * -4.0D, 1.5D, vec3d.z * -4.0D);
} }
}); });
} }

View file

@ -8,10 +8,13 @@ import org.apache.commons.lang3.mutable.MutableInt;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; 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.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.SlimeBlock;
import net.minecraft.client.particle.DiggingParticle; import net.minecraft.client.particle.DiggingParticle;
import net.minecraft.client.particle.ParticleManager; import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
@ -261,6 +264,14 @@ public class BlockHelper {
} }
} }
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_) { 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_); return !p_220056_0_.isIn(BlockTags.LEAVES) && Block.doesSideFillSquare(p_220056_0_.getCollisionShape(p_220056_1_, p_220056_2_), p_220056_3_);
} }

View file

@ -4,7 +4,6 @@
"package": "com.simibubi.create.foundation.mixin", "package": "com.simibubi.create.foundation.mixin",
"compatibilityLevel": "JAVA_8", "compatibilityLevel": "JAVA_8",
"refmap": "create.refmap.json", "refmap": "create.refmap.json",
"mixins": ["StepSoundMixin"],
"client": [ "client": [
"TileWorldHookMixin", "TileWorldHookMixin",
"CancelTileEntityRenderMixin", "CancelTileEntityRenderMixin",
@ -13,7 +12,8 @@
"NetworkLightUpdateMixin", "NetworkLightUpdateMixin",
"RenderHooksMixin", "RenderHooksMixin",
"ShaderCloseMixin", "ShaderCloseMixin",
"TileRemoveMixin" "TileRemoveMixin",
"StepSoundMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1