Skip to content
This repository has been archived by the owner on May 4, 2024. It is now read-only.

Commit

Permalink
Structure placement code (multi-structure support)
Browse files Browse the repository at this point in the history
  • Loading branch information
robotgryphon committed Mar 12, 2024
1 parent 7e0f6f8 commit 77917dc
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {

Expand Down Expand Up @@ -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) {

Expand All @@ -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);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<RoomStructureInfo> 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<RoomStructurePlacement> CODEC = StringRepresentable.fromEnum(RoomStructurePlacement::values);

private final String name;

RoomStructurePlacement(String name) {
this.name = name;
}

@Override
public @NotNull String getSerializedName() {
return name;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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<RoomStructureInfo> structures) {

public static final ResourceKey<Registry<RoomTemplate>> 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<RoomTemplate> 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() {
Expand Down

0 comments on commit 77917dc

Please sign in to comment.