Actually playable

- Fixed some left-over math bugs
- Greatly improved contraption-player collision response, especially with rotating structures
This commit is contained in:
simibubi 2020-07-12 23:57:27 +02:00
parent 1ea7eeb040
commit c58310b293
5 changed files with 124 additions and 73 deletions

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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)

View file

@ -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);