Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 91 additions & 6 deletions shared/src/main/java/art/arcane/volmlib/util/noise/CNG.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
@Data
public class CNG {
private static final int UNSIGNED_MEMO_CAPACITY = 64;
private static final int FRACTURE_AXES = 3;
private static final long AXIS_SEED_STRIDE = 0x9E3779B97F4A7C15L;
private static final ThreadLocal<UnsignedMemo> UNSIGNED_MEMO = ThreadLocal.withInitial(UnsignedMemo::new);
public static final NoiseInjector ADD = (s, v) -> new double[]{s + v, 1};
public static final NoiseInjector SRC_SUBTRACT = (s, v) -> new double[]{s - v < 0 ? 0 : s - v, -1};
Expand Down Expand Up @@ -88,6 +90,16 @@ public class CNG {
@Setter(AccessLevel.NONE)
@EqualsAndHashCode.Exclude
@ToString.Exclude
private NoiseType generatorType;
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
@EqualsAndHashCode.Exclude
@ToString.Exclude
private transient volatile CNG[] axisVariants;
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
@EqualsAndHashCode.Exclude
@ToString.Exclude
private transient boolean unsignedFractureMemoizable;
private transient boolean identityPostFastPath;
private transient boolean identitySignedFastPath;
Expand All @@ -112,6 +124,7 @@ public CNG(RNG random, double opacity, int octaves) {

public CNG(RNG random, NoiseType type, double opacity, int octaves) {
this(random, type.create(random.nextParallelRNG((long) ((1113334944L * opacity) + 12923)).lmax()), opacity, octaves);
generatorType = type;
}

public CNG(RNG random, NoiseGenerator generator, double opacity, int octaves) {
Expand Down Expand Up @@ -140,6 +153,75 @@ public CNG(RNG random, NoiseGenerator generator, double opacity, int octaves) {
refreshFastPathState();
}

/**
* Seed of the noise that warps one axis of a fracture. Axis 0 keeps the fracture's own seed; the others get
* uncorrelated seeds, so every axis is warped by its own field read at the same coordinates.
*/
public static long fractureAxisSeed(long fractureSeed, int axis) {
requireFractureAxis(axis);
if (axis == 0) {
return fractureSeed;
}
long mixed = fractureSeed + (axis * AXIS_SEED_STRIDE);
mixed = (mixed ^ (mixed >>> 30)) * 0xBF58476D1CE4E5B9L;
mixed = (mixed ^ (mixed >>> 27)) * 0x94D049BB133111EBL;
return mixed ^ (mixed >>> 31);
}

private static void requireFractureAxis(int axis) {
if (axis < 0 || axis >= FRACTURE_AXES) {
throw new IllegalArgumentException("Fracture axis must be 0, 1 or 2");
}
}

/**
* This noise with its generator reseeded by {@link #fractureAxisSeed(long, int)}, for use as the fracture of the
* given axis. Every other setting is copied, and the nested fracture and children are shared. Axis 0 is this noise.
* The copy is a plain CNG, so a subclass that changes how it samples should override this.
*/
public CNG reseededForAxis(int axis) {
requireFractureAxis(axis);
if (axis == 0) {
return this;
}
CNG[] variants = axisVariants;
if (variants != null && variants[axis] != null) {
return variants[axis];
}
return publishAxisVariant(variants, axis);
}

private CNG publishAxisVariant(CNG[] current, int axis) {
CNG variant = createAxisVariant(axis);
CNG[] published = current == null ? new CNG[FRACTURE_AXES] : current.clone();
published[axis] = variant;
axisVariants = published;
return variant;
}

private CNG createAxisVariant(int axis) {
long axisSeed = fractureAxisSeed(rng == null ? 0L : rng.getSeed(), axis);
RNG axisRng = new RNG(axisSeed);
CNG variant = generatorType != null
? new CNG(axisRng, generatorType, opacity, oct)
: new CNG(axisRng, new OffsetNoiseGenerator(generator, axisSeed), opacity, oct);
variant.scale = scale;
variant.bakedScale = bakedScale;
variant.fscale = fscale;
variant.fracture = fracture;
variant.children = children == null ? null : new KList<>(children);
variant.injector = injector;
variant.injectorMode = injectorMode;
variant.noscale = noscale;
variant.power = power;
variant.up = up;
variant.down = down;
variant.patch = patch;
variant.customGenerator = customGenerator;
variant.invalidateLocalState();
return variant;
}

public static CNG signature(RNG rng) {
return signature(rng, NoiseType.SIMPLEX);
}
Expand Down Expand Up @@ -394,6 +476,7 @@ public void setCache(FloatCache cache) {

public void setGenerator(NoiseGenerator generator) {
this.generator = generator;
generatorType = null;
noscale = generator.isNoScale();
if (generator instanceof OctaveNoise octaveNoise) {
octaveNoise.setOctaves(oct);
Expand Down Expand Up @@ -790,7 +873,8 @@ private double getNoise(double x, double z, UnsignedMemo memo) {

double fractureX = memo == null ? fracture.noiseFast2D(x, z) : fracture.memoizedNoise2D(x, z, memo);
double fx = x + ((fractureX - 0.5D) * fscale);
double fractureZ = memo == null ? fracture.noiseFast2D(z, x) : fracture.memoizedNoise2D(z, x, memo);
CNG fractureForZ = fracture.reseededForAxis(1);
double fractureZ = memo == null ? fractureForZ.noiseFast2D(x, z) : fractureForZ.memoizedNoise2D(x, z, memo);
double fz = z + ((fractureZ - 0.5D) * fscale);
return generator.noise(fx * scl, fz * scl) * opacity;
}
Expand All @@ -804,8 +888,8 @@ private double getNoise(double x, double y, double z) {
}

double fx = x + ((fracture.noiseFast3D(x, y, z) - 0.5D) * fscale);
double fy = y + ((fracture.noiseFast2D(y, x) - 0.5D) * fscale);
double fz = z + ((fracture.noiseFast3D(z, x, y) - 0.5D) * fscale);
double fy = y + ((fracture.reseededForAxis(1).noiseFast2D(x, y) - 0.5D) * fscale);
double fz = z + ((fracture.reseededForAxis(2).noiseFast3D(x, y, z) - 0.5D) * fscale);
return generator.noise(fx * scl, fy * scl, fz * scl) * opacity;
}

Expand Down Expand Up @@ -1434,6 +1518,7 @@ private void invalidateLocalState() {
fastPathStateDirty = true;
cache = null;
coordCacheIdentity = new Object();
axisVariants = null;
}

private boolean referencesChild(CNG candidate) {
Expand Down Expand Up @@ -1555,7 +1640,7 @@ private double getSignedNoise(double x, double z, SignedCaches caches) {
}

double fx = x + (localFracture.fractureSigned2D(x, z, caches) * signedFractureScale);
double fz = z + (localFracture.fractureSigned2D(z, x, caches) * signedFractureScale);
double fz = z + (localFracture.reseededForAxis(1).fractureSigned2D(x, z, caches) * signedFractureScale);
return signedWithOpacity(localGenerator.noiseSigned(fx * scl, fz * scl));
}

Expand All @@ -1569,8 +1654,8 @@ private double getSignedNoise(double x, double y, double z, SignedCaches caches)
}

double fx = x + (localFracture.fractureSigned3D(x, y, z, caches) * signedFractureScale);
double fy = y + (localFracture.fractureSigned2D(y, x, caches) * signedFractureScale);
double fz = z + (localFracture.fractureSigned3D(z, x, y, caches) * signedFractureScale);
double fy = y + (localFracture.reseededForAxis(1).fractureSigned2D(x, y, caches) * signedFractureScale);
double fz = z + (localFracture.reseededForAxis(2).fractureSigned3D(x, y, z, caches) * signedFractureScale);
return signedWithOpacity(localGenerator.noiseSigned(fx * scl, fy * scl, fz * scl));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class CellGenerator {
private final FastNoiseDouble fn;
private final FastNoiseDouble fd;
private final CNG cng;
private final CNG secondAxisShuffle;
private final CNG thirdAxisShuffle;

@Getter
@Setter
Expand All @@ -39,6 +41,8 @@ public CellGenerator(RNG rng) {
shuffle = 128;
cellScale = 0.73;
cng = CNG.signature(rng.nextParallelRNG(3204));
secondAxisShuffle = cng.reseededForAxis(1);
thirdAxisShuffle = cng.reseededForAxis(2);
RNG rx = rng.nextParallelRNG(8735652);
long s = rx.lmax();
fn = new FastNoiseDouble(s);
Expand All @@ -52,27 +56,27 @@ public CellGenerator(RNG rng) {
}

public double getDistance(double x, double z) {
return ((fd.GetCellular(((x * cellScale) + (cng.noise(x, z) * shuffle)), ((z * cellScale) + (cng.noise(z, x) * shuffle)))) + 1f) / 2f;
return ((fd.GetCellular(((x * cellScale) + (cng.noise(x, z) * shuffle)), ((z * cellScale) + (secondAxisShuffle.noise(x, z) * shuffle)))) + 1f) / 2f;
}

public double getDistance(double x, double y, double z) {
return ((fd.GetCellular(((x * cellScale) + (cng.noise(x, y, z) * shuffle)), ((y * cellScale) + (cng.noise(x, y, z) * shuffle)), ((z * cellScale) + (cng.noise(z, y, x) * shuffle)))) + 1f) / 2f;
return ((fd.GetCellular(((x * cellScale) + (cng.noise(x, y, z) * shuffle)), ((y * cellScale) + (cng.noise(x, y, z) * shuffle)), ((z * cellScale) + (thirdAxisShuffle.noise(x, y, z) * shuffle)))) + 1f) / 2f;
}

public double getValue(double x, double z, int possibilities) {
if (possibilities == 1) {
return 0;
}

return ((fn.GetCellular(((x * cellScale) + (cng.noise(x, z) * shuffle)), ((z * cellScale) + (cng.noise(z, x) * shuffle))) + 1f) / 2f) * (possibilities - 1);
return ((fn.GetCellular(((x * cellScale) + (cng.noise(x, z) * shuffle)), ((z * cellScale) + (secondAxisShuffle.noise(x, z) * shuffle))) + 1f) / 2f) * (possibilities - 1);
}

public double getValue(double x, double y, double z, int possibilities) {
if (possibilities == 1) {
return 0;
}

return ((fn.GetCellular(((x * cellScale) + (cng.noise(x, z) * shuffle)), ((y * 8 * cellScale) + (cng.noise(x, y * 8) * shuffle)), ((z * cellScale) + (cng.noise(z, x) * shuffle))) + 1f) / 2f) * (possibilities - 1);
return ((fn.GetCellular(((x * cellScale) + (cng.noise(x, z) * shuffle)), ((y * 8 * cellScale) + (cng.noise(x, y * 8) * shuffle)), ((z * cellScale) + (secondAxisShuffle.noise(x, z) * shuffle))) + 1f) / 2f) * (possibilities - 1);
}

public int getIndex(double x, double z, int possibilities) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ private int node(CNG cng) {
entry.identityPost = cng.isIdentityPostFastPath();
entry.identitySigned = cng.isIdentitySignedFastPath();
if (cng.getFracture() != null && !cng.isNoscale()) {
entry.fracture = node(cng.getFracture());
entry.fractureSource = cng.getFracture();
entry.fracture = node(entry.fractureSource);
}
KList<CNG> children = cng.getChildren();
if (children != null && !children.isEmpty()) {
Expand Down Expand Up @@ -191,7 +192,7 @@ private void resolveDependencies(NodeEntry entry, TermEntry term) {
case NoiseProgram.UNSIGNED_2D, NoiseProgram.NOISE_2D -> {
if (fracture >= 0) {
term.dep0 = term(fracture, NoiseProgram.UNSIGNED_2D, a0, a1, 0);
term.dep1 = term(fracture, NoiseProgram.UNSIGNED_2D, a1, a0, 0);
term.dep1 = term(fractureNode(entry, 1), NoiseProgram.UNSIGNED_2D, a0, a1, 0);
}
term.applyPost = term.mode == NoiseProgram.NOISE_2D || !entry.identityPost;
if (term.applyPost && entry.children != null) {
Expand All @@ -203,14 +204,14 @@ private void resolveDependencies(NodeEntry entry, TermEntry term) {
term.dep0 = term(term.node, NoiseProgram.UNSIGNED_2D, a0, a1, 0);
} else if (fracture >= 0) {
term.dep0 = term(fracture, NoiseProgram.SIGNED_2D, a0, a1, 0);
term.dep1 = term(fracture, NoiseProgram.SIGNED_2D, a1, a0, 0);
term.dep1 = term(fractureNode(entry, 1), NoiseProgram.SIGNED_2D, a0, a1, 0);
}
}
case NoiseProgram.UNSIGNED_3D, NoiseProgram.NOISE_3D -> {
if (fracture >= 0) {
term.dep0 = term(fracture, NoiseProgram.UNSIGNED_3D, a0, a1, a2);
term.dep1 = term(fracture, NoiseProgram.UNSIGNED_2D, a1, a0, 0);
term.dep2 = term(fracture, NoiseProgram.UNSIGNED_3D, a2, a0, a1);
term.dep1 = term(fractureNode(entry, 1), NoiseProgram.UNSIGNED_2D, a0, a1, 0);
term.dep2 = term(fractureNode(entry, 2), NoiseProgram.UNSIGNED_3D, a0, a1, a2);
}
term.applyPost = term.mode == NoiseProgram.NOISE_3D || !entry.identityPost;
if (term.applyPost && entry.children != null) {
Expand All @@ -222,13 +223,17 @@ private void resolveDependencies(NodeEntry entry, TermEntry term) {
term.dep0 = term(term.node, NoiseProgram.UNSIGNED_3D, a0, a1, a2);
} else if (fracture >= 0) {
term.dep0 = term(fracture, NoiseProgram.SIGNED_3D, a0, a1, a2);
term.dep1 = term(fracture, NoiseProgram.SIGNED_2D, a1, a0, 0);
term.dep2 = term(fracture, NoiseProgram.SIGNED_3D, a2, a0, a1);
term.dep1 = term(fractureNode(entry, 1), NoiseProgram.SIGNED_2D, a0, a1, 0);
term.dep2 = term(fractureNode(entry, 2), NoiseProgram.SIGNED_3D, a0, a1, a2);
}
}
}
}

private int fractureNode(NodeEntry entry, int axis) {
return node(entry.fractureSource.reseededForAxis(axis));
}

private int[] childTerms(NodeEntry entry, int mode, int axis0, int axis1, int axis2) {
int[] childTerms = new int[entry.children.length];
for (int child = 0; child < childTerms.length; child++) {
Expand All @@ -253,6 +258,7 @@ static final class NodeEntry {
boolean identityPost;
boolean identitySigned;
ChildInjector injector;
CNG fractureSource;
int fracture = -1;
int[] children;

Expand Down
Loading
Loading