diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/OccupancySet.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/OccupancySet.java new file mode 100644 index 00000000..4795c5d4 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/OccupancySet.java @@ -0,0 +1,105 @@ +package me.cortex.voxy.client.core.rendering.building; + +import org.lwjgl.system.MemoryUtil; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.Random; + +//Block occupancy, 2 lvl compacted bitset for occluding block existance +// TODO: need to add neighboring chunk data aswell? or somehow do a linking thing where this is stored in a secondary storage +// where we can link them together (or store the neighbor faces seperately or something) might be out of scope for this class +public class OccupancySet { + private long topLvl;//4x4x4 + private final long[] bottomLvl = new long[(4*4*4)*8]; + public void set(final int pos) { + final long topBit = 1L<>6)] |= 1L<<(botIdx&63); + } + + private boolean get(int pos) { + final long topBit = 1L<>6)]&(1L<<(botIdx&63)))!=0; + } + + public void reset() { + if (this.topLvl != 0) { + Arrays.fill(this.bottomLvl, 0); + } + this.topLvl = 0; + } + + public int writeSize() { + return 8+Long.bitCount(this.topLvl)*8; + } + + public boolean isEmpty() { + return this.topLvl == 0; + } + + public void write(long ptr, boolean asLongs) { + if (asLongs) { + MemoryUtil.memPutLong(ptr, this.topLvl); ptr += 8; + int cnt = Long.bitCount(this.topLvl); + for (int i = 0; i < cnt; i++) { + for (int j = 0; j < 8; j++) { + MemoryUtil.memPutLong(ptr, this.bottomLvl[i*8+j]); ptr += 8; + } + } + } else { + MemoryUtil.memPutInt(ptr, (int) (this.topLvl>>>32)); ptr += 4; + MemoryUtil.memPutInt(ptr, (int) this.topLvl); ptr += 4; + int cnt = Long.bitCount(this.topLvl); + for (int i = 0; i < cnt; i++) { + for (int j = 0; j < 8; j++) { + long v = this.bottomLvl[i*8+j]; + MemoryUtil.memPutInt(ptr, (int) (v>>>32)); ptr += 4; + MemoryUtil.memPutInt(ptr, (int) v); ptr += 4; + } + } + } + } + + public static void main(String[] args) { + for (int q = 0; q < 1000; q++) { + var o = new OccupancySet(); + var r = new Random(12523532643L*q); + var bs = new BitSet(32 * 32 * 32); + for (int i = 0; i < 5000; i++) { + int p = r.nextInt(32 * 32 * 32); + o.set(p); + bs.set(p); + + for (int j = 0; j < 32 * 32 * 32; j++) { + if (o.get(j) != bs.get(j)) throw new IllegalStateException(); + } + } + } + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java index e31d6723..3080a2db 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java @@ -17,6 +17,8 @@ import java.util.Arrays; public class RenderDataFactory { + private static final boolean BUILD_OCCUPANCY_SET = false; + private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true; private static final boolean DISABLE_CULL_SAME_OCCLUDES = false;//TODO: FIX TRANSLUCENTS (e.g. stained glass) breaking on chunk boarders with this set to false (it might be something else????) @@ -60,6 +62,8 @@ public class RenderDataFactory { private int quadCount = 0; + private final OccupancySet occupancy = new OccupancySet(); + //Wont work for double sided quads private final class Mesher extends ScanMesher2D { public int auxiliaryPosition = 0; @@ -218,7 +222,7 @@ public class RenderDataFactory { long pureFluid = 0; long partialFluid = 0; - int neighborAcquireMsk = 0;//-+x, -+z, -+y + int neighborAcquireMskAndFlags = 0;//-+x, -+z, -+y for (int i = 0; i < 32*32*32;) { long block = rawSectionData[i];//Get the block mapping if (Mapper.isAir(block)) {//If it is air, just emit lighting @@ -267,8 +271,8 @@ public class RenderDataFactory { neighborMsk += (((((i - 33) >> 5) & 0x1F) == 0) ? 0b10000 : 0)*(((int)notEmpty)!=0?1:0);//-z neighborMsk += (((((i - 1) >> 5) & 0x1F) == 31) ? 0b100000 : 0)*((notEmpty>>>32)!=0?1:0);//+z - neighborAcquireMsk |= neighborMsk; - + neighborAcquireMskAndFlags |= neighborMsk; + neighborAcquireMskAndFlags |= opaque!=0?(1<<6):0; opaque = 0; notEmpty = 0; @@ -276,7 +280,7 @@ public class RenderDataFactory { partialFluid = 0; } } - return neighborAcquireMsk; + return neighborAcquireMskAndFlags; } private void acquireNeighborData(WorldSection section, int msk) { @@ -1568,6 +1572,29 @@ public class RenderDataFactory { } } + //Build the occupancy set (used for AO) from the set of fully opaque blocks (atm, this can change in the future if needed to a special occupancy bitset) + private final void buildOccupancy() { + //We basicly want to record all the points where we go from air to solid or solid to air (this is to just get better compression) + for (int i = 0; i < 32*32; i++) { + int occ = 0; + int msk = this.opaqueMasks[i]; + //x + occ |= msk^(msk>>1); + occ |= msk^(msk<<1); + //y + occ |= i<32*31?msk^this.opaqueMasks[i+32]:0; + occ |= 31>31!=0) {//We failed to get everything so throw exception - throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true); + int neighborMskAndFlags = this.prepareSectionData(section._unsafeGetRawDataArray()); + if ((neighborMskAndFlags&(1<<31))!=0) {//We failed to get everything so throw exception + throw new IdNotYetComputedException(neighborMskAndFlags&((1<<20)-1), true); } + int neighborMsk = neighborMskAndFlags&0b11_11_11; + int flags = neighborMskAndFlags>>>6; if (CHECK_NEIGHBOR_FACE_OCCLUSION) { this.acquireNeighborData(section, neighborMsk); } @@ -1628,6 +1659,11 @@ public class RenderDataFactory { throw e; } + //We only care if we have quads + if (BUILD_OCCUPANCY_SET && this.quadCount != 0 && (flags&1) != 0) { + this.buildOccupancy(); + } + //TODO:NOTE! when doing face culling of translucent blocks, // if the connecting type of the translucent block is the same AND the face is full, discard it // this stops e.g. multiple layers of glass (and ocean) from having 3000 layers of quads etc