mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-29 08:27:03 +01:00
Oriented bouncing and sliding
- Slime blocks on a moving Contraption are now bouncy - Ice blocks on moving Contraptions are now very slippery
This commit is contained in:
parent
4ec25798a5
commit
0bb18db4b6
4 changed files with 227 additions and 70 deletions
|
@ -10,19 +10,18 @@ import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
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.MutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
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.content.logistics.block.redstone.RedstoneContactBlock;
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
|
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.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 com.simibubi.create.foundation.utility.worldWrappers.WrappedWorld;
|
||||||
|
|
||||||
import net.minecraft.block.AbstractButtonBlock;
|
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.AxisAlignedBB;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
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.util.palette.PaletteHashMap;
|
||||||
import net.minecraft.village.PointOfInterestType;
|
import net.minecraft.village.PointOfInterestType;
|
||||||
import net.minecraft.world.IWorld;
|
import net.minecraft.world.IWorld;
|
||||||
|
@ -106,6 +116,7 @@ import net.minecraftforge.registries.GameData;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -126,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<List<AxisAlignedBB>> simplifiedEntityColliderProvider;
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
public Map<BlockPos, TileEntity> presentTileEntities;
|
public Map<BlockPos, TileEntity> presentTileEntities;
|
||||||
public List<TileEntity> maybeInstancedTileEntities;
|
public List<TileEntity> maybeInstancedTileEntities;
|
||||||
|
@ -148,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +193,7 @@ 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.gatherBBsOffThread();
|
||||||
return contraption;
|
return contraption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +257,7 @@ 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 onEntityInitialize(World world, AbstractContraptionEntity contraptionEntity) {
|
public void onEntityInitialize(World world, AbstractContraptionEntity contraptionEntity) {
|
||||||
|
@ -268,6 +282,15 @@ public abstract class Contraption {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEntityTick(World world) {
|
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));
|
fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, world.isRemote));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,6 +1181,22 @@ public abstract class Contraption {
|
||||||
return new EmptyLighter(this);
|
return new EmptyLighter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void gatherBBsOffThread() {
|
||||||
|
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(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<BlockPos> blocks, Direction.Axis axis) {
|
public static float getRadius(Set<BlockPos> blocks, Direction.Axis axis) {
|
||||||
switch (axis) {
|
switch (axis) {
|
||||||
case X:
|
case X:
|
||||||
|
|
|
@ -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.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;
|
||||||
|
|
||||||
|
@ -37,6 +38,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;
|
||||||
|
@ -104,40 +107,45 @@ public class ContraptionCollider {
|
||||||
AxisAlignedBB entityBounds = entity.getBoundingBox();
|
AxisAlignedBB entityBounds = entity.getBoundingBox();
|
||||||
Vec3d motion = entity.getMotion();
|
Vec3d motion = entity.getMotion();
|
||||||
float yawOffset = rotation.getYawOffset();
|
float yawOffset = rotation.getYawOffset();
|
||||||
|
|
||||||
Vec3d position = getWorldToLocalTranslation(entity, anchorVec, rotationMatrix, yawOffset);
|
Vec3d 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
|
||||||
|
// TODO: is it worth filtering out far away bbs?
|
||||||
|
final Vec3d 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<Vec3d> collisionResponse = new MutableObject<>(Vec3d.ZERO);
|
MutableObject<Vec3d> collisionResponse = new MutableObject<>(Vec3d.ZERO);
|
||||||
|
MutableObject<Vec3d> normal = new MutableObject<>(Vec3d.ZERO);
|
||||||
|
MutableObject<Vec3d> location = new MutableObject<>(Vec3d.ZERO);
|
||||||
MutableBoolean surfaceCollision = new MutableBoolean(false);
|
MutableBoolean surfaceCollision = new MutableBoolean(false);
|
||||||
MutableFloat temporalResponse = new MutableFloat(1);
|
MutableFloat temporalResponse = new MutableFloat(1);
|
||||||
Vec3d obbCenter = obb.getCenter();
|
Vec3d 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) {
|
||||||
Vec3d currentResponse = collisionResponse.getValue();
|
Vec3d currentResponse = collisionResponse.getValue();
|
||||||
obb.setCenter(obbCenter.add(currentResponse));
|
obb.setCenter(obbCenter.add(currentResponse));
|
||||||
ContinuousSeparationManifold intersect = obb.intersect(bb, motion);
|
ContinuousSeparationManifold intersect = obb.intersect(bb, motion);
|
||||||
|
@ -148,15 +156,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)
|
Vec3d collidingNormal = intersect.getCollisionNormal();
|
||||||
temporalResponse.setValue(timeOfImpact);
|
Vec3d collisionPosition = intersect.getCollisionPosition();
|
||||||
continue;
|
|
||||||
|
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);
|
boolean nearest = timeOfImpact >= 0 && temporalResponse.getValue() > timeOfImpact;
|
||||||
if (separation != null && !separation.equals(Vec3d.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)
|
||||||
|
@ -175,6 +196,9 @@ public class ContraptionCollider {
|
||||||
|
|
||||||
// Resolve collision
|
// Resolve collision
|
||||||
Vec3d entityMotion = entity.getMotion();
|
Vec3d entityMotion = entity.getMotion();
|
||||||
|
Vec3d entityMotionNoTemporal = entityMotion;
|
||||||
|
Vec3d collisionNormal = normal.getValue();
|
||||||
|
Vec3d collisionLocation = location.getValue();
|
||||||
Vec3d totalResponse = collisionResponse.getValue();
|
Vec3d totalResponse = collisionResponse.getValue();
|
||||||
boolean hardCollision = !totalResponse.equals(Vec3d.ZERO);
|
boolean hardCollision = !totalResponse.equals(Vec3d.ZERO);
|
||||||
boolean temporalCollision = temporalResponse.getValue() != 1;
|
boolean temporalCollision = temporalResponse.getValue() != 1;
|
||||||
|
@ -187,8 +211,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(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) {
|
if (temporalCollision) {
|
||||||
double idealVerticalMotion = motionResponse.y;
|
double idealVerticalMotion = motionResponse.y;
|
||||||
if (idealVerticalMotion != entityMotion.y) {
|
if (idealVerticalMotion != entityMotion.y) {
|
||||||
|
@ -216,6 +279,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();
|
||||||
|
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())
|
if (!hardCollision && surfaceCollision.isFalse())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -229,10 +304,14 @@ public class ContraptionCollider {
|
||||||
|
|
||||||
if (surfaceCollision.isTrue()) {
|
if (surfaceCollision.isTrue()) {
|
||||||
entity.fallDistance = 0;
|
entity.fallDistance = 0;
|
||||||
entity.onGround = true;
|
|
||||||
contraptionEntity.collidingEntities.put(entity, new MutableInt(0));
|
contraptionEntity.collidingEntities.put(entity, new MutableInt(0));
|
||||||
|
boolean canWalk = bounce != 0 || slide == 0;
|
||||||
|
if (canWalk || !rotation.hasVerticalRotation()) {
|
||||||
|
if (canWalk)
|
||||||
|
entity.onGround = true;
|
||||||
if (entity instanceof ItemEntity)
|
if (entity instanceof ItemEntity)
|
||||||
entityMotion = entityMotion.mul(.5f, 1, .5f);
|
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,
|
||||||
|
@ -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) {
|
public static Vec3d getWorldToLocalTranslation(Entity entity, AbstractContraptionEntity contraptionEntity) {
|
||||||
return getWorldToLocalTranslation(entity, contraptionEntity.getAnchorVec(), contraptionEntity.getRotationState());
|
return getWorldToLocalTranslation(entity, contraptionEntity.getAnchorVec(), contraptionEntity.getRotationState());
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,24 +35,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, Vec3d axis, double TL, double rA, double rB,
|
static boolean separate(ContinuousSeparationManifold mf, Vec3d 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);
|
||||||
|
@ -81,7 +82,12 @@ public class ContinuousOBBCollider extends OBBCollider {
|
||||||
Vec3d normalizedAxis = axis.normalize();
|
Vec3d 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) {
|
||||||
|
@ -97,45 +103,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(Vec3d.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(Vec3d.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) {
|
|
||||||
// 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);
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -147,10 +127,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;
|
||||||
|
Vec3d collisionPosition;
|
||||||
|
|
||||||
Vec3d stepSeparationAxis;
|
Vec3d stepSeparationAxis;
|
||||||
double stepSeparation;
|
double stepSeparation;
|
||||||
|
|
||||||
|
Vec3d normalAxis;
|
||||||
|
double normalSeparation;
|
||||||
|
|
||||||
public double getTimeOfImpact() {
|
public double getTimeOfImpact() {
|
||||||
if (latestCollisionEntryTime == UNDEFINED)
|
if (latestCollisionEntryTime == UNDEFINED)
|
||||||
return UNDEFINED;
|
return UNDEFINED;
|
||||||
|
@ -163,6 +147,14 @@ public class ContinuousOBBCollider extends OBBCollider {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vec3d getCollisionNormal() {
|
||||||
|
return normalAxis == null ? null : createSeparationVec(normalSeparation, normalAxis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3d getCollisionPosition() {
|
||||||
|
return collisionPosition;
|
||||||
|
}
|
||||||
|
|
||||||
public Vec3d asSeparationVec(double obbStepHeight) {
|
public Vec3d asSeparationVec(double obbStepHeight) {
|
||||||
if (isDiscreteCollision) {
|
if (isDiscreteCollision) {
|
||||||
if (stepSeparation <= obbStepHeight)
|
if (stepSeparation <= obbStepHeight)
|
||||||
|
|
|
@ -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.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
@ -256,4 +259,12 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue