mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-29 08:27:03 +01:00
Robust entry collisions
- Collision separation now supports motion sweeping in order to avoid tunnelling when entities drop onto contraptions from a greater height - Further improved the collision response - Entities can no longer be clipped into solid walls by the collision response
This commit is contained in:
parent
85d10a7ce5
commit
ebc2944788
7 changed files with 508 additions and 208 deletions
|
@ -1,20 +1,26 @@
|
||||||
package com.simibubi.create.content.contraptions.components.structureMovement;
|
package com.simibubi.create.content.contraptions.components.structureMovement;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static net.minecraft.entity.Entity.collideBoundingBoxHeuristically;
|
||||||
|
import static net.minecraft.entity.Entity.horizontalMag;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||||
import org.apache.commons.lang3.mutable.MutableObject;
|
import org.apache.commons.lang3.mutable.MutableObject;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour;
|
import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour;
|
||||||
|
import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold;
|
||||||
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.utility.AngleHelper;
|
import com.simibubi.create.foundation.utility.AngleHelper;
|
||||||
|
@ -29,7 +35,7 @@ import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.MoverType;
|
import net.minecraft.entity.MoverType;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.util.DamageSource;
|
||||||
import net.minecraft.util.Direction;
|
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;
|
||||||
|
@ -37,12 +43,14 @@ import net.minecraft.util.ReuseableStream;
|
||||||
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.ISelectionContext;
|
||||||
import net.minecraft.util.math.shapes.VoxelShape;
|
import net.minecraft.util.math.shapes.VoxelShape;
|
||||||
|
import net.minecraft.util.math.shapes.VoxelShapes;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.common.util.Constants.NBT;
|
|
||||||
import net.minecraftforge.event.TickEvent.ClientTickEvent;
|
import net.minecraftforge.event.TickEvent.ClientTickEvent;
|
||||||
import net.minecraftforge.event.TickEvent.Phase;
|
import net.minecraftforge.event.TickEvent.Phase;
|
||||||
import net.minecraftforge.event.TickEvent.WorldTickEvent;
|
import net.minecraftforge.event.TickEvent.WorldTickEvent;
|
||||||
|
@ -54,9 +62,11 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||||
@EventBusSubscriber
|
@EventBusSubscriber
|
||||||
public class ContraptionCollider {
|
public class ContraptionCollider {
|
||||||
|
|
||||||
|
public static DamageSource damageSourceContraptionSuffocate =
|
||||||
|
new DamageSource("create.contraption_suffocate").setDamageBypassesArmor();
|
||||||
public static boolean wasClientPlayerGrounded;
|
public static boolean wasClientPlayerGrounded;
|
||||||
public static Cache<World, List<WeakReference<ContraptionEntity>>> activeContraptions = CacheBuilder.newBuilder()
|
public static Cache<World, List<WeakReference<ContraptionEntity>>> activeContraptions = CacheBuilder.newBuilder()
|
||||||
.expireAfterAccess(20, SECONDS)
|
.expireAfterAccess(40, SECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
@ -86,7 +96,7 @@ public class ContraptionCollider {
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void entityCollisionHappensPreWorldTick(WorldTickEvent event) {
|
public static void entityCollisionHappensPreWorldTick(WorldTickEvent event) {
|
||||||
if (event.phase == Phase.START)
|
if (event.phase == Phase.END)
|
||||||
return;
|
return;
|
||||||
World world = event.world;
|
World world = event.world;
|
||||||
runCollisions(world);
|
runCollisions(world);
|
||||||
|
@ -124,15 +134,17 @@ public class ContraptionCollider {
|
||||||
double conRotY = contraptionRotation.y;
|
double conRotY = contraptionRotation.y;
|
||||||
double conRotZ = contraptionRotation.x;
|
double conRotZ = contraptionRotation.x;
|
||||||
|
|
||||||
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(2),
|
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(2)
|
||||||
contraptionEntity::canCollideWith)) {
|
.expand(0, 32, 0), contraptionEntity::canCollideWith)) {
|
||||||
if (entity instanceof PlayerEntity && !world.isRemote)
|
boolean serverPlayer = entity instanceof PlayerEntity && !world.isRemote;
|
||||||
return;
|
|
||||||
|
|
||||||
|
// Transform entity position and motion to local space
|
||||||
Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
|
Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
|
||||||
Vec3d entityPosition = entity.getPositionVec();
|
Vec3d entityPosition = entity.getPositionVec();
|
||||||
Vec3d centerY = new Vec3d(0, entity.getBoundingBox()
|
AxisAlignedBB entityBounds = entity.getBoundingBox();
|
||||||
.getYSize() / 2, 0);
|
Vec3d centerY = new Vec3d(0, entityBounds.getYSize() / 2, 0);
|
||||||
|
Vec3d motion = entity.getMotion();
|
||||||
|
boolean axisAlignedCollision = contraptionRotation.equals(Vec3d.ZERO);
|
||||||
|
|
||||||
Vec3d position =
|
Vec3d position =
|
||||||
entityPosition.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0))
|
entityPosition.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0))
|
||||||
|
@ -143,27 +155,22 @@ public class ContraptionCollider {
|
||||||
position = position.add(centerOfBlock)
|
position = position.add(centerOfBlock)
|
||||||
.subtract(centerY)
|
.subtract(centerY)
|
||||||
.subtract(entityPosition);
|
.subtract(entityPosition);
|
||||||
AxisAlignedBB localBB = entity.getBoundingBox()
|
|
||||||
.offset(position)
|
// Find all potential block shapes to collide with
|
||||||
|
AxisAlignedBB localBB = entityBounds.offset(position)
|
||||||
.grow(1.0E-7D);
|
.grow(1.0E-7D);
|
||||||
|
ReuseableStream<VoxelShape> potentialHits =
|
||||||
String nbtMotionKey = "ContraptionCollisionFeedback";
|
getPotentiallyCollidedShapes(world, contraption, localBB.expand(motion));
|
||||||
CompoundNBT entityData = entity.getPersistentData();
|
|
||||||
Vec3d previousIntersection = Vec3d.ZERO;
|
|
||||||
if (entityData.contains(nbtMotionKey)) {
|
|
||||||
previousIntersection = VecHelper.readNBT(entityData.getList(nbtMotionKey, NBT.TAG_DOUBLE));
|
|
||||||
entity.setMotion(entity.getMotion()
|
|
||||||
.subtract(previousIntersection.mul(1, 0, 1)));
|
|
||||||
entityData.remove(nbtMotionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReuseableStream<VoxelShape> potentialHits = getPotentiallyCollidedShapes(world, contraption, localBB);
|
|
||||||
if (potentialHits.createStream()
|
if (potentialHits.createStream()
|
||||||
.count() == 0)
|
.count() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!axisAlignedCollision)
|
||||||
|
motion = VecHelper.rotate(motion, -conRotX, -conRotY, -conRotZ);
|
||||||
|
|
||||||
|
// Prepare entity bounds
|
||||||
OrientedBB obb = new OrientedBB(localBB);
|
OrientedBB obb = new OrientedBB(localBB);
|
||||||
if (!contraptionRotation.equals(Vec3d.ZERO)) {
|
if (!axisAlignedCollision) {
|
||||||
Matrix3d rotation = new Matrix3d().asIdentity();
|
Matrix3d rotation = new Matrix3d().asIdentity();
|
||||||
rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(-conRotX)));
|
rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(-conRotX)));
|
||||||
rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(conRotY)));
|
rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(conRotY)));
|
||||||
|
@ -171,32 +178,87 @@ public class ContraptionCollider {
|
||||||
obb.setRotation(rotation);
|
obb.setRotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vec3d visualizerOrigin = new Vec3d(10, 64, 0);
|
||||||
|
// CollisionDebugger.OBB = obb.copy();
|
||||||
|
// CollisionDebugger.OBB.move(visualizerOrigin);
|
||||||
|
|
||||||
MutableObject<Vec3d> collisionResponse = new MutableObject<>(Vec3d.ZERO);
|
MutableObject<Vec3d> collisionResponse = new MutableObject<>(Vec3d.ZERO);
|
||||||
|
MutableObject<Vec3d> allowedMotion = new MutableObject<>(motion);
|
||||||
|
MutableBoolean futureCollision = new MutableBoolean(false);
|
||||||
|
MutableBoolean surfaceCollision = new MutableBoolean(false);
|
||||||
Vec3d obbCenter = obb.getCenter();
|
Vec3d obbCenter = obb.getCenter();
|
||||||
|
|
||||||
|
// Apply separation maths
|
||||||
|
List<AxisAlignedBB> bbs = new ArrayList<>();
|
||||||
potentialHits.createStream()
|
potentialHits.createStream()
|
||||||
.forEach(shape -> {
|
.forEach(shape -> shape.toBoundingBoxList()
|
||||||
Vec3d currentResponse = collisionResponse.getValue();
|
.forEach(bbs::add));
|
||||||
shape.toBoundingBoxList()
|
|
||||||
.parallelStream()
|
|
||||||
.forEach(bb -> {
|
|
||||||
obb.setCenter(obbCenter.add(currentResponse));
|
|
||||||
Vec3d intersect = obb.intersect(bb);
|
|
||||||
if (intersect != null)
|
|
||||||
collisionResponse.setValue(currentResponse.add(intersect));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
for (AxisAlignedBB bb : bbs) {
|
||||||
|
Vec3d currentResponse = collisionResponse.getValue();
|
||||||
|
obb.setCenter(obbCenter.add(currentResponse));
|
||||||
|
ContinuousSeparationManifold intersect = obb.intersect(bb, allowedMotion.getValue());
|
||||||
|
// OutlineParams params = CreateClient.outliner.showAABB(bb, bb.offset(visualizerOrigin))
|
||||||
|
// .withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED);
|
||||||
|
// params.colored(0xffffff);
|
||||||
|
|
||||||
|
if (intersect == null)
|
||||||
|
continue;
|
||||||
|
if (surfaceCollision.isFalse())
|
||||||
|
surfaceCollision.setValue(intersect.isSurfaceCollision());
|
||||||
|
|
||||||
|
double timeOfImpact = intersect.getTimeOfImpact();
|
||||||
|
if (timeOfImpact > 0 && timeOfImpact < 1) {
|
||||||
|
futureCollision.setTrue();
|
||||||
|
// Vec3d prev = allowedMotion.getValue();
|
||||||
|
allowedMotion.setValue(intersect.getAllowedMotion(allowedMotion.getValue()));
|
||||||
|
// Debug.debugChat("Allowed Motion FROM " + prev.toString());
|
||||||
|
// Debug.debugChat("Allowed Motion TO " + allowedMotion.getValue()
|
||||||
|
// .toString());
|
||||||
|
// params.colored(0x4499ff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Vec3d separation = intersect.asSeparationVec();
|
||||||
|
if (separation != null && !separation.equals(Vec3d.ZERO)) {
|
||||||
|
collisionResponse.setValue(currentResponse.add(separation));
|
||||||
|
// Debug.debugChat("Collision " + currentResponse.add(separation)
|
||||||
|
// .toString());
|
||||||
|
// params.colored(0xff9944);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug.debugChat("----");
|
||||||
|
|
||||||
|
// Resolve collision
|
||||||
Vec3d entityMotion = entity.getMotion();
|
Vec3d entityMotion = entity.getMotion();
|
||||||
Vec3d totalResponse = collisionResponse.getValue();
|
Vec3d totalResponse = collisionResponse.getValue();
|
||||||
|
Vec3d motionResponse = allowedMotion.getValue();
|
||||||
|
|
||||||
if (totalResponse == Vec3d.ZERO)
|
if (futureCollision.isTrue() && !serverPlayer) {
|
||||||
|
if (!axisAlignedCollision)
|
||||||
|
motionResponse = VecHelper.rotate(motionResponse, conRotX, conRotY, conRotZ);
|
||||||
|
if (motionResponse.y != entityMotion.y) {
|
||||||
|
entity.setMotion(entityMotion.mul(1, 0, 1)
|
||||||
|
.add(0, motionResponse.y, 0));
|
||||||
|
entityMotion = entity.getMotion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!axisAlignedCollision)
|
||||||
|
totalResponse = VecHelper.rotate(totalResponse, conRotX, conRotY, conRotZ);
|
||||||
|
|
||||||
|
if (surfaceCollision.isTrue()) {
|
||||||
|
// entity.handleFallDamage(entity.fallDistance, 1); tunnelling issue
|
||||||
|
entity.fallDistance = 0;
|
||||||
|
entity.onGround = true;
|
||||||
|
if (!serverPlayer) {
|
||||||
|
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> checkForClientPlayerCollision(entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalResponse.equals(Vec3d.ZERO))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
totalResponse = VecHelper.rotate(totalResponse, conRotX, Axis.X);
|
|
||||||
totalResponse = VecHelper.rotate(totalResponse, conRotY, Axis.Y);
|
|
||||||
totalResponse = VecHelper.rotate(totalResponse, conRotZ, Axis.Z);
|
|
||||||
|
|
||||||
double motionX = entityMotion.getX();
|
double motionX = entityMotion.getX();
|
||||||
double motionY = entityMotion.getY();
|
double motionY = entityMotion.getY();
|
||||||
double motionZ = entityMotion.getZ();
|
double motionZ = entityMotion.getZ();
|
||||||
|
@ -205,7 +267,6 @@ public class ContraptionCollider {
|
||||||
double intersectZ = totalResponse.getZ();
|
double intersectZ = totalResponse.getZ();
|
||||||
|
|
||||||
double horizonalEpsilon = 1 / 128f;
|
double horizonalEpsilon = 1 / 128f;
|
||||||
|
|
||||||
if (motionX != 0 && Math.abs(intersectX) > horizonalEpsilon && motionX > 0 == intersectX < 0)
|
if (motionX != 0 && Math.abs(intersectX) > horizonalEpsilon && motionX > 0 == intersectX < 0)
|
||||||
entityMotion = entityMotion.mul(0, 1, 1);
|
entityMotion = entityMotion.mul(0, 1, 1);
|
||||||
if (motionY != 0 && intersectY != 0 && motionY > 0 == intersectY < 0)
|
if (motionY != 0 && intersectY != 0 && motionY > 0 == intersectY < 0)
|
||||||
|
@ -213,28 +274,62 @@ public class ContraptionCollider {
|
||||||
if (motionZ != 0 && Math.abs(intersectZ) > horizonalEpsilon && motionZ > 0 == intersectZ < 0)
|
if (motionZ != 0 && Math.abs(intersectZ) > horizonalEpsilon && motionZ > 0 == intersectZ < 0)
|
||||||
entityMotion = entityMotion.mul(1, 1, 0);
|
entityMotion = entityMotion.mul(1, 1, 0);
|
||||||
|
|
||||||
entityMotion = entityMotion.add(totalResponse.mul(1, 0, 1));
|
|
||||||
contraptionEntity.collidingEntities.add(entity);
|
|
||||||
entity.velocityChanged = true;
|
|
||||||
|
|
||||||
if (totalResponse.y > 0) {
|
|
||||||
entity.handleFallDamage(entity.fallDistance, 1);
|
|
||||||
entity.fallDistance = 0;
|
|
||||||
entity.onGround = true;
|
|
||||||
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> checkForClientPlayerCollision(entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity instanceof ServerPlayerEntity)
|
if (entity instanceof ServerPlayerEntity)
|
||||||
((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
|
((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
|
||||||
|
|
||||||
entity.setMotion(entityMotion);
|
if (!serverPlayer) {
|
||||||
Vec3d epos = entityPosition;
|
Vec3d allowedMovement = getAllowedMovement(totalResponse, entity);
|
||||||
entity.setPosition(epos.x, epos.y + totalResponse.y, epos.z);
|
contraptionEntity.collidingEntities.add(entity);
|
||||||
entityData.put(nbtMotionKey, VecHelper.writeNBT(totalResponse));
|
entity.velocityChanged = true;
|
||||||
|
entity.setPosition(entityPosition.x + allowedMovement.x, entityPosition.y + allowedMovement.y,
|
||||||
|
entityPosition.z + allowedMovement.z);
|
||||||
|
entity.setMotion(entityMotion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** From Entity#getAllowedMovement **/
|
||||||
|
static Vec3d getAllowedMovement(Vec3d movement, Entity e) {
|
||||||
|
AxisAlignedBB bb = e.getBoundingBox();
|
||||||
|
ISelectionContext ctx = ISelectionContext.forEntity(e);
|
||||||
|
World world = e.world;
|
||||||
|
VoxelShape voxelshape = world.getWorldBorder()
|
||||||
|
.getShape();
|
||||||
|
Stream<VoxelShape> stream =
|
||||||
|
VoxelShapes.compare(voxelshape, VoxelShapes.create(bb.shrink(1.0E-7D)), IBooleanFunction.AND)
|
||||||
|
? Stream.empty()
|
||||||
|
: Stream.of(voxelshape);
|
||||||
|
Stream<VoxelShape> stream1 = world.getEmptyCollisionShapes(e, bb.expand(movement), ImmutableSet.of());
|
||||||
|
ReuseableStream<VoxelShape> reuseablestream = new ReuseableStream<>(Stream.concat(stream1, stream));
|
||||||
|
Vec3d vec3d = movement.lengthSquared() == 0.0D ? movement
|
||||||
|
: collideBoundingBoxHeuristically(e, movement, bb, world, ctx, reuseablestream);
|
||||||
|
boolean flag = movement.x != vec3d.x;
|
||||||
|
boolean flag1 = movement.y != vec3d.y;
|
||||||
|
boolean flag2 = movement.z != vec3d.z;
|
||||||
|
boolean flag3 = e.onGround || flag1 && movement.y < 0.0D;
|
||||||
|
if (e.stepHeight > 0.0F && flag3 && (flag || flag2)) {
|
||||||
|
Vec3d vec3d1 = collideBoundingBoxHeuristically(e, new Vec3d(movement.x, (double) e.stepHeight, movement.z),
|
||||||
|
bb, world, ctx, reuseablestream);
|
||||||
|
Vec3d vec3d2 = collideBoundingBoxHeuristically(e, new Vec3d(0.0D, (double) e.stepHeight, 0.0D),
|
||||||
|
bb.expand(movement.x, 0.0D, movement.z), world, ctx, reuseablestream);
|
||||||
|
if (vec3d2.y < (double) e.stepHeight) {
|
||||||
|
Vec3d vec3d3 = collideBoundingBoxHeuristically(e, new Vec3d(movement.x, 0.0D, movement.z),
|
||||||
|
bb.offset(vec3d2), world, ctx, reuseablestream).add(vec3d2);
|
||||||
|
if (horizontalMag(vec3d3) > horizontalMag(vec3d1)) {
|
||||||
|
vec3d1 = vec3d3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizontalMag(vec3d1) > horizontalMag(vec3d)) {
|
||||||
|
return vec3d1.add(collideBoundingBoxHeuristically(e, new Vec3d(0.0D, -vec3d1.y + movement.y, 0.0D),
|
||||||
|
bb.offset(vec3d1), world, ctx, reuseablestream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec3d;
|
||||||
|
}
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
private static void checkForClientPlayerCollision(Entity entity) {
|
private static void checkForClientPlayerCollision(Entity entity) {
|
||||||
if (entity != Minecraft.getInstance().player)
|
if (entity != Minecraft.getInstance().player)
|
||||||
|
@ -310,7 +405,7 @@ public class ContraptionCollider {
|
||||||
double width = localBB.getXSize();
|
double width = localBB.getXSize();
|
||||||
double horizontalFactor = (height > width && width != 0) ? height / width : 1;
|
double horizontalFactor = (height > width && width != 0) ? height / width : 1;
|
||||||
double verticalFactor = (width > height && height != 0) ? width / height : 1;
|
double verticalFactor = (width > height && height != 0) ? width / height : 1;
|
||||||
AxisAlignedBB blockScanBB = localBB.grow(.5f);
|
AxisAlignedBB blockScanBB = localBB.grow(0.5f);
|
||||||
blockScanBB = blockScanBB.grow(horizontalFactor, verticalFactor, horizontalFactor);
|
blockScanBB = blockScanBB.grow(horizontalFactor, verticalFactor, horizontalFactor);
|
||||||
|
|
||||||
BlockPos min = new BlockPos(blockScanBB.minX, blockScanBB.minY, blockScanBB.minZ);
|
BlockPos min = new BlockPos(blockScanBB.minX, blockScanBB.minY, blockScanBB.minZ);
|
||||||
|
|
|
@ -3,30 +3,35 @@ package com.simibubi.create.foundation.collision;
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||||
import com.simibubi.create.AllSpecialTextures;
|
import com.simibubi.create.AllSpecialTextures;
|
||||||
import com.simibubi.create.CreateClient;
|
import com.simibubi.create.CreateClient;
|
||||||
|
import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold;
|
||||||
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
|
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
|
||||||
|
import com.simibubi.create.foundation.utility.AngleHelper;
|
||||||
import com.simibubi.create.foundation.utility.MatrixStacker;
|
import com.simibubi.create.foundation.utility.MatrixStacker;
|
||||||
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
|
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.util.math.AxisAlignedBB;
|
import net.minecraft.util.math.AxisAlignedBB;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.BlockRayTraceResult;
|
||||||
|
import net.minecraft.util.math.RayTraceResult;
|
||||||
|
import net.minecraft.util.math.RayTraceResult.Type;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
public class CollisionDebugger {
|
public class CollisionDebugger {
|
||||||
|
|
||||||
public static AxisAlignedBB AABB = null;
|
public static AxisAlignedBB AABB = new AxisAlignedBB(BlockPos.ZERO.up(10));
|
||||||
public static OrientedBB OBB = null;
|
public static OrientedBB OBB = new OrientedBB(new AxisAlignedBB(BlockPos.ZERO));
|
||||||
static Vec3d seperation;
|
public static Vec3d motion = Vec3d.ZERO;
|
||||||
|
static ContinuousSeparationManifold seperation;
|
||||||
static double angle = 0;
|
static double angle = 0;
|
||||||
static AABBOutline outline;
|
static AABBOutline outline;
|
||||||
|
|
||||||
public static void onScroll(double delta) {
|
public static void onScroll(double delta) {
|
||||||
// angle += delta;
|
angle += delta;
|
||||||
// movingBB = new OrientedBB(new AxisAlignedBB(BlockPos.ZERO).expand(0, 1, 0));
|
OBB.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle)));
|
||||||
// movingBB.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
||||||
if (OBB == null)
|
|
||||||
return;
|
|
||||||
ms.push();
|
ms.push();
|
||||||
outline = new AABBOutline(OBB.getAsAxisAlignedBB());
|
outline = new AABBOutline(OBB.getAsAxisAlignedBB());
|
||||||
outline.getParams()
|
outline.getParams()
|
||||||
|
@ -47,12 +52,12 @@ public class CollisionDebugger {
|
||||||
ms.pop();
|
ms.pop();
|
||||||
|
|
||||||
ms.push();
|
ms.push();
|
||||||
if (seperation != null) {
|
if (motion.length() != 0 && (seperation == null || seperation.getTimeOfImpact() != 1)) {
|
||||||
outline.getParams()
|
outline.getParams()
|
||||||
.colored(0x65ff44)
|
.colored(0x6544ff)
|
||||||
.lineWidth(1 / 32f);
|
.lineWidth(1 / 32f);
|
||||||
MatrixStacker.of(ms)
|
MatrixStacker.of(ms)
|
||||||
.translate(seperation)
|
.translate(seperation != null ? seperation.getAllowedMotion(motion) : motion)
|
||||||
.translate(OBB.center);
|
.translate(OBB.center);
|
||||||
ms.peek()
|
ms.peek()
|
||||||
.getModel()
|
.getModel()
|
||||||
|
@ -62,16 +67,47 @@ public class CollisionDebugger {
|
||||||
outline.render(ms, buffer);
|
outline.render(ms, buffer);
|
||||||
}
|
}
|
||||||
ms.pop();
|
ms.pop();
|
||||||
|
|
||||||
|
ms.push();
|
||||||
|
if (seperation != null) {
|
||||||
|
Vec3d asSeparationVec = seperation.asSeparationVec();
|
||||||
|
if (asSeparationVec != null) {
|
||||||
|
outline.getParams()
|
||||||
|
.colored(0x65ff44)
|
||||||
|
.lineWidth(1 / 32f);
|
||||||
|
MatrixStacker.of(ms)
|
||||||
|
.translate(asSeparationVec)
|
||||||
|
.translate(OBB.center);
|
||||||
|
ms.peek()
|
||||||
|
.getModel()
|
||||||
|
.multiply(OBB.rotation.getAsMatrix4f());
|
||||||
|
MatrixStacker.of(ms)
|
||||||
|
.translateBack(OBB.center);
|
||||||
|
outline.render(ms, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ms.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tick() {
|
public static void tick() {
|
||||||
if (OBB == null)
|
AABB = new AxisAlignedBB(BlockPos.ZERO.up(60)).offset(.5, 0, .5);
|
||||||
return;
|
motion = new Vec3d(0, -2, -.5f);
|
||||||
if (AABB == null)
|
RayTraceResult mouse = Minecraft.getInstance().objectMouseOver;
|
||||||
return;
|
if (mouse != null && mouse.getType() == Type.BLOCK) {
|
||||||
seperation = OBB.intersect(AABB);
|
BlockRayTraceResult hit = (BlockRayTraceResult) mouse;
|
||||||
|
OBB.setCenter(hit.getHitVec());
|
||||||
|
seperation = OBB.intersect(AABB, motion);
|
||||||
|
}
|
||||||
CreateClient.outliner.showAABB(AABB, AABB)
|
CreateClient.outliner.showAABB(AABB, AABB)
|
||||||
.withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null);
|
.withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void showDebugLine(Vec3d relativeStart, Vec3d relativeEnd, int color, String id, int offset) {
|
||||||
|
Vec3d center = CollisionDebugger.AABB.getCenter()
|
||||||
|
.add(0, 1 + offset / 16f, 0);
|
||||||
|
CreateClient.outliner.showLine(id + OBBCollider.checkCount, center.add(relativeStart), center.add(relativeEnd))
|
||||||
|
.colored(color)
|
||||||
|
.lineWidth(1 / 32f);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
package com.simibubi.create.foundation.collision;
|
||||||
|
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
import static java.lang.Math.signum;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
|
public class ContinuousOBBCollider extends OBBCollider {
|
||||||
|
|
||||||
|
public static ContinuousSeparationManifold separateBBs(Vec3d cA, Vec3d cB, Vec3d eA, Vec3d eB, Matrix3d m,
|
||||||
|
Vec3d motion) {
|
||||||
|
ContinuousSeparationManifold mf = new ContinuousSeparationManifold();
|
||||||
|
|
||||||
|
Vec3d diff = cB.subtract(cA);
|
||||||
|
|
||||||
|
m.transpose();
|
||||||
|
Vec3d diff2 = m.transform(diff);
|
||||||
|
Vec3d motion2 = m.transform(motion);
|
||||||
|
m.transpose();
|
||||||
|
|
||||||
|
double a00 = abs(m.m00);
|
||||||
|
double a01 = abs(m.m01);
|
||||||
|
double a02 = abs(m.m02);
|
||||||
|
double a10 = abs(m.m10);
|
||||||
|
double a11 = abs(m.m11);
|
||||||
|
double a12 = abs(m.m12);
|
||||||
|
double a20 = abs(m.m20);
|
||||||
|
double a21 = abs(m.m21);
|
||||||
|
double a22 = abs(m.m22);
|
||||||
|
|
||||||
|
Vec3d uB0 = new Vec3d(m.m00, m.m10, m.m20);
|
||||||
|
Vec3d uB1 = new Vec3d(m.m01, m.m11, m.m21);
|
||||||
|
Vec3d uB2 = new Vec3d(m.m02, m.m12, m.m22);
|
||||||
|
|
||||||
|
checkCount = 0;
|
||||||
|
|
||||||
|
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 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)))
|
||||||
|
return mf;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean separate(ContinuousSeparationManifold mf, Vec3d axis, double TL, double rA, double rB,
|
||||||
|
double projectedMotion) {
|
||||||
|
checkCount++;
|
||||||
|
double distance = abs(TL);
|
||||||
|
double diff = distance - (rA + rB);
|
||||||
|
|
||||||
|
boolean discreteCollision = diff <= 0;
|
||||||
|
if (!discreteCollision && signum(projectedMotion) == signum(TL))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
double sTL = signum(TL);
|
||||||
|
double value = sTL * abs(diff);
|
||||||
|
|
||||||
|
double entryTime = 0;
|
||||||
|
double exitTime = Double.MAX_VALUE;
|
||||||
|
if (!discreteCollision) {
|
||||||
|
mf.isDiscreteCollision = false;
|
||||||
|
|
||||||
|
if (abs(value) > abs(projectedMotion))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
entryTime = abs(value) / abs(projectedMotion);
|
||||||
|
exitTime = (diff + abs(rA) + abs(rB)) / abs(projectedMotion);
|
||||||
|
mf.latestCollisionEntryTime = Math.max(entryTime, mf.latestCollisionEntryTime);
|
||||||
|
mf.earliestCollisionExitTime = Math.min(exitTime, mf.earliestCollisionExitTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isBestSeperation = distance != 0 && -(diff) <= abs(mf.separation);
|
||||||
|
// boolean isBestSeperation = discreteCollision && checkCount == 5; // Debug specific separations
|
||||||
|
|
||||||
|
if (isBestSeperation) {
|
||||||
|
|
||||||
|
mf.axis = axis.normalize();
|
||||||
|
mf.separation = value;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ContinuousSeparationManifold extends SeparationManifold {
|
||||||
|
|
||||||
|
static final double UNDEFINED = -1;
|
||||||
|
double latestCollisionEntryTime = UNDEFINED;
|
||||||
|
double earliestCollisionExitTime = Double.MAX_VALUE;
|
||||||
|
boolean isDiscreteCollision = true;
|
||||||
|
|
||||||
|
public double getTimeOfImpact() {
|
||||||
|
if (latestCollisionEntryTime == UNDEFINED)
|
||||||
|
return UNDEFINED;
|
||||||
|
if (latestCollisionEntryTime > earliestCollisionExitTime)
|
||||||
|
return UNDEFINED;
|
||||||
|
return latestCollisionEntryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSurfaceCollision() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3d getAllowedMotion(Vec3d motion) {
|
||||||
|
double length = motion.length();
|
||||||
|
return motion.normalize()
|
||||||
|
.scale(getTimeOfImpact() * length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3d asSeparationVec() {
|
||||||
|
if (isDiscreteCollision)
|
||||||
|
return super.asSeparationVec();
|
||||||
|
double t = getTimeOfImpact();
|
||||||
|
if (t == UNDEFINED)
|
||||||
|
return null;
|
||||||
|
return Vec3d.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,6 +20,9 @@ public class Matrix3d {
|
||||||
|
|
||||||
public Matrix3d asXRotation(float radians) {
|
public Matrix3d asXRotation(float radians) {
|
||||||
asIdentity();
|
asIdentity();
|
||||||
|
if (radians == 0)
|
||||||
|
return this;
|
||||||
|
|
||||||
double s = MathHelper.sin(radians);
|
double s = MathHelper.sin(radians);
|
||||||
double c = MathHelper.cos(radians);
|
double c = MathHelper.cos(radians);
|
||||||
m22 = m11 = c;
|
m22 = m11 = c;
|
||||||
|
@ -30,6 +33,9 @@ public class Matrix3d {
|
||||||
|
|
||||||
public Matrix3d asYRotation(float radians) {
|
public Matrix3d asYRotation(float radians) {
|
||||||
asIdentity();
|
asIdentity();
|
||||||
|
if (radians == 0)
|
||||||
|
return this;
|
||||||
|
|
||||||
double s = MathHelper.sin(radians);
|
double s = MathHelper.sin(radians);
|
||||||
double c = MathHelper.cos(radians);
|
double c = MathHelper.cos(radians);
|
||||||
m00 = m22 = c;
|
m00 = m22 = c;
|
||||||
|
@ -40,6 +46,9 @@ public class Matrix3d {
|
||||||
|
|
||||||
public Matrix3d asZRotation(float radians) {
|
public Matrix3d asZRotation(float radians) {
|
||||||
asIdentity();
|
asIdentity();
|
||||||
|
if (radians == 0)
|
||||||
|
return this;
|
||||||
|
|
||||||
double s = MathHelper.sin(radians);
|
double s = MathHelper.sin(radians);
|
||||||
double c = MathHelper.cos(radians);
|
double c = MathHelper.cos(radians);
|
||||||
m00 = m11 = c;
|
m00 = m11 = c;
|
||||||
|
@ -104,12 +113,9 @@ public class Matrix3d {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3d transform(Vec3d vec) {
|
public Vec3d transform(Vec3d vec) {
|
||||||
double x = vec.x;
|
double x = vec.x * m00 + vec.y * m01 + vec.z * m02;
|
||||||
double y = vec.y;
|
double y = vec.x * m10 + vec.y * m11 + vec.z * m12;
|
||||||
double z = vec.z;
|
double z = vec.x * m20 + vec.y * m21 + vec.z * m22;
|
||||||
x = x * m00 + y * m01 + z * m02;
|
|
||||||
y = x * m10 + y * m11 + z * m12;
|
|
||||||
z = x * m20 + y * m21 + z * m22;
|
|
||||||
return new Vec3d(x, y, z);
|
return new Vec3d(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
package com.simibubi.create.foundation.collision;
|
||||||
|
|
||||||
|
import static com.simibubi.create.foundation.collision.CollisionDebugger.showDebugLine;
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
import static java.lang.Math.signum;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
|
public class OBBCollider {
|
||||||
|
|
||||||
|
static final Vec3d uA0 = new Vec3d(1, 0, 0);
|
||||||
|
static final Vec3d uA1 = new Vec3d(0, 1, 0);
|
||||||
|
static final Vec3d uA2 = new Vec3d(0, 0, 1);
|
||||||
|
|
||||||
|
public static Vec3d separateBBs(Vec3d cA, Vec3d cB, Vec3d eA, Vec3d eB, Matrix3d m) {
|
||||||
|
SeparationManifold mf = new SeparationManifold();
|
||||||
|
|
||||||
|
Vec3d t = cB.subtract(cA);
|
||||||
|
|
||||||
|
double a00 = abs(m.m00);
|
||||||
|
double a01 = abs(m.m01);
|
||||||
|
double a02 = abs(m.m02);
|
||||||
|
double a10 = abs(m.m10);
|
||||||
|
double a11 = abs(m.m11);
|
||||||
|
double a12 = abs(m.m12);
|
||||||
|
double a20 = abs(m.m20);
|
||||||
|
double a21 = abs(m.m21);
|
||||||
|
double a22 = abs(m.m22);
|
||||||
|
|
||||||
|
Vec3d uB0 = new Vec3d(m.m00, m.m10, m.m20);
|
||||||
|
Vec3d uB1 = new Vec3d(m.m01, m.m11, m.m21);
|
||||||
|
Vec3d uB2 = new Vec3d(m.m02, m.m12, m.m22);
|
||||||
|
|
||||||
|
checkCount = 0;
|
||||||
|
|
||||||
|
if (
|
||||||
|
// Separate along A's local axes (global XYZ)
|
||||||
|
!(isSeparatedAlong(mf, uA0, t.x, eA.x, a00 * eB.x + a01 * eB.y + a02 * eB.z)
|
||||||
|
|| isSeparatedAlong(mf, uA1, t.y, eA.y, a10 * eB.x + a11 * eB.y + a12 * eB.z)
|
||||||
|
|| isSeparatedAlong(mf, uA2, t.z, eA.z, a20 * eB.x + a21 * eB.y + a22 * eB.z)
|
||||||
|
|
||||||
|
// Separate along B's local axes
|
||||||
|
|| isSeparatedAlong(mf, uB0, (t.x * m.m00 + t.y * m.m10 + t.z * m.m20),
|
||||||
|
eA.x * a00 + eA.y * a10 + eA.z * a20, eB.x)
|
||||||
|
|| isSeparatedAlong(mf, uB1, (t.x * m.m01 + t.y * m.m11 + t.z * m.m21),
|
||||||
|
eA.x * a01 + eA.y * a11 + eA.z * a21, eB.y)
|
||||||
|
|| isSeparatedAlong(mf, uB2, (t.x * m.m02 + t.y * m.m12 + t.z * m.m22),
|
||||||
|
eA.x * a02 + eA.y * a12 + eA.z * a22, eB.z)))
|
||||||
|
return mf.asSeparationVec();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int checkCount = 0;
|
||||||
|
|
||||||
|
static boolean isSeparatedAlong(SeparationManifold mf, Vec3d axis, double TL, double rA, double rB) {
|
||||||
|
checkCount++;
|
||||||
|
double distance = abs(TL);
|
||||||
|
double diff = distance - (rA + rB);
|
||||||
|
if (diff > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// boolean isBestSeperation = distance != 0 && -(diff) <= abs(bestSeparation.getValue());
|
||||||
|
boolean isBestSeperation = checkCount == 2; // Debug specific separations
|
||||||
|
|
||||||
|
if (isBestSeperation) {
|
||||||
|
double sTL = signum(TL);
|
||||||
|
double value = sTL * abs(diff);
|
||||||
|
mf.axis = axis.normalize();
|
||||||
|
mf.separation = value;
|
||||||
|
|
||||||
|
// Visualize values
|
||||||
|
if (CollisionDebugger.AABB != null) {
|
||||||
|
Vec3d normalizedAxis = axis.normalize();
|
||||||
|
showDebugLine(Vec3d.ZERO, normalizedAxis.scale(TL), 0xbb00bb, "tl", 4);
|
||||||
|
showDebugLine(Vec3d.ZERO, normalizedAxis.scale(sTL * rA), 0xff4444, "ra", 3);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SeparationManifold {
|
||||||
|
Vec3d axis;
|
||||||
|
double separation;
|
||||||
|
|
||||||
|
public SeparationManifold() {
|
||||||
|
axis = Vec3d.ZERO;
|
||||||
|
separation = Double.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3d asSeparationVec() {
|
||||||
|
double sep = separation;
|
||||||
|
return axis.normalize()
|
||||||
|
.scale(signum(sep) * (abs(sep) + 1E-4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,6 @@
|
||||||
package com.simibubi.create.foundation.collision;
|
package com.simibubi.create.foundation.collision;
|
||||||
|
|
||||||
import static java.lang.Math.abs;
|
import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold;
|
||||||
|
|
||||||
import org.apache.commons.lang3.mutable.MutableDouble;
|
|
||||||
import org.apache.commons.lang3.mutable.MutableObject;
|
|
||||||
|
|
||||||
import com.simibubi.create.CreateClient;
|
|
||||||
|
|
||||||
import net.minecraft.util.math.AxisAlignedBB;
|
import net.minecraft.util.math.AxisAlignedBB;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
@ -29,140 +24,26 @@ public class OrientedBB {
|
||||||
this.extents = extents;
|
this.extents = extents;
|
||||||
this.setRotation(rotation);
|
this.setRotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrientedBB copy() {
|
public OrientedBB copy() {
|
||||||
return new OrientedBB(center, extents, rotation);
|
return new OrientedBB(center, extents, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3d intersect(AxisAlignedBB bb) {
|
public Vec3d intersect(AxisAlignedBB bb) {
|
||||||
Vec3d extentsA = extentsFromBB(bb);
|
Vec3d extentsA = extentsFromBB(bb);
|
||||||
Vec3d intersects = separateBBs(bb.getCenter(), center, extentsA, extents, rotation);
|
Vec3d intersects = OBBCollider.separateBBs(bb.getCenter(), center, extentsA, extents, rotation);
|
||||||
return intersects;
|
return intersects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ContinuousSeparationManifold intersect(AxisAlignedBB bb, Vec3d motion) {
|
||||||
|
Vec3d extentsA = extentsFromBB(bb);
|
||||||
|
return ContinuousOBBCollider.separateBBs(bb.getCenter(), center, extentsA, extents, rotation, motion);
|
||||||
|
}
|
||||||
|
|
||||||
private static Vec3d extentsFromBB(AxisAlignedBB bb) {
|
private static Vec3d extentsFromBB(AxisAlignedBB bb) {
|
||||||
return new Vec3d(bb.getXSize() / 2, bb.getYSize() / 2, bb.getZSize() / 2);
|
return new Vec3d(bb.getXSize() / 2, bb.getYSize() / 2, bb.getZSize() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vec3d separateBBs(Vec3d cA, Vec3d cB, Vec3d eA, Vec3d eB, Matrix3d m) {
|
|
||||||
Vec3d t = cB.subtract(cA);
|
|
||||||
double a00 = abs(m.m00);
|
|
||||||
double a01 = abs(m.m01);
|
|
||||||
double a02 = abs(m.m02);
|
|
||||||
double a10 = abs(m.m10);
|
|
||||||
double a11 = abs(m.m11);
|
|
||||||
double a12 = abs(m.m12);
|
|
||||||
double a20 = abs(m.m20);
|
|
||||||
double a21 = abs(m.m21);
|
|
||||||
double a22 = abs(m.m22);
|
|
||||||
|
|
||||||
MutableObject<Vec3d> bestAxis = new MutableObject<>(Vec3d.ZERO);
|
|
||||||
MutableDouble bestSep = new MutableDouble(Double.MAX_VALUE);
|
|
||||||
|
|
||||||
Vec3d uA0 = new Vec3d(1, 0, 0);
|
|
||||||
Vec3d uA1 = new Vec3d(0, 1, 0);
|
|
||||||
Vec3d uA2 = new Vec3d(0, 0, 1);
|
|
||||||
|
|
||||||
Vec3d uB0 = new Vec3d(m.m00, m.m10, m.m20);
|
|
||||||
Vec3d uB1 = new Vec3d(m.m01, m.m11, m.m21);
|
|
||||||
Vec3d uB2 = new Vec3d(m.m02, m.m12, m.m22);
|
|
||||||
|
|
||||||
checkCount = 0;
|
|
||||||
|
|
||||||
if (
|
|
||||||
|
|
||||||
// Separate along A's local axes (global XYZ)
|
|
||||||
!(isSeparatedAlong(bestAxis, bestSep, uA0, t.x, eA.x, a00 * eB.x + a01 * eB.y + a02 * eB.z)
|
|
||||||
|| isSeparatedAlong(bestAxis, bestSep, uA1, t.y, eA.y, a10 * eB.x + a11 * eB.y + a12 * eB.z)
|
|
||||||
|| isSeparatedAlong(bestAxis, bestSep, uA2, t.z, eA.z, a20 * eB.x + a21 * eB.y + a22 * eB.z)
|
|
||||||
|
|
||||||
// Separate along B's local axes
|
|
||||||
|| isSeparatedAlong(bestAxis, bestSep, uB0, (t.x * m.m00 + t.y * m.m10 + t.z * m.m20),
|
|
||||||
eA.x * a00 + eA.y * a10 + eA.z * a20, eB.x)
|
|
||||||
|| isSeparatedAlong(bestAxis, bestSep, uB1, (t.x * m.m01 + t.y * m.m11 + t.z * m.m21),
|
|
||||||
eA.x * a01 + eA.y * a11 + eA.z * a21, eB.y)
|
|
||||||
|| isSeparatedAlong(bestAxis, bestSep, uB2, (t.x * m.m02 + t.y * m.m12 + t.z * m.m22),
|
|
||||||
eA.x * a02 + eA.y * a12 + eA.z * a22, eB.z)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following checks (edge-to-edge) need special separation logic. They are
|
|
||||||
* not necessary as long as the obb is only rotated around one axis at a time
|
|
||||||
* (Which is the case for contraptions at the moment)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Separate along axes perpendicular to AxB
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA0.crossProduct(uB0), t.z * m.m10 - t.y * m.m20,
|
|
||||||
// eA.y * a20 + eA.z * a10, eB.y * a02 + eB.z * a01)
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA0.crossProduct(uB1), t.z * m.m11 - t.y * m.m21,
|
|
||||||
// eA.y * a21 + eA.z * a11, eB.x * a02 + eB.z * a00)
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA0.crossProduct(uB2), t.z * m.m12 - t.y * m.m22,
|
|
||||||
// eA.y * a22 + eA.z * a12, eB.x * a01 + eB.y * a00)
|
|
||||||
//
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA1.crossProduct(uB0), t.x * m.m20 - t.z * m.m00,
|
|
||||||
// eA.x * a20 + eA.z * a00, eB.y * a12 + eB.z * a11)
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA1.crossProduct(uB1), t.x * m.m21 - t.z * m.m01,
|
|
||||||
// eA.x * a21 + eA.z * a01, eB.x * a12 + eB.z * a10)
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA1.crossProduct(uB2), t.x * m.m22 - t.z * m.m02,
|
|
||||||
// eA.x * a22 + eA.z * a02, eB.x * a11 + eB.y * a10)
|
|
||||||
//
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA2.crossProduct(uB0), t.y * m.m00 - t.x * m.m10,
|
|
||||||
// eA.x * a10 + eA.y * a00, eB.y * a22 + eB.z * a21)
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA2.crossProduct(uB1), t.y * m.m01 - t.x * m.m11,
|
|
||||||
// eA.x * a11 + eA.y * a01, eB.x * a22 + eB.z * a20)
|
|
||||||
// || isSeparatedAlong(bestAxis, bestSep, uA2.crossProduct(uB2), t.y * m.m02 - t.x * m.m12,
|
|
||||||
// eA.x * a12 + eA.y * a02, eB.x * a21 + eB.y * a20)
|
|
||||||
))
|
|
||||||
|
|
||||||
return bestAxis.getValue()
|
|
||||||
.normalize()
|
|
||||||
.scale(bestSep.getValue());
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int checkCount = 0;
|
|
||||||
|
|
||||||
static boolean isSeparatedAlong(MutableObject<Vec3d> bestAxis, MutableDouble bestSeparation, Vec3d axis, double TL,
|
|
||||||
double rA, double rB) {
|
|
||||||
double distance = abs(TL);
|
|
||||||
|
|
||||||
checkCount++;
|
|
||||||
|
|
||||||
double diff = distance - (rA + rB);
|
|
||||||
if (diff > 0)
|
|
||||||
return true;
|
|
||||||
boolean isBestSeperation = distance != 0 && -(diff) <= abs(bestSeparation.getValue());
|
|
||||||
// boolean isBestSeperation = checkCount == 12; // Debug specific separations
|
|
||||||
if (isBestSeperation) {
|
|
||||||
bestAxis.setValue(axis.normalize());
|
|
||||||
double sTL = Math.signum(TL);
|
|
||||||
double value = sTL * abs(diff);
|
|
||||||
bestSeparation.setValue(value);
|
|
||||||
|
|
||||||
// Visualize values
|
|
||||||
// if (CollisionDebugger.staticBB != null) {
|
|
||||||
// Vec3d normalizedAxis = axis.normalize();
|
|
||||||
// showDebugLine(Vec3d.ZERO, normalizedAxis.scale(TL), 0xbb00bb, "tl", 4);
|
|
||||||
// showDebugLine(Vec3d.ZERO, normalizedAxis.scale(sTL * rA), 0xff4444, "ra", 3);
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showDebugLine(Vec3d relativeStart, Vec3d relativeEnd, int color, String id, int offset) {
|
|
||||||
Vec3d center = CollisionDebugger.AABB.getCenter()
|
|
||||||
.add(0, 1 + offset / 16f, 0);
|
|
||||||
CreateClient.outliner.showLine(id + checkCount, center.add(relativeStart), center.add(relativeEnd))
|
|
||||||
.colored(color)
|
|
||||||
.lineWidth(1 / 32f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Matrix3d getRotation() {
|
public Matrix3d getRotation() {
|
||||||
return rotation;
|
return rotation;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +59,7 @@ public class OrientedBB {
|
||||||
public void setCenter(Vec3d center) {
|
public void setCenter(Vec3d center) {
|
||||||
this.center = center;
|
this.center = center;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(Vec3d offset) {
|
public void move(Vec3d offset) {
|
||||||
setCenter(getCenter().add(offset));
|
setCenter(getCenter().add(offset));
|
||||||
}
|
}
|
||||||
|
@ -188,4 +69,33 @@ public class OrientedBB {
|
||||||
.grow(extents.x, extents.y, extents.z);
|
.grow(extents.x, extents.y, extents.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following checks (edge-to-edge) need special separation logic. They are
|
||||||
|
* not necessary as long as the obb is only rotated around one axis at a time
|
||||||
|
* (Which is the case for contraptions at the moment)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Separate along axes perpendicular to AxB
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA0.crossProduct(uB0), t.z * m.m10 - t.y * m.m20,
|
||||||
|
// eA.y * a20 + eA.z * a10, eB.y * a02 + eB.z * a01)
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA0.crossProduct(uB1), t.z * m.m11 - t.y * m.m21,
|
||||||
|
// eA.y * a21 + eA.z * a11, eB.x * a02 + eB.z * a00)
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA0.crossProduct(uB2), t.z * m.m12 - t.y * m.m22,
|
||||||
|
// eA.y * a22 + eA.z * a12, eB.x * a01 + eB.y * a00)
|
||||||
|
//
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA1.crossProduct(uB0), t.x * m.m20 - t.z * m.m00,
|
||||||
|
// eA.x * a20 + eA.z * a00, eB.y * a12 + eB.z * a11)
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA1.crossProduct(uB1), t.x * m.m21 - t.z * m.m01,
|
||||||
|
// eA.x * a21 + eA.z * a01, eB.x * a12 + eB.z * a10)
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA1.crossProduct(uB2), t.x * m.m22 - t.z * m.m02,
|
||||||
|
// eA.x * a22 + eA.z * a02, eB.x * a11 + eB.y * a10)
|
||||||
|
//
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA2.crossProduct(uB0), t.y * m.m00 - t.x * m.m10,
|
||||||
|
// eA.x * a10 + eA.y * a00, eB.y * a22 + eB.z * a21)
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA2.crossProduct(uB1), t.y * m.m01 - t.x * m.m11,
|
||||||
|
// eA.x * a11 + eA.y * a01, eB.x * a22 + eB.z * a20)
|
||||||
|
// || isSeparatedAlong(bestAxis, bestSep, uA2.crossProduct(uB2), t.y * m.m02 - t.x * m.m12,
|
||||||
|
// eA.x * a12 + eA.y * a02, eB.x * a21 + eB.y * a20)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,14 @@ public class AngleHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float rad(double angle) {
|
public static float rad(double angle) {
|
||||||
|
if (angle == 0)
|
||||||
|
return 0;
|
||||||
return (float) (angle / 180 * Math.PI);
|
return (float) (angle / 180 * Math.PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float deg(double angle) {
|
public static float deg(double angle) {
|
||||||
|
if (angle == 0)
|
||||||
|
return 0;
|
||||||
return (float) (angle * 180 / Math.PI);
|
return (float) (angle * 180 / Math.PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue