mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-23 23:31:00 +00:00
Studio FIxes
This commit is contained in:
@@ -134,29 +134,30 @@ public class CommandStudio implements DirectorExecutor {
|
||||
|
||||
@Director(description = "Close an open studio project", aliases = {"x", "c"}, sync = true)
|
||||
public void close() {
|
||||
VolmitSender commandSender = sender();
|
||||
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
||||
sender().sendMessage(C.RED + "No open studio projects.");
|
||||
commandSender.sendMessage(C.RED + "No open studio projects.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender().sendMessage(C.YELLOW + "Closing studio...");
|
||||
commandSender.sendMessage(C.YELLOW + "Closing studio...");
|
||||
Iris.service(StudioSVC.class).close().whenComplete((result, throwable) -> J.s(() -> {
|
||||
if (throwable != null) {
|
||||
sender().sendMessage(C.RED + "Studio close failed: " + throwable.getMessage());
|
||||
commandSender.sendMessage(C.RED + "Studio close failed: " + throwable.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.loader.IrisData;
|
||||
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.BoardProvider;
|
||||
import art.arcane.volmlib.util.board.BoardSettings;
|
||||
@@ -97,9 +98,12 @@ public class BoardSVC implements IrisService, BoardProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IrisToolbelt.isIrisStudioWorld(p.getWorld())) {
|
||||
if (isEligibleWorld(p)) {
|
||||
boards.computeIfAbsent(p, PlayerBoard::new);
|
||||
} else remove(p);
|
||||
return;
|
||||
}
|
||||
|
||||
remove(p);
|
||||
}
|
||||
|
||||
private void remove(Player player) {
|
||||
@@ -132,6 +136,20 @@ public class BoardSVC implements IrisService, BoardProvider {
|
||||
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
|
||||
public class PlayerBoard {
|
||||
private final Player player;
|
||||
@@ -159,18 +177,12 @@ public class BoardSVC implements IrisService, BoardProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IrisToolbelt.isIrisStudioWorld(player.getWorld())) {
|
||||
if (!isEligibleWorld(player)) {
|
||||
boards.remove(player);
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
||||
board.update();
|
||||
schedule(20);
|
||||
return;
|
||||
}
|
||||
|
||||
update();
|
||||
board.update();
|
||||
schedule(20);
|
||||
|
||||
+346
-49
@@ -163,6 +163,7 @@ public class IrisCaveCarver3D {
|
||||
maxY = Math.min(maxY, rangeMaxY);
|
||||
}
|
||||
int sampleStep = Math.max(1, profile.getSampleStep());
|
||||
boolean exactSampling = sampleStep <= 2;
|
||||
int surfaceClearance = Math.max(0, profile.getSurfaceClearance());
|
||||
int surfaceBreakDepth = Math.max(0, profile.getSurfaceBreakDepth());
|
||||
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());
|
||||
double recoveryThresholdBoost = Math.max(0, profile.getRecoveryThresholdBoost());
|
||||
if (carved < minCarveCells && recoveryThresholdBoost > 0D) {
|
||||
carved += carvePassLattice(
|
||||
int carved;
|
||||
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,
|
||||
x0,
|
||||
z0,
|
||||
@@ -256,33 +278,32 @@ public class IrisCaveCarver3D {
|
||||
matterByY,
|
||||
resolvedMinWeight,
|
||||
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,
|
||||
false
|
||||
);
|
||||
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(
|
||||
chunk,
|
||||
x0,
|
||||
@@ -300,9 +321,31 @@ public class IrisCaveCarver3D {
|
||||
matterByY,
|
||||
resolvedMinWeight,
|
||||
resolvedThresholdPenalty,
|
||||
recoveryThresholdBoost,
|
||||
true
|
||||
0D,
|
||||
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(
|
||||
MantleChunk<Matter> chunk,
|
||||
int x0,
|
||||
@@ -557,6 +719,137 @@ public class IrisCaveCarver3D {
|
||||
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) {
|
||||
double density = signed(baseDensity.noiseFast3D(x, y, z)) * baseWeight;
|
||||
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[] fullWeights = 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[] tileLocalX = 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 {
|
||||
private static final int CHUNK_SIZE = 16;
|
||||
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 FIELD_SIZE = CHUNK_SIZE + (BLEND_RADIUS * 2);
|
||||
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) {
|
||||
BlendScratch blendScratch = BLEND_SCRATCH.get();
|
||||
IrisCaveProfile[] profileField = blendScratch.profileField;
|
||||
Map<IrisCaveProfile, double[]> tileProfileWeights = blendScratch.tileProfileWeights;
|
||||
Map<IrisCaveProfile, double[]> columnProfileWeights = blendScratch.columnProfileWeights;
|
||||
IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = blendScratch.activeProfiles;
|
||||
IrisCaveProfile[] kernelProfiles = blendScratch.kernelProfiles;
|
||||
double[] kernelProfileWeights = blendScratch.kernelProfileWeights;
|
||||
activeProfiles.clear();
|
||||
fillProfileField(profileField, chunkX, chunkZ, complex, resolverState, caveBiomeCache);
|
||||
|
||||
for (int tileX = 0; tileX < TILE_COUNT; tileX++) {
|
||||
for (int tileZ = 0; tileZ < TILE_COUNT; tileZ++) {
|
||||
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
|
||||
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
|
||||
int profileCount = 0;
|
||||
int sampleLocalX = (tileX * TILE_SIZE) + 1;
|
||||
int sampleLocalZ = (tileZ * TILE_SIZE) + 1;
|
||||
int centerX = sampleLocalX + BLEND_RADIUS;
|
||||
int centerZ = sampleLocalZ + BLEND_RADIUS;
|
||||
int centerX = localX + BLEND_RADIUS;
|
||||
int centerZ = localZ + BLEND_RADIUS;
|
||||
double totalKernelWeight = 0D;
|
||||
|
||||
for (int kernelIndex = 0; kernelIndex < KERNEL_SIZE; kernelIndex++) {
|
||||
@@ -164,30 +159,30 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
continue;
|
||||
}
|
||||
|
||||
int tileIndex = tileIndex(tileX, tileZ);
|
||||
int columnIndex = (localX << 4) | localZ;
|
||||
double dominantWeight = clampWeight(dominantKernelWeight / totalKernelWeight);
|
||||
double[] tileWeights = tileProfileWeights.get(dominantProfile);
|
||||
if (tileWeights == null) {
|
||||
tileWeights = new double[TILE_AREA];
|
||||
tileProfileWeights.put(dominantProfile, tileWeights);
|
||||
double[] weights = columnProfileWeights.get(dominantProfile);
|
||||
if (weights == null) {
|
||||
weights = new double[CHUNK_AREA];
|
||||
columnProfileWeights.put(dominantProfile, weights);
|
||||
} else if (!activeProfiles.containsKey(dominantProfile)) {
|
||||
Arrays.fill(tileWeights, 0D);
|
||||
Arrays.fill(weights, 0D);
|
||||
}
|
||||
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()) {
|
||||
double[] tileWeights = tileProfileWeights.get(profile);
|
||||
if (tileWeights == null) {
|
||||
double[] weights = columnProfileWeights.get(profile);
|
||||
if (weights == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double totalWeight = 0D;
|
||||
double maxWeight = 0D;
|
||||
for (double weight : tileWeights) {
|
||||
for (double weight : weights) {
|
||||
totalWeight += weight;
|
||||
if (weight > maxWeight) {
|
||||
maxWeight = weight;
|
||||
@@ -198,12 +193,11 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
continue;
|
||||
}
|
||||
|
||||
double averageWeight = totalWeight / TILE_AREA;
|
||||
tileWeightedProfiles.add(new WeightedProfile(profile, tileWeights, averageWeight, null));
|
||||
double averageWeight = totalWeight / CHUNK_AREA;
|
||||
columnWeightedProfiles.add(new WeightedProfile(profile, weights, averageWeight, null));
|
||||
}
|
||||
|
||||
List<WeightedProfile> boundedTileProfiles = limitAndMergeBlendedProfiles(tileWeightedProfiles, MAX_BLENDED_PROFILE_PASSES, TILE_AREA);
|
||||
List<WeightedProfile> blendedProfiles = expandTileWeightedProfiles(boundedTileProfiles);
|
||||
List<WeightedProfile> blendedProfiles = limitAndMergeBlendedProfiles(columnWeightedProfiles, MAX_BLENDED_PROFILE_PASSES, CHUNK_AREA);
|
||||
List<WeightedProfile> resolvedProfiles = resolveDimensionCarvingProfiles(chunkX, chunkZ, resolverState, blendScratch);
|
||||
resolvedProfiles.addAll(blendedProfiles);
|
||||
return resolvedProfiles;
|
||||
@@ -216,8 +210,8 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
return weightedProfiles;
|
||||
}
|
||||
|
||||
Map<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionTilePlans = blendScratch.dimensionTilePlans;
|
||||
dimensionTilePlans.clear();
|
||||
Map<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionColumnPlans = blendScratch.dimensionColumnPlans;
|
||||
dimensionColumnPlans.clear();
|
||||
|
||||
for (IrisDimensionCarvingEntry entry : entries) {
|
||||
if (entry == null || !entry.isEnabled()) {
|
||||
@@ -229,13 +223,13 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
continue;
|
||||
}
|
||||
|
||||
IrisDimensionCarvingEntry[] tilePlan = dimensionTilePlans.computeIfAbsent(entry, key -> new IrisDimensionCarvingEntry[TILE_AREA]);
|
||||
buildDimensionTilePlan(tilePlan, chunkX, chunkZ, entry, resolverState);
|
||||
IrisDimensionCarvingEntry[] columnPlan = dimensionColumnPlans.computeIfAbsent(entry, key -> new IrisDimensionCarvingEntry[CHUNK_AREA]);
|
||||
buildDimensionColumnPlan(columnPlan, chunkX, chunkZ, entry, resolverState);
|
||||
|
||||
Map<IrisCaveProfile, double[]> rootProfileTileWeights = new IdentityHashMap<>();
|
||||
Map<IrisCaveProfile, double[]> rootProfileColumnWeights = new IdentityHashMap<>();
|
||||
IrisRange worldYRange = entry.getWorldYRange();
|
||||
for (int tileIndex = 0; tileIndex < TILE_AREA; tileIndex++) {
|
||||
IrisDimensionCarvingEntry resolvedEntry = tilePlan[tileIndex];
|
||||
for (int columnIndex = 0; columnIndex < CHUNK_AREA; columnIndex++) {
|
||||
IrisDimensionCarvingEntry resolvedEntry = columnPlan[columnIndex];
|
||||
IrisBiome resolvedBiome = IrisDimensionCarvingResolver.resolveEntryBiome(getEngineMantle().getEngine(), resolvedEntry, resolverState);
|
||||
if (resolvedBiome == null) {
|
||||
continue;
|
||||
@@ -246,75 +240,33 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
continue;
|
||||
}
|
||||
|
||||
double[] tileWeights = rootProfileTileWeights.computeIfAbsent(profile, key -> new double[TILE_AREA]);
|
||||
tileWeights[tileIndex] = 1D;
|
||||
double[] columnWeights = rootProfileColumnWeights.computeIfAbsent(profile, key -> new double[CHUNK_AREA]);
|
||||
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()));
|
||||
for (Map.Entry<IrisCaveProfile, double[]> profileEntry : profileEntries) {
|
||||
double[] columnWeights = expandTileWeightsToColumns(profileEntry.getValue());
|
||||
weightedProfiles.add(new WeightedProfile(profileEntry.getKey(), columnWeights, -1D, worldYRange));
|
||||
weightedProfiles.add(new WeightedProfile(profileEntry.getKey(), profileEntry.getValue(), -1D, worldYRange));
|
||||
}
|
||||
}
|
||||
|
||||
return weightedProfiles;
|
||||
}
|
||||
|
||||
private void buildDimensionTilePlan(IrisDimensionCarvingEntry[] tilePlan, int chunkX, int chunkZ, IrisDimensionCarvingEntry entry, IrisDimensionCarvingResolver.State resolverState) {
|
||||
for (int tileX = 0; tileX < TILE_COUNT; tileX++) {
|
||||
int worldX = (chunkX << 4) + (tileX * TILE_SIZE);
|
||||
for (int tileZ = 0; tileZ < TILE_COUNT; tileZ++) {
|
||||
int worldZ = (chunkZ << 4) + (tileZ * TILE_SIZE);
|
||||
int tileIndex = tileIndex(tileX, tileZ);
|
||||
tilePlan[tileIndex] = IrisDimensionCarvingResolver.resolveFromRoot(getEngineMantle().getEngine(), entry, worldX, worldZ, resolverState);
|
||||
private void buildDimensionColumnPlan(IrisDimensionCarvingEntry[] columnPlan, int chunkX, int chunkZ, IrisDimensionCarvingEntry entry, IrisDimensionCarvingResolver.State resolverState) {
|
||||
int baseX = chunkX << 4;
|
||||
int baseZ = chunkZ << 4;
|
||||
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
|
||||
int worldX = baseX + localX;
|
||||
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
|
||||
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) {
|
||||
int startX = (chunkX << 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[] kernelProfiles = new IrisCaveProfile[KERNEL_SIZE];
|
||||
private final double[] kernelProfileWeights = new double[KERNEL_SIZE];
|
||||
private final IdentityHashMap<IrisCaveProfile, double[]> tileProfileWeights = new IdentityHashMap<>();
|
||||
private final IdentityHashMap<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionTilePlans = new IdentityHashMap<>();
|
||||
private final IdentityHashMap<IrisCaveProfile, double[]> columnProfileWeights = new IdentityHashMap<>();
|
||||
private final IdentityHashMap<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionColumnPlans = new IdentityHashMap<>();
|
||||
private final IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = new IdentityHashMap<>();
|
||||
private final int[] chunkSurfaceHeights = new int[CHUNK_AREA];
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class IrisCaveProfile {
|
||||
@MinNumber(1)
|
||||
@MaxNumber(8)
|
||||
@Desc("Vertical sample step used while evaluating cave density.")
|
||||
private int sampleStep = 2;
|
||||
private int sampleStep = 1;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(4096)
|
||||
|
||||
Reference in New Issue
Block a user