rewrite jigsaw placement logic to prevent placements from being too close

This commit is contained in:
CrazyDev22
2024-05-10 15:58:26 +02:00
parent b70e94dc65
commit 8b5d1d0298
2 changed files with 93 additions and 49 deletions
@@ -18,7 +18,6 @@
package com.volmit.iris.engine.mantle.components; package com.volmit.iris.engine.mantle.components;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.jigsaw.PlannedStructure; import com.volmit.iris.engine.jigsaw.PlannedStructure;
import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.IrisMantleComponent;
@@ -35,34 +34,31 @@ import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.noise.CNG; import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.noise.NoiseType; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
public class MantleJigsawComponent extends IrisMantleComponent { public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) { public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, MantleFlag.JIGSAW); super(engineMantle, MantleFlag.JIGSAW);
} cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
private RNG applyNoise(int x, int z) {
long seed = Cache.key(x, z) + getEngineMantle().getEngine().getSeedManager().getJigsaw();
CNG cng = CNG.signatureFast(new RNG(seed), NoiseType.WHITE, NoiseType.GLOB);
return new RNG((long) (seed * cng.noise(x, z)));
} }
@Override @Override
public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) { public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) {
RNG rng = applyNoise(x, z);
int xxx = 8 + (x << 4); int xxx = 8 + (x << 4);
int zzz = 8 + (z << 4); int zzz = 8 + (z << 4);
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz); IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz); IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz);
generateJigsaw(writer, rng, x, z, biome, region); generateJigsaw(writer, x, z, biome, region);
} }
@ChunkCoordinates @ChunkCoordinates
private void generateJigsaw(MantleWriter writer, RNG rng, int x, int z, IrisBiome biome, IrisRegion region) { private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) {
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
if (getDimension().getStronghold() != null) { if (getDimension().getStronghold() != null) {
List<Position2> poss = getDimension().getStrongholds(seed()); List<Position2> poss = getDimension().getStrongholds(seed());
@@ -70,7 +66,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
for (Position2 pos : poss) { for (Position2 pos : poss) {
if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) { if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) {
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getDimension().getStronghold()); IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getDimension().getStronghold());
place(writer, pos.toIris(), structure, rng); place(writer, pos.toIris(), structure, new RNG(seed));
return; return;
} }
} }
@@ -80,27 +76,23 @@ public class MantleJigsawComponent extends IrisMantleComponent {
KSet<Position2> cachedRegions = new KSet<>(); KSet<Position2> cachedRegions = new KSet<>();
KMap<String, KSet<Position2>> cache = new KMap<>(); KMap<String, KSet<Position2>> cache = new KMap<>();
KMap<Position2, Double> distanceCache = new KMap<>(); KMap<Position2, Double> distanceCache = new KMap<>();
boolean placed = placeStructures(writer, rng, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache); boolean placed = placeStructures(writer, seed, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache);
if (!placed) if (!placed)
placed = placeStructures(writer, rng, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache); placed = placeStructures(writer, seed, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache);
if (!placed) if (!placed)
placeStructures(writer, rng, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache); placeStructures(writer, seed, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache);
} }
@ChunkCoordinates @ChunkCoordinates
private boolean placeStructures(MantleWriter writer, RNG rng, int x, int z, KList<IrisJigsawStructurePlacement> structures, private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList<IrisJigsawStructurePlacement> structures,
KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) { KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
for (IrisJigsawStructurePlacement i : structures) { IrisJigsawStructurePlacement i = pick(structures, seed, x, z);
if (rng.nextInt(i.getRarity()) == 0) { if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
if (checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache)) return false;
continue; RNG rng = new RNG(seed);
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15)); IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure()); IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
if (place(writer, position, structure, rng)) return place(writer, position, structure, rng);
return true;
}
}
return false;
} }
@ChunkCoordinates @ChunkCoordinates
@@ -138,7 +130,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
public IrisJigsawStructure guess(int x, int z) { public IrisJigsawStructure guess(int x, int z) {
// todo The guess doesnt bring into account that the placer may return -1 // todo The guess doesnt bring into account that the placer may return -1
// todo doesnt bring skipped placements into account // todo doesnt bring skipped placements into account
RNG rng = applyNoise(x, z); long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8); IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8);
IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8); IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8);
@@ -154,29 +146,26 @@ public class MantleJigsawComponent extends IrisMantleComponent {
} }
} }
for (IrisJigsawStructurePlacement i : biome.getJigsawStructures()) { IrisJigsawStructurePlacement i = pick(biome.getJigsawStructures(), seed, x, z);
if (rng.nextInt(i.getRarity()) == 0) { if (i == null) i = pick(region.getJigsawStructures(), seed, x, z);
return getData().getJigsawStructureLoader().load(i.getStructure()); if (i == null) i = pick(getDimension().getJigsawStructures(), seed, x, z);
} return i != null ? getData().getJigsawStructureLoader().load(i.getStructure()) : null;
} }
for (IrisJigsawStructurePlacement i : region.getJigsawStructures()) { @Nullable
if (rng.nextInt(i.getRarity()) == 0) { @ChunkCoordinates
return getData().getJigsawStructureLoader().load(i.getStructure()); private IrisJigsawStructurePlacement pick(List<IrisJigsawStructurePlacement> structures, long seed, int x, int z) {
} return IRare.pick(structures.stream()
} .filter(p -> p.shouldPlace(jigsaw(), x, z))
.toList(), new RNG(seed).nextDouble());
for (IrisJigsawStructurePlacement i : getDimension().getJigsawStructures()) {
if (rng.nextInt(i.getRarity()) == 0) {
return getData().getJigsawStructureLoader().load(i.getStructure());
}
}
return null;
} }
@BlockCoordinates @BlockCoordinates
private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) { private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) {
return new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine()); return new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine());
} }
private long jigsaw() {
return getEngineMantle().getEngine().getSeedManager().getJigsaw();
}
} }
@@ -18,20 +18,23 @@
package com.volmit.iris.engine.object; package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.annotations.ArrayType; import com.volmit.iris.engine.object.annotations.ArrayType;
import com.volmit.iris.engine.object.annotations.Desc; import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.MinNumber;
import com.volmit.iris.engine.object.annotations.RegistryListResource; import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.engine.object.annotations.Required; import com.volmit.iris.engine.object.annotations.Required;
import com.volmit.iris.engine.object.annotations.Snippet; import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.math.RNG;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@Snippet("jigsaw-structure-placement") @Snippet("jigsaw-structure-placement")
@Accessors(chain = true) @Accessors(chain = true)
@NoArgsConstructor @NoArgsConstructor
@@ -39,16 +42,33 @@ import lombok.experimental.Accessors;
@Desc("Represents a jigsaw structure placer") @Desc("Represents a jigsaw structure placer")
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class IrisJigsawStructurePlacement { public class IrisJigsawStructurePlacement implements IRare {
@RegistryListResource(IrisJigsawStructure.class) @RegistryListResource(IrisJigsawStructure.class)
@Required @Required
@Desc("The structure to place") @Desc("The structure to place")
private String structure; private String structure;
@Required @Required
@Desc("The 1 in X chance rarity") @Desc("The 1 in X chance rarity applies when generating multiple structures at once")
private int rarity = 100; private int rarity = 100;
@Required
@Desc("The salt to use when generating the structure (to differentiate structures)")
private int salt = 76134;
@Required
@MinNumber(0)
@Desc("Average distance in chunks between two neighboring generation attempts")
private int spacing = 32;
@Required
@MinNumber(0)
@Desc("Minimum distance in chunks between two neighboring generation attempts\nThe maximum distance of two neighboring generation attempts is 2*spacing - separation")
private int separation = 16;
@Desc("The method used to spread the structure")
private SpreadType spreadType = SpreadType.TRIANGULAR;
@ArrayType(type = IrisJigsawMinDistance.class) @ArrayType(type = IrisJigsawMinDistance.class)
@Desc("List of minimum distances to check for") @Desc("List of minimum distances to check for")
private KList<IrisJigsawMinDistance> minDistances = new KList<>(); private KList<IrisJigsawMinDistance> minDistances = new KList<>();
@@ -64,4 +84,39 @@ public class IrisJigsawStructurePlacement {
private int toChunks(int blocks) { private int toChunks(int blocks) {
return (int) Math.ceil(blocks / 16d); return (int) Math.ceil(blocks / 16d);
} }
@ChunkCoordinates
public boolean shouldPlace(long seed, int x, int z) {
if (separation > spacing) {
separation = spacing;
Iris.warn("JigsawStructurePlacement: separation must be less than or equal to spacing");
}
int i = Math.floorDiv(x, spacing);
int j = Math.floorDiv(z, spacing);
RNG rng = new RNG(i * 341873128712L + j * 132897987541L + seed + salt);
int k = spacing - separation;
int l = spreadType.apply(rng, k);
int m = spreadType.apply(rng, k);
return i * spacing + l == x && j * spacing + m == z;
}
public enum SpreadType {
LINEAR(RNG::i),
TRIANGULAR((rng, bound) -> (rng.i(bound) + rng.i(bound)) / 2);
private final SpreadMethod method;
SpreadType(SpreadMethod method) {
this.method = method;
}
public int apply(RNG rng, int bound) {
return method.apply(rng, bound);
}
}
private interface SpreadMethod {
int apply(RNG rng, int bound);
}
} }