Теперь строится worldinfo для будующей симуляции

This commit is contained in:
2026-08-01 18:06:41 +04:00
parent eb497f257a
commit 41238fddb0
6 changed files with 195 additions and 147 deletions
@@ -0,0 +1,49 @@
package su.divan2000.veila.client.render.fog;
public record FogBlockProperties(
boolean solid,
int sign,
int magnitude
) {
public static final FogBlockProperties DEFAULT = new FogBlockProperties(false, 0, 0);
public FogBlockProperties {
if (sign < -1 || sign > 1) {
throw new IllegalArgumentException("sign must be -1, 0, or 1");
}
if (magnitude < 0 || magnitude > 63) {
throw new IllegalArgumentException("magnitude must be 0-63");
}
}
/**
* Упаковывает свойства в один байт:
* Бит 7: solid (1 = solid)
* Бит 6: sign (0 = source/neutral, 1 = absorber)
* Биты 0-5: magnitude (0-63)
*/
public byte pack() {
int packed = 0;
if (solid) {
packed |= 0b10000000;
}
if (sign < 0) {
packed |= 0b01000000;
}
packed |= (magnitude & 0b00111111);
return (byte) packed;
}
public static FogBlockProperties unpack(byte packed) {
boolean solid = (packed & 0b10000000) != 0;
boolean isAbsorber = (packed & 0b01000000) != 0;
int magnitude = packed & 0b00111111;
int sign = isAbsorber ? -1 : (magnitude > 0 ? 1 : 0);
return new FogBlockProperties(solid, sign, magnitude);
}
}
@@ -0,0 +1,96 @@
package su.divan2000.veila.client.render.fog;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.fluid.Fluids;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.BlockTags;
public final class FogBlockRegistry {
private static final byte[] BLOCK_CACHE;
private static final boolean[] BLOCK_CACHE_INITIALIZED;
// Предвычисленное значение для waterlogged блоков (вода)
private static final byte WATER_VALUE;
static {
int blockCount = Registries.BLOCK.size();
BLOCK_CACHE = new byte[blockCount];
BLOCK_CACHE_INITIALIZED = new boolean[blockCount];
// Waterlogged блок всегда содержит воду
WATER_VALUE = new FogBlockProperties(true, 1, 60).pack();
}
private FogBlockRegistry() {
}
public static byte getPackedProperties(BlockState state) {
// СНАЧАЛА проверяем конкретно воду (waterlogged или обычный блок воды)
if (state.getFluidState().getFluid() == Fluids.WATER) {
return WATER_VALUE;
}
Block block = state.getBlock();
int id = Registries.BLOCK.getRawId(block);
if (BLOCK_CACHE_INITIALIZED[id]) {
return BLOCK_CACHE[id];
}
byte packed = computePackedProperties(state);
BLOCK_CACHE[id] = packed;
BLOCK_CACHE_INITIALIZED[id] = true;
return packed;
}
private static byte computePackedProperties(BlockState state) {
Block block = state.getBlock();
boolean solid = computeSolid(state);
// Источники тумана (sign = +1)
// WATER уже обработан в getPackedProperties
if (block == Blocks.GRASS || block == Blocks.TALL_GRASS || block == Blocks.FERN) {
return new FogBlockProperties(solid, 1, 10).pack();
}
if (block == Blocks.VINE) {
return new FogBlockProperties(solid, 1, 5).pack();
}
// Поглотители тумана (sign = -1)
if (block == Blocks.LAVA) {
return new FogBlockProperties(solid, -1, 50).pack();
}
if (block == Blocks.MAGMA_BLOCK) {
return new FogBlockProperties(solid, -1, 40).pack();
}
if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) {
return new FogBlockProperties(solid, -1, 30).pack();
}
if (block == Blocks.CAMPFIRE || block == Blocks.SOUL_CAMPFIRE) {
return new FogBlockProperties(solid, -1, 25).pack();
}
if (block == Blocks.TORCH || block == Blocks.WALL_TORCH ||
block == Blocks.SOUL_TORCH || block == Blocks.SOUL_WALL_TORCH) {
return new FogBlockProperties(solid, -1, 15).pack();
}
if (block == Blocks.LANTERN || block == Blocks.SOUL_LANTERN) {
return new FogBlockProperties(solid, -1, 10).pack();
}
// Нейтральные блоки (sign = 0)
return new FogBlockProperties(solid, 0, 0).pack();
}
@SuppressWarnings("deprecation")
private static boolean computeSolid(BlockState state) {
if (state.isAir()) return false;
// Waterlogged уже обработан в getPackedProperties
if (!state.getFluidState().isEmpty()) return true; // лава и другие жидкости
if (state.isIn(BlockTags.LEAVES)) return false;
return state.blocksMovement();
}
}
@@ -20,17 +20,7 @@ public final class FogChunkProvider {
return client.world.getChunkManager().getWorldChunk(chunkX, chunkZ, false) != null;
}
public static float computeVoxelValue(BlockState state) {
if (state.isOf(Blocks.POPPY)) {
return 0.2f;
} else if (state.isOf(Blocks.GRASS)) {
return 0.4f;
} else {
return 0.0f;
}
}
public static boolean fillChunkIntoArray(int chunkX, int chunkZ, float[] array) {
public static boolean fillChunkIntoArray(int chunkX, int chunkZ, byte[] array) {
MinecraftClient client = MinecraftClient.getInstance();
if (!isChunkLoaded(chunkX, chunkZ)) return false;
@@ -42,10 +32,9 @@ public final class FogChunkProvider {
int topYExclusive = bottomY + sections.length * 16;
int index = 0;
byte airValue = FogBlockRegistry.getPackedProperties(Blocks.AIR.getDefaultState());
// Порядок циклов Z -> Y -> X критичен для glTexSubImage3D
for (int z = 0; z < FogWorldVolume.CHUNK_SIZE; z++) {
// Кэшируем секции и их состояние
int currentSectionIndex = -1;
ChunkSection currentSection = null;
PalettedContainer<BlockState> currentContainer = null;
@@ -54,9 +43,8 @@ public final class FogChunkProvider {
for (int y = 0; y < FogWorldVolume.SIZE_Y; y++) {
int worldY = FogWorldVolume.MIN_Y + y;
// За пределами мира (например, ниже -64)
if (worldY < bottomY || worldY >= topYExclusive) {
Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, 0.0f);
Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, airValue);
index += FogWorldVolume.CHUNK_SIZE;
continue;
}
@@ -76,25 +64,25 @@ public final class FogChunkProvider {
int localY = worldY & 15;
// ОПТИМИЗАЦИЯ: Если текущая секция полностью пустая (только воздух)
if (currentIsEmpty) {
Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, 0.0f);
Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, airValue);
index += FogWorldVolume.CHUNK_SIZE;
continue;
}
// Предварительное вычисление базового индекса для строки Y и Z
// Формула локального индекса в секции 16x16x16: (y << 8) | (z << 4) | x
int baseIndex = (localY << 8) | (z << 4);
for (int x = 0; x < FogWorldVolume.CHUNK_SIZE; x++) {
BlockState currentBlock = currentContainer.get(baseIndex | x);
array[index++] = computeVoxelValue(currentBlock);
array[index++] = FogBlockRegistry.getPackedProperties(currentBlock);
}
}
}
return true;
}
public static byte computePackedVoxelValue(BlockState state) {
return FogBlockRegistry.getPackedProperties(state);
}
}
@@ -28,7 +28,7 @@ public final class FogChunkStreamer {
private static final ChunkStatus[] statuses = new ChunkStatus[TOTAL_CHUNKS];
private static final boolean[] readyToUpload = new boolean[TOTAL_CHUNKS];
private static final float[][] preparedData = new float[TOTAL_CHUNKS][CHUNK_DATA_SIZE];
private static final byte[][] preparedData = new byte[TOTAL_CHUNKS][CHUNK_DATA_SIZE];
private static final int[] preparedWorldChunkX = new int[TOTAL_CHUNKS];
private static final int[] preparedWorldChunkZ = new int[TOTAL_CHUNKS];
@@ -175,7 +175,7 @@ public final class FogChunkStreamer {
}
// Вычисляем значение для этого вокселя
float value = FogChunkProvider.computeVoxelValue(event.newState);
byte value = FogChunkProvider.computePackedVoxelValue(event.newState());
// Обновляем один воксель в текстуре
FogWorldVolume.updateSingleVoxel(
@@ -3,18 +3,12 @@ package su.divan2000.veila.client.render.fog;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
public final class FogWorldVolume {
/*
* Размер всего объёма
*/
public static final int SIZE_X = 256;
public static final int SIZE_Z = 256;
@@ -22,156 +16,81 @@ public final class FogWorldVolume {
public static final int MAX_Y = 320;
public static final int SIZE_Y = MAX_Y - MIN_Y;
/*
* Размер одного чанка
*/
public static final int CHUNK_SIZE = 16;
public static final int CHUNKS_X = SIZE_X / CHUNK_SIZE;
public static final int CHUNKS_Z = SIZE_Z / CHUNK_SIZE;
/*
* OpenGL
*/
private static int texture = -1;
/*
* Буфер для одного чанка
*
* 16 × 384 × 16
*/
private static final float[] ZEROS_ARRAY = new float[CHUNK_SIZE * SIZE_Y * CHUNK_SIZE];
private static final byte[] ZEROS_ARRAY;
static {
java.util.Arrays.fill(ZEROS_ARRAY, 0.0f);
ZEROS_ARRAY = new byte[CHUNK_SIZE * SIZE_Y * CHUNK_SIZE];
byte airValue = new FogBlockProperties(false, 0, 0).pack();
java.util.Arrays.fill(ZEROS_ARRAY, airValue);
}
private static final FloatBuffer CHUNK_BUFFER =
BufferUtils.createFloatBuffer(
CHUNK_SIZE *
SIZE_Y *
CHUNK_SIZE
);
private static final ByteBuffer CHUNK_BUFFER =
BufferUtils.createByteBuffer(CHUNK_SIZE * SIZE_Y * CHUNK_SIZE);
private static final FloatBuffer SINGLE_VOXEL_BUFFER =
BufferUtils.createFloatBuffer(1);
private static final ByteBuffer SINGLE_VOXEL_BUFFER =
BufferUtils.createByteBuffer(1);
private FogWorldVolume() {
}
public static void init() {
if (texture != -1)
return;
if (texture != -1) return;
texture = GL11.glGenTextures();
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
texture
);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
GL30.glTexImage3D(
GL12.GL_TEXTURE_3D,
0,
GL30.GL_R16F,
GL30.GL_R8UI,
SIZE_X,
SIZE_Y,
SIZE_Z,
0,
GL11.GL_RED,
GL11.GL_FLOAT,
GL30.GL_RED_INTEGER,
GL11.GL_UNSIGNED_BYTE,
(ByteBuffer) null
);
GL11.glTexParameteri(
GL12.GL_TEXTURE_3D,
GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR
);
GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(
GL12.GL_TEXTURE_3D,
GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_LINEAR
);
GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_S, GL12.GL_REPEAT);
GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_R, GL12.GL_REPEAT);
GL11.glTexParameteri(
GL12.GL_TEXTURE_3D,
GL12.GL_TEXTURE_WRAP_S,
GL12.GL_REPEAT
);
GL11.glTexParameteri(
GL12.GL_TEXTURE_3D,
GL12.GL_TEXTURE_WRAP_T,
GL12.GL_CLAMP_TO_EDGE
);
GL11.glTexParameteri(
GL12.GL_TEXTURE_3D,
GL12.GL_TEXTURE_WRAP_R,
GL12.GL_REPEAT
);
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
0
);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
clear();
}
public static void clear() {
CHUNK_BUFFER.clear();
int size =
CHUNK_SIZE *
SIZE_Y *
CHUNK_SIZE;
for (int i = 0; i < size; i++)
CHUNK_BUFFER.put(0.0f);
CHUNK_BUFFER.put(ZEROS_ARRAY);
CHUNK_BUFFER.flip();
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
texture
);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
for (int chunkZ = 0; chunkZ < CHUNKS_Z; chunkZ++) {
for (int chunkX = 0; chunkX < CHUNKS_X; chunkX++) {
uploadChunk(
chunkX,
chunkZ,
CHUNK_BUFFER
);
uploadChunk(chunkX, chunkZ, CHUNK_BUFFER);
CHUNK_BUFFER.rewind();
}
}
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
0
);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
}
public static void uploadChunk(
int textureChunkX,
int textureChunkZ,
FloatBuffer data
) {
public static void uploadChunk(int textureChunkX, int textureChunkZ, ByteBuffer data) {
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
// Сбрасываем ВСЕ pixel store параметры, которые мог оставить Minecraft
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
@@ -180,17 +99,14 @@ public final class FogWorldVolume {
GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D,
0,
textureChunkX * CHUNK_SIZE,
0,
textureChunkZ * CHUNK_SIZE,
CHUNK_SIZE,
SIZE_Y,
CHUNK_SIZE,
GL11.GL_RED,
GL11.GL_FLOAT,
GL30.GL_RED_INTEGER,
GL11.GL_UNSIGNED_BYTE,
data
);
@@ -203,7 +119,7 @@ public final class FogWorldVolume {
int localX,
int localY,
int localZ,
float value
byte value
) {
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
@@ -223,8 +139,8 @@ public final class FogWorldVolume {
localY,
textureChunkZ * CHUNK_SIZE + localZ,
1, 1, 1,
GL11.GL_RED,
GL11.GL_FLOAT,
GL30.GL_RED_INTEGER,
GL11.GL_UNSIGNED_BYTE,
SINGLE_VOXEL_BUFFER
);
@@ -232,7 +148,7 @@ public final class FogWorldVolume {
}
public static void uploadZeros(int textureChunkX, int textureChunkZ) {
FloatBuffer buffer = chunkBuffer();
ByteBuffer buffer = chunkBuffer();
buffer.put(ZEROS_ARRAY);
buffer.flip();
uploadChunk(textureChunkX, textureChunkZ, buffer);
@@ -241,23 +157,20 @@ public final class FogWorldVolume {
public static void uploadChunkFromArray(
int textureChunkX,
int textureChunkZ,
float[] data
byte[] data
) {
FloatBuffer buffer = chunkBuffer();
ByteBuffer buffer = chunkBuffer();
buffer.put(data);
buffer.flip();
uploadChunk(textureChunkX, textureChunkZ, buffer);
}
public static FloatBuffer chunkBuffer() {
public static ByteBuffer chunkBuffer() {
CHUNK_BUFFER.clear();
return CHUNK_BUFFER;
}
public static int getTexture() {
return texture;
}
}
@@ -2,7 +2,7 @@
uniform sampler2D DiffuseSampler;
uniform sampler2D DepthSampler;
uniform sampler3D FogVolume;
uniform usampler3D FogVolume;
uniform mat4 InverseProjection;
uniform mat4 InverseView;
@@ -23,7 +23,7 @@ out vec4 FragColor;
const float FOG_SIZE_X = 256.0;
const float FOG_SIZE_Z = FOG_SIZE_X;
const int MAX_STEPS = 256; // Уменьшили количество шагов благодаря адаптивности
const int MAX_STEPS = 256;
// Высота мира
const float WORLD_BOTTOM = -64.0;
@@ -39,7 +39,7 @@ const float MAX_DISTANCE = (FOG_SIZE_X / 2.0) - 18.0;
const float GAMMA = 2.0;
// Включение jittering для устранения banding артефактов
const bool USE_JITTERING = true;
const bool USE_JITTERING = false;
//------------------------------------------------------------
@@ -103,7 +103,9 @@ float density(vec3 worldPos)
// Псевдослучайное значение для jittering (детерминированное по UV)
float random(vec2 st)
{
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
vec3 p3 = fract(vec3(st.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
// Функция нелинейного маппинга параметра t [0,1] в расстояние