mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-27 06:20:02 +00:00
Whoops Core
This commit is contained in:
1 parent
98e51cba8d
commit
0782bbf0b4
31 files changed
+1178
-190
No files matched your search
@@ -418,7 +418,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
||||
JSONObject parsed = new JSONObject(rawText);
|
||||
JsonSchemaValidator.validateTopLevelKeys(parsed, rawText, j, resourceTypeName, objectClass);
|
||||
if (objectClass == IrisDimension.class) {
|
||||
PackValidator.requireValidStaticObjects(manager.getDataFolder(), name, parsed);
|
||||
PackValidator.requireValidObjectSettings(manager.getDataFolder(), name, parsed);
|
||||
}
|
||||
T t = getManager().getGson()
|
||||
.fromJson(preprocess(parsed).toString(0), objectClass);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package art.arcane.iris.core.pack;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisDimensionType;
|
||||
import art.arcane.iris.engine.object.IrisObjectScale;
|
||||
import art.arcane.iris.engine.object.IrisWorldBoundary;
|
||||
import art.arcane.volmlib.util.json.JSONArray;
|
||||
import art.arcane.volmlib.util.json.JSONObject;
|
||||
@@ -57,6 +58,7 @@ final class PackDimensionValidator {
|
||||
validateDimensionHeights(packFolder, dimensionKey, dimJson, blockingErrors);
|
||||
validateWorldBoundary(dimensionKey, dimJson, blockingErrors);
|
||||
validateStaticObjects(packFolder, dimensionKey, dimJson, blockingErrors);
|
||||
validateObjectScaleFactor(dimensionKey, dimJson, blockingErrors);
|
||||
validateDimensionStack(packFolder, dimensionKey, dimJson, dimensionKeys, blockingErrors);
|
||||
|
||||
JSONArray regionsArray = dimJson.optJSONArray("regions");
|
||||
@@ -102,6 +104,11 @@ final class PackDimensionValidator {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateObjectScaleFactor(String dimensionKey, JSONObject dimension, List<String> errors) {
|
||||
validateFiniteNumber(dimension, "allObjectScaleFactor", IrisObjectScale.MINIMUM_FACTOR,
|
||||
IrisObjectScale.MAXIMUM_FACTOR, "Dimension '" + dimensionKey + "'", errors);
|
||||
}
|
||||
|
||||
static void validateDimensionStack(File packFolder, String dimensionKey, JSONObject dimension, Set<String> dimensionKeys,
|
||||
List<String> blockingErrors) {
|
||||
if (!dimension.has("dimensionStack")) {
|
||||
|
||||
@@ -86,9 +86,10 @@ public final class PackValidator {
|
||||
return validate(packFolder, false);
|
||||
}
|
||||
|
||||
public static void requireValidStaticObjects(File packFolder, String dimensionKey, JSONObject dimension) {
|
||||
public static void requireValidObjectSettings(File packFolder, String dimensionKey, JSONObject dimension) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
PackDimensionValidator.validateStaticObjects(packFolder, dimensionKey, dimension, errors);
|
||||
PackDimensionValidator.validateObjectScaleFactor(dimensionKey, dimension, errors);
|
||||
if (!errors.isEmpty()) {
|
||||
throw new IllegalArgumentException(String.join("; ", errors));
|
||||
}
|
||||
|
||||
@@ -150,7 +150,8 @@ public class TreeSVC implements IrisService {
|
||||
}
|
||||
|
||||
saplingPlane.forEach(block -> block.setType(Material.AIR));
|
||||
IrisObject object = worldAccess.getData().getObjectLoader().load(pool.getRandom(RNG.r));
|
||||
IrisObject object = placement.scaleObject(RNG.r,
|
||||
worldAccess.getData().getObjectLoader().load(pool.getRandom(RNG.r)), dimension);
|
||||
|
||||
if (object == null) {
|
||||
return;
|
||||
|
||||
+8
-2
@@ -477,8 +477,9 @@ public class IrisStructureComponent extends IrisMantleComponent {
|
||||
if (object == null) {
|
||||
return;
|
||||
}
|
||||
int horizontalSpan = Math.max(1, Math.max(object.getW(), object.getD()));
|
||||
int verticalSpan = Math.max(1, object.getH());
|
||||
double scale = Math.max(1D, getDimension().getAllObjectScaleFactor());
|
||||
int horizontalSpan = scaledMarkerSpan(Math.max(object.getW(), object.getD()), scale);
|
||||
int verticalSpan = scaledMarkerSpan(object.getH(), scale);
|
||||
ObjectMarkerBounds search = intersections.expand(horizontalSpan - 1, verticalSpan - 1, worldHeight);
|
||||
KList<ObjectBlockPosition> positions = new KList<>();
|
||||
int minChunkX = Math.floorDiv(search.minX, 16);
|
||||
@@ -516,6 +517,11 @@ public class IrisStructureComponent extends IrisMantleComponent {
|
||||
return decoded != null && !decoded.structureAware();
|
||||
}
|
||||
|
||||
static int scaledMarkerSpan(int dimension, double scale) {
|
||||
return scale <= 1D ? Math.max(1, dimension)
|
||||
: (int) Math.ceil(Math.max(1, dimension) * scale);
|
||||
}
|
||||
|
||||
static ObjectMarkerBounds markerSearchBounds(int minX, int minY, int minZ,
|
||||
int maxX, int maxY, int maxZ,
|
||||
int horizontalSpan, int verticalSpan,
|
||||
|
||||
+4
-4
@@ -188,7 +188,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
||||
if (raw == null) {
|
||||
continue;
|
||||
}
|
||||
IrisObject obj0 = placement.getScale().get(rng, raw);
|
||||
IrisObject obj0 = placement.scaleObject(rng, raw, getDimension());
|
||||
if (obj0 == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -248,7 +248,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
||||
if (raw == null) {
|
||||
continue;
|
||||
}
|
||||
IrisObject obj0 = placement.getScale().get(rng, raw);
|
||||
IrisObject obj0 = placement.scaleObject(rng, raw, getDimension());
|
||||
if (obj0 == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -340,7 +340,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
||||
if (raw == null) {
|
||||
continue;
|
||||
}
|
||||
IrisObject obj0 = placement.getScale().get(rng, raw);
|
||||
IrisObject obj0 = placement.scaleObject(rng, raw, getDimension());
|
||||
if (obj0 == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -628,7 +628,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
||||
size = IrisObject.sampleSize(file);
|
||||
sizeCache.put(objectKey, size);
|
||||
}
|
||||
int reach = MantleObjectComponent.calculatePlacementReach(size, placement);
|
||||
int reach = MantleObjectComponent.calculatePlacementReach(size, placement, placement.getMaximumScale(getDimension()));
|
||||
if (reach > 128 && warnedLargeObjects.add(objectKey)) {
|
||||
IrisLogging.warn("Floating object " + objectKey + " has a large placement reach (" + reach + " blocks) and may increase memory usage!");
|
||||
}
|
||||
|
||||
+17
-15
@@ -622,7 +622,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
IObjectPlacer placer = golden ? new GoldenDebugObjectPlacer(basePlacer, scope + "/" + p.getName()) : basePlacer;
|
||||
int density = Math.max(1, p.getDensity());
|
||||
for (int i = 0; i < density; i++) {
|
||||
IrisObject variant = p.getVariantObject(getData(), rng);
|
||||
IrisObject variant = placement.scaleObject(rng, p.getVariantObject(getData(), rng), getDimension());
|
||||
if (variant == null) {
|
||||
if (golden) {
|
||||
IrisLogging.debug("Goldendebug procedural pick: chunk=" + x + "," + z
|
||||
@@ -887,7 +887,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
|
||||
for (int i = 0; i < density; i++) {
|
||||
attempts++;
|
||||
IrisObject v = objectPlacement.getScale().get(rng, objectPlacement.getObject(complex, rng));
|
||||
IrisObject v = objectPlacement.scaleObject(rng, objectPlacement.getObject(complex, rng), getDimension());
|
||||
if (v == null) {
|
||||
nullObjects++;
|
||||
if (traceRegen) {
|
||||
@@ -1030,7 +1030,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
|
||||
for (int i = 0; i < density; i++) {
|
||||
attempts++;
|
||||
IrisObject object = objectPlacement.getScale().get(rng, objectPlacement.getObject(complex, rng));
|
||||
IrisObject object = objectPlacement.scaleObject(rng, objectPlacement.getObject(complex, rng), getDimension());
|
||||
if (object == null) {
|
||||
nullObjects++;
|
||||
if (traceRegen) {
|
||||
@@ -1261,7 +1261,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
int density = objectPlacement.getDensity(rng, minX, minZ, getData());
|
||||
|
||||
for (int i = 0; i < density; i++) {
|
||||
IrisObject v = objectPlacement.getScale().get(rng, objectPlacement.getObject(complex, rng));
|
||||
IrisObject v = objectPlacement.scaleObject(rng, objectPlacement.getObject(upperCtx, rng), upperCtx.getDimension());
|
||||
if (v == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -1647,7 +1647,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
private Set<String> guessPlacedKeys(RNG rng, int x, int z, IrisObjectPlacement objectPlacement) {
|
||||
Set<String> f = new KSet<>();
|
||||
for (int i = 0; i < objectPlacement.getDensity(rng, x, z, getData()); i++) {
|
||||
IrisObject v = objectPlacement.getScale().get(rng, objectPlacement.getObject(getComplex(), rng));
|
||||
IrisObject v = objectPlacement.scaleObject(rng, objectPlacement.getObject(getComplex(), rng), getDimension());
|
||||
if (v == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -1708,11 +1708,12 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
radius = Math.max(radius, computePlacementRadius(
|
||||
region.getObjects(),
|
||||
dimension,
|
||||
dataProvider,
|
||||
sizeCache,
|
||||
warnedLargeObjects
|
||||
));
|
||||
radius = Math.max(radius, computeProceduralRadius(region.getProceduralObjects(), dataProvider));
|
||||
radius = Math.max(radius, computeProceduralRadius(region.getProceduralObjects(), dimension, dataProvider));
|
||||
}
|
||||
for (IrisBiome biome : dimension.getReachableBiomes(dataProvider)) {
|
||||
if (biome == null) {
|
||||
@@ -1720,17 +1721,19 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
radius = Math.max(radius, computePlacementRadius(
|
||||
biome.getObjects(),
|
||||
dimension,
|
||||
dataProvider,
|
||||
sizeCache,
|
||||
warnedLargeObjects
|
||||
));
|
||||
radius = Math.max(radius, computeProceduralRadius(biome.getProceduralObjects(), dataProvider));
|
||||
radius = Math.max(radius, computeProceduralRadius(biome.getProceduralObjects(), dimension, dataProvider));
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
private int computePlacementRadius(
|
||||
KList<IrisObjectPlacement> placements,
|
||||
IrisDimension dimension,
|
||||
DataProvider dataProvider,
|
||||
KMap<String, IrisBlockVector> sizeCache,
|
||||
KSet<String> warnedLargeObjects
|
||||
@@ -1746,7 +1749,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
if (size == null) {
|
||||
continue;
|
||||
}
|
||||
int reach = calculatePlacementReach(size, placement);
|
||||
int reach = calculatePlacementReach(size, placement, placement.getMaximumScale(dimension));
|
||||
if (reach > 128 && warnedLargeObjects.add(objectKey)) {
|
||||
IrisLogging.warn("Object " + objectKey + " has a large placement reach (" + reach + " blocks) and may increase memory usage!");
|
||||
}
|
||||
@@ -1759,7 +1762,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
return radius;
|
||||
}
|
||||
|
||||
private int computeProceduralRadius(IrisProceduralObjects procedural, DataProvider dataProvider) {
|
||||
private int computeProceduralRadius(IrisProceduralObjects procedural, IrisDimension dimension, DataProvider dataProvider) {
|
||||
if (procedural == null || procedural.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1778,13 +1781,13 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
continue;
|
||||
}
|
||||
IrisBlockVector size = new IrisBlockVector(variant.getW(), variant.getH(), variant.getD());
|
||||
radius = Math.max(radius, calculatePlacementReach(size, objectPlacement));
|
||||
radius = Math.max(radius, calculatePlacementReach(size, objectPlacement, objectPlacement.getMaximumScale(dimension)));
|
||||
}
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
static int calculatePlacementReach(IrisBlockVector size, IrisObjectPlacement placement) {
|
||||
static int calculatePlacementReach(IrisBlockVector size, IrisObjectPlacement placement, double scale) {
|
||||
if (size == null) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1792,7 +1795,6 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
return Math.max(Math.abs(size.getBlockX()), Math.abs(size.getBlockZ()));
|
||||
}
|
||||
|
||||
double scale = placement.getScale() != null ? Math.max(1D, placement.getScale().getMaxScale()) : 1D;
|
||||
int width = scaledDimension(size.getBlockX(), scale);
|
||||
int height = scaledDimension(size.getBlockY(), scale);
|
||||
int depth = scaledDimension(size.getBlockZ(), scale);
|
||||
@@ -1823,7 +1825,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
int reach = calculatePlacementReach(
|
||||
new IrisBlockVector(object.getW(), object.getH(), object.getD()),
|
||||
placement);
|
||||
placement, 1D);
|
||||
return complex.allowsNewGenerationFootprint(
|
||||
saturatedOffset(anchorX, -reach),
|
||||
saturatedOffset(anchorZ, -reach),
|
||||
@@ -1838,10 +1840,10 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
|
||||
private static int scaledDimension(int dimension, double scale) {
|
||||
int absoluteDimension = Math.abs(dimension);
|
||||
if (scale <= 1D) {
|
||||
if (scale == 1D) {
|
||||
return absoluteDimension;
|
||||
}
|
||||
return (int) Math.ceil((absoluteDimension * scale) + (scale * 2D));
|
||||
return (int) Math.ceil(absoluteDimension * scale);
|
||||
}
|
||||
|
||||
private static int calculateTranslationReach(IrisObjectTranslate translate, IrisObjectRotation rotation, boolean rotateVertically) {
|
||||
|
||||
@@ -186,6 +186,10 @@ public class IrisDimension extends IrisRegistrant {
|
||||
@MaxNumber(16)
|
||||
@Desc("Minimum surface-support buffer, in blocks, applied to every surface object placement in this dimension. A placement may ask for more but never less.")
|
||||
private int objectSurfaceSupportBuffer = 2;
|
||||
@MinNumber(IrisObjectScale.MINIMUM_FACTOR)
|
||||
@MaxNumber(IrisObjectScale.MAXIMUM_FACTOR)
|
||||
@Desc("Default size factor for objects in this dimension. 0.5 halves their size and 2 doubles it. Any explicit object or placement scale, including 1, overrides this factor. Jigsaw structures are excluded.")
|
||||
private double allObjectScaleFactor = 1D;
|
||||
@Desc("The world environment")
|
||||
private IrisEnvironment environment = IrisEnvironment.NORMAL;
|
||||
@RegistryListResource(IrisRegion.class)
|
||||
@@ -319,6 +323,16 @@ public class IrisDimension extends IrisRegistrant {
|
||||
return staticObjectLayer.aquire(() -> IrisStaticObjectLayer.compile(this, data));
|
||||
}
|
||||
|
||||
public double getAllObjectScaleFactor() {
|
||||
return IrisObjectScale.requireValidFactor(allObjectScaleFactor, "allObjectScaleFactor");
|
||||
}
|
||||
|
||||
public IrisDimension setAllObjectScaleFactor(double factor) {
|
||||
allObjectScaleFactor = IrisObjectScale.requireValidFactor(factor, "allObjectScaleFactor");
|
||||
staticObjectLayer.reset();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the version-content gate over every static object so the pack report lists the entries this server cannot
|
||||
* place. The static layer makes the same decision when it compiles; validation calls this on its detached loader
|
||||
|
||||
@@ -162,8 +162,8 @@ public class IrisObjectPlacement {
|
||||
private KList<IrisObjectReplace> edit = new KList<>();
|
||||
@Desc("Translate this object's placement")
|
||||
private IrisObjectTranslate translate = new IrisObjectTranslate();
|
||||
@Desc("Scale Objects")
|
||||
private IrisObjectScale scale = new IrisObjectScale();
|
||||
@Desc("Explicit object scale. When omitted, the dimension's allObjectScaleFactor applies. Any authored scale, including size 1, overrides that factor.")
|
||||
private IrisObjectScale scale;
|
||||
@ArrayType(min = 1, type = IrisObjectLoot.class)
|
||||
@Desc("The loot tables to apply to these objects")
|
||||
private KList<IrisObjectLoot> loot = new KList<>();
|
||||
@@ -363,6 +363,18 @@ public class IrisObjectPlacement {
|
||||
return data.getObjectLoader().load(pool.get(random.nextInt(pool.size())));
|
||||
}
|
||||
|
||||
public IrisObject scaleObject(RNG random, IrisObject object, IrisDimension dimension) {
|
||||
return scale == null
|
||||
? IrisObjectScale.getFixed(object, dimension == null ? 1D : dimension.getAllObjectScaleFactor())
|
||||
: scale.get(random, object);
|
||||
}
|
||||
|
||||
public double getMaximumScale(IrisDimension dimension) {
|
||||
return scale == null
|
||||
? dimension == null ? 1D : dimension.getAllObjectScaleFactor()
|
||||
: scale.getMaxScale();
|
||||
}
|
||||
|
||||
public boolean matches(IrisTreeSize size, TreeType type) {
|
||||
for (IrisTree i : getTrees()) {
|
||||
if (i.matches(size, type)) {
|
||||
|
||||
@@ -125,6 +125,7 @@ final class IrisObjectPlacementRunner {
|
||||
boolean preventingDecay = placer.isPreventingDecay();
|
||||
boolean waterlogCandidate = config.isWaterloggable() || config.isUnderwater();
|
||||
boolean rawStructurePiece = config.getMode() == ObjectPlaceMode.STRUCTURE_PIECE;
|
||||
boolean paint = yv < 0 && config.getMode() == ObjectPlaceMode.PAINT;
|
||||
boolean organicFloor = config.getMode() == ObjectPlaceMode.ORGANIC_STILT;
|
||||
boolean ceilingHang = config.getMode() == ObjectPlaceMode.CEILING_HANG;
|
||||
boolean organic = organicFloor || ceilingHang;
|
||||
@@ -257,7 +258,10 @@ final class IrisObjectPlacementRunner {
|
||||
}
|
||||
}
|
||||
} else if (config.getMode().equals(ObjectPlaceMode.PAINT)) {
|
||||
y = placer.getHighest(x, z, self.getLoader(), config.isUnderwater()) + rty;
|
||||
int anchorX = x + (translating ? (int) Math.round(translateOffset.getX()) : 0);
|
||||
int anchorZ = z + (translating ? (int) Math.round(translateOffset.getZ()) : 0);
|
||||
y = (c != null ? c.getSurface()
|
||||
: placer.getHighest(anchorX, anchorZ, self.getLoader(), config.isUnderwater())) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||
bail = true;
|
||||
@@ -302,12 +306,30 @@ final class IrisObjectPlacementRunner {
|
||||
return -1;
|
||||
}
|
||||
|
||||
IrisPaintSurfaceProjection paintProjection = null;
|
||||
KList<IrisBlockVector> paintSupport = null;
|
||||
if (paint) {
|
||||
int buffer = config.isRequireSurfaceSupport() ? Math.max(0, config.getSurfaceSupportBuffer()) : 0;
|
||||
int anchorX = x + (translating ? (int) Math.round(translateOffset.getX()) : 0);
|
||||
int anchorZ = z + (translating ? (int) Math.round(translateOffset.getZ()) : 0);
|
||||
paintProjection = IrisPaintSurfaceProjection.create(placer, new IrisPaintSurfaceProjection.Request(
|
||||
self.getLoader(), x + placementBounds.minX() - buffer, x + placementBounds.maxX() + buffer,
|
||||
z + placementBounds.minZ() - buffer, z + placementBounds.maxZ() + buffer,
|
||||
anchorX, y - rty, anchorZ, config.isUnderwater(), config.getHeightmap() != null));
|
||||
if (paintProjection == null) {
|
||||
return -1;
|
||||
}
|
||||
paintSupport = transformedPaintSupport(spin, translateOffset, surfaceWarp, warpHalf, x, y + yrand, z);
|
||||
}
|
||||
|
||||
if (yv < 0
|
||||
&& config.getCarvingSupport().supportsSurface()
|
||||
&& config.getMode() != ObjectPlaceMode.FLOATING
|
||||
&& !rawStructurePiece
|
||||
&& IrisSurfaceSupport.intersectsHydrology(oplacer, x, z, config.getTranslate(),
|
||||
config.getRotation(), spinx, spiny, spinz, self.getSurfaceSupportOffsets())) {
|
||||
&& (paint ? IrisSurfaceSupport.intersectsHydrology(oplacer, x, z, null, null,
|
||||
0, 0, 0, paintSupport)
|
||||
: IrisSurfaceSupport.intersectsHydrology(oplacer, x, z, config.getTranslate(),
|
||||
config.getRotation(), spinx, spiny, spinz, self.getSurfaceSupportOffsets()))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -325,9 +347,12 @@ final class IrisObjectPlacementRunner {
|
||||
&& !config.isUnderwater()
|
||||
&& !config.isOnwater()
|
||||
&& config.isRequireSurfaceSupport()
|
||||
&& IrisSurfaceSupport.isUnsupported(oplacer, self.getLoader(), x, z, config.getTranslate(),
|
||||
&& (paint ? IrisSurfaceSupport.isUnsupported(oplacer, self.getLoader(), x, z, null, null,
|
||||
0, 0, 0, paintSupport, config.getSurfaceSupportBuffer(), config.getSurfaceSupportDepth(),
|
||||
paintProjection)
|
||||
: IrisSurfaceSupport.isUnsupported(oplacer, self.getLoader(), x, z, config.getTranslate(),
|
||||
config.getRotation(), spinx, spiny, spinz, self.getSurfaceSupportOffsets(),
|
||||
config.getSurfaceSupportBuffer(), config.getSurfaceSupportDepth())) {
|
||||
config.getSurfaceSupportBuffer(), config.getSurfaceSupportDepth()))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -351,22 +376,27 @@ final class IrisObjectPlacementRunner {
|
||||
}
|
||||
|
||||
if (!rawStructurePiece && nativeStructureVetoes(placer, config, spin, placementBounds, translating, translateOffset, ceilingHang,
|
||||
yv < 0 && config.getMode() == ObjectPlaceMode.PAINT, warpMargin, x, y + yrand, z)) {
|
||||
paintProjection, surfaceWarp, warpHalf, yrand, warpMargin, x, y + yrand, z)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean paint = yv < 0 && config.getMode() == ObjectPlaceMode.PAINT;
|
||||
WorldBounds worldBounds = null;
|
||||
if (config.isBore() || (!config.isForcePlace() && !rawStructurePiece
|
||||
&& (!config.getAllowedCollisions().isEmpty() || !config.getForbiddenCollisions().isEmpty()))) {
|
||||
worldBounds = resolveWorldBounds(placer, config, placementBounds, paint, x, y + yrand, z);
|
||||
worldBounds = resolveWorldBounds(placementBounds, paintProjection, yrand, x, y + yrand, z);
|
||||
}
|
||||
|
||||
if (!config.isForcePlace() && !rawStructurePiece && (!config.getAllowedCollisions().isEmpty() || !config.getForbiddenCollisions().isEmpty())) {
|
||||
Engine engine = requireDataEngine(rdata, "collision settings");
|
||||
for (int i = worldBounds.minX(); i <= worldBounds.maxX(); i++) {
|
||||
for (int j = worldBounds.minY(); j <= worldBounds.maxY(); j++) {
|
||||
for (int k = worldBounds.minZ(); k <= worldBounds.maxZ(); k++) {
|
||||
for (int k = worldBounds.minZ(); k <= worldBounds.maxZ(); k++) {
|
||||
int surface = paint ? paintProjection.surfaceY(i, k) : 0;
|
||||
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
||||
continue;
|
||||
}
|
||||
int minimumY = paint ? surface + placementBounds.minY() + Math.floorDiv(self.h, 2) + yrand : worldBounds.minY();
|
||||
int maximumY = paint ? surface + placementBounds.maxY() + Math.floorDiv(self.h, 2) + yrand : worldBounds.maxY();
|
||||
for (int j = minimumY; j <= maximumY; j++) {
|
||||
String placementMarker = placer.getData(i, j, k, String.class);
|
||||
PlacedObject p = engine.resolveObjectPlacementMarker(i, k, placementMarker);
|
||||
if (p == null) continue;
|
||||
@@ -387,8 +417,14 @@ final class IrisObjectPlacementRunner {
|
||||
|
||||
if (config.isBore()) {
|
||||
for (int i = worldBounds.minX(); i <= worldBounds.maxX(); i++) {
|
||||
for (int j = worldBounds.minY() - config.getBoreExtendMinY(); j <= worldBounds.maxY() + config.getBoreExtendMaxY(); j++) {
|
||||
for (int k = worldBounds.minZ(); k <= worldBounds.maxZ(); k++) {
|
||||
for (int k = worldBounds.minZ(); k <= worldBounds.maxZ(); k++) {
|
||||
int surface = paint ? paintProjection.surfaceY(i, k) : 0;
|
||||
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
||||
continue;
|
||||
}
|
||||
int minimumY = paint ? surface + placementBounds.minY() + Math.floorDiv(self.h, 2) + yrand : worldBounds.minY();
|
||||
int maximumY = paint ? surface + placementBounds.maxY() + Math.floorDiv(self.h, 2) + yrand : worldBounds.maxY();
|
||||
for (int j = minimumY - config.getBoreExtendMinY(); j <= maximumY + config.getBoreExtendMaxY(); j++) {
|
||||
placer.set(i, j, k, IrisObject.States.air());
|
||||
}
|
||||
}
|
||||
@@ -546,8 +582,14 @@ final class IrisObjectPlacementRunner {
|
||||
zz += surfaceWarp.fitDouble(-warpHalf, warpHalf, i.getZ() + z, i.getY() + y, i.getX() + x);
|
||||
}
|
||||
|
||||
if (yv < 0 && (config.getMode().equals(ObjectPlaceMode.PAINT)) && !B.isVineBlock(data)) {
|
||||
yy = (int) Math.round(i.getY()) + Math.floorDiv(self.h, 2) + placer.getHighest(xx, zz, self.getLoader(), config.isUnderwater());
|
||||
if (paint) {
|
||||
int surface = paintProjection.surfaceY(xx, zz);
|
||||
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
||||
continue;
|
||||
}
|
||||
if (!B.isVineBlock(data)) {
|
||||
yy = (int) Math.round(i.getY()) + Math.floorDiv(self.h, 2) + surface + yrand;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isMeld() && !rawStructurePiece && !placer.isSolid(xx, yy, zz)) {
|
||||
@@ -864,6 +906,26 @@ final class IrisObjectPlacementRunner {
|
||||
return !wouldReplace && (rawStructurePiece || !air);
|
||||
}
|
||||
|
||||
private KList<IrisBlockVector> transformedPaintSupport(SpinKernel spin, IrisBlockVector translateOffset,
|
||||
CNG warp, double half, int x, int y, int z) {
|
||||
KList<IrisBlockVector> offsets = new KList<>();
|
||||
for (IrisBlockVector source : self.getSurfaceSupportOffsets()) {
|
||||
IrisBlockVector offset = source.clone();
|
||||
spin.rotate(offset);
|
||||
if (translateOffset != null) {
|
||||
offset.add(translateOffset);
|
||||
}
|
||||
int worldX = x + (int) Math.round(offset.getX());
|
||||
int worldZ = z + (int) Math.round(offset.getZ());
|
||||
if (warp != null) {
|
||||
worldX += warp.fitDouble(-half, half, offset.getX() + x, offset.getY() + y, offset.getZ() + z);
|
||||
worldZ += warp.fitDouble(-half, half, offset.getZ() + z, offset.getY() + y, offset.getX() + x);
|
||||
}
|
||||
offsets.add(new IrisBlockVector(worldX - x, 0, worldZ - z));
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
private TransformedBounds transformedBounds(SpinKernel spin, boolean translating, IrisBlockVector translateOffset,
|
||||
boolean ceilingHang, int margin) {
|
||||
int sourceMinX = -self.getCenter().getBlockX();
|
||||
@@ -909,13 +971,13 @@ final class IrisObjectPlacementRunner {
|
||||
return new TransformedBounds(minX - margin, maxX + margin, minY, maxY, minZ - margin, maxZ + margin);
|
||||
}
|
||||
|
||||
private WorldBounds resolveWorldBounds(IObjectPlacer placer, IrisObjectPlacement config, TransformedBounds bounds,
|
||||
boolean paint, int x, int y, int z) {
|
||||
private WorldBounds resolveWorldBounds(TransformedBounds bounds, IrisPaintSurfaceProjection projection,
|
||||
int randomY, int x, int y, int z) {
|
||||
int minX = x + bounds.minX();
|
||||
int maxX = x + bounds.maxX();
|
||||
int minZ = z + bounds.minZ();
|
||||
int maxZ = z + bounds.maxZ();
|
||||
if (!paint) {
|
||||
if (projection == null) {
|
||||
return new WorldBounds(minX, maxX, y + bounds.minY(), y + bounds.maxY(), minZ, maxZ);
|
||||
}
|
||||
|
||||
@@ -923,12 +985,15 @@ final class IrisObjectPlacementRunner {
|
||||
int maximumSurface = Integer.MIN_VALUE;
|
||||
for (int worldX = minX; worldX <= maxX; worldX++) {
|
||||
for (int worldZ = minZ; worldZ <= maxZ; worldZ++) {
|
||||
int surface = placer.getHighest(worldX, worldZ, self.getLoader(), config.isUnderwater());
|
||||
int surface = projection.surfaceY(worldX, worldZ);
|
||||
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
||||
continue;
|
||||
}
|
||||
minimumSurface = Math.min(minimumSurface, surface);
|
||||
maximumSurface = Math.max(maximumSurface, surface);
|
||||
}
|
||||
}
|
||||
int paintOffset = Math.floorDiv(self.h, 2);
|
||||
int paintOffset = Math.floorDiv(self.h, 2) + randomY;
|
||||
return new WorldBounds(minX, maxX, minimumSurface + bounds.minY() + paintOffset,
|
||||
maximumSurface + bounds.maxY() + paintOffset, minZ, maxZ);
|
||||
}
|
||||
@@ -960,7 +1025,8 @@ final class IrisObjectPlacementRunner {
|
||||
*/
|
||||
private boolean nativeStructureVetoes(IObjectPlacer placer, IrisObjectPlacement config, SpinKernel spin, TransformedBounds bounds,
|
||||
boolean translating, IrisBlockVector translateOffset, boolean ceilingHang,
|
||||
boolean paint, int warpMargin, int x, int y, int z) {
|
||||
IrisPaintSurfaceProjection projection, CNG surfaceWarp, double warpHalf,
|
||||
int randomY, int warpMargin, int x, int y, int z) {
|
||||
Engine engine = placer.getEngine();
|
||||
if (engine == null) {
|
||||
return false;
|
||||
@@ -976,8 +1042,8 @@ final class IrisObjectPlacementRunner {
|
||||
}
|
||||
|
||||
int worldY = y + engine.getMinHeight();
|
||||
int envelopeMinY = paint ? Integer.MIN_VALUE : worldY + bounds.minY() - warpMargin;
|
||||
int envelopeMaxY = paint ? Integer.MAX_VALUE : worldY + bounds.maxY() + warpMargin;
|
||||
int envelopeMinY = projection != null ? Integer.MIN_VALUE : worldY + bounds.minY() - warpMargin;
|
||||
int envelopeMaxY = projection != null ? Integer.MAX_VALUE : worldY + bounds.maxY() + warpMargin;
|
||||
boolean envelopeMeetsPiece = false;
|
||||
for (NativeStructureVolume volume : volumes) {
|
||||
if (volume.intersects(minX, envelopeMinY, minZ, maxX, envelopeMaxY, maxZ)) {
|
||||
@@ -1010,14 +1076,24 @@ final class IrisObjectPlacementRunner {
|
||||
|
||||
int xx = x + (int) Math.round(i.getX());
|
||||
int zz = z + (int) Math.round(i.getZ());
|
||||
int yy = paint
|
||||
? (int) Math.round(i.getY()) + Math.floorDiv(self.h, 2)
|
||||
+ placer.getHighest(xx, zz, self.getLoader(), config.isUnderwater())
|
||||
: y + (int) Math.round(i.getY());
|
||||
int yy = y + (int) Math.round(i.getY());
|
||||
if (projection != null) {
|
||||
if (surfaceWarp != null) {
|
||||
xx += surfaceWarp.fitDouble(-warpHalf, warpHalf, i.getX() + x, i.getY() + y, i.getZ() + z);
|
||||
zz += surfaceWarp.fitDouble(-warpHalf, warpHalf, i.getZ() + z, i.getY() + y, i.getX() + x);
|
||||
}
|
||||
int surface = projection.surfaceY(xx, zz);
|
||||
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
||||
continue;
|
||||
}
|
||||
if (!B.isVineBlock(state)) {
|
||||
yy = (int) Math.round(i.getY()) + Math.floorDiv(self.h, 2) + surface + randomY;
|
||||
}
|
||||
}
|
||||
int worldBlockY = yy + engine.getMinHeight();
|
||||
|
||||
for (NativeStructureVolume volume : volumes) {
|
||||
if (volume.containsWithin(xx, worldBlockY, zz, warpMargin)) {
|
||||
if (volume.containsWithin(xx, worldBlockY, zz, projection == null ? warpMargin : 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ import java.util.Map;
|
||||
@Desc("Scale objects")
|
||||
@Data
|
||||
public class IrisObjectScale {
|
||||
public static final double MINIMUM_FACTOR = 0.01D;
|
||||
public static final double MAXIMUM_FACTOR = 50D;
|
||||
private static final long CACHE_MIN_ESTIMATED_BYTES = 16L * 1024L * 1024L;
|
||||
private static final long CACHE_MAX_ESTIMATED_BYTES = 64L * 1024L * 1024L;
|
||||
private static final long CACHE_ENTRY_ESTIMATED_BYTES = 512L;
|
||||
@@ -88,15 +90,32 @@ public class IrisObjectScale {
|
||||
return minimumScale != 1 || maximumScale != 1;
|
||||
}
|
||||
|
||||
public IrisObjectScale setSize(double size) {
|
||||
this.size = requireValidFactor(size, "size");
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisObjectScale setMinimumScale(double minimumScale) {
|
||||
this.minimumScale = requireValidFactor(minimumScale, "minimumScale");
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisObjectScale setMaximumScale(double maximumScale) {
|
||||
this.maximumScale = requireValidFactor(maximumScale, "maximumScale");
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMaxSizeFor(int indim) {
|
||||
return (int) Math.ceil(getMaxScale() * indim);
|
||||
}
|
||||
|
||||
public double getMaxScale() {
|
||||
if (size != 1) {
|
||||
return size;
|
||||
ScaleRequest request = snapshotRequest();
|
||||
if (!request.shouldScale()) {
|
||||
return 1D;
|
||||
}
|
||||
return Math.max(minimumScale, maximumScale);
|
||||
return request.size() != 1D ? request.size()
|
||||
: Math.max(request.minimumScale(), request.maximumScale());
|
||||
}
|
||||
|
||||
public IrisObject get(RNG rng, IrisObject origin) {
|
||||
@@ -109,7 +128,27 @@ public class IrisObjectScale {
|
||||
return origin;
|
||||
}
|
||||
|
||||
int variantIndex = request.selectVariant(rng);
|
||||
return get(origin, request, request.selectVariant(rng));
|
||||
}
|
||||
|
||||
public static IrisObject getFixed(IrisObject origin, double factor) {
|
||||
requireValidFactor(factor, "Object scale");
|
||||
if (origin == null || factor == 1D) {
|
||||
return origin;
|
||||
}
|
||||
return get(origin, new ScaleRequest(factor, 1D, 1D, 7,
|
||||
IrisObjectPlacementScaleInterpolator.NONE), 0);
|
||||
}
|
||||
|
||||
public static double requireValidFactor(double factor, String name) {
|
||||
if (!Double.isFinite(factor) || factor < MINIMUM_FACTOR || factor > MAXIMUM_FACTOR) {
|
||||
throw new IllegalArgumentException(name + " must be finite and between "
|
||||
+ MINIMUM_FACTOR + " and " + MAXIMUM_FACTOR + ".");
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
|
||||
private static IrisObject get(IrisObject origin, ScaleRequest request, int variantIndex) {
|
||||
CacheKey key = CACHE.key(origin, request, variantIndex);
|
||||
CacheLookup lookup = CACHE.lookup(key);
|
||||
if (lookup.variant() != null) {
|
||||
@@ -145,6 +184,9 @@ public class IrisObjectScale {
|
||||
}
|
||||
|
||||
private ScaleRequest snapshotRequest() {
|
||||
requireValidFactor(size, "size");
|
||||
requireValidFactor(minimumScale, "minimumScale");
|
||||
requireValidFactor(maximumScale, "maximumScale");
|
||||
IrisObjectPlacementScaleInterpolator configuredInterpolation = interpolation == null
|
||||
? IrisObjectPlacementScaleInterpolator.NONE
|
||||
: interpolation;
|
||||
|
||||
@@ -88,130 +88,99 @@ final class IrisObjectTransforms {
|
||||
}
|
||||
|
||||
static IrisObject scaledAroundOrigin(IrisObject self, double scale, IrisObjectPlacementScaleInterpolator interpolation) {
|
||||
if (!Double.isFinite(scale) || scale <= 0 || scale > 50) {
|
||||
throw new IllegalArgumentException("Object scale must be finite, greater than zero, and at most 50");
|
||||
}
|
||||
return scaled(self, scale, interpolation, ScaleOrigin.SAVED);
|
||||
}
|
||||
|
||||
private static IrisObject scaled(IrisObject self, double scale, IrisObjectPlacementScaleInterpolator interpolation,
|
||||
ScaleOrigin origin) {
|
||||
IrisObjectScale.requireValidFactor(scale, "Object scale");
|
||||
if (interpolation == null) {
|
||||
interpolation = IrisObjectPlacementScaleInterpolator.NONE;
|
||||
}
|
||||
boolean savedOrigin = origin == ScaleOrigin.SAVED;
|
||||
IrisVector sm1 = new IrisVector(scale - 1, scale - 1, scale - 1);
|
||||
scale = Math.max(0.001, Math.min(50, scale));
|
||||
if (!savedOrigin && scale < 1) {
|
||||
scale = scale - 0.0001;
|
||||
}
|
||||
|
||||
VectorMap<PlatformBlockState> placeBlock = new VectorMap<>();
|
||||
VectorMap<TileData> placeTile = new VectorMap<>();
|
||||
VectorMap<IrisBlockVector> placeMax = savedOrigin && scale > 1 ? new VectorMap<>() : null;
|
||||
|
||||
IrisVector center;
|
||||
IrisObject oo;
|
||||
boolean hasTiles;
|
||||
List<ScaledVoxel> voxels;
|
||||
IrisObject output;
|
||||
self.readLock.lock();
|
||||
try {
|
||||
center = new IrisVector(self.getCenter().getX(), self.getCenter().getY(), self.getCenter().getZ());
|
||||
if (self.getH() == 2) {
|
||||
center = center.setY(center.getBlockY() + 0.5);
|
||||
}
|
||||
if (self.getW() == 2) {
|
||||
center = center.setX(center.getBlockX() + 0.5);
|
||||
}
|
||||
if (self.getD() == 2) {
|
||||
center = center.setZ(center.getBlockZ() + 0.5);
|
||||
}
|
||||
|
||||
oo = savedOrigin ? createOriginScaledObject(self, scale)
|
||||
: new IrisObject((int) Math.ceil((self.w * scale) + (scale * 2)), (int) Math.ceil((self.h * scale) + (scale * 2)), (int) Math.ceil((self.d * scale) + (scale * 2)));
|
||||
oo.setLoadKey(self.getLoadKey());
|
||||
oo.setLoader(self.getLoader());
|
||||
oo.setLoadFile(self.getLoadFile());
|
||||
hasTiles = !self.states.isEmpty();
|
||||
output = savedOrigin ? createOriginScaledObject(self, scale)
|
||||
: createCenteredScaledObject(self, scale);
|
||||
output.setLoadKey(self.getLoadKey());
|
||||
output.setLoader(self.getLoader());
|
||||
output.setLoadFile(self.getLoadFile());
|
||||
voxels = new ArrayList<>(self.blocks.size());
|
||||
Vector3i sourceCenter = self.getCenter();
|
||||
IrisBlockVector targetCenter = new IrisBlockVector(output.getCenter().getX(),
|
||||
output.getCenter().getY(), output.getCenter().getZ());
|
||||
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : self.blocks) {
|
||||
PlatformBlockState bd = entry.getValue();
|
||||
IrisBlockVector sourcePosition = entry.getKey();
|
||||
IrisBlockVector position = savedOrigin
|
||||
? scaledMinimum(sourcePosition, scale)
|
||||
: sourcePosition.clone().add(IrisObject.HALF).subtract(center)
|
||||
.multiply(scale).add(sm1).toBlockVector();
|
||||
placeBlock.put(position, bd);
|
||||
if (placeMax != null) {
|
||||
placeMax.put(position, scaledMaximum(sourcePosition, scale));
|
||||
}
|
||||
if (hasTiles) {
|
||||
TileData tile = self.states.get(entry.getKey());
|
||||
if (tile == null) {
|
||||
placeTile.remove(position);
|
||||
} else {
|
||||
placeTile.put(position, tile);
|
||||
}
|
||||
IrisBlockVector position = savedOrigin ? sourcePosition : new IrisBlockVector(
|
||||
sourcePosition.getBlockX() + sourceCenter.getX(),
|
||||
sourcePosition.getBlockY() + sourceCenter.getY(),
|
||||
sourcePosition.getBlockZ() + sourceCenter.getZ());
|
||||
IrisBlockVector minimum = scaledMinimum(position, scale);
|
||||
IrisBlockVector maximum = scaledMaximum(position, scale);
|
||||
if (!savedOrigin) {
|
||||
minimum.subtract(targetCenter);
|
||||
maximum.subtract(targetCenter);
|
||||
}
|
||||
TileData tile = self.states.get(sourcePosition);
|
||||
voxels.add(new ScaledVoxel(sourcePosition.clone(), minimum, maximum, entry.getValue(),
|
||||
tile == null ? null : tile.clone()));
|
||||
}
|
||||
} finally {
|
||||
self.readLock.unlock();
|
||||
}
|
||||
|
||||
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : placeBlock) {
|
||||
IrisBlockVector v = entry.getKey();
|
||||
TileData tile = hasTiles ? placeTile.get(v) : null;
|
||||
if (scale > 1) {
|
||||
IrisVector minimum = savedOrigin ? v : v.clone().add(center);
|
||||
IrisVector maximum = savedOrigin ? placeMax.get(v) : v.clone().add(center).add(sm1);
|
||||
int minX = Math.min(minimum.getBlockX(), maximum.getBlockX());
|
||||
int maxX = Math.max(minimum.getBlockX(), maximum.getBlockX());
|
||||
int minY = Math.min(minimum.getBlockY(), maximum.getBlockY());
|
||||
int maxY = Math.max(minimum.getBlockY(), maximum.getBlockY());
|
||||
int minZ = Math.min(minimum.getBlockZ(), maximum.getBlockZ());
|
||||
int maxZ = Math.max(minimum.getBlockZ(), maximum.getBlockZ());
|
||||
IrisBlockVector position = new IrisBlockVector(minX, minY, minZ);
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
position.setX(x);
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
position.setZ(z);
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
position.setY(y);
|
||||
oo.blocks.put(position, entry.getValue());
|
||||
if (hasTiles) {
|
||||
if (tile == null) {
|
||||
oo.states.remove(position);
|
||||
} else {
|
||||
oo.states.put(position, tile.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IrisBlockVector position = savedOrigin
|
||||
? v
|
||||
: oo.getSigned(v.getBlockX(), v.getBlockY(), v.getBlockZ());
|
||||
oo.blocks.put(position, entry.getValue());
|
||||
if (hasTiles) {
|
||||
if (tile == null) {
|
||||
oo.states.remove(position);
|
||||
} else {
|
||||
oo.states.put(position, tile.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scale != Math.rint(scale)) {
|
||||
voxels.sort(Comparator.comparingInt((ScaledVoxel voxel) -> voxel.source().getBlockX())
|
||||
.thenComparingInt(voxel -> voxel.source().getBlockY())
|
||||
.thenComparingInt(voxel -> voxel.source().getBlockZ()));
|
||||
}
|
||||
for (ScaledVoxel voxel : voxels) {
|
||||
writeScaledVoxel(output, voxel);
|
||||
}
|
||||
|
||||
VectorMap<PlatformBlockState> scaledBlocks = oo.blocks;
|
||||
VectorMap<PlatformBlockState> scaledBlocks = output.blocks;
|
||||
if (scale > 1) {
|
||||
switch (interpolation) {
|
||||
case TRILINEAR -> trilinear(oo, (int) Math.round(scale));
|
||||
case TRICUBIC -> tricubic(oo, (int) Math.round(scale));
|
||||
case TRIHERMITE -> trihermite(oo, (int) Math.round(scale));
|
||||
case TRILINEAR -> trilinear(output, (int) Math.round(scale));
|
||||
case TRICUBIC -> tricubic(output, (int) Math.round(scale));
|
||||
case TRIHERMITE -> trihermite(output, (int) Math.round(scale));
|
||||
}
|
||||
}
|
||||
removeInapplicableTiles(output, scaledBlocks);
|
||||
return output;
|
||||
}
|
||||
|
||||
removeInapplicableTiles(oo, scaledBlocks);
|
||||
return oo;
|
||||
private static void writeScaledVoxel(IrisObject output, ScaledVoxel voxel) {
|
||||
IrisBlockVector minimum = voxel.minimum();
|
||||
IrisBlockVector maximum = voxel.maximum();
|
||||
IrisBlockVector position = minimum.clone();
|
||||
for (int x = minimum.getBlockX(); x <= maximum.getBlockX(); x++) {
|
||||
position.setX(x);
|
||||
for (int z = minimum.getBlockZ(); z <= maximum.getBlockZ(); z++) {
|
||||
position.setZ(z);
|
||||
for (int y = minimum.getBlockY(); y <= maximum.getBlockY(); y++) {
|
||||
position.setY(y);
|
||||
output.blocks.put(position, voxel.block());
|
||||
if (voxel.tile() == null) {
|
||||
output.states.remove(position);
|
||||
} else {
|
||||
output.states.put(position, voxel.tile().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IrisObject createCenteredScaledObject(IrisObject source, double scale) {
|
||||
IrisObject object = new IrisObject((int) Math.ceil(source.w * scale),
|
||||
(int) Math.ceil(source.h * scale), (int) Math.ceil(source.d * scale));
|
||||
Vector3i center = object.getCenter();
|
||||
object.aabb.aquire(() -> new AxisAlignedBB(
|
||||
new IrisPosition(-center.getX(), -center.getY(), -center.getZ()),
|
||||
new IrisPosition(object.w - center.getX() - 1, object.h - center.getY() - 1,
|
||||
object.d - center.getZ() - 1)));
|
||||
return object;
|
||||
}
|
||||
|
||||
private static IrisObject createOriginScaledObject(IrisObject source, double scale) {
|
||||
@@ -499,6 +468,10 @@ final class IrisObjectTransforms {
|
||||
private record NearestBlock(int x, int y, int z, int rank, PlatformBlockState state) {
|
||||
}
|
||||
|
||||
private record ScaledVoxel(IrisBlockVector source, IrisBlockVector minimum, IrisBlockVector maximum,
|
||||
PlatformBlockState block, TileData tile) {
|
||||
}
|
||||
|
||||
private enum ScaleOrigin {
|
||||
DEFAULT,
|
||||
SAVED
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
final class IrisPaintSurfaceProjection {
|
||||
static final int MISSING = Integer.MIN_VALUE;
|
||||
private static final int MAXIMUM_STEP = 4;
|
||||
private static final int MAXIMUM_CELLS = 1 << 22;
|
||||
|
||||
private final Request request;
|
||||
private final int depth;
|
||||
private final int[] surfaces;
|
||||
|
||||
private IrisPaintSurfaceProjection(Request request, int[] surfaces) {
|
||||
this.request = request;
|
||||
depth = request.maxZ() - request.minZ() + 1;
|
||||
this.surfaces = surfaces;
|
||||
}
|
||||
|
||||
static IrisPaintSurfaceProjection create(IObjectPlacer placer, Request request) {
|
||||
long width = (long) request.maxX() - request.minX() + 1;
|
||||
long depth = (long) request.maxZ() - request.minZ() + 1;
|
||||
long cells = width * depth;
|
||||
if (width <= 0 || depth <= 0 || width > MAXIMUM_CELLS || depth > MAXIMUM_CELLS
|
||||
|| cells > MAXIMUM_CELLS || request.anchorX() < request.minX()
|
||||
|| request.anchorX() > request.maxX() || request.anchorZ() < request.minZ()
|
||||
|| request.anchorZ() > request.maxZ()) {
|
||||
return null;
|
||||
}
|
||||
int[] surfaces = new int[(int) cells];
|
||||
Arrays.fill(surfaces, MISSING);
|
||||
IrisPaintSurfaceProjection projection = new IrisPaintSurfaceProjection(request, surfaces);
|
||||
return projection.resolve(placer) ? projection : null;
|
||||
}
|
||||
|
||||
int surfaceY(int x, int z) {
|
||||
if (x < request.minX() || x > request.maxX() || z < request.minZ() || z > request.maxZ()) {
|
||||
return MISSING;
|
||||
}
|
||||
return surfaces[index(x, z)];
|
||||
}
|
||||
|
||||
private boolean resolve(IObjectPlacer placer) {
|
||||
int[] highest = new int[surfaces.length];
|
||||
Arrays.fill(highest, MISSING);
|
||||
int[] pending = new int[surfaces.length];
|
||||
int anchor = index(request.anchorX(), request.anchorZ());
|
||||
highest[anchor] = placer.getHighest(request.anchorX(), request.anchorZ(), request.data(), request.ignoreFluid());
|
||||
if (!accepts(placer, request.anchorX(), request.anchorZ(), request.anchorY(), highest[anchor])) {
|
||||
return false;
|
||||
}
|
||||
surfaces[anchor] = request.anchorY();
|
||||
pending[0] = anchor;
|
||||
int count = 1;
|
||||
for (int cursor = 0; cursor < count; cursor++) {
|
||||
int cell = pending[cursor];
|
||||
int x = request.minX() + cell / depth;
|
||||
int z = request.minZ() + cell % depth;
|
||||
int reference = surfaces[cell];
|
||||
count = visit(placer, (long) x - 1, z, reference, highest, pending, count);
|
||||
count = visit(placer, (long) x + 1, z, reference, highest, pending, count);
|
||||
count = visit(placer, x, (long) z - 1, reference, highest, pending, count);
|
||||
count = visit(placer, x, (long) z + 1, reference, highest, pending, count);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int visit(IObjectPlacer placer, long candidateX, long candidateZ, int reference,
|
||||
int[] highest, int[] pending, int count) {
|
||||
if (candidateX < request.minX() || candidateX > request.maxX()
|
||||
|| candidateZ < request.minZ() || candidateZ > request.maxZ()) {
|
||||
return count;
|
||||
}
|
||||
int x = (int) candidateX;
|
||||
int z = (int) candidateZ;
|
||||
int cell = index(x, z);
|
||||
if (surfaces[cell] != MISSING) {
|
||||
return count;
|
||||
}
|
||||
if (highest[cell] == MISSING) {
|
||||
highest[cell] = placer.getHighest(x, z, request.data(), request.ignoreFluid());
|
||||
}
|
||||
for (int distance = 0; distance <= MAXIMUM_STEP; distance++) {
|
||||
long lower = (long) reference - distance;
|
||||
if (accepts(placer, x, z, lower, highest[cell])) {
|
||||
surfaces[cell] = (int) lower;
|
||||
pending[count++] = cell;
|
||||
return count;
|
||||
}
|
||||
long upper = (long) reference + distance;
|
||||
if (distance > 0 && accepts(placer, x, z, upper, highest[cell])) {
|
||||
surfaces[cell] = (int) upper;
|
||||
pending[count++] = cell;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean accepts(IObjectPlacer placer, int x, int z, long y, int highest) {
|
||||
if (y <= Integer.MIN_VALUE || y >= Integer.MAX_VALUE || y > highest
|
||||
|| !matchesNeighbour((long) x - 1, z, y) || !matchesNeighbour((long) x + 1, z, y)
|
||||
|| !matchesNeighbour(x, (long) z - 1, y) || !matchesNeighbour(x, (long) z + 1, y)) {
|
||||
return false;
|
||||
}
|
||||
if (y == highest) {
|
||||
return request.virtualHeightmap()
|
||||
|| solid(placer, x, (int) y, z)
|
||||
|| !request.ignoreFluid() && y == placer.getFluidHeight(x, z);
|
||||
}
|
||||
if (request.virtualHeightmap()) {
|
||||
return false;
|
||||
}
|
||||
return solid(placer, x, (int) y, z) && !solid(placer, x, (int) y + 1, z);
|
||||
}
|
||||
|
||||
private boolean matchesNeighbour(long x, long z, long y) {
|
||||
if (x < request.minX() || x > request.maxX() || z < request.minZ() || z > request.maxZ()) {
|
||||
return true;
|
||||
}
|
||||
int neighbour = surfaceY((int) x, (int) z);
|
||||
return neighbour == MISSING || Math.abs(y - neighbour) <= MAXIMUM_STEP;
|
||||
}
|
||||
|
||||
private boolean solid(IObjectPlacer placer, int x, int y, int z) {
|
||||
return placer.isSurfaceSolid(x, y, z) && !placer.isCarved(x, y, z);
|
||||
}
|
||||
|
||||
private int index(int x, int z) {
|
||||
return (x - request.minX()) * depth + z - request.minZ();
|
||||
}
|
||||
|
||||
record Request(IrisData data, int minX, int maxX, int minZ, int maxZ,
|
||||
int anchorX, int anchorY, int anchorZ, boolean ignoreFluid, boolean virtualHeightmap) {
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ public class IrisStaticObject {
|
||||
private IrisStaticObjectRotation rotation = new IrisStaticObjectRotation();
|
||||
@MinNumber(MINIMUM_SCALE)
|
||||
@MaxNumber(MAXIMUM_SCALE)
|
||||
@Desc("Fixed size multiplier. 1 preserves the saved size, 0.5 halves it and 2 doubles it.")
|
||||
private double scale = 1D;
|
||||
@Desc("Explicit fixed size multiplier. When omitted, the dimension's allObjectScaleFactor applies. An explicit 1 preserves the saved size.")
|
||||
private Double scale;
|
||||
@Desc("Interpolation used when enlarging the object. NONE preserves blocky shapes.")
|
||||
private IrisObjectPlacementScaleInterpolator scaleInterpolation = IrisObjectPlacementScaleInterpolator.NONE;
|
||||
@ArrayType(type = IrisObjectReplace.class)
|
||||
@@ -74,9 +74,8 @@ public class IrisStaticObject {
|
||||
throw new IllegalArgumentException("rotation must be an object.");
|
||||
}
|
||||
rotation.validate();
|
||||
if (!Double.isFinite(scale) || scale < MINIMUM_SCALE || scale > MAXIMUM_SCALE) {
|
||||
throw new IllegalArgumentException("scale must be finite and between "
|
||||
+ MINIMUM_SCALE + " and " + MAXIMUM_SCALE + ".");
|
||||
if (scale != null) {
|
||||
IrisObjectScale.requireValidFactor(scale, "scale");
|
||||
}
|
||||
if (scaleInterpolation == null) {
|
||||
throw new IllegalArgumentException("scaleInterpolation must be a supported interpolation mode.");
|
||||
@@ -141,12 +140,23 @@ public class IrisStaticObject {
|
||||
.setMode(ObjectPlaceMode.STRUCTURE_PIECE)
|
||||
.setForcePlace(true)
|
||||
.setRotation(rotation.toRotation())
|
||||
.setScale(new IrisObjectScale().setSize(scale).setInterpolation(scaleInterpolation))
|
||||
.setScale(scale == null ? null
|
||||
: new IrisObjectScale().setSize(scale).setInterpolation(scaleInterpolation))
|
||||
.setEdit(new KList<>(edit))
|
||||
.setBore(bore)
|
||||
.setSmartBore(smartBore);
|
||||
}
|
||||
|
||||
public IrisStaticObject setScale(Double scale) {
|
||||
this.scale = scale == null ? null : IrisObjectScale.requireValidFactor(scale, "scale");
|
||||
return this;
|
||||
}
|
||||
|
||||
public double resolveScale(IrisDimension dimension) {
|
||||
return scale == null ? dimension.getAllObjectScaleFactor()
|
||||
: IrisObjectScale.requireValidFactor(scale, "scale");
|
||||
}
|
||||
|
||||
private static void validateEdit(IrisObjectReplace replacement, int index) {
|
||||
if (replacement.getFind() == null || replacement.getFind().isEmpty()
|
||||
|| replacement.getFind().contains(null)) {
|
||||
|
||||
@@ -55,8 +55,9 @@ public final class IrisStaticObjectLayer {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("Object '" + entry.getObject() + "' could not be loaded");
|
||||
}
|
||||
if (entry.getScale() != 1D) {
|
||||
object = object.scaledAroundOrigin(entry.getScale(), entry.getScaleInterpolation());
|
||||
double scale = entry.resolveScale(dimension);
|
||||
if (scale != 1D) {
|
||||
object = object.scaledAroundOrigin(scale, entry.getScaleInterpolation());
|
||||
} else if (entry.isSmartBore()) {
|
||||
object = object.copy();
|
||||
}
|
||||
|
||||
@@ -24,11 +24,20 @@ public final class IrisSurfaceSupport {
|
||||
IrisObjectTranslate translate, IrisObjectRotation rotation,
|
||||
int spinX, int spinY, int spinZ,
|
||||
List<IrisBlockVector> supportOffsets, int buffer, int minSolidDepth) {
|
||||
return isUnsupported(placer, data, x, z, translate, rotation, spinX, spinY, spinZ,
|
||||
supportOffsets, buffer, minSolidDepth, null);
|
||||
}
|
||||
|
||||
static boolean isUnsupported(IObjectPlacer placer, IrisData data, int x, int z,
|
||||
IrisObjectTranslate translate, IrisObjectRotation rotation,
|
||||
int spinX, int spinY, int spinZ,
|
||||
List<IrisBlockVector> supportOffsets, int buffer, int minSolidDepth,
|
||||
IrisPaintSurfaceProjection projection) {
|
||||
Stencil stencil = STENCIL.get();
|
||||
if (!stencil.build(supportOffsets, translate, rotation, spinX, spinY, spinZ, Math.max(0, buffer))) {
|
||||
return true;
|
||||
}
|
||||
return stencil.anyColumnUnsupported(placer, data, x, z, Math.max(1, minSolidDepth));
|
||||
return stencil.anyColumnUnsupported(placer, data, x, z, Math.max(1, minSolidDepth), projection);
|
||||
}
|
||||
|
||||
public static boolean intersectsHydrology(IObjectPlacer placer, int x, int z,
|
||||
@@ -165,13 +174,14 @@ public final class IrisSurfaceSupport {
|
||||
}
|
||||
|
||||
/** Centre-outward so the common failure, a hole right under the anchor, costs one column. */
|
||||
private boolean anyColumnUnsupported(IObjectPlacer placer, IrisData data, int x, int z, int minSolidDepth) {
|
||||
private boolean anyColumnUnsupported(IObjectPlacer placer, IrisData data, int x, int z, int minSolidDepth,
|
||||
IrisPaintSurfaceProjection projection) {
|
||||
int maxRing = Math.max(
|
||||
Math.max(centerX, width - 1 - centerX),
|
||||
Math.max(centerZ, depth - 1 - centerZ));
|
||||
for (int ring = 0; ring <= maxRing; ring++) {
|
||||
if (ring == 0) {
|
||||
if (isColumnUnsupported(placer, data, x, z, centerX, centerZ, minSolidDepth)) {
|
||||
if (isColumnUnsupported(placer, data, x, z, centerX, centerZ, minSolidDepth, projection)) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
@@ -182,14 +192,14 @@ public final class IrisSurfaceSupport {
|
||||
int lowZ = centerZ - ring;
|
||||
int highZ = centerZ + ring;
|
||||
for (int sx = lowX; sx <= highX; sx++) {
|
||||
if (isColumnUnsupported(placer, data, x, z, sx, lowZ, minSolidDepth)
|
||||
|| isColumnUnsupported(placer, data, x, z, sx, highZ, minSolidDepth)) {
|
||||
if (isColumnUnsupported(placer, data, x, z, sx, lowZ, minSolidDepth, projection)
|
||||
|| isColumnUnsupported(placer, data, x, z, sx, highZ, minSolidDepth, projection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int sz = lowZ + 1; sz < highZ; sz++) {
|
||||
if (isColumnUnsupported(placer, data, x, z, lowX, sz, minSolidDepth)
|
||||
|| isColumnUnsupported(placer, data, x, z, highX, sz, minSolidDepth)) {
|
||||
if (isColumnUnsupported(placer, data, x, z, lowX, sz, minSolidDepth, projection)
|
||||
|| isColumnUnsupported(placer, data, x, z, highX, sz, minSolidDepth, projection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -214,7 +224,8 @@ public final class IrisSurfaceSupport {
|
||||
}
|
||||
|
||||
private boolean isColumnUnsupported(IObjectPlacer placer, IrisData data, int x, int z,
|
||||
int stencilX, int stencilZ, int minSolidDepth) {
|
||||
int stencilX, int stencilZ, int minSolidDepth,
|
||||
IrisPaintSurfaceProjection projection) {
|
||||
if (stencilX < 0 || stencilX >= width || stencilZ < 0 || stencilZ >= depth
|
||||
|| !cells[(stencilX * depth) + stencilZ]) {
|
||||
return false;
|
||||
@@ -222,9 +233,14 @@ public final class IrisSurfaceSupport {
|
||||
|
||||
int columnX = x + minX + stencilX;
|
||||
int columnZ = z + minZ + stencilZ;
|
||||
int surfaceY = placer.getHighest(columnX, columnZ, data, true);
|
||||
int surfaceY = projection == null ? placer.getHighest(columnX, columnZ, data, true)
|
||||
: projection.surfaceY(columnX, columnZ);
|
||||
if (surfaceY == IrisPaintSurfaceProjection.MISSING) {
|
||||
return false;
|
||||
}
|
||||
for (int below = 0; below < minSolidDepth; below++) {
|
||||
if (placer.isCarved(columnX, surfaceY - below, columnZ)) {
|
||||
if (placer.isCarved(columnX, surfaceY - below, columnZ)
|
||||
|| projection != null && !placer.isSurfaceSolid(columnX, surfaceY - below, columnZ)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package art.arcane.iris.util.common.director.specialhandlers;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisObjectScale;
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import art.arcane.volmlib.util.director.handlers.base.DoubleHandlerBase;
|
||||
|
||||
public final class ObjectScaleHandler extends DoubleHandlerBase {
|
||||
@Override
|
||||
public Double parse(String input, boolean force) throws DirectorParsingException {
|
||||
if (input.trim().equalsIgnoreCase("dimension")) {
|
||||
return null;
|
||||
}
|
||||
double factor = super.parse(input, force);
|
||||
try {
|
||||
return IrisObjectScale.requireValidFactor(factor, "scale");
|
||||
} catch (IllegalArgumentException failure) {
|
||||
throw new DirectorParsingException(failure.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Double value) {
|
||||
return value == null ? "dimension" : super.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRandomDefault() {
|
||||
return "dimension";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user