mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-05-19 16:10:42 +00:00
Studio FIxes
This commit is contained in:
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
466077434
|
149256635
|
||||||
@@ -134,29 +134,30 @@ public class CommandStudio implements DirectorExecutor {
|
|||||||
|
|
||||||
@Director(description = "Close an open studio project", aliases = {"x", "c"}, sync = true)
|
@Director(description = "Close an open studio project", aliases = {"x", "c"}, sync = true)
|
||||||
public void close() {
|
public void close() {
|
||||||
|
VolmitSender commandSender = sender();
|
||||||
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
||||||
sender().sendMessage(C.RED + "No open studio projects.");
|
commandSender.sendMessage(C.RED + "No open studio projects.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender().sendMessage(C.YELLOW + "Closing studio...");
|
commandSender.sendMessage(C.YELLOW + "Closing studio...");
|
||||||
Iris.service(StudioSVC.class).close().whenComplete((result, throwable) -> J.s(() -> {
|
Iris.service(StudioSVC.class).close().whenComplete((result, throwable) -> J.s(() -> {
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
sender().sendMessage(C.RED + "Studio close failed: " + throwable.getMessage());
|
commandSender.sendMessage(C.RED + "Studio close failed: " + throwable.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null && result.failureCause() != null) {
|
if (result != null && result.failureCause() != null) {
|
||||||
sender().sendMessage(C.RED + "Studio close failed: " + result.failureCause().getMessage());
|
commandSender.sendMessage(C.RED + "Studio close failed: " + result.failureCause().getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null && result.startupCleanupQueued()) {
|
if (result != null && result.startupCleanupQueued()) {
|
||||||
sender().sendMessage(C.YELLOW + "Studio closed. Remaining world-family cleanup was queued for startup fallback.");
|
commandSender.sendMessage(C.YELLOW + "Studio closed. Remaining world-family cleanup was queued for startup fallback.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender().sendMessage(C.GREEN + "Studio closed.");
|
commandSender.sendMessage(C.GREEN + "Studio closed.");
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import art.arcane.iris.Iris;
|
|||||||
import art.arcane.iris.core.IrisSettings;
|
import art.arcane.iris.core.IrisSettings;
|
||||||
import art.arcane.iris.core.loader.IrisData;
|
import art.arcane.iris.core.loader.IrisData;
|
||||||
import art.arcane.iris.core.tools.IrisToolbelt;
|
import art.arcane.iris.core.tools.IrisToolbelt;
|
||||||
|
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import art.arcane.volmlib.util.board.Board;
|
import art.arcane.volmlib.util.board.Board;
|
||||||
import art.arcane.volmlib.util.board.BoardProvider;
|
import art.arcane.volmlib.util.board.BoardProvider;
|
||||||
import art.arcane.volmlib.util.board.BoardSettings;
|
import art.arcane.volmlib.util.board.BoardSettings;
|
||||||
@@ -97,9 +98,12 @@ public class BoardSVC implements IrisService, BoardProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IrisToolbelt.isIrisStudioWorld(p.getWorld())) {
|
if (isEligibleWorld(p)) {
|
||||||
boards.computeIfAbsent(p, PlayerBoard::new);
|
boards.computeIfAbsent(p, PlayerBoard::new);
|
||||||
} else remove(p);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void remove(Player player) {
|
private void remove(Player player) {
|
||||||
@@ -132,6 +136,20 @@ public class BoardSVC implements IrisService, BoardProvider {
|
|||||||
return board.lines;
|
return board.lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isEligibleWorld(Player player) {
|
||||||
|
if (player == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
World world = player.getWorld();
|
||||||
|
if (!IrisToolbelt.isIrisWorld(world)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlatformChunkGenerator access = IrisToolbelt.access(world);
|
||||||
|
return access != null && access.getEngine() != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class PlayerBoard {
|
public class PlayerBoard {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
@@ -159,18 +177,12 @@ public class BoardSVC implements IrisService, BoardProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IrisToolbelt.isIrisStudioWorld(player.getWorld())) {
|
if (!isEligibleWorld(player)) {
|
||||||
boards.remove(player);
|
boards.remove(player);
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
|
||||||
board.update();
|
|
||||||
schedule(20);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
board.update();
|
board.update();
|
||||||
schedule(20);
|
schedule(20);
|
||||||
|
|||||||
+346
-49
@@ -163,6 +163,7 @@ public class IrisCaveCarver3D {
|
|||||||
maxY = Math.min(maxY, rangeMaxY);
|
maxY = Math.min(maxY, rangeMaxY);
|
||||||
}
|
}
|
||||||
int sampleStep = Math.max(1, profile.getSampleStep());
|
int sampleStep = Math.max(1, profile.getSampleStep());
|
||||||
|
boolean exactSampling = sampleStep <= 2;
|
||||||
int surfaceClearance = Math.max(0, profile.getSurfaceClearance());
|
int surfaceClearance = Math.max(0, profile.getSurfaceClearance());
|
||||||
int surfaceBreakDepth = Math.max(0, profile.getSurfaceBreakDepth());
|
int surfaceBreakDepth = Math.max(0, profile.getSurfaceBreakDepth());
|
||||||
double surfaceBreakNoiseThreshold = profile.getSurfaceBreakNoiseThreshold();
|
double surfaceBreakNoiseThreshold = profile.getSurfaceBreakNoiseThreshold();
|
||||||
@@ -214,32 +215,53 @@ public class IrisCaveCarver3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int latticeStep = Math.max(2, sampleStep);
|
|
||||||
int carved = carvePassLattice(
|
|
||||||
chunk,
|
|
||||||
x0,
|
|
||||||
z0,
|
|
||||||
minY,
|
|
||||||
maxY,
|
|
||||||
latticeStep,
|
|
||||||
surfaceBreakThresholdBoost,
|
|
||||||
columnMaxY,
|
|
||||||
surfaceBreakFloorY,
|
|
||||||
surfaceBreakColumn,
|
|
||||||
columnThreshold,
|
|
||||||
clampedWeights,
|
|
||||||
verticalEdgeFade,
|
|
||||||
matterByY,
|
|
||||||
resolvedMinWeight,
|
|
||||||
resolvedThresholdPenalty,
|
|
||||||
0D,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
int minCarveCells = Math.max(0, profile.getMinCarveCells());
|
int minCarveCells = Math.max(0, profile.getMinCarveCells());
|
||||||
double recoveryThresholdBoost = Math.max(0, profile.getRecoveryThresholdBoost());
|
double recoveryThresholdBoost = Math.max(0, profile.getRecoveryThresholdBoost());
|
||||||
if (carved < minCarveCells && recoveryThresholdBoost > 0D) {
|
int carved;
|
||||||
carved += carvePassLattice(
|
if (exactSampling) {
|
||||||
|
carved = carvePassExact(
|
||||||
|
chunk,
|
||||||
|
x0,
|
||||||
|
z0,
|
||||||
|
minY,
|
||||||
|
maxY,
|
||||||
|
surfaceBreakThresholdBoost,
|
||||||
|
columnMaxY,
|
||||||
|
surfaceBreakFloorY,
|
||||||
|
surfaceBreakColumn,
|
||||||
|
columnThreshold,
|
||||||
|
clampedWeights,
|
||||||
|
verticalEdgeFade,
|
||||||
|
matterByY,
|
||||||
|
resolvedMinWeight,
|
||||||
|
resolvedThresholdPenalty,
|
||||||
|
0D,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
if (carved < minCarveCells && recoveryThresholdBoost > 0D) {
|
||||||
|
carved += carvePassExact(
|
||||||
|
chunk,
|
||||||
|
x0,
|
||||||
|
z0,
|
||||||
|
minY,
|
||||||
|
maxY,
|
||||||
|
surfaceBreakThresholdBoost,
|
||||||
|
columnMaxY,
|
||||||
|
surfaceBreakFloorY,
|
||||||
|
surfaceBreakColumn,
|
||||||
|
columnThreshold,
|
||||||
|
clampedWeights,
|
||||||
|
verticalEdgeFade,
|
||||||
|
matterByY,
|
||||||
|
resolvedMinWeight,
|
||||||
|
resolvedThresholdPenalty,
|
||||||
|
recoveryThresholdBoost,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int latticeStep = sampleStep;
|
||||||
|
carved = carvePassLattice(
|
||||||
chunk,
|
chunk,
|
||||||
x0,
|
x0,
|
||||||
z0,
|
z0,
|
||||||
@@ -256,33 +278,32 @@ public class IrisCaveCarver3D {
|
|||||||
matterByY,
|
matterByY,
|
||||||
resolvedMinWeight,
|
resolvedMinWeight,
|
||||||
resolvedThresholdPenalty,
|
resolvedThresholdPenalty,
|
||||||
recoveryThresholdBoost,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (carved == 0 && hasFallbackCandidates(columnMaxY, clampedWeights, minY, resolvedMinWeight)) {
|
|
||||||
carved += carvePassFallback(
|
|
||||||
chunk,
|
|
||||||
x0,
|
|
||||||
z0,
|
|
||||||
minY,
|
|
||||||
maxY,
|
|
||||||
sampleStep,
|
|
||||||
surfaceBreakThresholdBoost,
|
|
||||||
columnMaxY,
|
|
||||||
surfaceBreakFloorY,
|
|
||||||
surfaceBreakColumn,
|
|
||||||
columnThreshold,
|
|
||||||
clampedWeights,
|
|
||||||
verticalEdgeFade,
|
|
||||||
matterByY,
|
|
||||||
resolvedMinWeight,
|
|
||||||
resolvedThresholdPenalty,
|
|
||||||
0D,
|
0D,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
if (carved < minCarveCells && recoveryThresholdBoost > 0D) {
|
if (carved < minCarveCells && recoveryThresholdBoost > 0D) {
|
||||||
|
carved += carvePassLattice(
|
||||||
|
chunk,
|
||||||
|
x0,
|
||||||
|
z0,
|
||||||
|
minY,
|
||||||
|
maxY,
|
||||||
|
latticeStep,
|
||||||
|
surfaceBreakThresholdBoost,
|
||||||
|
columnMaxY,
|
||||||
|
surfaceBreakFloorY,
|
||||||
|
surfaceBreakColumn,
|
||||||
|
columnThreshold,
|
||||||
|
clampedWeights,
|
||||||
|
verticalEdgeFade,
|
||||||
|
matterByY,
|
||||||
|
resolvedMinWeight,
|
||||||
|
resolvedThresholdPenalty,
|
||||||
|
recoveryThresholdBoost,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (carved == 0 && hasFallbackCandidates(columnMaxY, clampedWeights, minY, resolvedMinWeight)) {
|
||||||
carved += carvePassFallback(
|
carved += carvePassFallback(
|
||||||
chunk,
|
chunk,
|
||||||
x0,
|
x0,
|
||||||
@@ -300,9 +321,31 @@ public class IrisCaveCarver3D {
|
|||||||
matterByY,
|
matterByY,
|
||||||
resolvedMinWeight,
|
resolvedMinWeight,
|
||||||
resolvedThresholdPenalty,
|
resolvedThresholdPenalty,
|
||||||
recoveryThresholdBoost,
|
0D,
|
||||||
true
|
false
|
||||||
);
|
);
|
||||||
|
if (carved < minCarveCells && recoveryThresholdBoost > 0D) {
|
||||||
|
carved += carvePassFallback(
|
||||||
|
chunk,
|
||||||
|
x0,
|
||||||
|
z0,
|
||||||
|
minY,
|
||||||
|
maxY,
|
||||||
|
sampleStep,
|
||||||
|
surfaceBreakThresholdBoost,
|
||||||
|
columnMaxY,
|
||||||
|
surfaceBreakFloorY,
|
||||||
|
surfaceBreakColumn,
|
||||||
|
columnThreshold,
|
||||||
|
clampedWeights,
|
||||||
|
verticalEdgeFade,
|
||||||
|
matterByY,
|
||||||
|
resolvedMinWeight,
|
||||||
|
resolvedThresholdPenalty,
|
||||||
|
recoveryThresholdBoost,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +355,125 @@ public class IrisCaveCarver3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int carvePassExact(
|
||||||
|
MantleChunk<Matter> chunk,
|
||||||
|
int x0,
|
||||||
|
int z0,
|
||||||
|
int minY,
|
||||||
|
int maxY,
|
||||||
|
double surfaceBreakThresholdBoost,
|
||||||
|
int[] columnMaxY,
|
||||||
|
int[] surfaceBreakFloorY,
|
||||||
|
boolean[] surfaceBreakColumn,
|
||||||
|
double[] columnThreshold,
|
||||||
|
double[] clampedWeights,
|
||||||
|
double[] verticalEdgeFade,
|
||||||
|
MatterCavern[] matterByY,
|
||||||
|
double minWeight,
|
||||||
|
double thresholdPenalty,
|
||||||
|
double thresholdBoost,
|
||||||
|
boolean skipExistingCarved
|
||||||
|
) {
|
||||||
|
int carved = 0;
|
||||||
|
Scratch scratch = SCRATCH.get();
|
||||||
|
double[] passThreshold = scratch.passThreshold;
|
||||||
|
int[] activeColumnIndices = scratch.activeColumnIndices;
|
||||||
|
int[] activeColumnTopY = scratch.activeColumnTopY;
|
||||||
|
int activeColumnCount = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < 256; index++) {
|
||||||
|
double columnWeight = clampedWeights[index];
|
||||||
|
if (columnWeight <= minWeight || columnMaxY[index] < minY) {
|
||||||
|
passThreshold[index] = Double.NaN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
passThreshold[index] = columnThreshold[index] + thresholdBoost - ((1D - columnWeight) * thresholdPenalty);
|
||||||
|
activeColumnIndices[activeColumnCount] = index;
|
||||||
|
activeColumnTopY[activeColumnCount] = columnMaxY[index];
|
||||||
|
activeColumnCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeColumnCount == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] planeColumnIndices = scratch.planeColumnIndices;
|
||||||
|
double[] planeDensity = scratch.planeDensity;
|
||||||
|
int minSection = minY >> 4;
|
||||||
|
int maxSection = maxY >> 4;
|
||||||
|
|
||||||
|
for (int sectionIndex = minSection; sectionIndex <= maxSection; sectionIndex++) {
|
||||||
|
int sectionMinY = Math.max(minY, sectionIndex << 4);
|
||||||
|
int sectionMaxY = Math.min(maxY, (sectionIndex << 4) + 15);
|
||||||
|
MatterSlice<MatterCavern> cavernSlice = resolveCavernSlice(scratch, chunk, sectionIndex);
|
||||||
|
|
||||||
|
for (int y = sectionMinY; y <= sectionMaxY; y++) {
|
||||||
|
int planeCount = 0;
|
||||||
|
for (int activeIndex = 0; activeIndex < activeColumnCount; activeIndex++) {
|
||||||
|
if (activeColumnTopY[activeIndex] < y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
planeColumnIndices[planeCount] = activeColumnIndices[activeIndex];
|
||||||
|
planeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (planeCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillDensityPlane(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
|
||||||
|
int fadeIndex = y - minY;
|
||||||
|
int localY = y & 15;
|
||||||
|
MatterCavern matter = matterByY[fadeIndex];
|
||||||
|
|
||||||
|
if (skipExistingCarved) {
|
||||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
|
||||||
|
int columnIndex = planeColumnIndices[planeIndex];
|
||||||
|
double localThreshold = passThreshold[columnIndex];
|
||||||
|
if (surfaceBreakColumn[columnIndex] && y >= surfaceBreakFloorY[columnIndex]) {
|
||||||
|
localThreshold += surfaceBreakThresholdBoost;
|
||||||
|
}
|
||||||
|
localThreshold -= verticalEdgeFade[fadeIndex];
|
||||||
|
if (planeDensity[planeIndex] > localThreshold) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int localX = columnIndex >> 4;
|
||||||
|
int localZ = columnIndex & 15;
|
||||||
|
if (cavernSlice.get(localX, localY, localZ) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cavernSlice.set(localX, localY, localZ, matter);
|
||||||
|
carved++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
|
||||||
|
int columnIndex = planeColumnIndices[planeIndex];
|
||||||
|
double localThreshold = passThreshold[columnIndex];
|
||||||
|
if (surfaceBreakColumn[columnIndex] && y >= surfaceBreakFloorY[columnIndex]) {
|
||||||
|
localThreshold += surfaceBreakThresholdBoost;
|
||||||
|
}
|
||||||
|
localThreshold -= verticalEdgeFade[fadeIndex];
|
||||||
|
if (planeDensity[planeIndex] > localThreshold) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int localX = columnIndex >> 4;
|
||||||
|
int localZ = columnIndex & 15;
|
||||||
|
cavernSlice.set(localX, localY, localZ, matter);
|
||||||
|
carved++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return carved;
|
||||||
|
}
|
||||||
|
|
||||||
private int carvePassLattice(
|
private int carvePassLattice(
|
||||||
MantleChunk<Matter> chunk,
|
MantleChunk<Matter> chunk,
|
||||||
int x0,
|
int x0,
|
||||||
@@ -557,6 +719,137 @@ public class IrisCaveCarver3D {
|
|||||||
return sampleDensityWarpModules(x, y, z);
|
return sampleDensityWarpModules(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillDensityPlane(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
|
||||||
|
if (!hasWarp) {
|
||||||
|
if (!hasModules) {
|
||||||
|
fillDensityPlaneNoWarpNoModules(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillDensityPlaneNoWarpModules(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasModules) {
|
||||||
|
fillDensityPlaneWarpOnly(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillDensityPlaneWarpModules(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillDensityPlaneNoWarpNoModules(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
|
||||||
|
CNG localBaseDensity = baseDensity;
|
||||||
|
CNG localDetailDensity = detailDensity;
|
||||||
|
double localBaseWeight = baseWeight;
|
||||||
|
double localDetailWeight = detailWeight;
|
||||||
|
double normalization = inverseNormalization;
|
||||||
|
|
||||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
|
||||||
|
int columnIndex = planeColumnIndices[planeIndex];
|
||||||
|
int x = x0 + (columnIndex >> 4);
|
||||||
|
int z = z0 + (columnIndex & 15);
|
||||||
|
double density = signed(localBaseDensity.noiseFast3D(x, y, z)) * localBaseWeight;
|
||||||
|
density += signed(localDetailDensity.noiseFast3D(x, y, z)) * localDetailWeight;
|
||||||
|
planeDensity[planeIndex] = density * normalization;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillDensityPlaneNoWarpModules(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
|
||||||
|
CNG localBaseDensity = baseDensity;
|
||||||
|
CNG localDetailDensity = detailDensity;
|
||||||
|
ModuleState[] localModules = modules;
|
||||||
|
double localBaseWeight = baseWeight;
|
||||||
|
double localDetailWeight = detailWeight;
|
||||||
|
double normalization = inverseNormalization;
|
||||||
|
|
||||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
|
||||||
|
int columnIndex = planeColumnIndices[planeIndex];
|
||||||
|
int x = x0 + (columnIndex >> 4);
|
||||||
|
int z = z0 + (columnIndex & 15);
|
||||||
|
double density = signed(localBaseDensity.noiseFast3D(x, y, z)) * localBaseWeight;
|
||||||
|
density += signed(localDetailDensity.noiseFast3D(x, y, z)) * localDetailWeight;
|
||||||
|
for (int moduleIndex = 0; moduleIndex < localModules.length; moduleIndex++) {
|
||||||
|
ModuleState module = localModules[moduleIndex];
|
||||||
|
if (y < module.minY || y > module.maxY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double moduleDensity = signed(module.density.noiseFast3D(x, y, z)) - module.threshold;
|
||||||
|
if (module.invert) {
|
||||||
|
moduleDensity = -moduleDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
density += moduleDensity * module.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
planeDensity[planeIndex] = density * normalization;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillDensityPlaneWarpOnly(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
|
||||||
|
CNG localBaseDensity = baseDensity;
|
||||||
|
CNG localDetailDensity = detailDensity;
|
||||||
|
CNG localWarpDensity = warpDensity;
|
||||||
|
double localBaseWeight = baseWeight;
|
||||||
|
double localDetailWeight = detailWeight;
|
||||||
|
double localWarpStrength = warpStrength;
|
||||||
|
double normalization = inverseNormalization;
|
||||||
|
|
||||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
|
||||||
|
int columnIndex = planeColumnIndices[planeIndex];
|
||||||
|
double x = x0 + (columnIndex >> 4);
|
||||||
|
double z = z0 + (columnIndex & 15);
|
||||||
|
double warpA = signed(localWarpDensity.noiseFast3D(x, y, z));
|
||||||
|
double warpB = signed(localWarpDensity.noiseFast3D(x + 31.37D, y - 17.21D, z + 23.91D));
|
||||||
|
double warpedX = x + (warpA * localWarpStrength);
|
||||||
|
double warpedY = y + (warpB * localWarpStrength);
|
||||||
|
double warpedZ = z + ((warpA - warpB) * 0.5D * localWarpStrength);
|
||||||
|
double density = signed(localBaseDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localBaseWeight;
|
||||||
|
density += signed(localDetailDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localDetailWeight;
|
||||||
|
planeDensity[planeIndex] = density * normalization;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillDensityPlaneWarpModules(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
|
||||||
|
CNG localBaseDensity = baseDensity;
|
||||||
|
CNG localDetailDensity = detailDensity;
|
||||||
|
CNG localWarpDensity = warpDensity;
|
||||||
|
ModuleState[] localModules = modules;
|
||||||
|
double localBaseWeight = baseWeight;
|
||||||
|
double localDetailWeight = detailWeight;
|
||||||
|
double localWarpStrength = warpStrength;
|
||||||
|
double normalization = inverseNormalization;
|
||||||
|
|
||||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
|
||||||
|
int columnIndex = planeColumnIndices[planeIndex];
|
||||||
|
double x = x0 + (columnIndex >> 4);
|
||||||
|
double z = z0 + (columnIndex & 15);
|
||||||
|
double warpA = signed(localWarpDensity.noiseFast3D(x, y, z));
|
||||||
|
double warpB = signed(localWarpDensity.noiseFast3D(x + 31.37D, y - 17.21D, z + 23.91D));
|
||||||
|
double warpedX = x + (warpA * localWarpStrength);
|
||||||
|
double warpedY = y + (warpB * localWarpStrength);
|
||||||
|
double warpedZ = z + ((warpA - warpB) * 0.5D * localWarpStrength);
|
||||||
|
double density = signed(localBaseDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localBaseWeight;
|
||||||
|
density += signed(localDetailDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localDetailWeight;
|
||||||
|
for (int moduleIndex = 0; moduleIndex < localModules.length; moduleIndex++) {
|
||||||
|
ModuleState module = localModules[moduleIndex];
|
||||||
|
if (y < module.minY || y > module.maxY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double moduleDensity = signed(module.density.noiseFast3D(warpedX, warpedY, warpedZ)) - module.threshold;
|
||||||
|
if (module.invert) {
|
||||||
|
moduleDensity = -moduleDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
density += moduleDensity * module.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
planeDensity[planeIndex] = density * normalization;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private double sampleDensityNoWarpNoModules(int x, int y, int z) {
|
private double sampleDensityNoWarpNoModules(int x, int y, int z) {
|
||||||
double density = signed(baseDensity.noiseFast3D(x, y, z)) * baseWeight;
|
double density = signed(baseDensity.noiseFast3D(x, y, z)) * baseWeight;
|
||||||
density += signed(detailDensity.noiseFast3D(x, y, z)) * detailWeight;
|
density += signed(detailDensity.noiseFast3D(x, y, z)) * detailWeight;
|
||||||
@@ -765,6 +1058,10 @@ public class IrisCaveCarver3D {
|
|||||||
private final double[] passThreshold = new double[256];
|
private final double[] passThreshold = new double[256];
|
||||||
private final double[] fullWeights = new double[256];
|
private final double[] fullWeights = new double[256];
|
||||||
private final double[] clampedColumnWeights = new double[256];
|
private final double[] clampedColumnWeights = new double[256];
|
||||||
|
private final int[] activeColumnIndices = new int[256];
|
||||||
|
private final int[] activeColumnTopY = new int[256];
|
||||||
|
private final int[] planeColumnIndices = new int[256];
|
||||||
|
private final double[] planeDensity = new double[256];
|
||||||
private final int[] tileIndices = new int[4];
|
private final int[] tileIndices = new int[4];
|
||||||
private final int[] tileLocalX = new int[4];
|
private final int[] tileLocalX = new int[4];
|
||||||
private final int[] tileLocalZ = new int[4];
|
private final int[] tileLocalZ = new int[4];
|
||||||
|
|||||||
+41
-89
@@ -46,9 +46,6 @@ import java.util.Map;
|
|||||||
public class MantleCarvingComponent extends IrisMantleComponent {
|
public class MantleCarvingComponent extends IrisMantleComponent {
|
||||||
private static final int CHUNK_SIZE = 16;
|
private static final int CHUNK_SIZE = 16;
|
||||||
private static final int CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE;
|
private static final int CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE;
|
||||||
private static final int TILE_SIZE = 2;
|
|
||||||
private static final int TILE_COUNT = CHUNK_SIZE / TILE_SIZE;
|
|
||||||
private static final int TILE_AREA = TILE_COUNT * TILE_COUNT;
|
|
||||||
private static final int BLEND_RADIUS = 3;
|
private static final int BLEND_RADIUS = 3;
|
||||||
private static final int FIELD_SIZE = CHUNK_SIZE + (BLEND_RADIUS * 2);
|
private static final int FIELD_SIZE = CHUNK_SIZE + (BLEND_RADIUS * 2);
|
||||||
private static final double MIN_WEIGHT = 0.08D;
|
private static final double MIN_WEIGHT = 0.08D;
|
||||||
@@ -104,20 +101,18 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
private List<WeightedProfile> resolveWeightedProfiles(int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap<IrisBiome> caveBiomeCache) {
|
private List<WeightedProfile> resolveWeightedProfiles(int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap<IrisBiome> caveBiomeCache) {
|
||||||
BlendScratch blendScratch = BLEND_SCRATCH.get();
|
BlendScratch blendScratch = BLEND_SCRATCH.get();
|
||||||
IrisCaveProfile[] profileField = blendScratch.profileField;
|
IrisCaveProfile[] profileField = blendScratch.profileField;
|
||||||
Map<IrisCaveProfile, double[]> tileProfileWeights = blendScratch.tileProfileWeights;
|
Map<IrisCaveProfile, double[]> columnProfileWeights = blendScratch.columnProfileWeights;
|
||||||
IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = blendScratch.activeProfiles;
|
IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = blendScratch.activeProfiles;
|
||||||
IrisCaveProfile[] kernelProfiles = blendScratch.kernelProfiles;
|
IrisCaveProfile[] kernelProfiles = blendScratch.kernelProfiles;
|
||||||
double[] kernelProfileWeights = blendScratch.kernelProfileWeights;
|
double[] kernelProfileWeights = blendScratch.kernelProfileWeights;
|
||||||
activeProfiles.clear();
|
activeProfiles.clear();
|
||||||
fillProfileField(profileField, chunkX, chunkZ, complex, resolverState, caveBiomeCache);
|
fillProfileField(profileField, chunkX, chunkZ, complex, resolverState, caveBiomeCache);
|
||||||
|
|
||||||
for (int tileX = 0; tileX < TILE_COUNT; tileX++) {
|
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
|
||||||
for (int tileZ = 0; tileZ < TILE_COUNT; tileZ++) {
|
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
|
||||||
int profileCount = 0;
|
int profileCount = 0;
|
||||||
int sampleLocalX = (tileX * TILE_SIZE) + 1;
|
int centerX = localX + BLEND_RADIUS;
|
||||||
int sampleLocalZ = (tileZ * TILE_SIZE) + 1;
|
int centerZ = localZ + BLEND_RADIUS;
|
||||||
int centerX = sampleLocalX + BLEND_RADIUS;
|
|
||||||
int centerZ = sampleLocalZ + BLEND_RADIUS;
|
|
||||||
double totalKernelWeight = 0D;
|
double totalKernelWeight = 0D;
|
||||||
|
|
||||||
for (int kernelIndex = 0; kernelIndex < KERNEL_SIZE; kernelIndex++) {
|
for (int kernelIndex = 0; kernelIndex < KERNEL_SIZE; kernelIndex++) {
|
||||||
@@ -164,30 +159,30 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tileIndex = tileIndex(tileX, tileZ);
|
int columnIndex = (localX << 4) | localZ;
|
||||||
double dominantWeight = clampWeight(dominantKernelWeight / totalKernelWeight);
|
double dominantWeight = clampWeight(dominantKernelWeight / totalKernelWeight);
|
||||||
double[] tileWeights = tileProfileWeights.get(dominantProfile);
|
double[] weights = columnProfileWeights.get(dominantProfile);
|
||||||
if (tileWeights == null) {
|
if (weights == null) {
|
||||||
tileWeights = new double[TILE_AREA];
|
weights = new double[CHUNK_AREA];
|
||||||
tileProfileWeights.put(dominantProfile, tileWeights);
|
columnProfileWeights.put(dominantProfile, weights);
|
||||||
} else if (!activeProfiles.containsKey(dominantProfile)) {
|
} else if (!activeProfiles.containsKey(dominantProfile)) {
|
||||||
Arrays.fill(tileWeights, 0D);
|
Arrays.fill(weights, 0D);
|
||||||
}
|
}
|
||||||
activeProfiles.put(dominantProfile, Boolean.TRUE);
|
activeProfiles.put(dominantProfile, Boolean.TRUE);
|
||||||
tileWeights[tileIndex] = dominantWeight;
|
weights[columnIndex] = dominantWeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WeightedProfile> tileWeightedProfiles = new ArrayList<>();
|
List<WeightedProfile> columnWeightedProfiles = new ArrayList<>();
|
||||||
for (IrisCaveProfile profile : activeProfiles.keySet()) {
|
for (IrisCaveProfile profile : activeProfiles.keySet()) {
|
||||||
double[] tileWeights = tileProfileWeights.get(profile);
|
double[] weights = columnProfileWeights.get(profile);
|
||||||
if (tileWeights == null) {
|
if (weights == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double totalWeight = 0D;
|
double totalWeight = 0D;
|
||||||
double maxWeight = 0D;
|
double maxWeight = 0D;
|
||||||
for (double weight : tileWeights) {
|
for (double weight : weights) {
|
||||||
totalWeight += weight;
|
totalWeight += weight;
|
||||||
if (weight > maxWeight) {
|
if (weight > maxWeight) {
|
||||||
maxWeight = weight;
|
maxWeight = weight;
|
||||||
@@ -198,12 +193,11 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double averageWeight = totalWeight / TILE_AREA;
|
double averageWeight = totalWeight / CHUNK_AREA;
|
||||||
tileWeightedProfiles.add(new WeightedProfile(profile, tileWeights, averageWeight, null));
|
columnWeightedProfiles.add(new WeightedProfile(profile, weights, averageWeight, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WeightedProfile> boundedTileProfiles = limitAndMergeBlendedProfiles(tileWeightedProfiles, MAX_BLENDED_PROFILE_PASSES, TILE_AREA);
|
List<WeightedProfile> blendedProfiles = limitAndMergeBlendedProfiles(columnWeightedProfiles, MAX_BLENDED_PROFILE_PASSES, CHUNK_AREA);
|
||||||
List<WeightedProfile> blendedProfiles = expandTileWeightedProfiles(boundedTileProfiles);
|
|
||||||
List<WeightedProfile> resolvedProfiles = resolveDimensionCarvingProfiles(chunkX, chunkZ, resolverState, blendScratch);
|
List<WeightedProfile> resolvedProfiles = resolveDimensionCarvingProfiles(chunkX, chunkZ, resolverState, blendScratch);
|
||||||
resolvedProfiles.addAll(blendedProfiles);
|
resolvedProfiles.addAll(blendedProfiles);
|
||||||
return resolvedProfiles;
|
return resolvedProfiles;
|
||||||
@@ -216,8 +210,8 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
return weightedProfiles;
|
return weightedProfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionTilePlans = blendScratch.dimensionTilePlans;
|
Map<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionColumnPlans = blendScratch.dimensionColumnPlans;
|
||||||
dimensionTilePlans.clear();
|
dimensionColumnPlans.clear();
|
||||||
|
|
||||||
for (IrisDimensionCarvingEntry entry : entries) {
|
for (IrisDimensionCarvingEntry entry : entries) {
|
||||||
if (entry == null || !entry.isEnabled()) {
|
if (entry == null || !entry.isEnabled()) {
|
||||||
@@ -229,13 +223,13 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrisDimensionCarvingEntry[] tilePlan = dimensionTilePlans.computeIfAbsent(entry, key -> new IrisDimensionCarvingEntry[TILE_AREA]);
|
IrisDimensionCarvingEntry[] columnPlan = dimensionColumnPlans.computeIfAbsent(entry, key -> new IrisDimensionCarvingEntry[CHUNK_AREA]);
|
||||||
buildDimensionTilePlan(tilePlan, chunkX, chunkZ, entry, resolverState);
|
buildDimensionColumnPlan(columnPlan, chunkX, chunkZ, entry, resolverState);
|
||||||
|
|
||||||
Map<IrisCaveProfile, double[]> rootProfileTileWeights = new IdentityHashMap<>();
|
Map<IrisCaveProfile, double[]> rootProfileColumnWeights = new IdentityHashMap<>();
|
||||||
IrisRange worldYRange = entry.getWorldYRange();
|
IrisRange worldYRange = entry.getWorldYRange();
|
||||||
for (int tileIndex = 0; tileIndex < TILE_AREA; tileIndex++) {
|
for (int columnIndex = 0; columnIndex < CHUNK_AREA; columnIndex++) {
|
||||||
IrisDimensionCarvingEntry resolvedEntry = tilePlan[tileIndex];
|
IrisDimensionCarvingEntry resolvedEntry = columnPlan[columnIndex];
|
||||||
IrisBiome resolvedBiome = IrisDimensionCarvingResolver.resolveEntryBiome(getEngineMantle().getEngine(), resolvedEntry, resolverState);
|
IrisBiome resolvedBiome = IrisDimensionCarvingResolver.resolveEntryBiome(getEngineMantle().getEngine(), resolvedEntry, resolverState);
|
||||||
if (resolvedBiome == null) {
|
if (resolvedBiome == null) {
|
||||||
continue;
|
continue;
|
||||||
@@ -246,75 +240,33 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double[] tileWeights = rootProfileTileWeights.computeIfAbsent(profile, key -> new double[TILE_AREA]);
|
double[] columnWeights = rootProfileColumnWeights.computeIfAbsent(profile, key -> new double[CHUNK_AREA]);
|
||||||
tileWeights[tileIndex] = 1D;
|
columnWeights[columnIndex] = 1D;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Map.Entry<IrisCaveProfile, double[]>> profileEntries = new ArrayList<>(rootProfileTileWeights.entrySet());
|
List<Map.Entry<IrisCaveProfile, double[]>> profileEntries = new ArrayList<>(rootProfileColumnWeights.entrySet());
|
||||||
profileEntries.sort((a, b) -> Integer.compare(a.getKey().hashCode(), b.getKey().hashCode()));
|
profileEntries.sort((a, b) -> Integer.compare(a.getKey().hashCode(), b.getKey().hashCode()));
|
||||||
for (Map.Entry<IrisCaveProfile, double[]> profileEntry : profileEntries) {
|
for (Map.Entry<IrisCaveProfile, double[]> profileEntry : profileEntries) {
|
||||||
double[] columnWeights = expandTileWeightsToColumns(profileEntry.getValue());
|
weightedProfiles.add(new WeightedProfile(profileEntry.getKey(), profileEntry.getValue(), -1D, worldYRange));
|
||||||
weightedProfiles.add(new WeightedProfile(profileEntry.getKey(), columnWeights, -1D, worldYRange));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return weightedProfiles;
|
return weightedProfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildDimensionTilePlan(IrisDimensionCarvingEntry[] tilePlan, int chunkX, int chunkZ, IrisDimensionCarvingEntry entry, IrisDimensionCarvingResolver.State resolverState) {
|
private void buildDimensionColumnPlan(IrisDimensionCarvingEntry[] columnPlan, int chunkX, int chunkZ, IrisDimensionCarvingEntry entry, IrisDimensionCarvingResolver.State resolverState) {
|
||||||
for (int tileX = 0; tileX < TILE_COUNT; tileX++) {
|
int baseX = chunkX << 4;
|
||||||
int worldX = (chunkX << 4) + (tileX * TILE_SIZE);
|
int baseZ = chunkZ << 4;
|
||||||
for (int tileZ = 0; tileZ < TILE_COUNT; tileZ++) {
|
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
|
||||||
int worldZ = (chunkZ << 4) + (tileZ * TILE_SIZE);
|
int worldX = baseX + localX;
|
||||||
int tileIndex = tileIndex(tileX, tileZ);
|
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
|
||||||
tilePlan[tileIndex] = IrisDimensionCarvingResolver.resolveFromRoot(getEngineMantle().getEngine(), entry, worldX, worldZ, resolverState);
|
int worldZ = baseZ + localZ;
|
||||||
|
int columnIndex = (localX << 4) | localZ;
|
||||||
|
columnPlan[columnIndex] = IrisDimensionCarvingResolver.resolveFromRoot(getEngineMantle().getEngine(), entry, worldX, worldZ, resolverState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<WeightedProfile> expandTileWeightedProfiles(List<WeightedProfile> tileWeightedProfiles) {
|
|
||||||
List<WeightedProfile> expandedProfiles = new ArrayList<>(tileWeightedProfiles.size());
|
|
||||||
for (WeightedProfile tileWeightedProfile : tileWeightedProfiles) {
|
|
||||||
double[] columnWeights = expandTileWeightsToColumns(tileWeightedProfile.columnWeights);
|
|
||||||
double averageWeight = computeAverageWeight(columnWeights, CHUNK_AREA);
|
|
||||||
expandedProfiles.add(new WeightedProfile(tileWeightedProfile.profile, columnWeights, averageWeight, tileWeightedProfile.worldYRange));
|
|
||||||
}
|
|
||||||
expandedProfiles.sort(MantleCarvingComponent::compareByCarveOrder);
|
|
||||||
return expandedProfiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double[] expandTileWeightsToColumns(double[] tileWeights) {
|
|
||||||
double[] columnWeights = new double[CHUNK_AREA];
|
|
||||||
if (tileWeights == null || tileWeights.length == 0) {
|
|
||||||
return columnWeights;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int tileX = 0; tileX < TILE_COUNT; tileX++) {
|
|
||||||
int columnX = tileX * TILE_SIZE;
|
|
||||||
int columnX2 = columnX + 1;
|
|
||||||
for (int tileZ = 0; tileZ < TILE_COUNT; tileZ++) {
|
|
||||||
int tileIndex = tileIndex(tileX, tileZ);
|
|
||||||
double weight = tileWeights[tileIndex];
|
|
||||||
if (weight <= 0D) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int columnZ = tileZ * TILE_SIZE;
|
|
||||||
int columnZ2 = columnZ + 1;
|
|
||||||
columnWeights[(columnX << 4) | columnZ] = weight;
|
|
||||||
columnWeights[(columnX << 4) | columnZ2] = weight;
|
|
||||||
columnWeights[(columnX2 << 4) | columnZ] = weight;
|
|
||||||
columnWeights[(columnX2 << 4) | columnZ2] = weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return columnWeights;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int tileIndex(int tileX, int tileZ) {
|
|
||||||
return (tileX * TILE_COUNT) + tileZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillProfileField(IrisCaveProfile[] profileField, int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap<IrisBiome> caveBiomeCache) {
|
private void fillProfileField(IrisCaveProfile[] profileField, int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap<IrisBiome> caveBiomeCache) {
|
||||||
int startX = (chunkX << 4) - BLEND_RADIUS;
|
int startX = (chunkX << 4) - BLEND_RADIUS;
|
||||||
int startZ = (chunkZ << 4) - BLEND_RADIUS;
|
int startZ = (chunkZ << 4) - BLEND_RADIUS;
|
||||||
@@ -554,8 +506,8 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
private final IrisCaveProfile[] profileField = new IrisCaveProfile[FIELD_SIZE * FIELD_SIZE];
|
private final IrisCaveProfile[] profileField = new IrisCaveProfile[FIELD_SIZE * FIELD_SIZE];
|
||||||
private final IrisCaveProfile[] kernelProfiles = new IrisCaveProfile[KERNEL_SIZE];
|
private final IrisCaveProfile[] kernelProfiles = new IrisCaveProfile[KERNEL_SIZE];
|
||||||
private final double[] kernelProfileWeights = new double[KERNEL_SIZE];
|
private final double[] kernelProfileWeights = new double[KERNEL_SIZE];
|
||||||
private final IdentityHashMap<IrisCaveProfile, double[]> tileProfileWeights = new IdentityHashMap<>();
|
private final IdentityHashMap<IrisCaveProfile, double[]> columnProfileWeights = new IdentityHashMap<>();
|
||||||
private final IdentityHashMap<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionTilePlans = new IdentityHashMap<>();
|
private final IdentityHashMap<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionColumnPlans = new IdentityHashMap<>();
|
||||||
private final IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = new IdentityHashMap<>();
|
private final IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = new IdentityHashMap<>();
|
||||||
private final int[] chunkSurfaceHeights = new int[CHUNK_AREA];
|
private final int[] chunkSurfaceHeights = new int[CHUNK_AREA];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class IrisCaveProfile {
|
|||||||
@MinNumber(1)
|
@MinNumber(1)
|
||||||
@MaxNumber(8)
|
@MaxNumber(8)
|
||||||
@Desc("Vertical sample step used while evaluating cave density.")
|
@Desc("Vertical sample step used while evaluating cave density.")
|
||||||
private int sampleStep = 2;
|
private int sampleStep = 1;
|
||||||
|
|
||||||
@MinNumber(0)
|
@MinNumber(0)
|
||||||
@MaxNumber(4096)
|
@MaxNumber(4096)
|
||||||
|
|||||||
+416
-28
@@ -5,6 +5,7 @@ import art.arcane.iris.engine.framework.Engine;
|
|||||||
import art.arcane.iris.engine.framework.EngineMetrics;
|
import art.arcane.iris.engine.framework.EngineMetrics;
|
||||||
import art.arcane.iris.engine.framework.SeedManager;
|
import art.arcane.iris.engine.framework.SeedManager;
|
||||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||||
|
import art.arcane.iris.engine.object.IrisCaveFieldModule;
|
||||||
import art.arcane.iris.engine.object.IrisCaveProfile;
|
import art.arcane.iris.engine.object.IrisCaveProfile;
|
||||||
import art.arcane.iris.engine.object.IrisDimension;
|
import art.arcane.iris.engine.object.IrisDimension;
|
||||||
import art.arcane.iris.engine.object.IrisGeneratorStyle;
|
import art.arcane.iris.engine.object.IrisGeneratorStyle;
|
||||||
@@ -12,8 +13,11 @@ import art.arcane.iris.engine.object.IrisRange;
|
|||||||
import art.arcane.iris.engine.object.IrisStyledRange;
|
import art.arcane.iris.engine.object.IrisStyledRange;
|
||||||
import art.arcane.iris.engine.object.IrisWorld;
|
import art.arcane.iris.engine.object.IrisWorld;
|
||||||
import art.arcane.iris.engine.object.NoiseStyle;
|
import art.arcane.iris.engine.object.NoiseStyle;
|
||||||
|
import art.arcane.iris.util.project.noise.CNG;
|
||||||
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||||
|
import art.arcane.volmlib.util.math.RNG;
|
||||||
import art.arcane.volmlib.util.matter.Matter;
|
import art.arcane.volmlib.util.matter.Matter;
|
||||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||||
import art.arcane.volmlib.util.matter.MatterSlice;
|
import art.arcane.volmlib.util.matter.MatterSlice;
|
||||||
@@ -24,6 +28,8 @@ import org.bukkit.block.data.BlockData;
|
|||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -41,49 +47,75 @@ import static org.mockito.Mockito.doReturn;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
public class IrisCaveCarver3DNearParityTest {
|
public class IrisCaveCarver3DNearParityTest {
|
||||||
|
private static Method sampleDensityMethod;
|
||||||
|
private static Field engineField;
|
||||||
|
private static Field dataField;
|
||||||
|
private static Field profileField;
|
||||||
|
private static Field surfaceBreakDensityField;
|
||||||
|
private static Field thresholdRngField;
|
||||||
|
private static Field carveAirField;
|
||||||
|
private static Field carveLavaField;
|
||||||
|
private static Field carveForcedAirField;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setupBukkit() {
|
public static void setupBukkit() throws Exception {
|
||||||
if (Bukkit.getServer() != null) {
|
if (Bukkit.getServer() == null) {
|
||||||
return;
|
Server server = mock(Server.class);
|
||||||
|
BlockData emptyBlockData = mock(BlockData.class);
|
||||||
|
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
|
||||||
|
doReturn("IrisTestServer").when(server).getName();
|
||||||
|
doReturn("1.0").when(server).getVersion();
|
||||||
|
doReturn("1.0").when(server).getBukkitVersion();
|
||||||
|
doReturn(emptyBlockData).when(server).createBlockData(any(Material.class));
|
||||||
|
doReturn(emptyBlockData).when(server).createBlockData(anyString());
|
||||||
|
Bukkit.setServer(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server server = mock(Server.class);
|
sampleDensityMethod = IrisCaveCarver3D.class.getDeclaredMethod("sampleDensityOptimized", int.class, int.class, int.class);
|
||||||
BlockData emptyBlockData = mock(BlockData.class);
|
sampleDensityMethod.setAccessible(true);
|
||||||
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
|
engineField = IrisCaveCarver3D.class.getDeclaredField("engine");
|
||||||
doReturn("IrisTestServer").when(server).getName();
|
engineField.setAccessible(true);
|
||||||
doReturn("1.0").when(server).getVersion();
|
dataField = IrisCaveCarver3D.class.getDeclaredField("data");
|
||||||
doReturn("1.0").when(server).getBukkitVersion();
|
dataField.setAccessible(true);
|
||||||
doReturn(emptyBlockData).when(server).createBlockData(any(Material.class));
|
profileField = IrisCaveCarver3D.class.getDeclaredField("profile");
|
||||||
doReturn(emptyBlockData).when(server).createBlockData(anyString());
|
profileField.setAccessible(true);
|
||||||
Bukkit.setServer(server);
|
surfaceBreakDensityField = IrisCaveCarver3D.class.getDeclaredField("surfaceBreakDensity");
|
||||||
|
surfaceBreakDensityField.setAccessible(true);
|
||||||
|
thresholdRngField = IrisCaveCarver3D.class.getDeclaredField("thresholdRng");
|
||||||
|
thresholdRngField.setAccessible(true);
|
||||||
|
carveAirField = IrisCaveCarver3D.class.getDeclaredField("carveAir");
|
||||||
|
carveAirField.setAccessible(true);
|
||||||
|
carveLavaField = IrisCaveCarver3D.class.getDeclaredField("carveLava");
|
||||||
|
carveLavaField.setAccessible(true);
|
||||||
|
carveForcedAirField = IrisCaveCarver3D.class.getDeclaredField("carveForcedAir");
|
||||||
|
carveForcedAirField.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void carvedCellDistributionStableAcrossEquivalentCarvers() {
|
public void carvedCellDistributionStableAcrossEquivalentCarvers() {
|
||||||
Engine engine = createEngine(128, 92);
|
Engine engine = createEngine(128, 92);
|
||||||
|
|
||||||
IrisCaveCarver3D firstCarver = new IrisCaveCarver3D(engine, createProfile());
|
IrisCaveCarver3D firstCarver = new IrisCaveCarver3D(engine, createProfile(true, true));
|
||||||
WriterCapture firstCapture = createWriterCapture(128);
|
WriterCapture firstCapture = createWriterCapture(128);
|
||||||
int firstCarved = firstCarver.carve(firstCapture.writer, 7, -3);
|
int firstCarved = firstCarver.carve(firstCapture.writer, 7, -3);
|
||||||
|
|
||||||
IrisCaveCarver3D secondCarver = new IrisCaveCarver3D(engine, createProfile());
|
IrisCaveCarver3D secondCarver = new IrisCaveCarver3D(engine, createProfile(true, true));
|
||||||
WriterCapture secondCapture = createWriterCapture(128);
|
WriterCapture secondCapture = createWriterCapture(128);
|
||||||
int secondCarved = secondCarver.carve(secondCapture.writer, 7, -3);
|
int secondCarved = secondCarver.carve(secondCapture.writer, 7, -3);
|
||||||
|
|
||||||
assertTrue(firstCarved > 0);
|
assertTrue(firstCarved > 0);
|
||||||
assertEquals(firstCarved, secondCarved);
|
assertEquals(firstCarved, secondCarved);
|
||||||
assertEquals(firstCapture.carvedCells, secondCapture.carvedCells);
|
assertEquals(firstCapture.carvedCells, secondCapture.carvedCells);
|
||||||
|
assertEquals(firstCapture.carvedLiquids, secondCapture.carvedLiquids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void latticePathCarvesChunkEdgesAndRespectsWorldHeightClipping() {
|
public void exactPathCarvesChunkEdgesAndRespectsWorldHeightClipping() {
|
||||||
Engine engine = createEngine(48, 46);
|
Engine engine = createEngine(48, 46);
|
||||||
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, createProfile());
|
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, createProfile(true, true));
|
||||||
WriterCapture capture = createWriterCapture(48);
|
WriterCapture capture = createWriterCapture(48);
|
||||||
double[] columnWeights = new double[256];
|
double[] columnWeights = fullWeights();
|
||||||
Arrays.fill(columnWeights, 1D);
|
int[] precomputedSurfaceHeights = filledHeights(46);
|
||||||
int[] precomputedSurfaceHeights = new int[256];
|
|
||||||
Arrays.fill(precomputedSurfaceHeights, 46);
|
|
||||||
|
|
||||||
int carved = carver.carve(capture.writer, 0, 0, columnWeights, 0D, 0D, new IrisRange(0D, 80D), precomputedSurfaceHeights);
|
int carved = carver.carve(capture.writer, 0, 0, columnWeights, 0D, 0D, new IrisRange(0D, 80D), precomputedSurfaceHeights);
|
||||||
|
|
||||||
@@ -96,6 +128,293 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
assertTrue(minY(capture.carvedCells) >= 0);
|
assertTrue(minY(capture.carvedCells) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exactPathMatchesNaiveReferenceWithoutWarpOrModules() throws Exception {
|
||||||
|
assertExactParity(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exactPathMatchesNaiveReferenceWithWarpAndModules() throws Exception {
|
||||||
|
assertExactParity(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void legacySampleStepTwoMatchesExactReference() throws Exception {
|
||||||
|
Engine engine = createEngine(96, 90);
|
||||||
|
double[] columnWeights = fullWeights();
|
||||||
|
int[] precomputedSurfaceHeights = filledHeights(90);
|
||||||
|
IrisRange worldYRange = new IrisRange(0D, 88D);
|
||||||
|
|
||||||
|
IrisCaveProfile exactProfile = createProfile(true, true).setSampleStep(1);
|
||||||
|
IrisCaveCarver3D exactCarver = new IrisCaveCarver3D(engine, exactProfile);
|
||||||
|
WriterCapture exactCapture = createWriterCapture(96);
|
||||||
|
int exactCarved = exactCarver.carve(exactCapture.writer, 5, -1, columnWeights, 0D, 0D, worldYRange, precomputedSurfaceHeights);
|
||||||
|
|
||||||
|
IrisCaveProfile legacyProfile = createProfile(true, true).setSampleStep(2);
|
||||||
|
IrisCaveCarver3D legacyCarver = new IrisCaveCarver3D(engine, legacyProfile);
|
||||||
|
WriterCapture legacyCapture = createWriterCapture(96);
|
||||||
|
int legacyCarved = legacyCarver.carve(legacyCapture.writer, 5, -1, columnWeights, 0D, 0D, worldYRange, precomputedSurfaceHeights);
|
||||||
|
|
||||||
|
assertEquals(exactCarved, legacyCarved);
|
||||||
|
assertEquals(exactCapture.carvedCells, legacyCapture.carvedCells);
|
||||||
|
assertEquals(exactCapture.carvedLiquids, legacyCapture.carvedLiquids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exactPathUsesExpectedLavaAndForcedAirBands() {
|
||||||
|
Engine engine = createEngine(48, 46);
|
||||||
|
double[] columnWeights = fullWeights();
|
||||||
|
int[] precomputedSurfaceHeights = filledHeights(46);
|
||||||
|
|
||||||
|
IrisCaveProfile lavaProfile = createProfile(false, false).setAllowLava(true).setAllowWater(false);
|
||||||
|
IrisCaveCarver3D lavaCarver = new IrisCaveCarver3D(engine, lavaProfile);
|
||||||
|
WriterCapture lavaCapture = createWriterCapture(48);
|
||||||
|
lavaCarver.carve(lavaCapture.writer, 0, 0, columnWeights, 0D, 0D, new IrisRange(0D, 80D), precomputedSurfaceHeights);
|
||||||
|
|
||||||
|
IrisCaveProfile forcedAirProfile = createProfile(false, false).setAllowLava(false).setAllowWater(false);
|
||||||
|
IrisCaveCarver3D forcedAirCarver = new IrisCaveCarver3D(engine, forcedAirProfile);
|
||||||
|
WriterCapture forcedAirCapture = createWriterCapture(48);
|
||||||
|
forcedAirCarver.carve(forcedAirCapture.writer, 0, 0, columnWeights, 0D, 0D, new IrisRange(0D, 80D), precomputedSurfaceHeights);
|
||||||
|
|
||||||
|
assertTrue(containsLiquidInRange(lavaCapture.carvedLiquids, 0, 18, (byte) 2));
|
||||||
|
assertTrue(containsLiquidInRange(forcedAirCapture.carvedLiquids, 0, 18, (byte) 3));
|
||||||
|
assertTrue(containsLiquidInRange(lavaCapture.carvedLiquids, 19, 47, (byte) 0));
|
||||||
|
assertTrue(containsLiquidInRange(forcedAirCapture.carvedLiquids, 19, 47, (byte) 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void optimizedExactPathOutperformsNaiveReference() throws Exception {
|
||||||
|
Engine engine = createEngine(128, 92);
|
||||||
|
IrisCaveCarver3D optimizedCarver = new IrisCaveCarver3D(engine, createProfile(true, true));
|
||||||
|
IrisCaveCarver3D naiveCarver = new IrisCaveCarver3D(engine, createProfile(true, true));
|
||||||
|
double[] columnWeights = fullWeights();
|
||||||
|
int[] precomputedSurfaceHeights = filledHeights(92);
|
||||||
|
IrisRange worldYRange = new IrisRange(0D, 96D);
|
||||||
|
|
||||||
|
for (int warmup = 0; warmup < 4; warmup++) {
|
||||||
|
runOptimizedOnce(optimizedCarver, 3, -2, columnWeights, worldYRange, precomputedSurfaceHeights, 128);
|
||||||
|
runNaiveOnce(naiveCarver, 3, -2, columnWeights, worldYRange, precomputedSurfaceHeights, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
long optimizedTime = 0L;
|
||||||
|
long naiveTime = 0L;
|
||||||
|
for (int iteration = 0; iteration < 10; iteration++) {
|
||||||
|
if ((iteration & 1) == 0) {
|
||||||
|
optimizedTime += runOptimizedOnce(optimizedCarver, 3, -2, columnWeights, worldYRange, precomputedSurfaceHeights, 128);
|
||||||
|
naiveTime += runNaiveOnce(naiveCarver, 3, -2, columnWeights, worldYRange, precomputedSurfaceHeights, 128);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
naiveTime += runNaiveOnce(naiveCarver, 3, -2, columnWeights, worldYRange, precomputedSurfaceHeights, 128);
|
||||||
|
optimizedTime += runOptimizedOnce(optimizedCarver, 3, -2, columnWeights, worldYRange, precomputedSurfaceHeights, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
double speedup = naiveTime / (double) optimizedTime;
|
||||||
|
assertTrue("expected at least 2.0x speedup but was " + speedup, speedup >= 2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertExactParity(boolean warp, boolean modules) throws Exception {
|
||||||
|
Engine engine = createEngine(96, 90);
|
||||||
|
double[] columnWeights = fullWeights();
|
||||||
|
int[] precomputedSurfaceHeights = filledHeights(90);
|
||||||
|
IrisRange worldYRange = new IrisRange(0D, 88D);
|
||||||
|
|
||||||
|
IrisCaveCarver3D optimizedCarver = new IrisCaveCarver3D(engine, createProfile(warp, modules));
|
||||||
|
WriterCapture optimizedCapture = createWriterCapture(96);
|
||||||
|
int optimizedCarved = optimizedCarver.carve(optimizedCapture.writer, 5, -1, columnWeights, 0D, 0D, worldYRange, precomputedSurfaceHeights);
|
||||||
|
|
||||||
|
IrisCaveCarver3D naiveCarver = new IrisCaveCarver3D(engine, createProfile(warp, modules));
|
||||||
|
WriterCapture naiveCapture = createWriterCapture(96);
|
||||||
|
int naiveCarved = carveNaiveExact(naiveCarver, naiveCapture.writer, 5, -1, columnWeights, worldYRange, precomputedSurfaceHeights);
|
||||||
|
|
||||||
|
assertEquals(optimizedCarved, naiveCarved);
|
||||||
|
assertEquals(optimizedCapture.carvedCells, naiveCapture.carvedCells);
|
||||||
|
assertEquals(optimizedCapture.carvedLiquids, naiveCapture.carvedLiquids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long runOptimizedOnce(IrisCaveCarver3D carver, int chunkX, int chunkZ, double[] columnWeights, IrisRange worldYRange, int[] precomputedSurfaceHeights, int worldHeight) {
|
||||||
|
WriterCapture capture = createWriterCapture(worldHeight);
|
||||||
|
long start = System.nanoTime();
|
||||||
|
carver.carve(capture.writer, chunkX, chunkZ, columnWeights, 0D, 0D, worldYRange, precomputedSurfaceHeights);
|
||||||
|
long elapsed = System.nanoTime() - start;
|
||||||
|
assertTrue(!capture.carvedCells.isEmpty());
|
||||||
|
return elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long runNaiveOnce(IrisCaveCarver3D carver, int chunkX, int chunkZ, double[] columnWeights, IrisRange worldYRange, int[] precomputedSurfaceHeights, int worldHeight) throws Exception {
|
||||||
|
WriterCapture capture = createWriterCapture(worldHeight);
|
||||||
|
long start = System.nanoTime();
|
||||||
|
int carved = carveNaiveExact(carver, capture.writer, chunkX, chunkZ, columnWeights, worldYRange, precomputedSurfaceHeights);
|
||||||
|
long elapsed = System.nanoTime() - start;
|
||||||
|
assertTrue(carved > 0);
|
||||||
|
return elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int carveNaiveExact(IrisCaveCarver3D carver, MantleWriter writer, int chunkX, int chunkZ, double[] columnWeights, IrisRange worldYRange, int[] precomputedSurfaceHeights) throws Exception {
|
||||||
|
Engine engine = (Engine) engineField.get(carver);
|
||||||
|
IrisData data = (IrisData) dataField.get(carver);
|
||||||
|
IrisCaveProfile profile = (IrisCaveProfile) profileField.get(carver);
|
||||||
|
CNG surfaceBreakDensity = (CNG) surfaceBreakDensityField.get(carver);
|
||||||
|
RNG thresholdRng = (RNG) thresholdRngField.get(carver);
|
||||||
|
MatterCavern carveAir = (MatterCavern) carveAirField.get(carver);
|
||||||
|
MatterCavern carveLava = (MatterCavern) carveLavaField.get(carver);
|
||||||
|
MatterCavern carveForcedAir = (MatterCavern) carveForcedAirField.get(carver);
|
||||||
|
|
||||||
|
double[] resolvedWeights = columnWeights;
|
||||||
|
if (resolvedWeights == null || resolvedWeights.length < 256) {
|
||||||
|
resolvedWeights = fullWeights();
|
||||||
|
}
|
||||||
|
|
||||||
|
int worldHeight = writer.getMantle().getWorldHeight();
|
||||||
|
int minY = Math.max(0, (int) Math.floor(profile.getVerticalRange().getMin()));
|
||||||
|
int maxY = Math.min(worldHeight - 1, (int) Math.ceil(profile.getVerticalRange().getMax()));
|
||||||
|
if (worldYRange != null) {
|
||||||
|
int worldMinHeight = engine.getWorld().minHeight();
|
||||||
|
int rangeMinY = (int) Math.floor(worldYRange.getMin() - worldMinHeight);
|
||||||
|
int rangeMaxY = (int) Math.ceil(worldYRange.getMax() - worldMinHeight);
|
||||||
|
minY = Math.max(minY, rangeMinY);
|
||||||
|
maxY = Math.min(maxY, rangeMaxY);
|
||||||
|
}
|
||||||
|
if (maxY < minY) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allowSurfaceBreak = profile.isAllowSurfaceBreak();
|
||||||
|
int surfaceClearance = Math.max(0, profile.getSurfaceClearance());
|
||||||
|
int surfaceBreakDepth = Math.max(0, profile.getSurfaceBreakDepth());
|
||||||
|
double surfaceBreakNoiseThreshold = profile.getSurfaceBreakNoiseThreshold();
|
||||||
|
double surfaceBreakThresholdBoost = Math.max(0D, profile.getSurfaceBreakThresholdBoost());
|
||||||
|
int[] columnTopY = new int[256];
|
||||||
|
int[] surfaceBreakFloorY = new int[256];
|
||||||
|
boolean[] surfaceBreakColumn = new boolean[256];
|
||||||
|
double[] passThreshold = new double[256];
|
||||||
|
double[] verticalEdgeFade = computeVerticalEdgeFade(profile, minY, maxY);
|
||||||
|
MatterCavern[] matterByY = computeMatterByY(engine, profile, carveAir, carveLava, carveForcedAir, minY, maxY);
|
||||||
|
|
||||||
|
int x0 = chunkX << 4;
|
||||||
|
int z0 = chunkZ << 4;
|
||||||
|
for (int localX = 0; localX < 16; localX++) {
|
||||||
|
int x = x0 + localX;
|
||||||
|
for (int localZ = 0; localZ < 16; localZ++) {
|
||||||
|
int z = z0 + localZ;
|
||||||
|
int columnIndex = (localX << 4) | localZ;
|
||||||
|
int columnSurfaceY;
|
||||||
|
if (precomputedSurfaceHeights != null && precomputedSurfaceHeights.length > columnIndex) {
|
||||||
|
columnSurfaceY = precomputedSurfaceHeights[columnIndex];
|
||||||
|
} else {
|
||||||
|
columnSurfaceY = engine.getHeight(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
int clearanceTopY = Math.min(maxY, Math.max(minY, columnSurfaceY - surfaceClearance));
|
||||||
|
boolean breakColumn = allowSurfaceBreak && signed(surfaceBreakDensity.noiseFast2D(x, z)) >= surfaceBreakNoiseThreshold;
|
||||||
|
int resolvedTopY = breakColumn ? Math.min(maxY, Math.max(minY, columnSurfaceY)) : clearanceTopY;
|
||||||
|
columnTopY[columnIndex] = resolvedTopY;
|
||||||
|
surfaceBreakFloorY[columnIndex] = Math.max(minY, columnSurfaceY - surfaceBreakDepth);
|
||||||
|
surfaceBreakColumn[columnIndex] = breakColumn;
|
||||||
|
double columnWeight = clampColumnWeight(resolvedWeights[columnIndex]);
|
||||||
|
if (columnWeight <= 0D || resolvedTopY < minY) {
|
||||||
|
passThreshold[columnIndex] = Double.NaN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
passThreshold[columnIndex] = profile.getDensityThreshold().get(thresholdRng, x, z, data) - profile.getThresholdBias();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
MantleChunk<Matter> chunk = writer.acquireChunk(chunkX, chunkZ);
|
||||||
|
if (chunk == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int carved = 0;
|
||||||
|
for (int localX = 0; localX < 16; localX++) {
|
||||||
|
int x = x0 + localX;
|
||||||
|
for (int localZ = 0; localZ < 16; localZ++) {
|
||||||
|
int z = z0 + localZ;
|
||||||
|
int columnIndex = (localX << 4) | localZ;
|
||||||
|
if (Double.isNaN(passThreshold[columnIndex])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int topY = columnTopY[columnIndex];
|
||||||
|
for (int y = minY; y <= topY; y++) {
|
||||||
|
double localThreshold = passThreshold[columnIndex];
|
||||||
|
if (surfaceBreakColumn[columnIndex] && y >= surfaceBreakFloorY[columnIndex]) {
|
||||||
|
localThreshold += surfaceBreakThresholdBoost;
|
||||||
|
}
|
||||||
|
localThreshold -= verticalEdgeFade[y - minY];
|
||||||
|
|
||||||
|
double density = (double) sampleDensityMethod.invoke(carver, x, y, z);
|
||||||
|
if (density > localThreshold) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matter sectionMatter = chunk.getOrCreate(y >> 4);
|
||||||
|
MatterSlice<MatterCavern> cavernSlice = sectionMatter.slice(MatterCavern.class);
|
||||||
|
cavernSlice.set(localX, y & 15, localZ, matterByY[y - minY]);
|
||||||
|
carved++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return carved;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[] computeVerticalEdgeFade(IrisCaveProfile profile, int minY, int maxY) {
|
||||||
|
int size = Math.max(0, maxY - minY + 1);
|
||||||
|
double[] verticalEdgeFade = new double[size];
|
||||||
|
int fadeRange = Math.max(0, profile.getVerticalEdgeFade());
|
||||||
|
double fadeStrength = Math.max(0D, profile.getVerticalEdgeFadeStrength());
|
||||||
|
if (size == 0 || fadeRange <= 0 || maxY <= minY || fadeStrength <= 0D) {
|
||||||
|
return verticalEdgeFade;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = minY; y <= maxY; y++) {
|
||||||
|
int floorDistance = y - minY;
|
||||||
|
int ceilingDistance = maxY - y;
|
||||||
|
int edgeDistance = Math.min(floorDistance, ceilingDistance);
|
||||||
|
int offsetIndex = y - minY;
|
||||||
|
if (edgeDistance >= fadeRange) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double t = Math.max(0D, Math.min(1D, edgeDistance / (double) fadeRange));
|
||||||
|
double smooth = t * t * (3D - (2D * t));
|
||||||
|
verticalEdgeFade[offsetIndex] = (1D - smooth) * fadeStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return verticalEdgeFade;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatterCavern[] computeMatterByY(Engine engine, IrisCaveProfile profile, MatterCavern carveAir, MatterCavern carveLava, MatterCavern carveForcedAir, int minY, int maxY) {
|
||||||
|
MatterCavern[] matterByY = new MatterCavern[Math.max(0, maxY - minY + 1)];
|
||||||
|
boolean allowLava = profile.isAllowLava();
|
||||||
|
boolean allowWater = profile.isAllowWater();
|
||||||
|
int lavaHeight = engine.getDimension().getCaveLavaHeight();
|
||||||
|
int fluidHeight = engine.getDimension().getFluidHeight();
|
||||||
|
|
||||||
|
for (int y = minY; y <= maxY; y++) {
|
||||||
|
int offset = y - minY;
|
||||||
|
if (allowLava && y <= lavaHeight) {
|
||||||
|
matterByY[offset] = carveLava;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (allowWater && y <= fluidHeight) {
|
||||||
|
matterByY[offset] = carveAir;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!allowLava && y <= lavaHeight) {
|
||||||
|
matterByY[offset] = carveForcedAir;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
matterByY[offset] = carveAir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matterByY;
|
||||||
|
}
|
||||||
|
|
||||||
private Engine createEngine(int worldHeight, int sampledHeight) {
|
private Engine createEngine(int worldHeight, int sampledHeight) {
|
||||||
Engine engine = mock(Engine.class);
|
Engine engine = mock(Engine.class);
|
||||||
IrisData data = mock(IrisData.class);
|
IrisData data = mock(IrisData.class);
|
||||||
@@ -117,7 +436,7 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IrisCaveProfile createProfile() {
|
private IrisCaveProfile createProfile(boolean warp, boolean modules) {
|
||||||
IrisCaveProfile profile = new IrisCaveProfile();
|
IrisCaveProfile profile = new IrisCaveProfile();
|
||||||
profile.setEnabled(true);
|
profile.setEnabled(true);
|
||||||
profile.setVerticalRange(new IrisRange(0D, 120D));
|
profile.setVerticalRange(new IrisRange(0D, 120D));
|
||||||
@@ -129,10 +448,10 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
profile.setSurfaceBreakStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.09D));
|
profile.setSurfaceBreakStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.09D));
|
||||||
profile.setBaseWeight(1D);
|
profile.setBaseWeight(1D);
|
||||||
profile.setDetailWeight(0.48D);
|
profile.setDetailWeight(0.48D);
|
||||||
profile.setWarpStrength(0.37D);
|
profile.setWarpStrength(warp ? 0.37D : 0D);
|
||||||
profile.setDensityThreshold(new IrisStyledRange(1D, 1D, new IrisGeneratorStyle(NoiseStyle.FLAT)));
|
profile.setDensityThreshold(new IrisStyledRange(1D, 1D, new IrisGeneratorStyle(NoiseStyle.FLAT)));
|
||||||
profile.setThresholdBias(0D);
|
profile.setThresholdBias(0D);
|
||||||
profile.setSampleStep(2);
|
profile.setSampleStep(1);
|
||||||
profile.setMinCarveCells(0);
|
profile.setMinCarveCells(0);
|
||||||
profile.setRecoveryThresholdBoost(0D);
|
profile.setRecoveryThresholdBoost(0D);
|
||||||
profile.setSurfaceClearance(5);
|
profile.setSurfaceClearance(5);
|
||||||
@@ -144,6 +463,26 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
profile.setWaterMinDepthBelowSurface(8);
|
profile.setWaterMinDepthBelowSurface(8);
|
||||||
profile.setWaterRequiresFloor(false);
|
profile.setWaterRequiresFloor(false);
|
||||||
profile.setAllowLava(true);
|
profile.setAllowLava(true);
|
||||||
|
if (modules) {
|
||||||
|
KList<IrisCaveFieldModule> caveModules = new KList<>();
|
||||||
|
caveModules.add(new IrisCaveFieldModule(
|
||||||
|
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.11D),
|
||||||
|
0.23D,
|
||||||
|
0.04D,
|
||||||
|
new IrisRange(0D, 72D),
|
||||||
|
false
|
||||||
|
));
|
||||||
|
caveModules.add(new IrisCaveFieldModule(
|
||||||
|
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.19D),
|
||||||
|
0.17D,
|
||||||
|
-0.06D,
|
||||||
|
new IrisRange(24D, 120D),
|
||||||
|
true
|
||||||
|
));
|
||||||
|
profile.setModules(caveModules);
|
||||||
|
} else {
|
||||||
|
profile.setModules(new KList<>());
|
||||||
|
}
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +495,7 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
Map<Integer, Matter> sections = new HashMap<>();
|
Map<Integer, Matter> sections = new HashMap<>();
|
||||||
Map<Integer, Map<Integer, MatterCavern>> sectionCells = new HashMap<>();
|
Map<Integer, Map<Integer, MatterCavern>> sectionCells = new HashMap<>();
|
||||||
Set<String> carvedCells = new HashSet<>();
|
Set<String> carvedCells = new HashSet<>();
|
||||||
|
Map<String, Byte> carvedLiquids = new HashMap<>();
|
||||||
|
|
||||||
doReturn(mantle).when(writer).getMantle();
|
doReturn(mantle).when(writer).getMantle();
|
||||||
doReturn(worldHeight).when(mantle).getWorldHeight();
|
doReturn(worldHeight).when(mantle).getWorldHeight();
|
||||||
@@ -167,15 +507,15 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matter created = createSection(sectionIndex, sectionCells, carvedCells);
|
Matter created = createSection(sectionIndex, sectionCells, carvedCells, carvedLiquids);
|
||||||
sections.put(sectionIndex, created);
|
sections.put(sectionIndex, created);
|
||||||
return created;
|
return created;
|
||||||
}).when(chunk).getOrCreate(anyInt());
|
}).when(chunk).getOrCreate(anyInt());
|
||||||
|
|
||||||
return new WriterCapture(writer, carvedCells);
|
return new WriterCapture(writer, carvedCells, carvedLiquids);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Matter createSection(int sectionIndex, Map<Integer, Map<Integer, MatterCavern>> sectionCells, Set<String> carvedCells) {
|
private Matter createSection(int sectionIndex, Map<Integer, Map<Integer, MatterCavern>> sectionCells, Set<String> carvedCells, Map<String, Byte> carvedLiquids) {
|
||||||
Matter matter = mock(Matter.class);
|
Matter matter = mock(Matter.class);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
MatterSlice<MatterCavern> slice = mock(MatterSlice.class);
|
MatterSlice<MatterCavern> slice = mock(MatterSlice.class);
|
||||||
@@ -195,13 +535,44 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
MatterCavern value = invocation.getArgument(3);
|
MatterCavern value = invocation.getArgument(3);
|
||||||
localCells.put(packLocal(localX, localY, localZ), value);
|
localCells.put(packLocal(localX, localY, localZ), value);
|
||||||
int worldY = (sectionIndex << 4) + localY;
|
int worldY = (sectionIndex << 4) + localY;
|
||||||
carvedCells.add(cellKey(localX, worldY, localZ));
|
String cellKey = cellKey(localX, worldY, localZ);
|
||||||
|
carvedCells.add(cellKey);
|
||||||
|
carvedLiquids.put(cellKey, value.getLiquid());
|
||||||
return null;
|
return null;
|
||||||
}).when(slice).set(anyInt(), anyInt(), anyInt(), any(MatterCavern.class));
|
}).when(slice).set(anyInt(), anyInt(), anyInt(), any(MatterCavern.class));
|
||||||
|
|
||||||
return matter;
|
return matter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double[] fullWeights() {
|
||||||
|
double[] columnWeights = new double[256];
|
||||||
|
Arrays.fill(columnWeights, 1D);
|
||||||
|
return columnWeights;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] filledHeights(int height) {
|
||||||
|
int[] heights = new int[256];
|
||||||
|
Arrays.fill(heights, height);
|
||||||
|
return heights;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double clampColumnWeight(double weight) {
|
||||||
|
if (Double.isNaN(weight) || Double.isInfinite(weight)) {
|
||||||
|
return 0D;
|
||||||
|
}
|
||||||
|
if (weight <= 0D) {
|
||||||
|
return 0D;
|
||||||
|
}
|
||||||
|
if (weight >= 1D) {
|
||||||
|
return 1D;
|
||||||
|
}
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double signed(double value) {
|
||||||
|
return (value * 2D) - 1D;
|
||||||
|
}
|
||||||
|
|
||||||
private int packLocal(int x, int y, int z) {
|
private int packLocal(int x, int y, int z) {
|
||||||
return (x << 8) | (y << 4) | z;
|
return (x << 8) | (y << 4) | z;
|
||||||
}
|
}
|
||||||
@@ -210,6 +581,21 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
return x + ":" + y + ":" + z;
|
return x + ":" + y + ":" + z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean containsLiquidInRange(Map<String, Byte> carvedLiquids, int minY, int maxY, byte liquid) {
|
||||||
|
for (Map.Entry<String, Byte> entry : carvedLiquids.entrySet()) {
|
||||||
|
if (entry.getValue() != liquid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] split = entry.getKey().split(":");
|
||||||
|
int y = Integer.parseInt(split[1]);
|
||||||
|
if (y >= minY && y <= maxY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasX(Set<String> carvedCells, int x) {
|
private boolean hasX(Set<String> carvedCells, int x) {
|
||||||
for (String cell : carvedCells) {
|
for (String cell : carvedCells) {
|
||||||
String[] split = cell.split(":");
|
String[] split = cell.split(":");
|
||||||
@@ -259,10 +645,12 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
private static final class WriterCapture {
|
private static final class WriterCapture {
|
||||||
private final MantleWriter writer;
|
private final MantleWriter writer;
|
||||||
private final Set<String> carvedCells;
|
private final Set<String> carvedCells;
|
||||||
|
private final Map<String, Byte> carvedLiquids;
|
||||||
|
|
||||||
private WriterCapture(MantleWriter writer, Set<String> carvedCells) {
|
private WriterCapture(MantleWriter writer, Set<String> carvedCells, Map<String, Byte> carvedLiquids) {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.carvedCells = carvedCells;
|
this.carvedCells = carvedCells;
|
||||||
|
this.carvedLiquids = carvedLiquids;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-24
@@ -17,7 +17,6 @@ import static org.junit.Assert.assertEquals;
|
|||||||
public class MantleCarvingComponentTop2BlendTest {
|
public class MantleCarvingComponentTop2BlendTest {
|
||||||
private static Constructor<?> weightedProfileConstructor;
|
private static Constructor<?> weightedProfileConstructor;
|
||||||
private static Method limitMethod;
|
private static Method limitMethod;
|
||||||
private static Method expandTileMethod;
|
|
||||||
private static Field profileField;
|
private static Field profileField;
|
||||||
private static Field columnWeightsField;
|
private static Field columnWeightsField;
|
||||||
|
|
||||||
@@ -28,8 +27,6 @@ public class MantleCarvingComponentTop2BlendTest {
|
|||||||
weightedProfileConstructor.setAccessible(true);
|
weightedProfileConstructor.setAccessible(true);
|
||||||
limitMethod = MantleCarvingComponent.class.getDeclaredMethod("limitAndMergeBlendedProfiles", List.class, int.class, int.class);
|
limitMethod = MantleCarvingComponent.class.getDeclaredMethod("limitAndMergeBlendedProfiles", List.class, int.class, int.class);
|
||||||
limitMethod.setAccessible(true);
|
limitMethod.setAccessible(true);
|
||||||
expandTileMethod = MantleCarvingComponent.class.getDeclaredMethod("expandTileWeightsToColumns", double[].class);
|
|
||||||
expandTileMethod.setAccessible(true);
|
|
||||||
profileField = weightedProfileClass.getDeclaredField("profile");
|
profileField = weightedProfileClass.getDeclaredField("profile");
|
||||||
profileField.setAccessible(true);
|
profileField.setAccessible(true);
|
||||||
columnWeightsField = weightedProfileClass.getDeclaredField("columnWeights");
|
columnWeightsField = weightedProfileClass.getDeclaredField("columnWeights");
|
||||||
@@ -67,21 +64,16 @@ public class MantleCarvingComponentTop2BlendTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tileWeightsExpandIntoFourColumnsPerTile() throws Exception {
|
public void topTwoMergePreservesIndependentWeightsAtHighColumnIndexes() throws Exception {
|
||||||
double[] tileWeights = new double[64];
|
WeightedInput input = createWeightedProfiles();
|
||||||
tileWeights[0] = 0.42D;
|
List<?> limited = invokeLimit(input.weightedProfiles(), 2);
|
||||||
tileWeights[9] = 0.73D;
|
|
||||||
double[] expanded = invokeExpand(tileWeights);
|
|
||||||
|
|
||||||
assertEquals(0.42D, expanded[(0 << 4) | 0], 0D);
|
Map<IrisCaveProfile, double[]> byProfile = extractWeightsByProfile(limited);
|
||||||
assertEquals(0.42D, expanded[(0 << 4) | 1], 0D);
|
IrisCaveProfile first = input.profiles().first();
|
||||||
assertEquals(0.42D, expanded[(1 << 4) | 0], 0D);
|
IrisCaveProfile second = input.profiles().second();
|
||||||
assertEquals(0.42D, expanded[(1 << 4) | 1], 0D);
|
|
||||||
|
|
||||||
assertEquals(0.73D, expanded[(2 << 4) | 2], 0D);
|
assertEquals(1.0D, byProfile.get(first)[255], 0D);
|
||||||
assertEquals(0.73D, expanded[(2 << 4) | 3], 0D);
|
assertEquals(1.0D, byProfile.get(second)[254], 0D);
|
||||||
assertEquals(0.73D, expanded[(3 << 4) | 2], 0D);
|
|
||||||
assertEquals(0.73D, expanded[(3 << 4) | 3], 0D);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private WeightedInput createWeightedProfiles() throws Exception {
|
private WeightedInput createWeightedProfiles() throws Exception {
|
||||||
@@ -90,17 +82,21 @@ public class MantleCarvingComponentTop2BlendTest {
|
|||||||
IrisCaveProfile third = new IrisCaveProfile().setEnabled(true).setBaseWeight(0.93D);
|
IrisCaveProfile third = new IrisCaveProfile().setEnabled(true).setBaseWeight(0.93D);
|
||||||
Profiles profiles = new Profiles(first, second, third);
|
Profiles profiles = new Profiles(first, second, third);
|
||||||
|
|
||||||
double[] firstWeights = new double[64];
|
double[] firstWeights = new double[256];
|
||||||
firstWeights[0] = 0.2D;
|
firstWeights[0] = 0.2D;
|
||||||
firstWeights[1] = 0.8D;
|
firstWeights[1] = 0.8D;
|
||||||
|
firstWeights[255] = 0.6D;
|
||||||
|
|
||||||
double[] secondWeights = new double[64];
|
double[] secondWeights = new double[256];
|
||||||
secondWeights[0] = 0.7D;
|
secondWeights[0] = 0.7D;
|
||||||
secondWeights[1] = 0.1D;
|
secondWeights[1] = 0.1D;
|
||||||
|
secondWeights[254] = 0.7D;
|
||||||
|
|
||||||
double[] thirdWeights = new double[64];
|
double[] thirdWeights = new double[256];
|
||||||
thirdWeights[0] = 0.3D;
|
thirdWeights[0] = 0.3D;
|
||||||
thirdWeights[1] = 0.4D;
|
thirdWeights[1] = 0.4D;
|
||||||
|
thirdWeights[254] = 0.3D;
|
||||||
|
thirdWeights[255] = 0.4D;
|
||||||
|
|
||||||
List<Object> weighted = new ArrayList<>();
|
List<Object> weighted = new ArrayList<>();
|
||||||
weighted.add(weightedProfileConstructor.newInstance(first, firstWeights, average(firstWeights), null));
|
weighted.add(weightedProfileConstructor.newInstance(first, firstWeights, average(firstWeights), null));
|
||||||
@@ -110,11 +106,7 @@ public class MantleCarvingComponentTop2BlendTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<?> invokeLimit(List<Object> weightedProfiles, int limit) throws Exception {
|
private List<?> invokeLimit(List<Object> weightedProfiles, int limit) throws Exception {
|
||||||
return (List<?>) limitMethod.invoke(null, weightedProfiles, limit, 64);
|
return (List<?>) limitMethod.invoke(null, weightedProfiles, limit, 256);
|
||||||
}
|
|
||||||
|
|
||||||
private double[] invokeExpand(double[] tileWeights) throws Exception {
|
|
||||||
return (double[]) expandTileMethod.invoke(null, (Object) tileWeights);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<IrisCaveProfile, double[]> extractWeightsByProfile(List<?> weightedProfiles) throws Exception {
|
private Map<IrisCaveProfile, double[]> extractWeightsByProfile(List<?> weightedProfiles) throws Exception {
|
||||||
|
|||||||
+15
-15
@@ -104,20 +104,20 @@ public class IrisDimensionCarvingResolverParityTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tileAnchoredChunkPlanResolutionIsStableAcrossRepeatedBuilds() {
|
public void columnAnchoredChunkPlanResolutionIsStableAcrossRepeatedBuilds() {
|
||||||
Fixture fixture = createMixedDepthFixture();
|
Fixture fixture = createMixedDepthFixture();
|
||||||
IrisDimensionCarvingResolver.State state = new IrisDimensionCarvingResolver.State();
|
IrisDimensionCarvingResolver.State state = new IrisDimensionCarvingResolver.State();
|
||||||
IrisDimensionCarvingEntry root = legacyResolveRootEntry(fixture.engine, 80);
|
IrisDimensionCarvingEntry root = legacyResolveRootEntry(fixture.engine, 80);
|
||||||
|
|
||||||
for (int chunkX = -24; chunkX <= 24; chunkX += 6) {
|
for (int chunkX = -24; chunkX <= 24; chunkX += 6) {
|
||||||
for (int chunkZ = -24; chunkZ <= 24; chunkZ += 6) {
|
for (int chunkZ = -24; chunkZ <= 24; chunkZ += 6) {
|
||||||
IrisDimensionCarvingEntry[] firstPlan = buildTilePlan(fixture.engine, root, chunkX, chunkZ, state);
|
IrisDimensionCarvingEntry[] firstPlan = buildColumnPlan(fixture.engine, root, chunkX, chunkZ, state);
|
||||||
IrisDimensionCarvingEntry[] secondPlan = buildTilePlan(fixture.engine, root, chunkX, chunkZ, state);
|
IrisDimensionCarvingEntry[] secondPlan = buildColumnPlan(fixture.engine, root, chunkX, chunkZ, state);
|
||||||
for (int tileIndex = 0; tileIndex < firstPlan.length; tileIndex++) {
|
for (int columnIndex = 0; columnIndex < firstPlan.length; columnIndex++) {
|
||||||
assertSame(
|
assertSame(
|
||||||
"tile plan mismatch at chunkX=" + chunkX + " chunkZ=" + chunkZ + " tileIndex=" + tileIndex,
|
"column plan mismatch at chunkX=" + chunkX + " chunkZ=" + chunkZ + " columnIndex=" + columnIndex,
|
||||||
firstPlan[tileIndex],
|
firstPlan[columnIndex],
|
||||||
secondPlan[tileIndex]
|
secondPlan[columnIndex]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,14 +272,14 @@ public class IrisDimensionCarvingResolverParityTest {
|
|||||||
return new Fixture(engine);
|
return new Fixture(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IrisDimensionCarvingEntry[] buildTilePlan(Engine engine, IrisDimensionCarvingEntry rootEntry, int chunkX, int chunkZ, IrisDimensionCarvingResolver.State state) {
|
private IrisDimensionCarvingEntry[] buildColumnPlan(Engine engine, IrisDimensionCarvingEntry rootEntry, int chunkX, int chunkZ, IrisDimensionCarvingResolver.State state) {
|
||||||
IrisDimensionCarvingEntry[] plan = new IrisDimensionCarvingEntry[64];
|
IrisDimensionCarvingEntry[] plan = new IrisDimensionCarvingEntry[256];
|
||||||
for (int tileX = 0; tileX < 8; tileX++) {
|
for (int localX = 0; localX < 16; localX++) {
|
||||||
int worldX = (chunkX << 4) + (tileX << 1);
|
int worldX = (chunkX << 4) + localX;
|
||||||
for (int tileZ = 0; tileZ < 8; tileZ++) {
|
for (int localZ = 0; localZ < 16; localZ++) {
|
||||||
int worldZ = (chunkZ << 4) + (tileZ << 1);
|
int worldZ = (chunkZ << 4) + localZ;
|
||||||
int tileIndex = (tileX * 8) + tileZ;
|
int columnIndex = (localX << 4) | localZ;
|
||||||
plan[tileIndex] = IrisDimensionCarvingResolver.resolveFromRoot(engine, rootEntry, worldX, worldZ, state);
|
plan[columnIndex] = IrisDimensionCarvingResolver.resolveFromRoot(engine, rootEntry, worldX, worldZ, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return plan;
|
return plan;
|
||||||
|
|||||||
Reference in New Issue
Block a user