Skip to content

Commit

Permalink
good progress
Browse files Browse the repository at this point in the history
- most of the process works
- need to make it only affect portals on the right surface for each block
- need to handle non cubes
- caching?
  • Loading branch information
TropheusJ committed Nov 18, 2023
1 parent 46b1bd7 commit 0cc05bd
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,11 @@ public final class Portal {
public static final double WIDTH = 1 - (2 * SIXTEENTH);
public static final double THICKNESS = 0;
public static final double COLLISION_BOX_SIDE_THICKNESS = 0.01;
public static final double COLLISION_BOX_DEPTH = 2 - (2 * COLLISION_BOX_SIDE_THICKNESS);
public static final double COLLISION_BOX_DEPTH = 4 - (2 * COLLISION_BOX_SIDE_THICKNESS);

public final int netId;
public final Vec3 origin;
public final AABB plane; // technically a box, but really thin on 1 axis
public final VoxelShape hole; // the hole this portal punches in the world to allow walking through
public final AABB collisionArea; // area in front of portal where entities will collide with blocks on the other side
public final AABB blockCollisionArea; // area behind portal where blocks are important to collision
public final Vec3 normal;
public final FrontAndTop orientation;
public final Quaternionf rotation;
Expand All @@ -43,6 +40,16 @@ public final class Portal {
public final int color;
public final UUID owner;

// plane-like hole this portal punches in the world to allow walking through
public final VoxelShape hole;
// area in front of portal that an entity must be in for cross-portal collision to apply
public final AABB entityCollisionArea;
// area in front of portal where collision will be matched to behind the other portal
public final AABB collisionCollectionArea;
// area behind portal where collision is modified to match other portal
public final AABB collisionModificationBox;


private int linkedNetId;
@Nullable
private Portal linked;
Expand Down Expand Up @@ -76,11 +83,16 @@ public Portal(int netId, Vec3 origin, FrontAndTop orientation, PortalShape shape
AABB holePlane = this.plane.inflate(-0.01);
this.hole = Shapes.create(holePlane);

this.collisionArea = this.plane.expandTowards(normal.scale(COLLISION_BOX_DEPTH)) // extend forwards
this.entityCollisionArea = AABB.ofSize(
origin.add(normal.scale(1.5)), // center 1.5 blocks away
3, 3, 3 // 3x3x3 box
);

this.collisionCollectionArea = this.plane.expandTowards(normal.scale(COLLISION_BOX_DEPTH)) // extend forwards
.inflate(COLLISION_BOX_SIDE_THICKNESS) // expand bounds by thickness
.move(normal.scale(COLLISION_BOX_SIDE_THICKNESS)); // move bounds off supporting wall

this.blockCollisionArea = this.plane.expandTowards(normal.scale(-COLLISION_BOX_DEPTH)) // extend backwards
this.collisionModificationBox = this.plane.expandTowards(normal.scale(-COLLISION_BOX_DEPTH)) // extend backwards
.inflate(COLLISION_BOX_SIDE_THICKNESS) // expand bounds by thickness
.move(normal.scale(-COLLISION_BOX_SIDE_THICKNESS)); // move bounds fully into supporting wall
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,18 @@
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

import net.minecraft.world.phys.shapes.VoxelShape;

import org.joml.Quaternionf;
import org.joml.Vector3f;

import java.util.List;

public class PortalRenderer {
public static final Color RED = new Color(1, 0, 0, 1);
public static final Color GREEN = new Color(0.5f, 1, 0.5f, 1);
public static final Color BLUE = new Color(0, 0, 1, 1);
public static final Color ORANGE = new Color(1, 0.5f, 0, 1);
Expand Down Expand Up @@ -98,8 +97,9 @@ private static void renderPortalDebug(Portal portal, WorldRenderContext ctx, Pos
Color planeColor = portal.isActive() ? ACTIVE_PLANE_COLOR : PLANE_COLOR;
renderBox(matrices, vertexConsumers, portal.plane, planeColor);
// collision bounds
renderBox(matrices, vertexConsumers, portal.collisionArea, PURPLE);
renderBox(matrices, vertexConsumers, portal.blockCollisionArea, CYAN);
renderBox(matrices, vertexConsumers, portal.entityCollisionArea, RED);
renderBox(matrices, vertexConsumers, portal.collisionCollectionArea, PURPLE);
renderBox(matrices, vertexConsumers, portal.collisionModificationBox, CYAN);
// cross-portal collision
Portal linked = portal.getLinked();
if (linked != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,147 @@

import io.github.fusionflux.portalcubed.content.portal.Portal;
import io.github.fusionflux.portalcubed.content.portal.PortalTeleportHandler;
import io.github.fusionflux.portalcubed.content.portal.manager.PortalManager;
import io.github.fusionflux.portalcubed.data.tags.PortalCubedEntityTags;
import io.github.fusionflux.portalcubed.framework.extension.EntityExt;
import io.github.fusionflux.portalcubed.framework.shape.VoxelShenanigans;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;

import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ApiStatus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class CollisionManager {
// thickness of a wall after the other side has been cut out for cross-portal collision
public static final double WALL_THICKNESS = 0.01;
public static final int RECURSION_DEPTH_LIMIT = 1;

private final PortalManager portals;
private final Level level;
private final Map<BlockPos, ShapePatch> patches;

public CollisionManager(Level level) {
public CollisionManager(PortalManager portalManager, Level level) {
this.portals = portalManager;
this.level = level;
this.patches = new HashMap<>();
}

@Nullable
public ShapePatch getPatchAt(BlockPos pos) {
return patches.get(pos);
// modifying a shape for a portal involves 3 parts:
// - cut out most of the shape to make it non-solid (leave the surface the portal is on intact)
// - add in collision from other side
// - cut a hole directly under the portal
public VoxelShape getPortalModifiedShape(VoxelShape original, BlockPos pos, Entity entity) {
if (getRecursionDepth(entity) > RECURSION_DEPTH_LIMIT)
return original;
if (entity.getType().is(PortalCubedEntityTags.PORTAL_BLACKLIST))
return original;
List<Portal> portals = this.getPortalsToUse(pos, entity);
if (portals.isEmpty())
return original;
incrementRecursionDepth(entity);
// move shape to absolute coords to match found collision shapes
VoxelShape shape = original.move(pos.getX(), pos.getY(), pos.getZ());
// cut out most of the shape beyond the surface
Vec3 normal = portals.get(0).normal;
Vec3 offsetIntoWall = normal.scale(-WALL_THICKNESS);
VoxelShape cubeAtPos = Shapes.block().move(pos.getX(), pos.getY(), pos.getZ());
VoxelShape cutBox = cubeAtPos.move(offsetIntoWall.x, offsetIntoWall.y, offsetIntoWall.z);
shape = Shapes.join(shape, cutBox, BooleanOp.ONLY_FIRST);
// first combine all collisions
List<Portal> relevantPortals = new ArrayList<>(); // portals the entity is interacting with
for (Portal portal : portals) {
Portal linked = portal.getLinked();
if (linked == null)
continue;
if (!portal.entityCollisionArea.contains(entity.position()))
continue; // out of collision area, don't care
relevantPortals.add(portal);
List<VoxelShape> shapes = VoxelShenanigans.getShapesBehindPortal(level, entity, portal, linked);
if (shapes.isEmpty())
continue; // all non-solid
// combine collisions
for (VoxelShape collisionShape : shapes) {
shape = Shapes.or(shape, collisionShape);
}
}
// crop the shape to the specific BlockPos
shape = Shapes.join(shape, cubeAtPos, BooleanOp.AND);
// cut portal holes
for (Portal portal : relevantPortals) {
shape = Shapes.join(shape, portal.hole, BooleanOp.ONLY_FIRST);
}
decrementRecursionDepth(entity);
// translate shape back to relative coords, and we're done here
return shape.move(-pos.getX(), -pos.getY(), -pos.getZ());
}

// in most cases, this list will have a size of 1
// in some cases, maybe 2, both on the same surface
// rarer, 3+
// in weird cases, the surface of each could potentially be different.
// when that happens, prefer nearest.
// returned list of portals is guaranteed to all have the same normal
private List<Portal> getPortalsToUse(BlockPos pos, Entity entity) {
Set<Portal> portals = this.portals.getPortalsAt(pos);
if (portals.isEmpty()) {
return List.of();
} else if (portals.size() == 1) {
return List.of(portals.iterator().next());
} else {
Map<Vec3, List<Portal>> byNormal = new HashMap<>();
for (Portal portal : portals) {
byNormal.computeIfAbsent(portal.normal, $ -> new ArrayList<>()).add(portal);
}
if (byNormal.size() == 1) {
for (List<Portal> list : byNormal.values()) {
return list;
}
throw new IllegalStateException("This should be impossible to reach");
} else {
// worst case, sort by distance to entity
Vec3 selectedNormal = null;
double closestSqrDist = Double.MAX_VALUE;
for (Map.Entry<Vec3, List<Portal>> entry : byNormal.entrySet()) {
for (Portal portal : entry.getValue()) {
double distSqr = portal.origin.distanceToSqr(entity.position());
if (distSqr < closestSqrDist) {
closestSqrDist = distSqr;
selectedNormal = entry.getKey();
}
}
}
return byNormal.get(selectedNormal);
}
}
}

@ApiStatus.Internal
public void handlePortalLink(Portal a, Portal b) {
this.handleNewPortal(a, b);
this.handleNewPortal(b, a);
}

@ApiStatus.Internal
public void handlePortalUnlink(Portal a, Portal b) {
this.removePortal(a);
this.removePortal(b);
}

private void handleNewPortal(Portal portal, Portal linked) {
// iterate through block positions in front of the output portal
BlockPos.betweenClosedStream(linked.collisionArea).forEach(posInFront -> {
BlockPos.betweenClosedStream(linked.collisionCollectionArea).forEach(posInFront -> {
BlockState state = level.getBlockState(posInFront);
if (state.isAir())
return; // easy skip, don't care
Expand All @@ -63,6 +162,24 @@ private void handleNewPortal(Portal portal, Portal linked) {
}

private void removePortal(Portal portal) {
BlockPos.betweenClosedStream(portal.blockCollisionArea).forEach(this.patches::remove);
BlockPos.betweenClosedStream(portal.collisionModificationBox).forEach(this.patches::remove);
}

private static int getRecursionDepth(Entity entity) {
return ((EntityExt) entity).pc$getPortalCollisionRecursionDepth();
}

private static void setRecursionDepth(Entity entity, int depth) {
((EntityExt) entity).pc$setPortalCollisionRecursionDepth(depth);
}

private static void incrementRecursionDepth(Entity entity) {
int depth = getRecursionDepth(entity);
setRecursionDepth(entity, depth + 1);
}

private static void decrementRecursionDepth(Entity entity) {
int depth = getRecursionDepth(entity);
setRecursionDepth(entity, depth - 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.stream.Collectors;

public abstract class PortalManager {
private final Level level;
protected final PortalStorage storage = new SectionPortalStorage();
protected final CollisionManager collisionManager;

Expand All @@ -33,7 +34,8 @@ public static PortalManager of(Level level) {
}

public PortalManager(Level level) {
this.collisionManager = new CollisionManager(level);
this.level = level;
this.collisionManager = new CollisionManager(this, level);
}

public CollisionManager getCollisionManager() {
Expand Down
91 changes: 0 additions & 91 deletions src/main/java/io/github/fusionflux/portalcubed/framework/Aaaa.java

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.github.fusionflux.portalcubed.framework.extension;

public interface EntityExt {
int pc$getPortalCollisionRecursionDepth();
void pc$setPortalCollisionRecursionDepth(int depth);
}
Loading

0 comments on commit 0cc05bd

Please sign in to comment.