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 info
mod_version=0.3 mod_version=0.3
minecraft_version=1.15.2 minecraft_version=1.15.2
forge_version=31.2.21 forge_version=31.2.31
# dependency versions # dependency versions
registrate_version=0.0.4.18 registrate_version=0.0.4.18

View file

@ -8,7 +8,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; 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.base.Predicates;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
@ -29,6 +29,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.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;
@ -41,6 +42,7 @@ 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;
@ -118,7 +120,11 @@ public class ContraptionCollider {
if (bounds == null) if (bounds == null)
return; 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)) { contraptionEntity::canCollideWith)) {
if (entity instanceof PlayerEntity && !world.isRemote) if (entity instanceof PlayerEntity && !world.isRemote)
return; return;
@ -127,11 +133,13 @@ public class ContraptionCollider {
Vec3d entityPosition = entity.getPositionVec(); Vec3d entityPosition = entity.getPositionVec();
Vec3d centerY = new Vec3d(0, entity.getBoundingBox() Vec3d centerY = new Vec3d(0, entity.getBoundingBox()
.getYSize() / 2, 0); .getYSize() / 2, 0);
Vec3d position = entityPosition.subtract(contraptionPosition)
.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0)) Vec3d position =
entityPosition.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0))
.add(centerY); .add(centerY);
position =
VecHelper.rotate(position, -contraptionRotation.z, -contraptionRotation.y, -contraptionRotation.x); position = position.subtract(contraptionPosition);
position = VecHelper.rotate(position, -conRotX, -conRotY, -conRotZ);
position = position.add(centerOfBlock) position = position.add(centerOfBlock)
.subtract(centerY) .subtract(centerY)
.subtract(entityPosition); .subtract(entityPosition);
@ -139,6 +147,16 @@ public class ContraptionCollider {
.offset(position) .offset(position)
.grow(1.0E-7D); .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); ReuseableStream<VoxelShape> potentialHits = getPotentiallyCollidedShapes(world, contraption, localBB);
if (potentialHits.createStream() if (potentialHits.createStream()
.count() == 0) .count() == 0)
@ -147,42 +165,59 @@ public class ContraptionCollider {
OrientedBB obb = new OrientedBB(localBB); OrientedBB obb = new OrientedBB(localBB);
if (!contraptionRotation.equals(Vec3d.ZERO)) { if (!contraptionRotation.equals(Vec3d.ZERO)) {
Matrix3d rotation = new Matrix3d().asIdentity(); Matrix3d rotation = new Matrix3d().asIdentity();
rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(contraptionRotation.z))); rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(-conRotX)));
rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(contraptionRotation.y))); rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(conRotY)));
rotation.multiply(new Matrix3d().asZRotation(AngleHelper.rad(contraptionRotation.x))); rotation.multiply(new Matrix3d().asZRotation(AngleHelper.rad(-conRotZ)));
obb.setRotation(rotation); obb.setRotation(rotation);
} }
MutableBoolean onCollide = new MutableBoolean(true); MutableObject<Vec3d> collisionResponse = new MutableObject<>(Vec3d.ZERO);
Vec3d obbCenter = obb.getCenter();
potentialHits.createStream() potentialHits.createStream()
.forEach(shape -> { .forEach(shape -> {
AxisAlignedBB bb = shape.getBoundingBox(); Vec3d currentResponse = collisionResponse.getValue();
shape.toBoundingBoxList()
.parallelStream()
.forEach(bb -> {
obb.setCenter(obbCenter.add(currentResponse));
Vec3d intersect = obb.intersect(bb); Vec3d intersect = obb.intersect(bb);
if (intersect == null) if (intersect != null)
return; collisionResponse.setValue(currentResponse.add(intersect));
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(); Vec3d entityMotion = entity.getMotion();
if (entityMotion.getX() > 0 == intersect.getX() < 0) Vec3d totalResponse = collisionResponse.getValue();
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()) { if (totalResponse == Vec3d.ZERO)
onCollide.setFalse(); 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); contraptionEntity.collidingEntities.add(entity);
entity.velocityChanged = true; entity.velocityChanged = true;
}
if (intersect.y > 0) { if (totalResponse.y > 0) {
entity.handleFallDamage(entity.fallDistance, 1); entity.handleFallDamage(entity.fallDistance, 1);
entity.fallDistance = 0; entity.fallDistance = 0;
entity.onGround = true; entity.onGround = true;
@ -191,7 +226,11 @@ public class ContraptionCollider {
if (entity instanceof ServerPlayerEntity) if (entity instanceof ServerPlayerEntity)
((ServerPlayerEntity) entity).connection.floatingTickCount = 0; ((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, public static ReuseableStream<VoxelShape> getPotentiallyCollidedShapes(World world, Contraption contraption,
AxisAlignedBB localBB) { 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); AxisAlignedBB blockScanBB = localBB.grow(.5f);
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);
BlockPos max = new BlockPos(blockScanBB.maxX, blockScanBB.maxY, blockScanBB.maxZ); 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.AllSpecialTextures;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
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 {
static AxisAlignedBB staticBB = new AxisAlignedBB(BlockPos.ZERO.up(10)); public static AxisAlignedBB AABB = null;
static OrientedBB movingBB = new OrientedBB(new AxisAlignedBB(BlockPos.ZERO)); public static OrientedBB OBB = null;
static Vec3d seperation; static Vec3d 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.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle))); // 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) { public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
if (OBB == null)
return;
ms.push(); ms.push();
outline = new AABBOutline(movingBB.getAsAxisAlignedBB()); outline = new AABBOutline(OBB.getAsAxisAlignedBB());
outline.getParams() outline.getParams()
.withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null) .withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null)
.colored(0xffffff); .colored(0xffffff);
@ -40,12 +37,12 @@ public class CollisionDebugger {
.lineWidth(1 / 64f) .lineWidth(1 / 64f)
.colored(0xff6544); .colored(0xff6544);
MatrixStacker.of(ms) MatrixStacker.of(ms)
.translate(movingBB.center); .translate(OBB.center);
ms.peek() ms.peek()
.getModel() .getModel()
.multiply(movingBB.rotation.getAsMatrix4f()); .multiply(OBB.rotation.getAsMatrix4f());
MatrixStacker.of(ms) MatrixStacker.of(ms)
.translateBack(movingBB.center); .translateBack(OBB.center);
outline.render(ms, buffer); outline.render(ms, buffer);
ms.pop(); ms.pop();
@ -56,26 +53,24 @@ public class CollisionDebugger {
.lineWidth(1 / 32f); .lineWidth(1 / 32f);
MatrixStacker.of(ms) MatrixStacker.of(ms)
.translate(seperation) .translate(seperation)
.translate(movingBB.center); .translate(OBB.center);
ms.peek() ms.peek()
.getModel() .getModel()
.multiply(movingBB.rotation.getAsMatrix4f()); .multiply(OBB.rotation.getAsMatrix4f());
MatrixStacker.of(ms) MatrixStacker.of(ms)
.translateBack(movingBB.center); .translateBack(OBB.center);
outline.render(ms, buffer); outline.render(ms, buffer);
} }
ms.pop(); ms.pop();
} }
public static void tick() { public static void tick() {
staticBB = new AxisAlignedBB(BlockPos.ZERO.up(60)); if (OBB == null)
RayTraceResult mouse = Minecraft.getInstance().objectMouseOver; return;
if (mouse != null && mouse.getType() == Type.BLOCK) { if (AABB == null)
BlockRayTraceResult hit = (BlockRayTraceResult) mouse; return;
movingBB.setCenter(hit.getHitVec()); seperation = OBB.intersect(AABB);
seperation = movingBB.intersect(staticBB); CreateClient.outliner.showAABB(AABB, AABB)
}
CreateClient.outliner.showAABB(staticBB, staticBB)
.withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null); .withFaceTexture(seperation == null ? AllSpecialTextures.CHECKERED : null);
} }

View file

@ -30,6 +30,10 @@ public class OrientedBB {
this.setRotation(rotation); this.setRotation(rotation);
} }
public OrientedBB copy() {
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 = separateBBs(bb.getCenter(), center, extentsA, extents, rotation);
@ -152,7 +156,7 @@ public class OrientedBB {
} }
static void showDebugLine(Vec3d relativeStart, Vec3d relativeEnd, int color, String id, int offset) { 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); .add(0, 1 + offset / 16f, 0);
CreateClient.outliner.showLine(id + checkCount, center.add(relativeStart), center.add(relativeEnd)) CreateClient.outliner.showLine(id + checkCount, center.add(relativeStart), center.add(relativeEnd))
.colored(color) .colored(color)
@ -175,6 +179,10 @@ public class OrientedBB {
this.center = center; this.center = center;
} }
public void move(Vec3d offset) {
setCenter(getCenter().add(offset));
}
public AxisAlignedBB getAsAxisAlignedBB() { public AxisAlignedBB getAsAxisAlignedBB() {
return new AxisAlignedBB(0, 0, 0, 0, 0, 0).offset(center) return new AxisAlignedBB(0, 0, 0, 0, 0, 0).offset(center)
.grow(extents.x, extents.y, extents.z); .grow(extents.x, extents.y, extents.z);

View file

@ -26,6 +26,8 @@ public class VecHelper {
public static Vec3d rotate(Vec3d vec, double deg, Axis axis) { public static Vec3d rotate(Vec3d vec, double deg, Axis axis) {
if (deg == 0) if (deg == 0)
return vec; return vec;
if (vec == Vec3d.ZERO)
return vec;
float angle = (float) (deg / 180f * Math.PI); float angle = (float) (deg / 180f * Math.PI);
double sin = MathHelper.sin(angle); double sin = MathHelper.sin(angle);