first commit

This commit is contained in:
lucashahmann committed 2026-11-04 13:36:01 +01:00
commit 07047c6ee4
53 files changed
+6267

No files matched your search

@@ -0,0 +1,125 @@
package de.lucas.litematica.placement;
import de.lucas.litematica.schematic.LitematicSchematic;
import de.lucas.litematica.schematic.SchematicRegion;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Immutable snapshot of a placement: schematic + origin + transform, with block states already transformed per
* palette entry. Render workers query blocks in world coordinates through this class without locking.
* A new view is created whenever the placement changes.
*/
public final class PlacementView {
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
private final LitematicSchematic schematic;
private final BlockPos origin;
private final PlacementTransform transform;
private final Vec3i localSize;
private final BoundingBox box;
private final List<SchematicRegion> regions;
private final BlockState[][] transformedPalettes;
public PlacementView(LitematicSchematic schematic, BlockPos origin, PlacementTransform transform) {
this.schematic = schematic;
this.origin = origin;
this.transform = transform;
this.localSize = schematic.size();
Vec3i placed = transform.transformSize(this.localSize);
this.box = new BoundingBox(origin.getX(), origin.getY(), origin.getZ(),
origin.getX() + placed.getX() - 1, origin.getY() + placed.getY() - 1, origin.getZ() + placed.getZ() - 1);
this.regions = schematic.regions();
this.transformedPalettes = new BlockState[this.regions.size()][];
for (int i = 0; i < this.regions.size(); i++) {
BlockState[] palette = this.regions.get(i).palette();
BlockState[] transformed = new BlockState[palette.length];
for (int j = 0; j < palette.length; j++) {
transformed[j] = transform.apply(palette[j]);
}
this.transformedPalettes[i] = transformed;
}
}
public LitematicSchematic schematic() {
return this.schematic;
}
public BlockPos origin() {
return this.origin;
}
public PlacementTransform transform() {
return this.transform;
}
/** World-space bounding box (inclusive). */
public BoundingBox box() {
return this.box;
}
/** Transformed block state at a world position; air outside the schematic. {@code scratch} avoids allocation. */
public BlockState getState(int x, int y, int z, BlockPos.MutableBlockPos scratch) {
int region = this.locate(x, y, z, scratch);
if (region < 0) {
return AIR;
}
SchematicRegion r = this.regions.get(region);
return this.transformedPalettes[region][r.paletteIndex(scratch.getX(), scratch.getY(), scratch.getZ())];
}
/** {@code true} if the schematic contains a block at this position that does not exist in the running game. */
public boolean isUnknown(int x, int y, int z, BlockPos.MutableBlockPos scratch) {
int region = this.locate(x, y, z, scratch);
if (region < 0) {
return false;
}
SchematicRegion r = this.regions.get(region);
return r.unknown()[r.paletteIndex(scratch.getX(), scratch.getY(), scratch.getZ())];
}
@Nullable
public CompoundTag getBlockEntity(int x, int y, int z, BlockPos.MutableBlockPos scratch) {
int region = this.locate(x, y, z, scratch);
return region < 0 ? null : this.regions.get(region).getBlockEntity(scratch.getX(), scratch.getY(), scratch.getZ());
}
/**
* Resolves a world position to a region index, leaving the region-local coordinates in {@code scratch}.
* Returns -1 if no region covers the position.
*/
private int locate(int x, int y, int z, BlockPos.MutableBlockPos scratch) {
if (!this.box.isInside(x, y, z)) {
return -1;
}
this.transform.toLocal(x - this.origin.getX(), y - this.origin.getY(), z - this.origin.getZ(), this.localSize, scratch);
int lx = scratch.getX(), ly = scratch.getY(), lz = scratch.getZ();
int fallback = -1;
for (int i = 0; i < this.regions.size(); i++) {
SchematicRegion r = this.regions.get(i);
int rx = lx - r.min().getX(), ry = ly - r.min().getY(), rz = lz - r.min().getZ();
if (r.contains(rx, ry, rz)) {
int index = r.paletteIndex(rx, ry, rz);
if (this.regions.size() == 1 || !r.palette()[index].isAir() || r.unknown()[index]) {
scratch.set(rx, ry, rz);
return i;
}
if (fallback < 0) {
fallback = i;
}
}
}
if (fallback >= 0) {
SchematicRegion r = this.regions.get(fallback);
scratch.set(lx - r.min().getX(), ly - r.min().getY(), lz - r.min().getZ());
}
return fallback;
}
}