diff --git a/src/main/java/me/cortex/voxy/common/voxelization/VoxelizedSection.java b/src/main/java/me/cortex/voxy/common/voxelization/VoxelizedSection.java index 25934d67..f741a062 100644 --- a/src/main/java/me/cortex/voxy/common/voxelization/VoxelizedSection.java +++ b/src/main/java/me/cortex/voxy/common/voxelization/VoxelizedSection.java @@ -10,6 +10,7 @@ public class VoxelizedSection { public int x; public int y; public int z; + public int lvl0NonAirCount; public final long[] section; public VoxelizedSection(long[] section) { this.section = section; @@ -51,6 +52,7 @@ public class VoxelizedSection { } public VoxelizedSection zero() { + this.lvl0NonAirCount = 0; Arrays.fill(this.section, 0); return this; } 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 5889fd37..6403c251 100644 --- a/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java +++ b/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java @@ -143,7 +143,7 @@ public class WorldConversionFactory { } - + int nonZeroCnt = 0; if (blockContainer.data.storage instanceof PackedIntegerArray bStor) { var bDat = bStor.getData(); int iterPerLong = (64 / bStor.getElementBits()) - 1; @@ -168,7 +168,7 @@ public class WorldConversionFactory { sample >>>= eBits; byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF); - + nonZeroCnt += (bId != 0)?1:0; data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]); } } else { @@ -181,12 +181,14 @@ public class WorldConversionFactory { data[i] = Mapper.airWithLight(lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF)); } } else { + nonZeroCnt = 4096; for (int i = 0; i <= 0xFFF; i++) { byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF); data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]); } } } + section.lvl0NonAirCount = nonZeroCnt; return section; } diff --git a/src/main/java/me/cortex/voxy/common/world/SaveLoadSystem3.java b/src/main/java/me/cortex/voxy/common/world/SaveLoadSystem3.java index 05b1c41f..fa7761ea 100644 --- a/src/main/java/me/cortex/voxy/common/world/SaveLoadSystem3.java +++ b/src/main/java/me/cortex/voxy/common/world/SaveLoadSystem3.java @@ -10,6 +10,8 @@ import me.cortex.voxy.commonImpl.VoxyCommon; import org.lwjgl.system.MemoryUtil; public class SaveLoadSystem3 { + public static final int STORAGE_VERSION = 0; + private record SerializationCache(Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) { public SerializationCache() { this(new Long2ShortOpenHashMap(512), ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024)); @@ -60,6 +62,7 @@ public class SaveLoadSystem3 { throw new IllegalStateException(); } + //TODO: note! can actually have the first (last?) byte of metadata be the storage version! long metadata = 0; metadata |= Integer.toUnsignedLong(LUT.size());//Bottom 2 bytes metadata |= Byte.toUnsignedLong(section.getNonEmptyChildren())<<16;//Next byte @@ -82,21 +85,26 @@ public class SaveLoadSystem3 { return false; } - long metadata = MemoryUtil.memGetLong(ptr); ptr += 8; + final long metadata = MemoryUtil.memGetLong(ptr); ptr += 8; section.nonEmptyChildren = (byte) ((metadata>>>16)&0xFF); - - int nonEmptyBlockCount = 0; - long lutBasePtr = ptr + WorldSection.SECTION_VOLUME*2; - var blockData = section.data; - for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) { - short lutId = MemoryUtil.memGetShort(ptr); ptr+=2; - long blockId = MemoryUtil.memGetLong(lutBasePtr+Short.toUnsignedLong(lutId)*8L); - nonEmptyBlockCount += Mapper.isAir(blockId)?0:1; - blockData[i] = blockId; + final long lutBasePtr = ptr + WorldSection.SECTION_VOLUME * 2; + if (section.lvl == 0) { + int nonEmptyBlockCount = 0; + final var blockData = section.data; + for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) { + final short lutId = MemoryUtil.memGetShort(ptr); ptr += 2; + final long blockId = MemoryUtil.memGetLong(lutBasePtr + Short.toUnsignedLong(lutId) * 8L); + nonEmptyBlockCount += Mapper.isAir(blockId) ? 0 : 1; + blockData[i] = blockId; + } + section.nonEmptyBlockCount = nonEmptyBlockCount; + } else { + final var blockData = section.data; + for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) { + blockData[i] = MemoryUtil.memGetLong(lutBasePtr + Short.toUnsignedLong(MemoryUtil.memGetShort(ptr)) * 8L);ptr += 2; + } } - section.nonEmptyBlockCount = nonEmptyBlockCount; - ptr = lutBasePtr + (metadata&0xFFFF)*8L; - + ptr = lutBasePtr + (metadata & 0xFFFF) * 8L; return true; } } diff --git a/src/main/java/me/cortex/voxy/common/world/WorldSection.java b/src/main/java/me/cortex/voxy/common/world/WorldSection.java index 87e4d8ba..aef9c187 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldSection.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldSection.java @@ -51,7 +51,7 @@ public final class WorldSection { //Serialized states long metadata; long[] data = null; - volatile int nonEmptyBlockCount = 0; + volatile int nonEmptyBlockCount = 0;//Note: only needed for level 0 sections volatile byte nonEmptyChildren; final ActiveSectionTracker tracker; @@ -120,7 +120,7 @@ public final class WorldSection { public int acquire(int count) { int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, count<<1)) + (count<<1); if ((state & 1) == 0) { - throw new IllegalStateException("Tried to acquire unloaded section"); + throw new IllegalStateException("Tried to acquire unloaded section: " + WorldEngine.pprintPos(this.key)); } return state>>1; } diff --git a/src/main/java/me/cortex/voxy/common/world/WorldUpdater.java b/src/main/java/me/cortex/voxy/common/world/WorldUpdater.java index 1d4eabb4..c335f931 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldUpdater.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldUpdater.java @@ -6,7 +6,6 @@ import me.cortex.voxy.common.world.other.Mapper; import static me.cortex.voxy.common.world.WorldEngine.*; public class WorldUpdater { - //TODO: move this to auxilery class so that it can take into account larger than 4 mip levels //Executes an update to the world and automatically updates all the parent mip layers up to level 4 (e.g. where 1 chunk section is 1 block big) //NOTE: THIS RUNS ON THE THREAD IT WAS EXECUTED ON, when this method exits, the calling method may assume that VoxelizedSection is no longer needed @@ -23,7 +22,7 @@ public class WorldUpdater { WorldSection previousSection = null; final var vdat = section.section; - for (int lvl = 0; lvl < MAX_LOD_LAYER+1; lvl++) { + for (int lvl = 0; lvl <= MAX_LOD_LAYER; lvl++) { var worldSection = into.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1)); int emptinessStateChange = 0; @@ -41,36 +40,59 @@ public class WorldUpdater { int by = (section.y&msk)<<(4-lvl); int bz = (section.z&msk)<<(4-lvl); - int nonAirCountDelta = 0; + int airCount = 0; boolean didStateChange = false; + //TODO: remove the nonAirCountDelta stuff if level != 0 + {//Do a bunch of funny math var secD = worldSection.data; - - int baseVIdx = VoxelizedSection.getBaseIndexForLevel(lvl); int baseSec = bx | (bz << 5) | (by << 10); + if (lvl == 0) { + final int secMsk = 0b1100|(0xf << 5) | (0xf << 10); + final int iSecMsk1 = (~secMsk) + 1; - int secMsk = 0xF >> lvl; - secMsk |= (secMsk << 5) | (secMsk << 10); - int iSecMsk1 =(~secMsk)+1; + int secIdx = 0; + //TODO: manually unroll and do e.g. 4 iterations per loop + for (int i = 0; i <= 0xFFF; i+=4) { + int cSecIdx = secIdx + baseSec; + secIdx = (secIdx + iSecMsk1) & secMsk; - int secIdx = 0; - //TODO: manually unroll and do e.g. 4 iterations per loop - for (int i = baseVIdx; i <= (0xFFF >> (lvl * 3)) + baseVIdx; i++) { - int cSecIdx = secIdx+baseSec; - secIdx = (secIdx + iSecMsk1)&secMsk; + long oldId0 = secD[cSecIdx+0]; secD[cSecIdx+0] = vdat[i+0]; + long oldId1 = secD[cSecIdx+1]; secD[cSecIdx+1] = vdat[i+1]; + long oldId2 = secD[cSecIdx+2]; secD[cSecIdx+2] = vdat[i+2]; + long oldId3 = secD[cSecIdx+3]; secD[cSecIdx+3] = vdat[i+3]; - long newId = vdat[i]; - long oldId = secD[cSecIdx]; secD[cSecIdx] = newId; - nonAirCountDelta += (Mapper.isAir(newId)?0:1)-(Mapper.isAir(oldId)?0:1);//its 0:1 cause its nonAir - didStateChange |= newId != oldId; + airCount += Mapper.isAir(oldId0)?1:0; didStateChange |= vdat[i+0] != oldId0; + airCount += Mapper.isAir(oldId1)?1:0; didStateChange |= vdat[i+1] != oldId1; + airCount += Mapper.isAir(oldId2)?1:0; didStateChange |= vdat[i+2] != oldId2; + airCount += Mapper.isAir(oldId3)?1:0; didStateChange |= vdat[i+3] != oldId3; + } + } else { + int baseVIdx = VoxelizedSection.getBaseIndexForLevel(lvl); + + int secMsk = 0xF >> lvl; + secMsk |= (secMsk << 5) | (secMsk << 10); + int iSecMsk1 = (~secMsk) + 1; + + int secIdx = 0; + //TODO: manually unroll and do e.g. 4 iterations per loop + for (int i = baseVIdx; i <= (0xFFF >> (lvl * 3)) + baseVIdx; i++) { + int cSecIdx = secIdx + baseSec; + secIdx = (secIdx + iSecMsk1) & secMsk; + long newId = vdat[i]; + long oldId = secD[cSecIdx]; + didStateChange |= newId != oldId; + secD[cSecIdx] = newId; + } } } - if (nonAirCountDelta != 0) { - worldSection.addNonEmptyBlockCount(nonAirCountDelta); - if (lvl == 0) { + if (lvl == 0) { + int nonAirCountDelta = section.lvl0NonAirCount-(4096-airCount); + if (nonAirCountDelta != 0) { + worldSection.addNonEmptyBlockCount(nonAirCountDelta); emptinessStateChange = worldSection.updateLvl0State() ? 2 : 0; } } diff --git a/src/main/java/me/cortex/voxy/common/world/other/Mapper.java b/src/main/java/me/cortex/voxy/common/world/other/Mapper.java index 099d73b2..fc5366b9 100644 --- a/src/main/java/me/cortex/voxy/common/world/other/Mapper.java +++ b/src/main/java/me/cortex/voxy/common/world/other/Mapper.java @@ -54,9 +54,8 @@ public class Mapper { public static boolean isAir(long id) { - int bId = getBlockId(id); //Note: air can mean void, cave or normal air, as the block state is remapped during ingesting - return bId == 0; + return (id&(((1L<<20)-1)<<27)) == 0; } public static int getBlockId(long id) { diff --git a/src/main/java/me/cortex/voxy/commonImpl/importers/DHImporter.java b/src/main/java/me/cortex/voxy/commonImpl/importers/DHImporter.java index 0cee02fe..3265de2e 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/importers/DHImporter.java +++ b/src/main/java/me/cortex/voxy/commonImpl/importers/DHImporter.java @@ -309,7 +309,16 @@ public class DHImporter implements IDataImporter { if ((x+1)%16==0) { for (int sz = 0; sz < 4; sz++) { for (int sy = 0; sy < this.worldHeightSections; sy++) { - System.arraycopy(storage, (sz|(sy<<2))<<12, section.section, 0, 16 * 16 * 16); + { + int base = (sz|(sy<<2))<<12; + int nonAirCount = 0; + final var dat = section.section; + for (int i = 0; i < 4096; i++) { + nonAirCount += Mapper.isAir(dat[i] = storage[i+base])?0:1; + } + section.lvl0NonAirCount = nonAirCount; + } + WorldConversionFactory.mipSection(section, this.engine.getMapper()); section.setPosition(X*4+(x>>4), sy+(this.bottomOfWorld>>4), (Z*4)+sz);