diff --git a/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java b/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java index f70afefc..18240cda 100644 --- a/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java +++ b/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java @@ -54,6 +54,7 @@ public static VoxyConfig loadOrCreate() { config.defaultSaveConfig = ContextSelectionSystem.DEFAULT_STORAGE_CONFIG; return config; } + public void save() { //Unsafe, todo: fixme! needs to be atomic! try { diff --git a/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java b/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java index 6eb373c5..c14954d4 100644 --- a/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java +++ b/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java @@ -90,9 +90,10 @@ public static void mipSection(VoxelizedSection section, Mapper mapper) { for (int z = 0; z < 16; z += 2) { for (int x = 0; x < 16; x += 2) { data[16*16*16 + i++] = - Mipper.mip( - data[G(x, y, z)], data[G(x+1, y, z)], data[G(x, y, z+1)], data[G(x+1, y, z+1)], - data[G(x, y+1, z)], data[G(x+1, y+1, z)], data[G(x, y+1, z+1)], data[G(x+1, y+1, z+1)], + Mipper.mip(new long[]{ + data[G(x, y, z)], data[G(x+1, y, z)], data[G(x, y, z+1)], data[G(x+1, y, z+1)], + data[G(x, y+1, z)], data[G(x+1, y+1, z)], data[G(x, y+1, z+1)], data[G(x+1, y+1, z+1)] + }, mapper); } } @@ -104,9 +105,10 @@ public static void mipSection(VoxelizedSection section, Mapper mapper) { for (int z = 0; z < 8; z += 2) { for (int x = 0; x < 8; x += 2) { data[16*16*16 + 8*8*8 + i++] = - Mipper.mip( - data[H(x, y, z)], data[H(x+1, y, z)], data[H(x, y, z+1)], data[H(x+1, y, z+1)], - data[H(x, y+1, z)], data[H(x+1, y+1, z)], data[H(x, y+1, z+1)], data[H(x+1, y+1, z+1)], + Mipper.mip(new long[]{ + data[H(x, y, z)], data[H(x + 1, y, z)], data[H(x, y, z + 1)], data[H(x + 1, y, z + 1)], + data[H(x, y + 1, z)], data[H(x + 1, y + 1, z)], data[H(x, y + 1, z + 1)], data[H(x + 1, y + 1, z + 1)] + }, mapper); } } @@ -118,9 +120,10 @@ public static void mipSection(VoxelizedSection section, Mapper mapper) { for (int z = 0; z < 4; z += 2) { for (int x = 0; x < 4; x += 2) { data[16*16*16 + 8*8*8 + 4*4*4 + i++] = - Mipper.mip( - data[I(x, y, z)], data[I(x+1, y, z)], data[I(x, y, z+1)], data[I(x+1, y, z+1)], - data[I(x, y+1, z)], data[I(x+1, y+1, z)], data[I(x, y+1, z+1)], data[I(x+1, y+1, z+1)], + Mipper.mip(new long[]{ + data[I(x, y, z)], data[I(x + 1, y, z)], data[I(x, y, z + 1)], data[I(x + 1, y, z + 1)], + data[I(x, y + 1, z)], data[I(x + 1, y + 1, z)], data[I(x, y + 1, z + 1)], data[I(x + 1, y + 1, z + 1)], + }, mapper); } } @@ -128,9 +131,10 @@ public static void mipSection(VoxelizedSection section, Mapper mapper) { //Mip L4 data[16*16*16 + 8*8*8 + 4*4*4 + 2*2*2] = - Mipper.mip( - data[J(0, 0, 0)], data[J(1, 0, 0)], data[J(0, 0, 1)], data[J(1, 0, 1)], - data[J(0, 1, 0)], data[J(1, 1, 0)], data[J(0, 1, 1)], data[J(1, 1, 1)], + Mipper.mip(new long[]{ + data[J(0, 0, 0)], data[J(1, 0, 0)], data[J(0, 0, 1)], data[J(1, 0, 1)], + data[J(0, 1, 0)], data[J(1, 1, 0)], data[J(0, 1, 1)], data[J(1, 1, 1)], + }, mapper); } } diff --git a/src/main/java/me/cortex/voxy/common/world/other/Mipper.java b/src/main/java/me/cortex/voxy/common/world/other/Mipper.java index 097d7b00..f92255e5 100644 --- a/src/main/java/me/cortex/voxy/common/world/other/Mipper.java +++ b/src/main/java/me/cortex/voxy/common/world/other/Mipper.java @@ -1,53 +1,122 @@ package me.cortex.voxy.common.world.other; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; + +import java.util.HashMap; +import java.util.Map; + import static me.cortex.voxy.common.world.other.Mapper.withLight; //Mipper for data public class Mipper { - //TODO: also pass in the level its mipping from, cause at lower levels you want to preserve block details - // but at higher details you want more air - public static long mip(long I000, long I100, long I001, long I101, - long I010, long I110, long I011, long I111, - Mapper mapper) { - //TODO: mip with respect to all the variables, what that means is take whatever has the highest count and return that - //TODO: also average out the light level and set that as the new light level - //For now just take the most top corner - - //TODO: i think it needs to compute the _max_ light level, since e.g. if a point is bright irl - // you can see it from really really damn far away. - // it could be a heavily weighted average with a huge preference to the top most lighting value - if (!Mapper.isAir(I111)) { - return I111; - } - if (!Mapper.isAir(I110)) { - return I110; - } - if (!Mapper.isAir(I011)) { - return I011; - } - if (!Mapper.isAir(I010)) { - return I010; - } - if (!Mapper.isAir(I101)) { - return I101; - } - if (!Mapper.isAir(I100)) { - return I100; - } - if (!Mapper.isAir(I001)) { - return I001; - } - if (!Mapper.isAir(I000)) { - return I000; +// public static long mip(long I000, long I100, long I001, long I101, +// long I010, long I110, long I011, long I111, +// Mapper mapper) { +// //TODO: mip with respect to all the variables, what that means is take whatever has the highest count and return that +// //TODO: also average out the light level and set that as the new light level +// //For now just take the most top corner +// +// //TODO: i think it needs to compute the _max_ light level, since e.g. if a point is bright irl +// // you can see it from really really damn far away. +// // it could be a heavily weighted average with a huge preference to the top most lighting value +// if (!Mapper.isAir(I111)) { +// return I111; +// } +// if (!Mapper.isAir(I110)) { +// return I110; +// } +// if (!Mapper.isAir(I011)) { +// return I011; +// } +// if (!Mapper.isAir(I010)) { +// return I010; +// } +// if (!Mapper.isAir(I101)) { +// return I101; +// } +// if (!Mapper.isAir(I100)) { +// return I100; +// } +// if (!Mapper.isAir(I001)) { +// return I001; +// } +// if (!Mapper.isAir(I000)) { +// return I000; +// } +// +// int blockLight = (Mapper.getLightId(I000)&0xF0)+(Mapper.getLightId(I001)&0xF0)+(Mapper.getLightId(I010)&0xF0)+(Mapper.getLightId(I011)&0xF0)+ +// (Mapper.getLightId(I100)&0xF0)+(Mapper.getLightId(I101)&0xF0)+(Mapper.getLightId(I110)&0xF0)+(Mapper.getLightId(I111)&0xF0); +// int skyLight = (Mapper.getLightId(I000)&0x0F)+(Mapper.getLightId(I001)&0x0F)+(Mapper.getLightId(I010)&0x0F)+(Mapper.getLightId(I011)&0x0F)+ +// (Mapper.getLightId(I100)&0x0F)+(Mapper.getLightId(I101)&0x0F)+(Mapper.getLightId(I110)&0x0F)+(Mapper.getLightId(I111)&0x0F); +// blockLight = blockLight/8; +// skyLight = (int) Math.ceil((double)skyLight/8); +// +// return withLight(I111, (blockLight<<4)|skyLight); +// } + +//TODO: also pass in the level its mipping from, cause at lower levels you want to preserve block details +// but at higher details you want more air + + private static HashMap blockWeights = new HashMap<>( + Map.of( + Blocks.AIR, 0.0f, + Blocks.CAVE_AIR, 0.0f, + Blocks.VOID_AIR, 0.0f, + Blocks.SHORT_GRASS, 0.25f, + Blocks.TALL_GRASS, 0.5f, + Blocks.WATER, 0.75f, + Blocks.DIRT_PATH, 1.5f, + Blocks.LAVA, 1.5f + ) + ); + + private static float getWeight(BlockState blockState) { + int luminance = blockState.getLuminance(); + float brightnessBoost = 1.0f + (luminance / 31.0f); + + float weight = blockWeights.getOrDefault(blockState.getBlock(), 1.0f); + + return weight * brightnessBoost; + } + + public static long mip(long[] blocks, Mapper mapper) { + Map computedWeights = new HashMap<>(); + float runningMax = -1.0f; + long maxIndex = 0; + + int avgBlockLight = 0; + int avgSkyLight = 0; + + for (int i = 7; i >= 0; i--) { // Iterate in reverse order to prioritize the top blocks + long block = blocks[i]; + + int blockId = Mapper.getBlockId(block); + int lightLevel = Mapper.getLightId(block); + BlockState blockState = mapper.getBlockStateFromBlockId(blockId); + + float newWeight = computedWeights.getOrDefault(blockId, 0.0f) + getWeight(blockState); + computedWeights.put(blockId, newWeight); + + if (newWeight > runningMax) { + runningMax = newWeight; + maxIndex = block; + } + + avgBlockLight += (lightLevel & 0xF0) >> 4; + + if(blockState.isOf(Blocks.SNOW) && i >= 4 ) { // If snow and a top block, remove lighting from block below + long blockBelow = blocks[i - 4]; + avgSkyLight -= Mapper.getLightId(blockBelow) & 0x0F; + } else { + avgSkyLight += lightLevel & 0x0F; + } } - int blockLight = (Mapper.getLightId(I000)&0xF0)+(Mapper.getLightId(I001)&0xF0)+(Mapper.getLightId(I010)&0xF0)+(Mapper.getLightId(I011)&0xF0)+ - (Mapper.getLightId(I100)&0xF0)+(Mapper.getLightId(I101)&0xF0)+(Mapper.getLightId(I110)&0xF0)+(Mapper.getLightId(I111)&0xF0); - int skyLight = (Mapper.getLightId(I000)&0x0F)+(Mapper.getLightId(I001)&0x0F)+(Mapper.getLightId(I010)&0x0F)+(Mapper.getLightId(I011)&0x0F)+ - (Mapper.getLightId(I100)&0x0F)+(Mapper.getLightId(I101)&0x0F)+(Mapper.getLightId(I110)&0x0F)+(Mapper.getLightId(I111)&0x0F); - blockLight = blockLight/8; - skyLight = (int) Math.ceil((double)skyLight/8); + avgBlockLight /= 8; + avgSkyLight = (int) Math.ceil((double) avgSkyLight / 8); - return withLight(I111, (blockLight<<4)|skyLight); + return withLight(maxIndex, (avgBlockLight << 4) | avgSkyLight); } }