Contact points and Friction

- Entities are now moved with the relative motion of their contact point
This commit is contained in:
simibubi 2020-07-15 15:01:59 +02:00
parent ebc2944788
commit 53a58def49
2 changed files with 76 additions and 49 deletions

View file

@ -130,28 +130,40 @@ public class ContraptionCollider {
if (bounds == null) if (bounds == null)
return; return;
Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
double conRotX = contraptionRotation.z; double conRotX = contraptionRotation.z;
double conRotY = contraptionRotation.y; double conRotY = contraptionRotation.y;
double conRotZ = contraptionRotation.x; double conRotZ = contraptionRotation.x;
Vec3d conMotion = contraptionPosition.subtract(contraptionEntity.getPrevPositionVec());
Vec3d conAngularMotion = contraptionRotation.subtract(contraptionEntity.getPrevRotationVec());
Vec3d contraptionCentreOffset = contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0);
boolean axisAlignedCollision = contraptionRotation.equals(Vec3d.ZERO);
Matrix3d rotation = null;
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(2) for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(2)
.expand(0, 32, 0), contraptionEntity::canCollideWith)) { .expand(0, 32, 0), contraptionEntity::canCollideWith)) {
boolean serverPlayer = entity instanceof PlayerEntity && !world.isRemote; boolean serverPlayer = entity instanceof PlayerEntity && !world.isRemote;
// Init matrix
if (rotation == null) {
rotation = new Matrix3d().asIdentity();
if (!axisAlignedCollision) {
rotation.multiply(new Matrix3d().asXRotation(AngleHelper.rad(-conRotX)));
rotation.multiply(new Matrix3d().asYRotation(AngleHelper.rad(conRotY)));
rotation.multiply(new Matrix3d().asZRotation(AngleHelper.rad(-conRotZ)));
}
}
// Transform entity position and motion to local space // Transform entity position and motion to local space
Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
Vec3d entityPosition = entity.getPositionVec(); Vec3d entityPosition = entity.getPositionVec();
AxisAlignedBB entityBounds = entity.getBoundingBox(); AxisAlignedBB entityBounds = entity.getBoundingBox();
Vec3d centerY = new Vec3d(0, entityBounds.getYSize() / 2, 0); Vec3d centerY = new Vec3d(0, entityBounds.getYSize() / 2, 0);
Vec3d motion = entity.getMotion(); Vec3d motion = entity.getMotion();
boolean axisAlignedCollision = contraptionRotation.equals(Vec3d.ZERO);
Vec3d position =
entityPosition.subtract(contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0))
.add(centerY);
Vec3d position = entityPosition.subtract(contraptionCentreOffset)
.add(centerY);
position = position.subtract(contraptionPosition); position = position.subtract(contraptionPosition);
position = VecHelper.rotate(position, -conRotX, -conRotY, -conRotZ); position = rotation.transform(position);
position = position.add(centerOfBlock) position = position.add(centerOfBlock)
.subtract(centerY) .subtract(centerY)
.subtract(entityPosition); .subtract(entityPosition);
@ -165,18 +177,10 @@ public class ContraptionCollider {
.count() == 0) .count() == 0)
continue; continue;
if (!axisAlignedCollision)
motion = VecHelper.rotate(motion, -conRotX, -conRotY, -conRotZ);
// Prepare entity bounds // Prepare entity bounds
OrientedBB obb = new OrientedBB(localBB); OrientedBB obb = new OrientedBB(localBB);
if (!axisAlignedCollision) { obb.setRotation(rotation);
Matrix3d rotation = new Matrix3d().asIdentity(); motion = rotation.transform(motion);
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);
}
// Vec3d visualizerOrigin = new Vec3d(10, 64, 0); // Vec3d visualizerOrigin = new Vec3d(10, 64, 0);
// CollisionDebugger.OBB = obb.copy(); // CollisionDebugger.OBB = obb.copy();
@ -233,10 +237,14 @@ public class ContraptionCollider {
Vec3d entityMotion = entity.getMotion(); Vec3d entityMotion = entity.getMotion();
Vec3d totalResponse = collisionResponse.getValue(); Vec3d totalResponse = collisionResponse.getValue();
Vec3d motionResponse = allowedMotion.getValue(); Vec3d motionResponse = allowedMotion.getValue();
boolean hardCollision = !totalResponse.equals(Vec3d.ZERO);
rotation.transpose();
motionResponse = rotation.transform(motionResponse);
totalResponse = rotation.transform(totalResponse);
rotation.transpose();
if (futureCollision.isTrue() && !serverPlayer) { if (futureCollision.isTrue() && !serverPlayer) {
if (!axisAlignedCollision)
motionResponse = VecHelper.rotate(motionResponse, conRotX, conRotY, conRotZ);
if (motionResponse.y != entityMotion.y) { if (motionResponse.y != entityMotion.y) {
entity.setMotion(entityMotion.mul(1, 0, 1) entity.setMotion(entityMotion.mul(1, 0, 1)
.add(0, motionResponse.y, 0)); .add(0, motionResponse.y, 0));
@ -244,47 +252,58 @@ public class ContraptionCollider {
} }
} }
if (!axisAlignedCollision) Vec3d contactPointMotion = Vec3d.ZERO;
totalResponse = VecHelper.rotate(totalResponse, conRotX, conRotY, conRotZ);
if (surfaceCollision.isTrue()) { if (surfaceCollision.isTrue()) {
// entity.handleFallDamage(entity.fallDistance, 1); tunnelling issue // entity.handleFallDamage(entity.fallDistance, 1); tunnelling issue
entity.fallDistance = 0; entity.fallDistance = 0;
entity.onGround = true; entity.onGround = true;
if (!serverPlayer) { if (!serverPlayer) {
Vec3d contactPoint = entityPosition.subtract(contraptionCentreOffset)
.subtract(contraptionPosition);
contactPoint =
VecHelper.rotate(contactPoint, conAngularMotion.z, conAngularMotion.y, conAngularMotion.x);
contactPoint = contactPoint.add(contraptionPosition)
.add(contraptionCentreOffset)
.add(conMotion);
contactPointMotion = contactPoint.subtract(entityPosition);
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> checkForClientPlayerCollision(entity)); DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> checkForClientPlayerCollision(entity));
} }
} }
if (totalResponse.equals(Vec3d.ZERO)) if (hardCollision) {
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);
}
if (!hardCollision && surfaceCollision.isFalse())
continue; continue;
double motionX = entityMotion.getX(); if (serverPlayer && entity instanceof ServerPlayerEntity) {
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);
if (entity instanceof ServerPlayerEntity)
((ServerPlayerEntity) entity).connection.floatingTickCount = 0; ((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
continue;
if (!serverPlayer) {
Vec3d allowedMovement = getAllowedMovement(totalResponse, entity);
contraptionEntity.collidingEntities.add(entity);
entity.velocityChanged = true;
entity.setPosition(entityPosition.x + allowedMovement.x, entityPosition.y + allowedMovement.y,
entityPosition.z + allowedMovement.z);
entity.setMotion(entityMotion);
} }
totalResponse = totalResponse.add(contactPointMotion);
Vec3d allowedMovement = getAllowedMovement(totalResponse, entity);
contraptionEntity.collidingEntities.add(entity);
entity.velocityChanged = true;
entity.setPosition(entityPosition.x + allowedMovement.x, entityPosition.y + allowedMovement.y,
entityPosition.z + allowedMovement.z);
entity.setMotion(entityMotion);
} }
} }

View file

@ -636,6 +636,14 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return new Vec3d(getPitch(1), getYaw(1), getRoll(1)); return new Vec3d(getPitch(1), getYaw(1), getRoll(1));
} }
public Vec3d getPrevRotationVec() {
return new Vec3d(getPitch(0), getYaw(0), getRoll(0));
}
public Vec3d getPrevPositionVec() {
return new Vec3d(prevPosX, prevPosY, prevPosZ);
}
public boolean canCollideWith(Entity e) { public boolean canCollideWith(Entity e) {
if (e instanceof PlayerEntity && e.isSpectator()) if (e instanceof PlayerEntity && e.isSpectator())
return false; return false;
@ -647,14 +655,14 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return false; return false;
if (e instanceof IProjectile) if (e instanceof IProjectile)
return false; return false;
Entity riding = this.getRidingEntity(); Entity riding = this.getRidingEntity();
while (riding != null) { while (riding != null) {
if (riding == e) if (riding == e)
return false; return false;
riding = riding.getRidingEntity(); riding = riding.getRidingEntity();
} }
return e.getPushReaction() == PushReaction.NORMAL; return e.getPushReaction() == PushReaction.NORMAL;
} }