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 {
|
public class RenderDataFactory {
|
||||||
|
private static final boolean BUILD_OCCUPANCY_SET = false;
|
||||||
|
|
||||||
private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true;
|
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????)
|
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 int quadCount = 0;
|
||||||
|
|
||||||
|
private final OccupancySet occupancy = new OccupancySet();
|
||||||
|
|
||||||
//Wont work for double sided quads
|
//Wont work for double sided quads
|
||||||
private final class Mesher extends ScanMesher2D {
|
private final class Mesher extends ScanMesher2D {
|
||||||
public int auxiliaryPosition = 0;
|
public int auxiliaryPosition = 0;
|
||||||
@@ -218,7 +222,7 @@ public class RenderDataFactory {
|
|||||||
long pureFluid = 0;
|
long pureFluid = 0;
|
||||||
long partialFluid = 0;
|
long partialFluid = 0;
|
||||||
|
|
||||||
int neighborAcquireMsk = 0;//-+x, -+z, -+y
|
int neighborAcquireMskAndFlags = 0;//-+x, -+z, -+y
|
||||||
for (int i = 0; i < 32*32*32;) {
|
for (int i = 0; i < 32*32*32;) {
|
||||||
long block = rawSectionData[i];//Get the block mapping
|
long block = rawSectionData[i];//Get the block mapping
|
||||||
if (Mapper.isAir(block)) {//If it is air, just emit lighting
|
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 - 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
|
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;
|
opaque = 0;
|
||||||
notEmpty = 0;
|
notEmpty = 0;
|
||||||
@@ -276,7 +280,7 @@ public class RenderDataFactory {
|
|||||||
partialFluid = 0;
|
partialFluid = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return neighborAcquireMsk;
|
return neighborAcquireMskAndFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void acquireNeighborData(WorldSection section, int msk) {
|
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
|
//section is already acquired and gets released by the parent
|
||||||
public BuiltSection generateMesh(WorldSection section) {
|
public BuiltSection generateMesh(WorldSection section) {
|
||||||
//TODO: FIXME: because of the exceptions that are thrown when aquiring modelId
|
//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.minX = Integer.MAX_VALUE;
|
||||||
this.minY = Integer.MAX_VALUE;
|
this.minY = Integer.MAX_VALUE;
|
||||||
this.minZ = Integer.MAX_VALUE;
|
this.minZ = Integer.MAX_VALUE;
|
||||||
@@ -1611,10 +1640,12 @@ public class RenderDataFactory {
|
|||||||
Arrays.fill(this.fluidMasks, 0);
|
Arrays.fill(this.fluidMasks, 0);
|
||||||
|
|
||||||
//Prepare everything
|
//Prepare everything
|
||||||
int neighborMsk = this.prepareSectionData(section._unsafeGetRawDataArray());
|
int neighborMskAndFlags = this.prepareSectionData(section._unsafeGetRawDataArray());
|
||||||
if (neighborMsk>>31!=0) {//We failed to get everything so throw exception
|
if ((neighborMskAndFlags&(1<<31))!=0) {//We failed to get everything so throw exception
|
||||||
throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true);
|
throw new IdNotYetComputedException(neighborMskAndFlags&((1<<20)-1), true);
|
||||||
}
|
}
|
||||||
|
int neighborMsk = neighborMskAndFlags&0b11_11_11;
|
||||||
|
int flags = neighborMskAndFlags>>>6;
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
||||||
this.acquireNeighborData(section, neighborMsk);
|
this.acquireNeighborData(section, neighborMsk);
|
||||||
}
|
}
|
||||||
@@ -1628,6 +1659,11 @@ public class RenderDataFactory {
|
|||||||
throw e;
|
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,
|
//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
|
// 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
|
// this stops e.g. multiple layers of glass (and ocean) from having 3000 layers of quads etc
|
||||||
|
|||||||
Reference in New Issue
Block a user