Учёт времени, погоды и биома
This commit is contained in:
@@ -0,0 +1,368 @@
|
|||||||
|
package su.divan2000.veila.client.render.fog;
|
||||||
|
|
||||||
|
import net.minecraft.registry.entry.RegistryEntry;
|
||||||
|
import net.minecraft.world.biome.Biome;
|
||||||
|
|
||||||
|
public final class FogBiomeParams {
|
||||||
|
|
||||||
|
public float emissionMultiplier;
|
||||||
|
public float targetDensityBase;
|
||||||
|
public float humidity;
|
||||||
|
public float timeSensitivity;
|
||||||
|
|
||||||
|
public FogBiomeParams(float emission, float targetDensityBase,
|
||||||
|
float humidity, float timeSensitivity) {
|
||||||
|
this.emissionMultiplier = emission;
|
||||||
|
this.targetDensityBase = targetDensityBase;
|
||||||
|
this.humidity = humidity;
|
||||||
|
this.timeSensitivity = timeSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FogBiomeParams forBiome(RegistryEntry<Biome> biomeEntry) {
|
||||||
|
Biome biome = biomeEntry.value();
|
||||||
|
|
||||||
|
// Полный идентификатор: "minecraft:plains", "biomesoplenty:cherry_blossom_grove"
|
||||||
|
String biomeId = biomeEntry.getKey()
|
||||||
|
.map(key -> key.getValue().toString())
|
||||||
|
.orElse("minecraft:unknown");
|
||||||
|
|
||||||
|
float emission = 1.0f;
|
||||||
|
float targetDensity = 0.03f;
|
||||||
|
float humidity = 0.5f;
|
||||||
|
float timeSensitivity = 1.0f;
|
||||||
|
|
||||||
|
switch (biomeId) {
|
||||||
|
// === Ванильные пустыни (никогда нет дымки) ===
|
||||||
|
case "minecraft:desert":
|
||||||
|
emission = 0.1f;
|
||||||
|
targetDensity = 0.0f;
|
||||||
|
humidity = 0.0f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные бесплодные земли ===
|
||||||
|
case "minecraft:badlands":
|
||||||
|
case "minecraft:wooded_badlands":
|
||||||
|
case "minecraft:eroded_badlands":
|
||||||
|
emission = 0.15f;
|
||||||
|
targetDensity = 0.0f;
|
||||||
|
humidity = 0.05f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные джунгли ===
|
||||||
|
case "minecraft:jungle":
|
||||||
|
case "minecraft:bamboo_jungle":
|
||||||
|
emission = 1.0f;
|
||||||
|
targetDensity = 0.045f;
|
||||||
|
humidity = 0.9f;
|
||||||
|
timeSensitivity = 0.3f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:sparse_jungle":
|
||||||
|
emission = 0.8f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.75f;
|
||||||
|
timeSensitivity = 0.5f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные болота ===
|
||||||
|
case "minecraft:swamp":
|
||||||
|
emission = 1.5f;
|
||||||
|
targetDensity = 0.09f;
|
||||||
|
humidity = 0.9f;
|
||||||
|
timeSensitivity = 0.4f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:mangrove_swamp":
|
||||||
|
emission = 1.4f;
|
||||||
|
targetDensity = 0.075f;
|
||||||
|
humidity = 0.85f;
|
||||||
|
timeSensitivity = 0.5f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные равнины ===
|
||||||
|
case "minecraft:plains":
|
||||||
|
case "minecraft:sunflower_plains":
|
||||||
|
emission = 1.0f;
|
||||||
|
targetDensity = 0.03f;
|
||||||
|
humidity = 0.5f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:snowy_plains":
|
||||||
|
emission = 0.85f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.55f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные луга и вишнёвые рощи ===
|
||||||
|
case "minecraft:meadow":
|
||||||
|
case "minecraft:cherry_grove":
|
||||||
|
emission = 1.1f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные леса ===
|
||||||
|
case "minecraft:forest":
|
||||||
|
case "minecraft:flower_forest":
|
||||||
|
case "minecraft:birch_forest":
|
||||||
|
case "minecraft:old_growth_birch_forest":
|
||||||
|
emission = 0.9f;
|
||||||
|
targetDensity = 0.036f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:dark_forest":
|
||||||
|
emission = 1.0f;
|
||||||
|
targetDensity = 0.04f;
|
||||||
|
humidity = 0.65f;
|
||||||
|
timeSensitivity = 0.85f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:mushroom_fields":
|
||||||
|
emission = 0.7f;
|
||||||
|
targetDensity = 0.025f;
|
||||||
|
humidity = 0.5f;
|
||||||
|
timeSensitivity = 0.8f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильная тайга ===
|
||||||
|
case "minecraft:taiga":
|
||||||
|
case "minecraft:snowy_taiga":
|
||||||
|
emission = 0.85f;
|
||||||
|
targetDensity = 0.038f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:old_growth_pine_taiga":
|
||||||
|
case "minecraft:old_growth_spruce_taiga":
|
||||||
|
emission = 0.95f;
|
||||||
|
targetDensity = 0.042f;
|
||||||
|
humidity = 0.7f;
|
||||||
|
timeSensitivity = 0.8f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные саванны ===
|
||||||
|
case "minecraft:savanna":
|
||||||
|
case "minecraft:savanna_plateau":
|
||||||
|
case "minecraft:windswept_savanna":
|
||||||
|
emission = 0.2f;
|
||||||
|
targetDensity = 0.005f;
|
||||||
|
humidity = 0.1f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные горы ===
|
||||||
|
case "minecraft:windswept_hills":
|
||||||
|
case "minecraft:windswept_gravelly_hills":
|
||||||
|
case "minecraft:windswept_forest":
|
||||||
|
emission = 0.6f;
|
||||||
|
targetDensity = 0.02f;
|
||||||
|
humidity = 0.35f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:grove":
|
||||||
|
emission = 0.7f;
|
||||||
|
targetDensity = 0.025f;
|
||||||
|
humidity = 0.4f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:snowy_slopes":
|
||||||
|
emission = 0.65f;
|
||||||
|
targetDensity = 0.022f;
|
||||||
|
humidity = 0.35f;
|
||||||
|
timeSensitivity = 0.95f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:jagged_peaks":
|
||||||
|
case "minecraft:frozen_peaks":
|
||||||
|
case "minecraft:stony_peaks":
|
||||||
|
emission = 0.5f;
|
||||||
|
targetDensity = 0.015f;
|
||||||
|
humidity = 0.25f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные побережья ===
|
||||||
|
case "minecraft:beach":
|
||||||
|
case "minecraft:snowy_beach":
|
||||||
|
emission = 0.75f;
|
||||||
|
targetDensity = 0.028f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.85f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:stony_shore":
|
||||||
|
emission = 0.6f;
|
||||||
|
targetDensity = 0.02f;
|
||||||
|
humidity = 0.45f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные реки ===
|
||||||
|
case "minecraft:river":
|
||||||
|
emission = 0.8f;
|
||||||
|
targetDensity = 0.032f;
|
||||||
|
humidity = 0.65f;
|
||||||
|
timeSensitivity = 0.8f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:frozen_river":
|
||||||
|
emission = 0.7f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.85f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильные океаны ===
|
||||||
|
case "minecraft:warm_ocean":
|
||||||
|
emission = 0.9f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.7f;
|
||||||
|
timeSensitivity = 0.7f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:lukewarm_ocean":
|
||||||
|
case "minecraft:deep_lukewarm_ocean":
|
||||||
|
emission = 0.85f;
|
||||||
|
targetDensity = 0.032f;
|
||||||
|
humidity = 0.65f;
|
||||||
|
timeSensitivity = 0.75f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:ocean":
|
||||||
|
case "minecraft:deep_ocean":
|
||||||
|
emission = 0.8f;
|
||||||
|
targetDensity = 0.03f;
|
||||||
|
humidity = 0.65f;
|
||||||
|
timeSensitivity = 0.8f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:cold_ocean":
|
||||||
|
case "minecraft:deep_cold_ocean":
|
||||||
|
emission = 0.75f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.85f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:frozen_ocean":
|
||||||
|
case "minecraft:deep_frozen_ocean":
|
||||||
|
emission = 0.7f;
|
||||||
|
targetDensity = 0.038f;
|
||||||
|
humidity = 0.55f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:ice_spikes":
|
||||||
|
emission = 0.7f;
|
||||||
|
targetDensity = 0.04f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.85f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильный Незер ===
|
||||||
|
case "minecraft:nether_wastes":
|
||||||
|
case "minecraft:soul_sand_valley":
|
||||||
|
case "minecraft:basalt_deltas":
|
||||||
|
case "minecraft:crimson_forest":
|
||||||
|
case "minecraft:warped_forest":
|
||||||
|
emission = 0.05f;
|
||||||
|
targetDensity = 0.0f;
|
||||||
|
humidity = 0.0f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Ванильный Край ===
|
||||||
|
case "minecraft:the_end":
|
||||||
|
case "minecraft:small_end_islands":
|
||||||
|
case "minecraft:end_midlands":
|
||||||
|
case "minecraft:end_highlands":
|
||||||
|
case "minecraft:end_barrens":
|
||||||
|
emission = 0.05f;
|
||||||
|
targetDensity = 0.0f;
|
||||||
|
humidity = 0.0f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "minecraft:the_void":
|
||||||
|
emission = 0.0f;
|
||||||
|
targetDensity = 0.0f;
|
||||||
|
humidity = 0.0f;
|
||||||
|
timeSensitivity = 1.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Примеры модовых биомов (можно добавлять свои) ===
|
||||||
|
// Biomes O' Plenty
|
||||||
|
case "biomesoplenty:cherry_blossom_grove":
|
||||||
|
emission = 1.1f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.65f;
|
||||||
|
timeSensitivity = 0.85f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "biomesoplenty:lavender_field":
|
||||||
|
emission = 1.0f;
|
||||||
|
targetDensity = 0.03f;
|
||||||
|
humidity = 0.55f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "biomesoplenty:wetland":
|
||||||
|
emission = 1.3f;
|
||||||
|
targetDensity = 0.06f;
|
||||||
|
humidity = 0.8f;
|
||||||
|
timeSensitivity = 0.6f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Terrestria
|
||||||
|
case "terrestria:redwood_forest":
|
||||||
|
emission = 1.0f;
|
||||||
|
targetDensity = 0.04f;
|
||||||
|
humidity = 0.7f;
|
||||||
|
timeSensitivity = 0.8f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Traverse
|
||||||
|
case "traverse:autumnal_wooded_hills":
|
||||||
|
emission = 0.9f;
|
||||||
|
targetDensity = 0.035f;
|
||||||
|
humidity = 0.6f;
|
||||||
|
timeSensitivity = 0.9f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// === Fallback для неизвестных биомов ===
|
||||||
|
default:
|
||||||
|
float temperature = biome.getTemperature();
|
||||||
|
float downfall = biome.weather.downfall();
|
||||||
|
|
||||||
|
humidity = downfall;
|
||||||
|
emission = 0.7f + downfall * 0.6f;
|
||||||
|
targetDensity = downfall * 0.06f;
|
||||||
|
timeSensitivity = 1.0f - downfall * 0.5f;
|
||||||
|
|
||||||
|
if (temperature < 0.3f) {
|
||||||
|
emission *= 1.2f;
|
||||||
|
targetDensity *= 1.3f;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FogBiomeParams(emission, targetDensity, humidity, timeSensitivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pack(float[] array, int offset) {
|
||||||
|
array[offset ] = emissionMultiplier;
|
||||||
|
array[offset + 1] = targetDensityBase;
|
||||||
|
array[offset + 2] = humidity;
|
||||||
|
array[offset + 3] = timeSensitivity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
package su.divan2000.veila.client.render.fog;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.registry.entry.RegistryEntry;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.Heightmap;
|
||||||
|
import net.minecraft.world.biome.Biome;
|
||||||
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
|
|
||||||
|
public final class FogBiomeStreamer {
|
||||||
|
|
||||||
|
private static final int TOTAL_CHUNKS =
|
||||||
|
FogWorldVolume.CHUNKS_X * FogWorldVolume.CHUNKS_Z;
|
||||||
|
private static final int TOTAL_WORDS = (TOTAL_CHUNKS + 63) / 64;
|
||||||
|
|
||||||
|
private static final int CHUNK_DATA_FLOATS =
|
||||||
|
FogWorldVolume.CHUNK_SIZE * FogWorldVolume.CHUNK_SIZE * 4;
|
||||||
|
|
||||||
|
private static final int MAX_CHUNKS_PER_TICK = 8;
|
||||||
|
private static volatile int burstFrames = 0;
|
||||||
|
|
||||||
|
private static final AtomicLongArray pendingMask = new AtomicLongArray(TOTAL_WORDS);
|
||||||
|
private static final AtomicLongArray readyMask = new AtomicLongArray(TOTAL_WORDS);
|
||||||
|
private static final Object sharedStateLock = new Object();
|
||||||
|
|
||||||
|
private static final float[][] preparedData = new float[TOTAL_CHUNKS][CHUNK_DATA_FLOATS];
|
||||||
|
private static final int[] preparedWorldChunkX = new int[TOTAL_CHUNKS];
|
||||||
|
private static final int[] preparedWorldChunkZ = new int[TOTAL_CHUNKS];
|
||||||
|
|
||||||
|
private static volatile int currentOriginChunkX;
|
||||||
|
private static volatile int currentOriginChunkZ;
|
||||||
|
private static volatile int currentRingChunkOffsetX;
|
||||||
|
private static volatile int currentRingChunkOffsetZ;
|
||||||
|
|
||||||
|
private static boolean firstUpdate = true;
|
||||||
|
|
||||||
|
private FogBiomeStreamer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int floorMod(int x, int y) {
|
||||||
|
int r = x % y;
|
||||||
|
return r < 0 ? r + y : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setBit(AtomicLongArray mask, int index) {
|
||||||
|
int wordIndex = index >> 6;
|
||||||
|
long bitMask = 1L << (index & 63);
|
||||||
|
while (true) {
|
||||||
|
long current = mask.get(wordIndex);
|
||||||
|
if ((current & bitMask) != 0) return;
|
||||||
|
if (mask.compareAndSet(wordIndex, current, current | bitMask)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void clearBit(AtomicLongArray mask, int index) {
|
||||||
|
int wordIndex = index >> 6;
|
||||||
|
long bitMask = 1L << (index & 63);
|
||||||
|
while (true) {
|
||||||
|
long current = mask.get(wordIndex);
|
||||||
|
if ((current & bitMask) == 0) return;
|
||||||
|
if (mask.compareAndSet(wordIndex, current, current & ~bitMask)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int nextSetBit(AtomicLongArray mask, int startIndex) {
|
||||||
|
for (int i = startIndex; i < TOTAL_CHUNKS; i++) {
|
||||||
|
int wordIndex = i >> 6;
|
||||||
|
long bitMask = 1L << (i & 63);
|
||||||
|
if ((mask.get(wordIndex) & bitMask) != 0) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update(
|
||||||
|
int oldOriginChunkX, int oldOriginChunkZ,
|
||||||
|
int newOriginChunkX, int newOriginChunkZ,
|
||||||
|
int ringChunkOffsetX, int ringChunkOffsetZ
|
||||||
|
) {
|
||||||
|
synchronized (sharedStateLock) {
|
||||||
|
currentOriginChunkX = newOriginChunkX;
|
||||||
|
currentOriginChunkZ = newOriginChunkZ;
|
||||||
|
currentRingChunkOffsetX = ringChunkOffsetX;
|
||||||
|
currentRingChunkOffsetZ = ringChunkOffsetZ;
|
||||||
|
|
||||||
|
if (firstUpdate) {
|
||||||
|
markAllPending();
|
||||||
|
firstUpdate = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dx = newOriginChunkX - oldOriginChunkX;
|
||||||
|
int dz = newOriginChunkZ - oldOriginChunkZ;
|
||||||
|
if (dx == 0 && dz == 0) return;
|
||||||
|
|
||||||
|
if (Math.abs(dx) >= FogWorldVolume.CHUNKS_X ||
|
||||||
|
Math.abs(dz) >= FogWorldVolume.CHUNKS_Z) {
|
||||||
|
markAllPending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dx > 0) markPendingRange(newOriginChunkX + FogWorldVolume.CHUNKS_X - dx,
|
||||||
|
newOriginChunkZ, dx, FogWorldVolume.CHUNKS_Z);
|
||||||
|
if (dx < 0) markPendingRange(newOriginChunkX,
|
||||||
|
newOriginChunkZ, -dx, FogWorldVolume.CHUNKS_Z);
|
||||||
|
if (dz > 0) markPendingRange(newOriginChunkX,
|
||||||
|
newOriginChunkZ + FogWorldVolume.CHUNKS_Z - dz,
|
||||||
|
FogWorldVolume.CHUNKS_X, dz);
|
||||||
|
if (dz < 0) markPendingRange(newOriginChunkX,
|
||||||
|
newOriginChunkZ, FogWorldVolume.CHUNKS_X, -dz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void markPendingRange(int worldX, int worldZ, int countX, int countZ) {
|
||||||
|
for (int i = 0; i < countX; i++) {
|
||||||
|
int wx = worldX + i;
|
||||||
|
for (int j = 0; j < countZ; j++) {
|
||||||
|
int wz = worldZ + j;
|
||||||
|
int physicalX = floorMod(wx, FogWorldVolume.CHUNKS_X);
|
||||||
|
int physicalZ = floorMod(wz, FogWorldVolume.CHUNKS_Z);
|
||||||
|
int physicalIndex = physicalZ * FogWorldVolume.CHUNKS_X + physicalX;
|
||||||
|
clearBit(readyMask, physicalIndex);
|
||||||
|
setBit(pendingMask, physicalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void markAllPending() {
|
||||||
|
for (int i = 0; i < TOTAL_WORDS; i++) {
|
||||||
|
pendingMask.set(i, -1L);
|
||||||
|
readyMask.set(i, 0L);
|
||||||
|
}
|
||||||
|
int extraBits = TOTAL_CHUNKS & 63;
|
||||||
|
if (extraBits != 0) {
|
||||||
|
pendingMask.set(TOTAL_WORDS - 1, (1L << extraBits) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void processPending() {
|
||||||
|
synchronized (sharedStateLock) {
|
||||||
|
int originX = currentOriginChunkX;
|
||||||
|
int originZ = currentOriginChunkZ;
|
||||||
|
int ringOffsetX = currentRingChunkOffsetX;
|
||||||
|
int ringOffsetZ = currentRingChunkOffsetZ;
|
||||||
|
|
||||||
|
int processedCount = 0;
|
||||||
|
int index = 0;
|
||||||
|
int limit = MAX_CHUNKS_PER_TICK + (burstFrames > 0 ? MAX_CHUNKS_PER_TICK : 0);
|
||||||
|
|
||||||
|
while (processedCount < limit) {
|
||||||
|
index = nextSetBit(pendingMask, index);
|
||||||
|
if (index < 0) break;
|
||||||
|
|
||||||
|
int physicalX = index % FogWorldVolume.CHUNKS_X;
|
||||||
|
int physicalZ = index / FogWorldVolume.CHUNKS_X;
|
||||||
|
|
||||||
|
int worldChunkX = originX + floorMod(physicalX - ringOffsetX, FogWorldVolume.CHUNKS_X);
|
||||||
|
int worldChunkZ = originZ + floorMod(physicalZ - ringOffsetZ, FogWorldVolume.CHUNKS_Z);
|
||||||
|
|
||||||
|
if (fillBiomesIntoArray(worldChunkX, worldChunkZ, preparedData[index])) {
|
||||||
|
preparedWorldChunkX[index] = worldChunkX;
|
||||||
|
preparedWorldChunkZ[index] = worldChunkZ;
|
||||||
|
setBit(readyMask, index);
|
||||||
|
clearBit(pendingMask, index);
|
||||||
|
processedCount++;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (burstFrames > 0) burstFrames--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean fillBiomesIntoArray(int chunkX, int chunkZ, float[] array) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client.world == null) return false;
|
||||||
|
|
||||||
|
WorldChunk chunk = client.world.getChunkManager()
|
||||||
|
.getWorldChunk(chunkX, chunkZ, false);
|
||||||
|
if (chunk == null) return false;
|
||||||
|
|
||||||
|
int topY = client.world.getTopY()-1;
|
||||||
|
|
||||||
|
int baseWorldX = chunkX << 4;
|
||||||
|
int baseWorldZ = chunkZ << 4;
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (int z = 0; z < FogWorldVolume.CHUNK_SIZE; z++) {
|
||||||
|
int worldZ = baseWorldZ + z;
|
||||||
|
for (int x = 0; x < FogWorldVolume.CHUNK_SIZE; x++) {
|
||||||
|
int worldX = baseWorldX + x;
|
||||||
|
|
||||||
|
int surfaceY = chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE).get(x, z);
|
||||||
|
BlockPos samplePos = new BlockPos(worldX, topY, worldZ);
|
||||||
|
RegistryEntry<Biome> biomeEntry = client.world.getBiome(samplePos);
|
||||||
|
|
||||||
|
FogBiomeParams params = FogBiomeParams.forBiome(biomeEntry);
|
||||||
|
params.pack(array, idx);
|
||||||
|
idx += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uploadReadyData() {
|
||||||
|
synchronized (sharedStateLock) {
|
||||||
|
int originX = currentOriginChunkX;
|
||||||
|
int originZ = currentOriginChunkZ;
|
||||||
|
int ringOffsetX = currentRingChunkOffsetX;
|
||||||
|
int ringOffsetZ = currentRingChunkOffsetZ;
|
||||||
|
|
||||||
|
int processed = 0;
|
||||||
|
int index = 0;
|
||||||
|
int limit = MAX_CHUNKS_PER_TICK + (burstFrames > 0 ? MAX_CHUNKS_PER_TICK : 0);
|
||||||
|
|
||||||
|
while (processed < limit) {
|
||||||
|
index = nextSetBit(readyMask, index);
|
||||||
|
if (index < 0) break;
|
||||||
|
|
||||||
|
int physicalX = index % FogWorldVolume.CHUNKS_X;
|
||||||
|
int physicalZ = index / FogWorldVolume.CHUNKS_X;
|
||||||
|
|
||||||
|
int currentWorldX = originX + floorMod(physicalX - ringOffsetX, FogWorldVolume.CHUNKS_X);
|
||||||
|
int currentWorldZ = originZ + floorMod(physicalZ - ringOffsetZ, FogWorldVolume.CHUNKS_Z);
|
||||||
|
|
||||||
|
if (preparedWorldChunkX[index] == currentWorldX &&
|
||||||
|
preparedWorldChunkZ[index] == currentWorldZ) {
|
||||||
|
|
||||||
|
FogBiomeTexture.uploadChunk(physicalX, physicalZ, preparedData[index]);
|
||||||
|
clearBit(readyMask, index);
|
||||||
|
processed++;
|
||||||
|
} else {
|
||||||
|
clearBit(readyMask, index);
|
||||||
|
setBit(pendingMask, index);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (burstFrames > 0) burstFrames--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setBurst(int frames) {
|
||||||
|
burstFrames = Math.max(burstFrames, frames);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package su.divan2000.veila.client.render.fog;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
public final class FogBiomeTexture {
|
||||||
|
|
||||||
|
public static final int SIZE_X = FogWorldVolume.SIZE_X;
|
||||||
|
public static final int SIZE_Z = FogWorldVolume.SIZE_Z;
|
||||||
|
|
||||||
|
private static int texture = -1;
|
||||||
|
|
||||||
|
private static final int[] uploadPBOs = new int[3];
|
||||||
|
private static int uploadPBOIndex = 0;
|
||||||
|
|
||||||
|
private static final int CHUNK_DATA_FLOATS =
|
||||||
|
FogWorldVolume.CHUNK_SIZE * FogWorldVolume.CHUNK_SIZE * 4;
|
||||||
|
private static final int CHUNK_DATA_BYTES = CHUNK_DATA_FLOATS * 4;
|
||||||
|
|
||||||
|
// Буфер размером с ОДИН чанк
|
||||||
|
private static final FloatBuffer CHUNK_BUFFER =
|
||||||
|
BufferUtils.createFloatBuffer(CHUNK_DATA_FLOATS);
|
||||||
|
|
||||||
|
// Данные одного чанка
|
||||||
|
private static final float[] EMPTY_CHUNK = new float[CHUNK_DATA_FLOATS];
|
||||||
|
|
||||||
|
// Данные одного пикселя (для clearRegions)
|
||||||
|
private static final float[] EMPTY_PIXEL = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
|
||||||
|
private FogBiomeTexture() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
if (texture != -1) return;
|
||||||
|
|
||||||
|
initUploadPBO();
|
||||||
|
|
||||||
|
texture = GL11.glGenTextures();
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
GL30.glTexImage2D(
|
||||||
|
GL11.GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL30.GL_RGBA16F,
|
||||||
|
SIZE_X,
|
||||||
|
SIZE_Z,
|
||||||
|
0,
|
||||||
|
GL11.GL_RGBA,
|
||||||
|
GL11.GL_FLOAT,
|
||||||
|
(ByteBuffer) null
|
||||||
|
);
|
||||||
|
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_REPEAT);
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_REPEAT);
|
||||||
|
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
// Кладем данные ОДНОГО чанка в буфер один раз
|
||||||
|
CHUNK_BUFFER.clear();
|
||||||
|
CHUNK_BUFFER.put(EMPTY_CHUNK);
|
||||||
|
CHUNK_BUFFER.flip();
|
||||||
|
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
// Для каждого чанка загружаем те же данные из буфера, делая rewind
|
||||||
|
for (int z = 0; z < FogWorldVolume.CHUNKS_Z; z++) {
|
||||||
|
for (int x = 0; x < FogWorldVolume.CHUNKS_X; x++) {
|
||||||
|
GL11.glTexSubImage2D(
|
||||||
|
GL11.GL_TEXTURE_2D, 0,
|
||||||
|
x * FogWorldVolume.CHUNK_SIZE,
|
||||||
|
z * FogWorldVolume.CHUNK_SIZE,
|
||||||
|
FogWorldVolume.CHUNK_SIZE,
|
||||||
|
FogWorldVolume.CHUNK_SIZE,
|
||||||
|
GL11.GL_RGBA, GL11.GL_FLOAT,
|
||||||
|
CHUNK_BUFFER
|
||||||
|
);
|
||||||
|
CHUNK_BUFFER.rewind(); // ← важно: возвращаем позицию к началу
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uploadChunk(int textureChunkX, int textureChunkZ, float[] data) {
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
int pbo = uploadPBOs[uploadPBOIndex % uploadPBOs.length];
|
||||||
|
uploadPBOIndex++;
|
||||||
|
|
||||||
|
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||||
|
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, CHUNK_DATA_BYTES, GL15.GL_STREAM_DRAW);
|
||||||
|
|
||||||
|
ByteBuffer mapped = GL15.glMapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, GL15.GL_WRITE_ONLY);
|
||||||
|
if (mapped != null) {
|
||||||
|
FloatBuffer floatMapped = mapped.asFloatBuffer();
|
||||||
|
floatMapped.put(data);
|
||||||
|
GL15.glUnmapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER);
|
||||||
|
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
|
||||||
|
|
||||||
|
GL11.glTexSubImage2D(
|
||||||
|
GL11.GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
textureChunkX * FogWorldVolume.CHUNK_SIZE,
|
||||||
|
textureChunkZ * FogWorldVolume.CHUNK_SIZE,
|
||||||
|
FogWorldVolume.CHUNK_SIZE,
|
||||||
|
FogWorldVolume.CHUNK_SIZE,
|
||||||
|
GL11.GL_RGBA,
|
||||||
|
GL11.GL_FLOAT,
|
||||||
|
0L
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearRegions(
|
||||||
|
int[] physX, int[] physZ,
|
||||||
|
int[] widthChunks, int[] depthChunks,
|
||||||
|
int count
|
||||||
|
) {
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int w = widthChunks[i] * FogWorldVolume.CHUNK_SIZE;
|
||||||
|
int d = depthChunks[i] * FogWorldVolume.CHUNK_SIZE;
|
||||||
|
int totalPixels = w * d;
|
||||||
|
|
||||||
|
FloatBuffer clearBuf = BufferUtils.createFloatBuffer(totalPixels * 4);
|
||||||
|
// Кладем пустые пиксели (RGBA = 0,0,0,0) по одному
|
||||||
|
for (int j = 0; j < totalPixels; j++) {
|
||||||
|
clearBuf.put(EMPTY_PIXEL);
|
||||||
|
}
|
||||||
|
clearBuf.flip();
|
||||||
|
|
||||||
|
GL11.glTexSubImage2D(
|
||||||
|
GL11.GL_TEXTURE_2D, 0,
|
||||||
|
physX[i] * FogWorldVolume.CHUNK_SIZE,
|
||||||
|
physZ[i] * FogWorldVolume.CHUNK_SIZE,
|
||||||
|
w, d,
|
||||||
|
GL11.GL_RGBA, GL11.GL_FLOAT,
|
||||||
|
clearBuf
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initUploadPBO() {
|
||||||
|
for (int i = 0; i < uploadPBOs.length; i++) {
|
||||||
|
uploadPBOs[i] = GL15.glGenBuffers();
|
||||||
|
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, uploadPBOs[i]);
|
||||||
|
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, CHUNK_DATA_BYTES, GL15.GL_STREAM_DRAW);
|
||||||
|
}
|
||||||
|
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getTexture() {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package su.divan2000.veila.client.render.fog;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public final class FogEnvironmentContext {
|
||||||
|
|
||||||
|
private static float emissionMultiplier = 1.0f;
|
||||||
|
private static float targetDensityMultiplier = 0.0f;
|
||||||
|
private static float fogDayRandom = 0.5f; // НОВЫЙ uniform
|
||||||
|
private static float rainMultiplier = 0.0f;
|
||||||
|
|
||||||
|
private static float smoothEmission = 1.0f;
|
||||||
|
private static float smoothTargetDensity = 0.0f;
|
||||||
|
private static float smoothRain = 0.0f;
|
||||||
|
|
||||||
|
private static SimpleNoiseSampler dayNoise;
|
||||||
|
private static Identifier lastWorldId = null;
|
||||||
|
|
||||||
|
private FogEnvironmentContext() {}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
dayNoise = new SimpleNoiseSampler(12345L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update(float tickDelta) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
World world = client.world;
|
||||||
|
if (world == null) return;
|
||||||
|
|
||||||
|
Identifier worldId = world.getRegistryKey().getValue();
|
||||||
|
if (!worldId.equals(lastWorldId)) {
|
||||||
|
lastWorldId = worldId;
|
||||||
|
long seed = worldId.toString().hashCode();
|
||||||
|
dayNoise = new SimpleNoiseSampler(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
long timeOfDay = world.getTimeOfDay() % 24000L;
|
||||||
|
long totalDays = world.getTimeOfDay() / 24000L;
|
||||||
|
|
||||||
|
// === 1. Трапеция для EMISSION ===
|
||||||
|
double noiseValue = dayNoise.sample(totalDays * 0.05, 0);
|
||||||
|
float dayOffset = (float)(noiseValue * 1000);
|
||||||
|
|
||||||
|
float p1 = 16000 + dayOffset;
|
||||||
|
float p2 = 20000;
|
||||||
|
float p3 = 23000;
|
||||||
|
float p4 = 3000 + dayOffset;
|
||||||
|
|
||||||
|
float baseEmission = trapezoid(timeOfDay, p1, p2, p3, p4);
|
||||||
|
|
||||||
|
float rain = world.getRainGradient(tickDelta);
|
||||||
|
float thunder = world.getThunderGradient(tickDelta);
|
||||||
|
float weatherBoost = 1.0f + rain * 2.0f + thunder * 1.0f;
|
||||||
|
|
||||||
|
float rawEmission = (baseEmission + 0.1f) * weatherBoost;
|
||||||
|
|
||||||
|
// === 2. Трапеция для TARGET DENSITY ===
|
||||||
|
// РАСШИРЕННОЕ окно: 18000-6000 (полночь - полдень)
|
||||||
|
// Это 12 часов игрового времени = 10 минут реального
|
||||||
|
float t1 = 18000; // полночь
|
||||||
|
float t2 = 20000; // 02:00
|
||||||
|
float t3 = 4000; // 10:00
|
||||||
|
float t4 = 6000; // полдень
|
||||||
|
|
||||||
|
float baseTargetDensity = trapezoid(timeOfDay, t1, t2, t3, t4);
|
||||||
|
|
||||||
|
// В дождь дымка всегда
|
||||||
|
float rainFactor = rain + thunder * 0.5f;
|
||||||
|
float rawTargetDensity = Math.max(baseTargetDensity, rainFactor);
|
||||||
|
|
||||||
|
// === 3. Случайное значение для "туманного дня" ===
|
||||||
|
// Это ОТДЕЛЬНЫЙ uniform, НЕ умноженный на трапецию
|
||||||
|
double dayNoiseValue = dayNoise.sample(totalDays * 0.1, 100);
|
||||||
|
float rawFogDayRandom = (float)(0.5 + dayNoiseValue * 0.5); // 0..1
|
||||||
|
|
||||||
|
// === 4. Сглаживание ===
|
||||||
|
float lerpSpeed = 0.08f;
|
||||||
|
smoothEmission = lerp(smoothEmission, rawEmission, lerpSpeed);
|
||||||
|
smoothTargetDensity = lerp(smoothTargetDensity, rawTargetDensity, lerpSpeed * 2.0f);
|
||||||
|
smoothRain = lerp(smoothRain, rainFactor, lerpSpeed * 2.0f);
|
||||||
|
// FogDayRandom НЕ сглаживаем — он постоянен в течение дня
|
||||||
|
fogDayRandom = rawFogDayRandom;
|
||||||
|
|
||||||
|
emissionMultiplier = smoothEmission;
|
||||||
|
targetDensityMultiplier = smoothTargetDensity;
|
||||||
|
rainMultiplier = smoothRain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float trapezoid(float x, float p1, float p2, float p3, float p4) {
|
||||||
|
x = ((x % 24000) + 24000) % 24000;
|
||||||
|
|
||||||
|
if (p4 < p1) {
|
||||||
|
if (x < p4) x += 24000;
|
||||||
|
if (p2 < p1) p2 += 24000;
|
||||||
|
if (p3 < p1) p3 += 24000;
|
||||||
|
p4 += 24000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x < p1) return 0.0f;
|
||||||
|
if (x < p2) return (x - p1) / (p2 - p1);
|
||||||
|
if (x < p3) return 1.0f;
|
||||||
|
if (x < p4) return 1.0f - (x - p3) / (p4 - p3);
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float lerp(float a, float b, float t) {
|
||||||
|
return a + (b - a) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float getEmissionMultiplier() { return emissionMultiplier; }
|
||||||
|
public static float getTargetDensityMultiplier() { return targetDensityMultiplier; }
|
||||||
|
public static float getFogDayRandom() { return fogDayRandom; }
|
||||||
|
public static float getRainMultiplier() { return rainMultiplier; }
|
||||||
|
}
|
||||||
@@ -200,7 +200,7 @@ public final class FogSimulationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lastStatsPrintTime == 0 || timestamp - lastStatsPrintTime >= STAT_WINDOW_NS) {
|
if (lastStatsPrintTime == 0 || timestamp - lastStatsPrintTime >= STAT_WINDOW_NS) {
|
||||||
printStepStats();
|
//printStepStats();
|
||||||
lastStatsPrintTime = timestamp;
|
lastStatsPrintTime = timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ public final class FogSimulationShader {
|
|||||||
private static int horizontalDiffusionLocation;
|
private static int horizontalDiffusionLocation;
|
||||||
private static int voxelSizeLocation;
|
private static int voxelSizeLocation;
|
||||||
private static int currentZLocation;
|
private static int currentZLocation;
|
||||||
|
private static int emissionMultiplierLocation;
|
||||||
|
private static int targetDensityMultiplierLocation;
|
||||||
|
private static int biomeParamsSamplerLocation;
|
||||||
|
private static int fogDayRandomLocation;
|
||||||
|
private static int rainMultiplierLocation;
|
||||||
|
|
||||||
private FogSimulationShader() {
|
private FogSimulationShader() {
|
||||||
}
|
}
|
||||||
@@ -64,6 +69,11 @@ public final class FogSimulationShader {
|
|||||||
horizontalDiffusionLocation = program.uniform("HorizontalDiffusion");
|
horizontalDiffusionLocation = program.uniform("HorizontalDiffusion");
|
||||||
voxelSizeLocation = program.uniform("VoxelSize");
|
voxelSizeLocation = program.uniform("VoxelSize");
|
||||||
currentZLocation = program.uniform("CurrentZ");
|
currentZLocation = program.uniform("CurrentZ");
|
||||||
|
biomeParamsSamplerLocation = program.uniform("BiomeParamsSampler");
|
||||||
|
emissionMultiplierLocation = program.uniform("EmissionMultiplier");
|
||||||
|
targetDensityMultiplierLocation = program.uniform("TargetDensityMultiplier");
|
||||||
|
fogDayRandomLocation = program.uniform("FogDayRandom");
|
||||||
|
rainMultiplierLocation = program.uniform("RainMultiplier");
|
||||||
|
|
||||||
fbo = GL30.glGenFramebuffers();
|
fbo = GL30.glGenFramebuffers();
|
||||||
|
|
||||||
@@ -138,6 +148,10 @@ public final class FogSimulationShader {
|
|||||||
int previousTex1_2D = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
|
int previousTex1_2D = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
|
||||||
int previousTex1_3D = GL11.glGetInteger(GL_TEXTURE_BINDING_3D);
|
int previousTex1_3D = GL11.glGetInteger(GL_TEXTURE_BINDING_3D);
|
||||||
|
|
||||||
|
GL13.glActiveTexture(GL13.GL_TEXTURE2);
|
||||||
|
int previousTex2_2D = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
|
||||||
|
int previousTex2_3D = GL11.glGetInteger(GL_TEXTURE_BINDING_3D);
|
||||||
|
|
||||||
// Blend state (на всякий случай)
|
// Blend state (на всякий случай)
|
||||||
boolean previousBlend = GL11.glIsEnabled(GL11.GL_BLEND);
|
boolean previousBlend = GL11.glIsEnabled(GL11.GL_BLEND);
|
||||||
boolean previousDepthTest = GL11.glIsEnabled(GL11.GL_DEPTH_TEST);
|
boolean previousDepthTest = GL11.glIsEnabled(GL11.GL_DEPTH_TEST);
|
||||||
@@ -179,6 +193,10 @@ public final class FogSimulationShader {
|
|||||||
GL11.glBindTexture(GL12.GL_TEXTURE_3D, fogRead);
|
GL11.glBindTexture(GL12.GL_TEXTURE_3D, fogRead);
|
||||||
GL20.glUniform1i(fogReadSamplerLocation, 1);
|
GL20.glUniform1i(fogReadSamplerLocation, 1);
|
||||||
|
|
||||||
|
GL13.glActiveTexture(GL13.GL_TEXTURE2);
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, FogBiomeTexture.getTexture());
|
||||||
|
GL20.glUniform1i(biomeParamsSamplerLocation, 2);
|
||||||
|
|
||||||
// Устанавливаем параметры
|
// Устанавливаем параметры
|
||||||
GL20.glUniform1f(globalEmissionMultiplierLocation, globalEmissionMultiplier);
|
GL20.glUniform1f(globalEmissionMultiplierLocation, globalEmissionMultiplier);
|
||||||
GL20.glUniform1f(globalAbsorptionMultiplierLocation, globalAbsorptionMultiplier);
|
GL20.glUniform1f(globalAbsorptionMultiplierLocation, globalAbsorptionMultiplier);
|
||||||
@@ -187,6 +205,11 @@ public final class FogSimulationShader {
|
|||||||
GL20.glUniform1f(upDiffusionLocation, upDiffusion);
|
GL20.glUniform1f(upDiffusionLocation, upDiffusion);
|
||||||
GL20.glUniform1f(downDiffusionLocation, downDiffusion);
|
GL20.glUniform1f(downDiffusionLocation, downDiffusion);
|
||||||
GL20.glUniform1f(horizontalDiffusionLocation, horizontalDiffusion);
|
GL20.glUniform1f(horizontalDiffusionLocation, horizontalDiffusion);
|
||||||
|
GL20.glUniform1f(emissionMultiplierLocation, FogEnvironmentContext.getEmissionMultiplier());
|
||||||
|
GL20.glUniform1f(targetDensityMultiplierLocation, FogEnvironmentContext.getTargetDensityMultiplier());
|
||||||
|
GL20.glUniform1f(fogDayRandomLocation, FogEnvironmentContext.getFogDayRandom());
|
||||||
|
GL20.glUniform1f(rainMultiplierLocation, FogEnvironmentContext.getRainMultiplier());
|
||||||
|
|
||||||
GL20.glUniform3f(voxelSizeLocation,
|
GL20.glUniform3f(voxelSizeLocation,
|
||||||
1.0f / FogWorldVolume.SIZE_X,
|
1.0f / FogWorldVolume.SIZE_X,
|
||||||
1.0f / FogWorldVolume.SIZE_Y,
|
1.0f / FogWorldVolume.SIZE_Y,
|
||||||
@@ -241,6 +264,10 @@ public final class FogSimulationShader {
|
|||||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, previousTex1_2D);
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, previousTex1_2D);
|
||||||
GL11.glBindTexture(GL12.GL_TEXTURE_3D, previousTex1_3D);
|
GL11.glBindTexture(GL12.GL_TEXTURE_3D, previousTex1_3D);
|
||||||
|
|
||||||
|
GL13.glActiveTexture(GL13.GL_TEXTURE2);
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, previousTex2_2D);
|
||||||
|
GL11.glBindTexture(GL12.GL_TEXTURE_3D, previousTex2_3D);
|
||||||
|
|
||||||
// Восстанавливаем активный текстурный юнит
|
// Восстанавливаем активный текстурный юнит
|
||||||
GL13.glActiveTexture(previousActiveTexture);
|
GL13.glActiveTexture(previousActiveTexture);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public final class FogSystem {
|
|||||||
private FogSystem() {
|
private FogSystem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void beginRender() {
|
public static void beginRender(float tickDelta) {
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
initialize();
|
initialize();
|
||||||
@@ -32,6 +32,11 @@ public final class FogSystem {
|
|||||||
// Освещение: загружаем готовые секции + инкрементальные обновления (render thread)
|
// Освещение: загружаем готовые секции + инкрементальные обновления (render thread)
|
||||||
FogLightStreamer.uploadReadySections();
|
FogLightStreamer.uploadReadySections();
|
||||||
|
|
||||||
|
// Биомы
|
||||||
|
FogBiomeStreamer.uploadReadyData();
|
||||||
|
|
||||||
|
FogEnvironmentContext.update(tickDelta);
|
||||||
|
|
||||||
FogSimulationManager.update();
|
FogSimulationManager.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +46,9 @@ public final class FogSystem {
|
|||||||
|
|
||||||
// Освещение: читаем данные секций (client thread, без OpenGL)
|
// Освещение: читаем данные секций (client thread, без OpenGL)
|
||||||
FogLightStreamer.prepareAllSections();
|
FogLightStreamer.prepareAllSections();
|
||||||
|
|
||||||
|
// Читаем биомы в client thread
|
||||||
|
FogBiomeStreamer.processPending();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize() {
|
||||||
@@ -48,6 +56,8 @@ public final class FogSystem {
|
|||||||
FogLightVolume.init();
|
FogLightVolume.init();
|
||||||
FogLightStreamer.init();
|
FogLightStreamer.init();
|
||||||
FogSimulationManager.init();
|
FogSimulationManager.init();
|
||||||
|
FogBiomeTexture.init();
|
||||||
|
FogEnvironmentContext.init();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,9 +113,18 @@ public final class FogSystem {
|
|||||||
ringChunkOffsetX,
|
ringChunkOffsetX,
|
||||||
ringChunkOffsetZ
|
ringChunkOffsetZ
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Вызов FogBiomeStreamer.update после FogLightStreamer.update
|
||||||
|
FogBiomeStreamer.update(
|
||||||
|
oldOriginChunkX, oldOriginChunkZ,
|
||||||
|
originChunkX, originChunkZ,
|
||||||
|
ringChunkOffsetX, ringChunkOffsetZ
|
||||||
|
);
|
||||||
|
|
||||||
// Boost uploads for a few frames to catch up after a window shift
|
// Boost uploads for a few frames to catch up after a window shift
|
||||||
FogChunkStreamer.setBurst(8);
|
FogChunkStreamer.setBurst(8);
|
||||||
FogLightStreamer.setBurst(8);
|
FogLightStreamer.setBurst(8);
|
||||||
|
FogBiomeStreamer.setBurst(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int originChunkX() {
|
public static int originChunkX() {
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package su.divan2000.veila.client.render.fog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Простой Perlin noise без зависимостей от Minecraft API.
|
||||||
|
*/
|
||||||
|
public final class SimpleNoiseSampler {
|
||||||
|
|
||||||
|
private final int[] perm = new int[512];
|
||||||
|
|
||||||
|
public SimpleNoiseSampler(long seed) {
|
||||||
|
java.util.Random random = new java.util.Random(seed);
|
||||||
|
int[] p = new int[256];
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
p[i] = i;
|
||||||
|
}
|
||||||
|
// Fisher-Yates shuffle
|
||||||
|
for (int i = 255; i > 0; i--) {
|
||||||
|
int j = random.nextInt(i + 1);
|
||||||
|
int temp = p[i];
|
||||||
|
p[i] = p[j];
|
||||||
|
p[j] = temp;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 512; i++) {
|
||||||
|
perm[i] = p[i & 255];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double sample(double x, double y) {
|
||||||
|
int X = (int)Math.floor(x) & 255;
|
||||||
|
int Y = (int)Math.floor(y) & 255;
|
||||||
|
|
||||||
|
x -= Math.floor(x);
|
||||||
|
y -= Math.floor(y);
|
||||||
|
|
||||||
|
double u = fade(x);
|
||||||
|
double v = fade(y);
|
||||||
|
|
||||||
|
int A = perm[X] + Y;
|
||||||
|
int AA = perm[A];
|
||||||
|
int AB = perm[A + 1];
|
||||||
|
int B = perm[X + 1] + Y;
|
||||||
|
int BA = perm[B];
|
||||||
|
int BB = perm[B + 1];
|
||||||
|
|
||||||
|
return lerp(v,
|
||||||
|
lerp(u, grad(perm[AA], x, y), grad(perm[BA], x - 1, y)),
|
||||||
|
lerp(u, grad(perm[AB], x, y - 1), grad(perm[BB], x - 1, y - 1))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double fade(double t) {
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double lerp(double t, double a, double b) {
|
||||||
|
return a + t * (b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double grad(int hash, double x, double y) {
|
||||||
|
int h = hash & 7;
|
||||||
|
double u = h < 4 ? x : y;
|
||||||
|
double v = h < 4 ? y : x;
|
||||||
|
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? 2.0 * v : -2.0 * v);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ public class GameRendererMixin {
|
|||||||
) {
|
) {
|
||||||
net.minecraft.client.MinecraftClient client = net.minecraft.client.MinecraftClient.getInstance();
|
net.minecraft.client.MinecraftClient client = net.minecraft.client.MinecraftClient.getInstance();
|
||||||
if (client.world != null && client.player != null) {
|
if (client.world != null && client.player != null) {
|
||||||
FogSystem.beginRender();
|
FogSystem.beginRender(tickDelta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,8 +43,6 @@ const float SCATTERING_COEFF = 1.0;
|
|||||||
const float AMBIENT_INTENSITY = 0.10;
|
const float AMBIENT_INTENSITY = 0.10;
|
||||||
const float MIN_TRANSMITTANCE = 0.01;
|
const float MIN_TRANSMITTANCE = 0.01;
|
||||||
|
|
||||||
const float AMBIENT_FOG = 0.0025;
|
|
||||||
|
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
|
|
||||||
vec3 reconstructViewPosition(vec2 uv)
|
vec3 reconstructViewPosition(vec2 uv)
|
||||||
@@ -99,7 +97,7 @@ float density(vec3 worldPos)
|
|||||||
{
|
{
|
||||||
vec3 uv = worldToUV(worldPos);
|
vec3 uv = worldToUV(worldPos);
|
||||||
if (uv.x < 0.0) return 0.0;
|
if (uv.x < 0.0) return 0.0;
|
||||||
return max(texture(FogVolume, uv).r, AMBIENT_FOG*texture(SkyLightVolume, uv).r);
|
return texture(FogVolume, uv).r;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 light(vec3 worldPos)
|
vec2 light(vec3 worldPos)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
uniform usampler3D WorldInfoSampler;
|
uniform usampler3D WorldInfoSampler;
|
||||||
uniform sampler3D FogReadSampler;
|
uniform sampler3D FogReadSampler;
|
||||||
|
uniform sampler2D BiomeParamsSampler;
|
||||||
|
|
||||||
uniform float GlobalEmissionMultiplier;
|
uniform float GlobalEmissionMultiplier;
|
||||||
uniform float GlobalAbsorptionMultiplier;
|
uniform float GlobalAbsorptionMultiplier;
|
||||||
@@ -14,16 +15,19 @@ uniform float HorizontalDiffusion;
|
|||||||
uniform vec3 VoxelSize;
|
uniform vec3 VoxelSize;
|
||||||
uniform float CurrentZ;
|
uniform float CurrentZ;
|
||||||
|
|
||||||
|
uniform float EmissionMultiplier;
|
||||||
|
uniform float TargetDensityMultiplier;
|
||||||
|
uniform float FogDayRandom; // НОВЫЙ: случайное значение [0, 1]
|
||||||
|
uniform float RainMultiplier;
|
||||||
|
|
||||||
in vec2 texCoord;
|
in vec2 texCoord;
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
// Получение эмиссии из magnitude
|
|
||||||
float getEmission(uint magnitude, bool isAbsorber) {
|
float getEmission(uint magnitude, bool isAbsorber) {
|
||||||
if (isAbsorber || magnitude == 0u) return 0.0;
|
if (isAbsorber || magnitude == 0u) return 0.0;
|
||||||
return float(magnitude) / 63.0 * 0.05;
|
return float(magnitude) / 63.0 * 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение абсорбции из magnitude
|
|
||||||
float getAbsorption(uint magnitude, bool isAbsorber) {
|
float getAbsorption(uint magnitude, bool isAbsorber) {
|
||||||
if (!isAbsorber || magnitude == 0u) return 0.0;
|
if (!isAbsorber || magnitude == 0u) return 0.0;
|
||||||
return float(magnitude) / 63.0 * 0.05;
|
return float(magnitude) / 63.0 * 0.05;
|
||||||
@@ -32,160 +36,130 @@ float getAbsorption(uint magnitude, bool isAbsorber) {
|
|||||||
void main() {
|
void main() {
|
||||||
vec3 uv = vec3(texCoord, CurrentZ);
|
vec3 uv = vec3(texCoord, CurrentZ);
|
||||||
|
|
||||||
// Читаем упакованное значение
|
// Параметры биома
|
||||||
uint packedSelf = texture(WorldInfoSampler, uv).r;
|
vec4 biomeParams = texture(BiomeParamsSampler, uv.xz);
|
||||||
|
float biomeEmission = biomeParams.r;
|
||||||
|
float biomeTargetDensityBase = biomeParams.g;
|
||||||
|
float biomeHumidity = biomeParams.b;
|
||||||
|
float biomeTimeSensitivity = biomeParams.a;
|
||||||
|
|
||||||
// Распаковываем через арифметику (битовые операции не доступны в GLSL 1.50)
|
// === Emission ===
|
||||||
|
float finalEmission = biomeEmission * EmissionMultiplier * GlobalEmissionMultiplier;
|
||||||
|
|
||||||
|
// === Target Density ===
|
||||||
|
// 1. Определяем, туманный ли сегодня день
|
||||||
|
float fogThreshold = 1.0 - biomeHumidity;
|
||||||
|
float hasFogToday = step(fogThreshold, FogDayRandom);
|
||||||
|
|
||||||
|
// В дождь дымка всегда (если humidity > 0)
|
||||||
|
float hasRain = step(0.01, RainMultiplier);
|
||||||
|
hasFogToday = max(hasFogToday, hasRain * step(0.01, biomeHumidity));
|
||||||
|
|
||||||
|
// 2. Применяем зависимость от времени суток
|
||||||
|
// Для plains (timeSensitivity = 1.0): дымка только в окно трапеции
|
||||||
|
// Для джунглей (timeSensitivity = 0.3): дымка почти всегда
|
||||||
|
float timeFactor = mix(1.0, TargetDensityMultiplier, biomeTimeSensitivity);
|
||||||
|
|
||||||
|
// 3. Итоговая плотность
|
||||||
|
float finalTargetDensity = biomeTargetDensityBase * hasFogToday * timeFactor;
|
||||||
|
|
||||||
|
// Читаем воксель мира
|
||||||
|
uint packedSelf = texture(WorldInfoSampler, uv).r;
|
||||||
bool selfSolid = (packedSelf >= 128u);
|
bool selfSolid = (packedSelf >= 128u);
|
||||||
uint tempSelf = packedSelf;
|
uint tempSelf = packedSelf;
|
||||||
if (selfSolid) tempSelf -= 128u;
|
if (selfSolid) tempSelf -= 128u;
|
||||||
|
|
||||||
bool selfIsAbsorber = (tempSelf >= 64u);
|
bool selfIsAbsorber = (tempSelf >= 64u);
|
||||||
if (selfIsAbsorber) tempSelf -= 64u;
|
if (selfIsAbsorber) tempSelf -= 64u;
|
||||||
|
|
||||||
uint selfMagnitude = tempSelf;
|
uint selfMagnitude = tempSelf;
|
||||||
|
|
||||||
// Если solid, плотность всегда 0
|
|
||||||
if (selfSolid) {
|
if (selfSolid) {
|
||||||
FragColor = vec4(0.0);
|
FragColor = vec4(0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Читаем текущую плотность
|
|
||||||
float fogOld = texture(FogReadSampler, uv).r;
|
float fogOld = texture(FogReadSampler, uv).r;
|
||||||
float fogNew = fogOld;
|
float fogNew = fogOld;
|
||||||
|
|
||||||
// === 1. Emission ===
|
// Emission
|
||||||
float emission = 0.0;
|
float emission = getEmission(selfMagnitude, selfIsAbsorber) * finalEmission;
|
||||||
|
|
||||||
// Эмиссия от текущего вокселя (если non-solid источник)
|
|
||||||
emission += getEmission(selfMagnitude, selfIsAbsorber) * GlobalEmissionMultiplier;
|
|
||||||
|
|
||||||
// Эмиссия от соседних solid источников
|
|
||||||
// Инициализируем массив через индексы (так надежнее в GLSL 1.50)
|
|
||||||
vec3 offsets[6];
|
vec3 offsets[6];
|
||||||
offsets[0] = vec3(VoxelSize.x, 0.0, 0.0); // +X
|
offsets[0] = vec3(VoxelSize.x, 0.0, 0.0);
|
||||||
offsets[1] = vec3(-VoxelSize.x, 0.0, 0.0); // -X
|
offsets[1] = vec3(-VoxelSize.x, 0.0, 0.0);
|
||||||
offsets[2] = vec3(0.0, VoxelSize.y, 0.0); // +Y (Up)
|
offsets[2] = vec3(0.0, VoxelSize.y, 0.0);
|
||||||
offsets[3] = vec3(0.0, -VoxelSize.y, 0.0); // -Y (Down)
|
offsets[3] = vec3(0.0, -VoxelSize.y, 0.0);
|
||||||
offsets[4] = vec3(0.0, 0.0, VoxelSize.z); // +Z
|
offsets[4] = vec3(0.0, 0.0, VoxelSize.z);
|
||||||
offsets[5] = vec3(0.0, 0.0, -VoxelSize.z); // -Z
|
offsets[5] = vec3(0.0, 0.0, -VoxelSize.z);
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
vec3 neighborUV = uv + offsets[i];
|
vec3 neighborUV = uv + offsets[i];
|
||||||
uint packedNeighbor = texture(WorldInfoSampler, neighborUV).r;
|
uint packedNeighbor = texture(WorldInfoSampler, neighborUV).r;
|
||||||
|
|
||||||
// Распаковываем соседа
|
|
||||||
bool neighborSolid = (packedNeighbor >= 128u);
|
bool neighborSolid = (packedNeighbor >= 128u);
|
||||||
uint tempNeighbor = packedNeighbor;
|
uint tempN = packedNeighbor;
|
||||||
if (neighborSolid) tempNeighbor -= 128u;
|
if (neighborSolid) tempN -= 128u;
|
||||||
|
bool neighborIsAbsorber = (tempN >= 64u);
|
||||||
bool neighborIsAbsorber = (tempNeighbor >= 64u);
|
if (neighborIsAbsorber) tempN -= 64u;
|
||||||
if (neighborIsAbsorber) tempNeighbor -= 64u;
|
uint neighborMagnitude = tempN;
|
||||||
|
|
||||||
uint neighborMagnitude = tempNeighbor;
|
|
||||||
|
|
||||||
// Solid источник влияет на non-solid соседей
|
|
||||||
if (neighborSolid) {
|
if (neighborSolid) {
|
||||||
emission += getEmission(neighborMagnitude, neighborIsAbsorber) * GlobalEmissionMultiplier;
|
emission += getEmission(neighborMagnitude, neighborIsAbsorber) * finalEmission;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fogNew += emission;
|
fogNew += emission;
|
||||||
|
|
||||||
// === 2. Absorption ===
|
// Absorption
|
||||||
float absorption = 0.0;
|
float absorption = fogOld * getAbsorption(selfMagnitude, selfIsAbsorber) * GlobalAbsorptionMultiplier;
|
||||||
|
|
||||||
// Поглощение от текущего вокселя (если non-solid поглотитель)
|
|
||||||
absorption += fogOld * getAbsorption(selfMagnitude, selfIsAbsorber) * GlobalAbsorptionMultiplier;
|
|
||||||
|
|
||||||
// Поглощение от соседних solid поглотителей
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
vec3 neighborUV = uv + offsets[i];
|
vec3 neighborUV = uv + offsets[i];
|
||||||
uint packedNeighbor = texture(WorldInfoSampler, neighborUV).r;
|
uint packedNeighbor = texture(WorldInfoSampler, neighborUV).r;
|
||||||
|
|
||||||
// Распаковываем соседа
|
|
||||||
bool neighborSolid = (packedNeighbor >= 128u);
|
bool neighborSolid = (packedNeighbor >= 128u);
|
||||||
uint tempNeighbor = packedNeighbor;
|
uint tempN = packedNeighbor;
|
||||||
if (neighborSolid) tempNeighbor -= 128u;
|
if (neighborSolid) tempN -= 128u;
|
||||||
|
bool neighborIsAbsorber = (tempN >= 64u);
|
||||||
bool neighborIsAbsorber = (tempNeighbor >= 64u);
|
if (neighborIsAbsorber) tempN -= 64u;
|
||||||
if (neighborIsAbsorber) tempNeighbor -= 64u;
|
uint neighborMagnitude = tempN;
|
||||||
|
|
||||||
uint neighborMagnitude = tempNeighbor;
|
|
||||||
|
|
||||||
// Solid поглотитель влияет на non-solid соседей
|
|
||||||
if (neighborSolid) {
|
if (neighborSolid) {
|
||||||
absorption += fogOld * getAbsorption(neighborMagnitude, neighborIsAbsorber) * GlobalAbsorptionMultiplier;
|
absorption += fogOld * getAbsorption(neighborMagnitude, neighborIsAbsorber) * GlobalAbsorptionMultiplier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fogNew -= absorption;
|
fogNew -= absorption;
|
||||||
|
|
||||||
// === 3. Diffusion ===
|
// Diffusion
|
||||||
float diffusion = 0.0;
|
float diffusion = 0.0;
|
||||||
|
|
||||||
// +X (right)
|
|
||||||
vec3 uvRight = uv + offsets[0];
|
vec3 uvRight = uv + offsets[0];
|
||||||
uint packedRight = texture(WorldInfoSampler, uvRight).r;
|
uint packedRight = texture(WorldInfoSampler, uvRight).r;
|
||||||
bool rightSolid = (packedRight >= 128u);
|
if (!(packedRight >= 128u)) {
|
||||||
if (!rightSolid) {
|
diffusion += (texture(FogReadSampler, uvRight).r - fogOld) * HorizontalDiffusion * DiffusionRate;
|
||||||
float fogRight = texture(FogReadSampler, uvRight).r;
|
|
||||||
diffusion += (fogRight - fogOld) * HorizontalDiffusion * DiffusionRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -X (left)
|
|
||||||
vec3 uvLeft = uv + offsets[1];
|
vec3 uvLeft = uv + offsets[1];
|
||||||
uint packedLeft = texture(WorldInfoSampler, uvLeft).r;
|
uint packedLeft = texture(WorldInfoSampler, uvLeft).r;
|
||||||
bool leftSolid = (packedLeft >= 128u);
|
if (!(packedLeft >= 128u)) {
|
||||||
if (!leftSolid) {
|
diffusion += (texture(FogReadSampler, uvLeft).r - fogOld) * HorizontalDiffusion * DiffusionRate;
|
||||||
float fogLeft = texture(FogReadSampler, uvLeft).r;
|
|
||||||
diffusion += (fogLeft - fogOld) * HorizontalDiffusion * DiffusionRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// +Y (up)
|
|
||||||
vec3 uvUp = uv + offsets[2];
|
vec3 uvUp = uv + offsets[2];
|
||||||
uint packedUp = texture(WorldInfoSampler, uvUp).r;
|
uint packedUp = texture(WorldInfoSampler, uvUp).r;
|
||||||
bool upSolid = (packedUp >= 128u);
|
if (!(packedUp >= 128u)) {
|
||||||
if (!upSolid) {
|
diffusion += (texture(FogReadSampler, uvUp).r - fogOld) * UpDiffusion * DiffusionRate;
|
||||||
float fogUp = texture(FogReadSampler, uvUp).r;
|
|
||||||
diffusion += (fogUp - fogOld) * UpDiffusion * DiffusionRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -Y (down)
|
|
||||||
vec3 uvDown = uv + offsets[3];
|
vec3 uvDown = uv + offsets[3];
|
||||||
uint packedDown = texture(WorldInfoSampler, uvDown).r;
|
uint packedDown = texture(WorldInfoSampler, uvDown).r;
|
||||||
bool downSolid = (packedDown >= 128u);
|
if (!(packedDown >= 128u)) {
|
||||||
if (!downSolid) {
|
diffusion += (texture(FogReadSampler, uvDown).r - fogOld) * DownDiffusion * DiffusionRate;
|
||||||
float fogDown = texture(FogReadSampler, uvDown).r;
|
|
||||||
diffusion += (fogDown - fogOld) * DownDiffusion * DiffusionRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// +Z
|
|
||||||
vec3 uvFront = uv + offsets[4];
|
vec3 uvFront = uv + offsets[4];
|
||||||
uint packedFront = texture(WorldInfoSampler, uvFront).r;
|
uint packedFront = texture(WorldInfoSampler, uvFront).r;
|
||||||
bool frontSolid = (packedFront >= 128u);
|
if (!(packedFront >= 128u)) {
|
||||||
if (!frontSolid) {
|
diffusion += (texture(FogReadSampler, uvFront).r - fogOld) * HorizontalDiffusion * DiffusionRate;
|
||||||
float fogFront = texture(FogReadSampler, uvFront).r;
|
|
||||||
diffusion += (fogFront - fogOld) * HorizontalDiffusion * DiffusionRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -Z
|
|
||||||
vec3 uvBack = uv + offsets[5];
|
vec3 uvBack = uv + offsets[5];
|
||||||
uint packedBack = texture(WorldInfoSampler, uvBack).r;
|
uint packedBack = texture(WorldInfoSampler, uvBack).r;
|
||||||
bool backSolid = (packedBack >= 128u);
|
if (!(packedBack >= 128u)) {
|
||||||
if (!backSolid) {
|
diffusion += (texture(FogReadSampler, uvBack).r - fogOld) * HorizontalDiffusion * DiffusionRate;
|
||||||
float fogBack = texture(FogReadSampler, uvBack).r;
|
|
||||||
diffusion += (fogBack - fogOld) * HorizontalDiffusion * DiffusionRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fogNew += diffusion;
|
fogNew += diffusion;
|
||||||
|
|
||||||
// === 4. Global Decay ===
|
// Target Decay
|
||||||
fogNew -= fogOld * GlobalDecay;
|
fogNew -= (fogOld - finalTargetDensity) * GlobalDecay;
|
||||||
|
|
||||||
// === 5. Clamp ===
|
|
||||||
fogNew = clamp(fogNew, 0.0, 1.0);
|
fogNew = clamp(fogNew, 0.0, 1.0);
|
||||||
|
|
||||||
FragColor = vec4(fogNew, 0.0, 0.0, 1.0);
|
FragColor = vec4(fogNew, 0.0, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
@@ -2,3 +2,8 @@ accessWidener v2 named
|
|||||||
accessible method net/minecraft/world/chunk/PalettedContainer get (I)Ljava/lang/Object;
|
accessible method net/minecraft/world/chunk/PalettedContainer get (I)Ljava/lang/Object;
|
||||||
accessible field net/minecraft/world/chunk/light/LightingProvider blockLightProvider Lnet/minecraft/world/chunk/light/ChunkLightProvider;
|
accessible field net/minecraft/world/chunk/light/LightingProvider blockLightProvider Lnet/minecraft/world/chunk/light/ChunkLightProvider;
|
||||||
accessible field net/minecraft/world/chunk/light/LightingProvider skyLightProvider Lnet/minecraft/world/chunk/light/ChunkLightProvider;
|
accessible field net/minecraft/world/chunk/light/LightingProvider skyLightProvider Lnet/minecraft/world/chunk/light/ChunkLightProvider;
|
||||||
|
accessible field net/minecraft/world/biome/Biome weather Lnet/minecraft/world/biome/Biome$Weather;
|
||||||
|
accessible class net/minecraft/world/biome/Biome$Weather
|
||||||
|
accessible method net/minecraft/world/biome/Biome$Weather downfall ()F
|
||||||
|
accessible method net/minecraft/world/biome/Biome$Weather temperature ()F
|
||||||
|
accessible method net/minecraft/world/biome/Biome$Weather hasPrecipitation ()Z
|
||||||
|
|||||||
Reference in New Issue
Block a user