wip occupancy
This commit is contained in:
@@ -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<<Integer.compress(pos, 0b11000_11000_11000);
|
||||
final int botIdx = Integer.compress(pos, 0b00111_00111_00111);
|
||||
|
||||
int baseBotIdx = Long.bitCount(this.topLvl&(topBit-1))*8;
|
||||
if ((this.topLvl & topBit) == 0) {
|
||||
//we need to shuffle up all the bottomlvl
|
||||
long toMove = this.topLvl & (~((topBit << 1) - 1));
|
||||
if (toMove != 0) {
|
||||
int base = baseBotIdx+8;//+8 cause were bubbling
|
||||
int count = Long.bitCount(toMove);
|
||||
for (int i = base+count*8-1; base<=i; i--) {
|
||||
this.bottomLvl[i] = this.bottomLvl[i-8];
|
||||
}
|
||||
for (int i = baseBotIdx; i<baseBotIdx+8; i++) {
|
||||
this.bottomLvl[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.topLvl |= topBit;
|
||||
}
|
||||
|
||||
this.bottomLvl[baseBotIdx+(botIdx>>6)] |= 1L<<(botIdx&63);
|
||||
}
|
||||
|
||||
private boolean get(int pos) {
|
||||
final long topBit = 1L<<Integer.compress(pos, 0b11000_11000_11000);
|
||||
final int botIdx = Integer.compress(pos, 0b00111_00111_00111);
|
||||
if ((this.topLvl & topBit) == 0) {
|
||||
return false;
|
||||
}
|
||||
int baseBotIdx = Long.bitCount(this.topLvl&(topBit-1))*8;
|
||||
|
||||
return (this.bottomLvl[baseBotIdx+(botIdx>>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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<i ?msk^this.opaqueMasks[i-32]:0;
|
||||
//z
|
||||
occ |= (i&31)<31?msk^this.opaqueMasks[i+1]:0;
|
||||
occ |= 0< (i&31)?msk^this.opaqueMasks[i-1]:0;
|
||||
|
||||
//We now have our occlusion mask, fill in our occupancy set
|
||||
for (;occ!=0;occ&=~Integer.lowestOneBit(occ)) {
|
||||
this.occupancy.set(i*32+Integer.numberOfTrailingZeros(occ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//section is already acquired and gets released by the parent
|
||||
public BuiltSection generateMesh(WorldSection section) {
|
||||
//TODO: FIXME: because of the exceptions that are thrown when aquiring modelId
|
||||
@@ -1598,6 +1625,8 @@ public class RenderDataFactory {
|
||||
}
|
||||
}
|
||||
|
||||
this.occupancy.reset();
|
||||
|
||||
this.minX = Integer.MAX_VALUE;
|
||||
this.minY = Integer.MAX_VALUE;
|
||||
this.minZ = Integer.MAX_VALUE;
|
||||
@@ -1611,10 +1640,12 @@ public class RenderDataFactory {
|
||||
Arrays.fill(this.fluidMasks, 0);
|
||||
|
||||
//Prepare everything
|
||||
int neighborMsk = this.prepareSectionData(section._unsafeGetRawDataArray());
|
||||
if (neighborMsk>>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
|
||||
|
||||
Reference in New Issue
Block a user