Actually playable
- Fixed some left-over math bugs - Greatly improved contraption-player collision response, especially with rotating structures
This commit is contained in:
parent
1ea7eeb040
commit
c58310b293
5 changed files with 124 additions and 73 deletions
|
@ -6,7 +6,7 @@ org.gradle.daemon=false
|
|||
# mod version info
|
||||
mod_version=0.3
|
||||
minecraft_version=1.15.2
|
||||
forge_version=31.2.21
|
||||
forge_version=31.2.31
|
||||
|
||||
# dependency versions
|
||||
registrate_version=0.0.4.18
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
import org.apache.commons.lang3.mutable.MutableObject;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.cache.Cache;
|
||||
|
@ -29,6 +29,7 @@ import net.minecraft.entity.EntityType;
|
|||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
import net.minecraft.util.Direction.AxisDirection;
|
||||
|
@ -41,6 +42,7 @@ import net.minecraft.world.World;
|
|||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.event.TickEvent.ClientTickEvent;
|
||||
import net.minecraftforge.event.TickEvent.Phase;
|
||||
import net.minecraftforge.event.TickEvent.WorldTickEvent;
|
||||
|
@ -118,7 +120,11 @@ public class ContraptionCollider {
|
|||
if (bounds == null)
|
||||
return;
|
||||
|
||||
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(1),
|
||||
double conRotX = contraptionRotation.z;
|
||||
double conRotY = contraptionRotation.y;
|
||||
double conRotZ = contraptionRotation.x;
|
||||
|
||||
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(2),
|
||||
contraptionEntity::canCollideWith)) {
|
||||
if (entity instanceof PlayerEntity && !world.isRemote)
|
||||
return;
|
||||
|
@ -127,11 +133,13 @@ public class ContraptionCollider {
|
|||
Vec3d entityPosition = entity.getPositionVec();
|
||||
Vec3d centerY = new Vec3d(0, entity.getBoundingBox()
|
||||
.getYSize() / 2, 0);
|
||||
Vec3d position = entityPosition.subtract(contraptionPosition)
|
||||
.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0))
|
||||
.add(centerY);
|
||||
position =
|
||||
VecHelper.rotate(position, -contraptionRotation.z, -contraptionRotation.y, -contraptionRotation.x);
|
||||
|
||||
Vec3d position =
|
||||
entityPosition.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0))
|
||||
.add(centerY);
|
||||
|
||||
position = position.subtract(contraptionPosition);
|
||||
position = VecHelper.rotate(position, -conRotX, -conRotY, -conRotZ);
|
||||
position = position.add(centerOfBlock)
|
||||
.subtract(centerY)
|
||||
.subtract(entityPosition);
|
||||
|
@ -139,6 +147,16 @@ public class ContraptionCollider {
|
|||
.offset(position)
|
||||
.grow(1.0E-7D);
|
||||
|
||||
String nbtMotionKey = "ContraptionCollisionFeedback";
|
||||
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()
|
||||
.count() == 0)
|
||||
|
@ -147,51 +165,72 @@ public class ContraptionCollider {
|
|||
OrientedBB obb = new OrientedBB(localBB);
|
||||
if (!contraptionRotation.equals(Vec3d.ZERO)) {
|
||||
Matrix3d rotation = new Matrix3d().asIdentity();
|
||||
rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(contraptionRotation.z)));
|
||||
rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(contraptionRotation.y)));
|
||||
rotation.multiply(new Matrix3d().asZRotation(AngleHelper.rad(contraptionRotation.x)));
|
||||
rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(-conRotX)));
|
||||
rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(conRotY)));
|
||||
rotation.multiply(new Matrix3d().asZRotation(AngleHelper.rad(-conRotZ)));
|
||||
obb.setRotation(rotation);
|
||||
}
|
||||
|
||||
MutableBoolean onCollide = new MutableBoolean(true);
|
||||
MutableObject<Vec3d> collisionResponse = new MutableObject<>(Vec3d.ZERO);
|
||||
Vec3d obbCenter = obb.getCenter();
|
||||
|
||||
potentialHits.createStream()
|
||||
.forEach(shape -> {
|
||||
AxisAlignedBB bb = shape.getBoundingBox();
|
||||
Vec3d intersect = obb.intersect(bb);
|
||||
if (intersect == null)
|
||||
return;
|
||||
intersect = VecHelper.rotate(intersect, contraptionRotation.z, contraptionRotation.y,
|
||||
contraptionRotation.x);
|
||||
|
||||
obb.setCenter(obb.getCenter()
|
||||
.add(intersect));
|
||||
entity.move(MoverType.PISTON, intersect);
|
||||
|
||||
Vec3d entityMotion = entity.getMotion();
|
||||
if (entityMotion.getX() > 0 == intersect.getX() < 0)
|
||||
entityMotion = entityMotion.mul(0, 1, 1);
|
||||
if (entityMotion.getY() > 0 == intersect.getY() < 0)
|
||||
entityMotion = entityMotion.mul(1, 0, 1);
|
||||
if (entityMotion.getZ() > 0 == intersect.getZ() < 0)
|
||||
entityMotion = entityMotion.mul(1, 1, 0);
|
||||
entity.setMotion(entityMotion);
|
||||
|
||||
if (onCollide.isTrue()) {
|
||||
onCollide.setFalse();
|
||||
contraptionEntity.collidingEntities.add(entity);
|
||||
entity.velocityChanged = true;
|
||||
}
|
||||
|
||||
if (intersect.y > 0) {
|
||||
entity.handleFallDamage(entity.fallDistance, 1);
|
||||
entity.fallDistance = 0;
|
||||
entity.onGround = true;
|
||||
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> checkForClientPlayerCollision(entity));
|
||||
}
|
||||
|
||||
if (entity instanceof ServerPlayerEntity)
|
||||
((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
|
||||
Vec3d currentResponse = collisionResponse.getValue();
|
||||
shape.toBoundingBoxList()
|
||||
.parallelStream()
|
||||
.forEach(bb -> {
|
||||
obb.setCenter(obbCenter.add(currentResponse));
|
||||
Vec3d intersect = obb.intersect(bb);
|
||||
if (intersect != null)
|
||||
collisionResponse.setValue(currentResponse.add(intersect));
|
||||
});
|
||||
});
|
||||
|
||||
Vec3d entityMotion = entity.getMotion();
|
||||
Vec3d totalResponse = collisionResponse.getValue();
|
||||
|
||||
if (totalResponse == Vec3d.ZERO)
|
||||
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 motionY = entityMotion.getY();
|
||||
double motionZ = entityMotion.getZ();
|
||||
double intersectX = totalResponse.getX();
|
||||
double intersectY = totalResponse.getY();
|
||||
double intersectZ = totalResponse.getZ();
|
||||
|
||||
double horizonalEpsilon = 1 / 128f;
|
||||
|
||||
if (motionX != 0 && Math.abs(intersectX) > horizonalEpsilon && motionX > 0 == intersectX < 0)
|
||||
entityMotion = entityMotion.mul(0, 1, 1);
|
||||
if (motionY != 0 && intersectY != 0 && motionY > 0 == intersectY < 0)
|
||||
entityMotion = entityMotion.mul(1, 0, 1);
|
||||
if (motionZ != 0 && Math.abs(intersectZ) > horizonalEpsilon && motionZ > 0 == intersectZ < 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)
|
||||
((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
|
||||
|
||||
entity.setMotion(entityMotion);
|
||||
Vec3d epos = entityPosition;
|
||||
entity.setPosition(epos.x, epos.y + totalResponse.y, epos.z);
|
||||
entityData.put(nbtMotionKey, VecHelper.writeNBT(totalResponse));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -266,7 +305,14 @@ public class ContraptionCollider {
|
|||
|
||||
public static ReuseableStream<VoxelShape> getPotentiallyCollidedShapes(World world, Contraption contraption,
|
||||
AxisAlignedBB localBB) {
|
||||
|
||||
double height = localBB.getYSize();
|
||||
double width = localBB.getXSize();
|
||||
double horizontalFactor = (height > width && width != 0) ? height / width : 1;
|
||||
double verticalFactor = (width > height && height != 0) ? width / height : 1;
|
||||
AxisAlignedBB blockScanBB = localBB.grow(.5f);
|
||||
blockScanBB = blockScanBB.grow(horizontalFactor, verticalFactor, horizontalFactor);
|
||||
|
||||
BlockPos min = new BlockPos(blockScanBB.minX, blockScanBB.minY, blockScanBB.minZ);
|
||||
BlockPos max = new BlockPos(blockScanBB.maxX, blockScanBB.maxY, blockScanBB.maxZ);
|
||||
|
||||
|
|
|
@ -4,34 +4,31 @@ import com.mojang.blaze3d.matrix.MatrixStack;
|
|||
import com.simibubi.create.AllSpecialTextures;
|
||||
import com.simibubi.create.CreateClient;
|
||||
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.outliner.AABBOutline;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
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;
|
||||
|
||||
public class CollisionDebugger {
|
||||
|
||||
static AxisAlignedBB staticBB = new AxisAlignedBB(BlockPos.ZERO.up(10));
|
||||
static OrientedBB movingBB = new OrientedBB(new AxisAlignedBB(BlockPos.ZERO));
|
||||
public static AxisAlignedBB AABB = null;
|
||||
public static OrientedBB OBB = null;
|
||||
static Vec3d seperation;
|
||||
static double angle = 0;
|
||||
static AABBOutline outline;
|
||||
|
||||
public static void onScroll(double delta) {
|
||||
angle += delta;
|
||||
movingBB.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle)));
|
||||
// angle += delta;
|
||||
// movingBB = new OrientedBB(new AxisAlignedBB(BlockPos.ZERO).expand(0, 1, 0));
|
||||
// movingBB.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle)));
|
||||
}
|
||||
|
||||
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
||||
if (OBB == null)
|
||||
return;
|
||||
ms.push();
|
||||
outline = new AABBOutline(movingBB.getAsAxisAlignedBB());
|
||||
outline = new AABBOutline(OBB.getAsAxisAlignedBB());
|
||||
outline.getParams()
|
||||
.withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null)
|
||||
.colored(0xffffff);
|
||||
|
@ -40,12 +37,12 @@ public class CollisionDebugger {
|
|||
.lineWidth(1 / 64f)
|
||||
.colored(0xff6544);
|
||||
MatrixStacker.of(ms)
|
||||
.translate(movingBB.center);
|
||||
.translate(OBB.center);
|
||||
ms.peek()
|
||||
.getModel()
|
||||
.multiply(movingBB.rotation.getAsMatrix4f());
|
||||
.multiply(OBB.rotation.getAsMatrix4f());
|
||||
MatrixStacker.of(ms)
|
||||
.translateBack(movingBB.center);
|
||||
.translateBack(OBB.center);
|
||||
outline.render(ms, buffer);
|
||||
ms.pop();
|
||||
|
||||
|
@ -56,26 +53,24 @@ public class CollisionDebugger {
|
|||
.lineWidth(1 / 32f);
|
||||
MatrixStacker.of(ms)
|
||||
.translate(seperation)
|
||||
.translate(movingBB.center);
|
||||
.translate(OBB.center);
|
||||
ms.peek()
|
||||
.getModel()
|
||||
.multiply(movingBB.rotation.getAsMatrix4f());
|
||||
.multiply(OBB.rotation.getAsMatrix4f());
|
||||
MatrixStacker.of(ms)
|
||||
.translateBack(movingBB.center);
|
||||
.translateBack(OBB.center);
|
||||
outline.render(ms, buffer);
|
||||
}
|
||||
ms.pop();
|
||||
}
|
||||
|
||||
public static void tick() {
|
||||
staticBB = new AxisAlignedBB(BlockPos.ZERO.up(60));
|
||||
RayTraceResult mouse = Minecraft.getInstance().objectMouseOver;
|
||||
if (mouse != null && mouse.getType() == Type.BLOCK) {
|
||||
BlockRayTraceResult hit = (BlockRayTraceResult) mouse;
|
||||
movingBB.setCenter(hit.getHitVec());
|
||||
seperation = movingBB.intersect(staticBB);
|
||||
}
|
||||
CreateClient.outliner.showAABB(staticBB, staticBB)
|
||||
if (OBB == null)
|
||||
return;
|
||||
if (AABB == null)
|
||||
return;
|
||||
seperation = OBB.intersect(AABB);
|
||||
CreateClient.outliner.showAABB(AABB, AABB)
|
||||
.withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,10 @@ public class OrientedBB {
|
|||
this.extents = extents;
|
||||
this.setRotation(rotation);
|
||||
}
|
||||
|
||||
public OrientedBB copy() {
|
||||
return new OrientedBB(center, extents, rotation);
|
||||
}
|
||||
|
||||
public Vec3d intersect(AxisAlignedBB bb) {
|
||||
Vec3d extentsA = extentsFromBB(bb);
|
||||
|
@ -152,7 +156,7 @@ public class OrientedBB {
|
|||
}
|
||||
|
||||
static void showDebugLine(Vec3d relativeStart, Vec3d relativeEnd, int color, String id, int offset) {
|
||||
Vec3d center = CollisionDebugger.staticBB.getCenter()
|
||||
Vec3d center = CollisionDebugger.AABB.getCenter()
|
||||
.add(0, 1 + offset / 16f, 0);
|
||||
CreateClient.outliner.showLine(id + checkCount, center.add(relativeStart), center.add(relativeEnd))
|
||||
.colored(color)
|
||||
|
@ -174,6 +178,10 @@ public class OrientedBB {
|
|||
public void setCenter(Vec3d center) {
|
||||
this.center = center;
|
||||
}
|
||||
|
||||
public void move(Vec3d offset) {
|
||||
setCenter(getCenter().add(offset));
|
||||
}
|
||||
|
||||
public AxisAlignedBB getAsAxisAlignedBB() {
|
||||
return new AxisAlignedBB(0, 0, 0, 0, 0, 0).offset(center)
|
||||
|
|
|
@ -26,6 +26,8 @@ public class VecHelper {
|
|||
public static Vec3d rotate(Vec3d vec, double deg, Axis axis) {
|
||||
if (deg == 0)
|
||||
return vec;
|
||||
if (vec == Vec3d.ZERO)
|
||||
return vec;
|
||||
|
||||
float angle = (float) (deg / 180f * Math.PI);
|
||||
double sin = MathHelper.sin(angle);
|
||||
|
|
Loading…
Reference in a new issue