From 77917dc46809c65b7666a6e3f773c64ee8bdeed3 Mon Sep 17 00:00:00 2001 From: Ted Senft Date: Tue, 12 Mar 2024 05:39:12 -0400 Subject: [PATCH] Structure placement code (multi-structure support) --- .../machines/api/util/AABBAligner.java | 76 +++++++++++++++++++ .../machines/api/util/AABBHelper.java | 28 ++++--- .../machines/api/util/BlockSpaceUtil.java | 4 +- .../machines/api/util/DirectionalMath.java | 24 ++++++ .../machines/api/util/VectorUtils.java | 20 +++++ .../machines/room/RoomRegistrar.java | 4 +- .../api/room/CompactRoomGenerator.java | 53 +++++++++---- .../machines/api/room/RoomApi.java | 7 ++ .../machines/api/room/RoomStructureInfo.java | 33 ++++++++ .../machines/api/room/RoomTemplate.java | 13 +++- 10 files changed, 232 insertions(+), 30 deletions(-) create mode 100644 core-api/src/main/java/dev/compactmods/machines/api/util/AABBAligner.java create mode 100644 core-api/src/main/java/dev/compactmods/machines/api/util/DirectionalMath.java create mode 100644 core-api/src/main/java/dev/compactmods/machines/api/util/VectorUtils.java create mode 100644 room-api/src/main/java/dev/compactmods/machines/api/room/RoomStructureInfo.java diff --git a/core-api/src/main/java/dev/compactmods/machines/api/util/AABBAligner.java b/core-api/src/main/java/dev/compactmods/machines/api/util/AABBAligner.java new file mode 100644 index 0000000..21f477f --- /dev/null +++ b/core-api/src/main/java/dev/compactmods/machines/api/util/AABBAligner.java @@ -0,0 +1,76 @@ +package dev.compactmods.machines.api.util; + +import net.minecraft.core.Direction; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector3d; + +public class AABBAligner { + + private final AABB outerBounds; + private final Vector3d size; + private Vector3d center; + + private AABBAligner(AABB outerBounds, Vector3d center, Vector3d size) { + this.outerBounds = outerBounds; + this.size = size; + this.center = center; + } + + public static AABBAligner create(AABB boundaries, AABB pendingAlign) { + return new AABBAligner(boundaries, VectorUtils.convert3d(pendingAlign.getCenter()), AABBHelper.sizeOf(pendingAlign)); + } + + public static AABBAligner create(AABB boundaries, Vector3d size) { + return new AABBAligner(boundaries, new Vector3d(), size); + } + + public AABBAligner center(Vec3 center) { + this.center = VectorUtils.convert3d(center); + return this; + } + + public AABBAligner center(Vector3d center) { + this.center = center; + return this; + } + + public AABBAligner center() { + this.center = VectorUtils.convert3d(outerBounds.getCenter()); + return this; + } + + public AABBAligner boundedDirection(Direction direction) { + var outerEdge = DirectionalMath.directionalEdge(direction, outerBounds); + var targetPoint = DirectionalMath.directionalEdge(direction, center, size); + + var offset = outerEdge.sub(targetPoint); + + center.add(offset); + + return this; + } + + public AABB align() { + return AABB.ofSize(VectorUtils.convert3d(center), size.x, size.y, size.z); + } + + public static AABB center(AABB source, Vec3 center) { + final var size = AABBHelper.sizeOf(source); + return AABB.ofSize(center, size.x, size.y, size.z); + } + + public static AABB center(AABB source, Vector3d center) { + return center(source, VectorUtils.convert3d(center)); + } + + public static AABB floor(AABB source, AABB within) { + double targetY = within.minY; + return floor(source, targetY); + } + + public static AABB floor(AABB source, double targetY) { + double offset = source.minY - targetY; + return source.move(0, offset * -1, 0); + } +} diff --git a/core-api/src/main/java/dev/compactmods/machines/api/util/AABBHelper.java b/core-api/src/main/java/dev/compactmods/machines/api/util/AABBHelper.java index bff5d56..b1e3b1f 100644 --- a/core-api/src/main/java/dev/compactmods/machines/api/util/AABBHelper.java +++ b/core-api/src/main/java/dev/compactmods/machines/api/util/AABBHelper.java @@ -2,9 +2,28 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import org.joml.Vector3d; public abstract class AABBHelper { + public static Vector3d sizeOf(AABB aabb) { + return new Vector3d(aabb.getXsize(), aabb.getYsize(), aabb.getZsize()); + } + + public static boolean fitsInside(AABB aabb, Vec3 vec3) { + return fitsInside(aabb, VectorUtils.convert3d(vec3)); + } + + public static boolean fitsInside(AABB aabb, Vector3d dimensions) { + return dimensions.x <= aabb.getXsize() && + dimensions.y <= aabb.getYsize() && + dimensions.z <= aabb.getZsize(); + } + + public static boolean fitsInside(AABB outer, AABB inner) { + return fitsInside(outer, sizeOf(inner)); + } + public static Vec3 minCorner(AABB aabb) { return new Vec3(aabb.minX, aabb.minY, aabb.minZ); } @@ -23,15 +42,6 @@ public static AABB normalizeWithin(AABB source, AABB within) { return source.move(offset); } - public static AABB alignFloor(AABB source, AABB within) { - double targetY = within.minY; - return alignFloor(source, targetY); - } - - public static AABB alignFloor(AABB source, double targetY) { - double offset = source.minY - targetY; - return source.move(0, offset * -1, 0); - } public static String toString(AABB aabb) { return "%s,%s,%s".formatted(aabb.getXsize(), aabb.getYsize(), aabb.getZsize()); diff --git a/core-api/src/main/java/dev/compactmods/machines/api/util/BlockSpaceUtil.java b/core-api/src/main/java/dev/compactmods/machines/api/util/BlockSpaceUtil.java index 263811d..4baa325 100644 --- a/core-api/src/main/java/dev/compactmods/machines/api/util/BlockSpaceUtil.java +++ b/core-api/src/main/java/dev/compactmods/machines/api/util/BlockSpaceUtil.java @@ -39,8 +39,8 @@ public static BlockPos centerWallBlockPos(AABB area, Direction direction) { if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) offset -= 1; - var centerWallBlock = center.relative(direction, offset); - return BlockPos.containing(centerWallBlock); + var centerWallPos = center.relative(direction, offset); + return BlockPos.containing(centerWallPos); } public static AABB getPlaneAABB(Direction direction) { diff --git a/core-api/src/main/java/dev/compactmods/machines/api/util/DirectionalMath.java b/core-api/src/main/java/dev/compactmods/machines/api/util/DirectionalMath.java new file mode 100644 index 0000000..e94869a --- /dev/null +++ b/core-api/src/main/java/dev/compactmods/machines/api/util/DirectionalMath.java @@ -0,0 +1,24 @@ +package dev.compactmods.machines.api.util; + +import net.minecraft.core.Direction; +import net.minecraft.world.phys.AABB; +import org.joml.Vector3d; + +public class DirectionalMath { + + public static Vector3d directionalEdge(Direction direction, Vector3d origin, Vector3d size) { + final var normal = direction.getNormal(); + + var x = origin.x + ((size.x / 2) * normal.getX()); + var y = origin.y + ((size.y / 2) * normal.getY()); + var z = origin.z + ((size.z / 2) * normal.getZ()); + + return new Vector3d(x, y, z); + } + + public static Vector3d directionalEdge(Direction direction, AABB aabb) { + var center = VectorUtils.convert3d(aabb.getCenter()); + return directionalEdge(direction, center, AABBHelper.sizeOf(aabb)); + } + +} diff --git a/core-api/src/main/java/dev/compactmods/machines/api/util/VectorUtils.java b/core-api/src/main/java/dev/compactmods/machines/api/util/VectorUtils.java new file mode 100644 index 0000000..fbae337 --- /dev/null +++ b/core-api/src/main/java/dev/compactmods/machines/api/util/VectorUtils.java @@ -0,0 +1,20 @@ +package dev.compactmods.machines.api.util; + +import net.minecraft.core.Vec3i; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector3d; + +public class VectorUtils { + + public static Vector3d convert3d(Vec3i vector) { + return new Vector3d(vector.getX(), vector.getY(), vector.getZ()); + } + + public static Vector3d convert3d(Vec3 vector) { + return new Vector3d(vector.x, vector.y, vector.z); + } + + public static Vec3 convert3d(Vector3d vector) { + return new Vec3(vector.x, vector.y, vector.z); + } +} diff --git a/core/src/main/java/dev/compactmods/machines/room/RoomRegistrar.java b/core/src/main/java/dev/compactmods/machines/room/RoomRegistrar.java index 425d782..fcca7e9 100644 --- a/core/src/main/java/dev/compactmods/machines/room/RoomRegistrar.java +++ b/core/src/main/java/dev/compactmods/machines/room/RoomRegistrar.java @@ -10,7 +10,7 @@ import dev.compactmods.machines.api.Constants; import dev.compactmods.machines.api.dimension.CompactDimension; import dev.compactmods.machines.api.dimension.MissingDimensionException; -import dev.compactmods.machines.api.util.AABBHelper; +import dev.compactmods.machines.api.util.AABBAligner; import dev.compactmods.machines.data.CodecBackedSavedData; import dev.compactmods.machines.room.graph.node.RoomRegistrationNode; import dev.compactmods.machines.util.MathUtil; @@ -70,7 +70,7 @@ public AABB getNextBoundaries(RoomTemplate template) { final var region = MathUtil.getRegionPositionByIndex(registrationNodes.size()); final var floor = MathUtil.getCenterWithY(region, 0); - return AABBHelper.alignFloor(template.getZeroBoundaries().move(floor), 0); + return AABBAligner.floor(template.getZeroBoundaries().move(floor), 0); } @Override diff --git a/room-api/src/main/java/dev/compactmods/machines/api/room/CompactRoomGenerator.java b/room-api/src/main/java/dev/compactmods/machines/api/room/CompactRoomGenerator.java index 6416f9a..d389a1c 100644 --- a/room-api/src/main/java/dev/compactmods/machines/api/room/CompactRoomGenerator.java +++ b/room-api/src/main/java/dev/compactmods/machines/api/room/CompactRoomGenerator.java @@ -1,7 +1,11 @@ package dev.compactmods.machines.api.room; import dev.compactmods.machines.api.WallConstants; +import dev.compactmods.machines.api.room.spatial.IRoomBoundaries; +import dev.compactmods.machines.api.util.AABBAligner; +import dev.compactmods.machines.api.util.AABBHelper; import dev.compactmods.machines.api.util.BlockSpaceUtil; +import dev.compactmods.machines.api.util.VectorUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; @@ -11,10 +15,13 @@ import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import org.joml.Vector3d; public class CompactRoomGenerator { @@ -53,7 +60,7 @@ public static void generateRoom(LevelAccessor world, AABB outerBounds) { * * @param world * @param outerBounds Outer dimensions of the room. - * @param block Block to use for walls. + * @param block Block to use for walls. */ public static void generateRoom(LevelAccessor world, AABB outerBounds, BlockState block) { @@ -67,20 +74,40 @@ public static void generateRoom(LevelAccessor world, AABB outerBounds, BlockStat .forEach(p -> world.setBlock(p, Blocks.AIR.defaultBlockState(), 7)); } - public static BlockPos cornerFromSize(Vec3i dimensions, Vec3 center) { - Vec3 offset = new Vec3( - Math.floor(dimensions.getX() / 2f), - Math.floor(dimensions.getY() / 2f), - Math.floor(dimensions.getZ() / 2f) - ); + public static void populateStructure(ServerLevel level, ResourceLocation template, AABB roomInnerBounds, RoomStructureInfo.RoomStructurePlacement placement) { + level.getStructureManager().get(template).ifPresent(tem -> { - return BlockPos.containing(center.subtract(offset)); - } + Vector3d templateSize = VectorUtils.convert3d(tem.getSize()); - public static void fillWithTemplate(ServerLevel level, ResourceLocation template, Vec3i dimensions, Vec3 center) { - level.getStructureManager().get(template).ifPresent(tem -> { - BlockPos placeAt = cornerFromSize(dimensions, center); - tem.placeInWorld(level, placeAt, placeAt, new StructurePlaceSettings(), level.random, Block.UPDATE_ALL); + if (!AABBHelper.fitsInside(roomInnerBounds, templateSize)) { + // skip: structure too large to place in room + return; + } + + var placementSettings = new StructurePlaceSettings() + .setRotation(Rotation.NONE) + .setMirror(Mirror.NONE); + + AABB placementBounds = null; + final AABBAligner aligner = AABBAligner.create(roomInnerBounds, templateSize); + switch (placement) { + case CENTERED -> placementBounds = aligner + .center(roomInnerBounds.getCenter()) + .align(); + + case CENTERED_CEILING -> placementBounds = aligner + .boundedDirection(Direction.UP) + .align(); + + case CENTERED_FLOOR -> placementBounds = aligner + .boundedDirection(Direction.DOWN) + .align(); + } + + if(placementBounds != null) { + BlockPos placeAt = BlockPos.containing(AABBHelper.minCorner(placementBounds)); + tem.placeInWorld(level, placeAt, placeAt, placementSettings, level.random, Block.UPDATE_ALL); + } }); } } diff --git a/room-api/src/main/java/dev/compactmods/machines/api/room/RoomApi.java b/room-api/src/main/java/dev/compactmods/machines/api/room/RoomApi.java index b5ecb2c..cbd24e0 100644 --- a/room-api/src/main/java/dev/compactmods/machines/api/room/RoomApi.java +++ b/room-api/src/main/java/dev/compactmods/machines/api/room/RoomApi.java @@ -55,6 +55,13 @@ public static RoomInstance newRoom(MinecraftServer server, RoomTemplate template final var instance = INSTANCE.registrar().createNew(template, owner); final var compactDim = CompactDimension.forServer(server); CompactRoomGenerator.generateRoom(compactDim, instance.boundaries().outerBounds()); + + if(!template.structures().isEmpty()) { + for(var struct : template.structures()) { + CompactRoomGenerator.populateStructure(compactDim, struct.template(), instance.boundaries().innerBounds(), struct.placement()); + } + } + return instance; } diff --git a/room-api/src/main/java/dev/compactmods/machines/api/room/RoomStructureInfo.java b/room-api/src/main/java/dev/compactmods/machines/api/room/RoomStructureInfo.java new file mode 100644 index 0000000..69ed986 --- /dev/null +++ b/room-api/src/main/java/dev/compactmods/machines/api/room/RoomStructureInfo.java @@ -0,0 +1,33 @@ +package dev.compactmods.machines.api.room; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.StringRepresentable; +import org.jetbrains.annotations.NotNull; + +public record RoomStructureInfo(ResourceLocation template, RoomStructurePlacement placement) { + public static final Codec CODEC = RecordCodecBuilder.create(inst -> inst.group( + ResourceLocation.CODEC.fieldOf("template").forGetter(RoomStructureInfo::template), + RoomStructurePlacement.CODEC.fieldOf("placement").forGetter(RoomStructureInfo::placement) + ).apply(inst, RoomStructureInfo::new)); + + public enum RoomStructurePlacement implements StringRepresentable { + CENTERED_CEILING("centered_ceiling"), + CENTERED("centered"), + CENTERED_FLOOR("centered_floor"); + + public static final StringRepresentable.StringRepresentableCodec CODEC = StringRepresentable.fromEnum(RoomStructurePlacement::values); + + private final String name; + + RoomStructurePlacement(String name) { + this.name = name; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } + } +} diff --git a/room-api/src/main/java/dev/compactmods/machines/api/room/RoomTemplate.java b/room-api/src/main/java/dev/compactmods/machines/api/room/RoomTemplate.java index 5e9f251..590c778 100644 --- a/room-api/src/main/java/dev/compactmods/machines/api/room/RoomTemplate.java +++ b/room-api/src/main/java/dev/compactmods/machines/api/room/RoomTemplate.java @@ -10,6 +10,11 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + import static dev.compactmods.machines.api.Constants.MOD_ID; /** @@ -20,21 +25,21 @@ * @param color The color of the machine blocks created for this template. * @param prefillTemplate A template (structure) file reference, if specified this will fill the new room post-generation */ -public record RoomTemplate(Vec3i internalDimensions, int color, ResourceLocation prefillTemplate) { +public record RoomTemplate(Vec3i internalDimensions, int color, List structures) { public static final ResourceKey> REGISTRY_KEY = ResourceKey.createRegistryKey(new ResourceLocation(MOD_ID, "room_templates")); - public static final ResourceLocation NO_TEMPLATE = new ResourceLocation(Constants.MOD_ID, "empty"); public static final RoomTemplate INVALID_TEMPLATE = new RoomTemplate(0, 0); public static Codec CODEC = RecordCodecBuilder.create(i -> i.group( Vec3i.CODEC.fieldOf("dimensions").forGetter(RoomTemplate::internalDimensions), Codec.INT.fieldOf("color").forGetter(RoomTemplate::color), - ResourceLocation.CODEC.optionalFieldOf("template", NO_TEMPLATE).forGetter(RoomTemplate::prefillTemplate) + RoomStructureInfo.CODEC.listOf().optionalFieldOf("structures", Collections.emptyList()) + .forGetter(RoomTemplate::structures) ).apply(i, RoomTemplate::new)); public RoomTemplate(int cubicSizeInternal, int color) { - this(new Vec3i(cubicSizeInternal, cubicSizeInternal, cubicSizeInternal), color, NO_TEMPLATE); + this(new Vec3i(cubicSizeInternal, cubicSizeInternal, cubicSizeInternal), color, Collections.emptyList()); } public AABB getZeroBoundaries() {