diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java index d44fd92b..bc14cf92 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java @@ -325,7 +325,7 @@ public class ModelFactory { metadata |= hasBiomeColourResolver?1:0; metadata |= blockRenderLayer == RenderLayer.getTranslucent()?2:0; metadata |= needsDoubleSidedQuads?4:0; - metadata |= (!blockState.getFluidState().isEmpty())?8:0;//Has a fluid state accosiacted with it + metadata |= ((!isFluid) && !blockState.getFluidState().isEmpty())?8:0;//Has a fluid state accosiacted with it and is not itself a fluid metadata |= isFluid?16:0;//Is a fluid metadata |= cullsSame?32:0; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java index a9af6772..7d4d318d 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java @@ -38,6 +38,7 @@ public class RenderDataFactory45 { private final int[] opaqueMasks = new int[32*32]; private final int[] nonOpaqueMasks = new int[32*32]; + private final int[] fluidMasks = new int[32*32];//Used to separately mesh fluids, allowing for fluid + blockstate //TODO: emit directly to memory buffer instead of long arrays @@ -206,29 +207,38 @@ public class RenderDataFactory45 { final var sectionData = this.sectionData; int opaque = 0; int notEmpty = 0; + int pureFluid = 0; + int partialFluid = 0; int neighborAcquireMsk = 0;//-+x, -+y, -+Z for (int i = 0; i < 32*32*32;) { long block = sectionData[i + 32 * 32 * 32];//Get the block mapping + if (Mapper.isAir(block)) {//If it is air, just emit lighting + sectionData[i * 2] = (block&(0xFFL<<56))>>1; + sectionData[i * 2 + 1] = 0; + } else { + int modelId = this.modelMan.getModelId(Mapper.getBlockId(block)); + long modelMetadata = this.modelMan.getModelMetadataFromClientId(modelId); - int modelId = this.modelMan.getModelId(Mapper.getBlockId(block)); - long modelMetadata = this.modelMan.getModelMetadataFromClientId(modelId); + sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata); + sectionData[i * 2 + 1] = modelMetadata; - sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata); - sectionData[i * 2 + 1] = modelMetadata; - - boolean isFullyOpaque = ModelQueries.isFullyOpaque(modelMetadata); - opaque |= (isFullyOpaque ? 1:0) << (i & 31); - notEmpty |= (modelId!=0 ? 1:0) << (i & 31); + int msk = 1 << (i & 31); + opaque |= ModelQueries.isFullyOpaque(modelMetadata) ? msk : 0; + notEmpty |= modelId != 0 ? msk : 0; + pureFluid |= ModelQueries.isFluid(modelMetadata) ? msk : 0; + partialFluid |= ModelQueries.containsFluid(modelMetadata) ? msk : 0; + } //TODO: here also do bitmask of what neighboring sections are needed to compute (may be getting rid of this in future) //Do increment here i++; - if ((i & 31) == 0) { + if ((i & 31) == 0 && notEmpty != 0) { this.opaqueMasks[(i >> 5) - 1] = opaque; - this.nonOpaqueMasks[(i >> 5) - 1] = notEmpty^opaque; + this.nonOpaqueMasks[(i >> 5) - 1] = (notEmpty^opaque)&~pureFluid; + this.fluidMasks[(i >> 5) - 1] = pureFluid|partialFluid; int neighborMsk = 0; //-+x @@ -236,16 +246,18 @@ public class RenderDataFactory45 { neighborMsk |= (notEmpty>>>30)&0b10;//+x //notEmpty = (notEmpty != 0)?1:0; - neighborMsk |= notEmpty!=0&&((i-1)>>10)==0?0b100:0;//-y - neighborMsk |= notEmpty!=0&&((i-1)>>10)==31?0b1000:0;//+y - neighborMsk |= notEmpty!=0&&(((i-1)>>5)&0x1F)==0?0b10000:0;//-z - neighborMsk |= notEmpty!=0&&(((i-1)>>5)&0x1F)==31?0b100000:0;//+z + neighborMsk |= (((i - 1) >> 10) == 0) ? 0b100 : 0;//-y + neighborMsk |= (((i - 1) >> 10) == 31) ? 0b1000 : 0;//+y + neighborMsk |= ((((i - 1) >> 5) & 0x1F) == 0) ? 0b10000 : 0;//-z + neighborMsk |= ((((i - 1) >> 5) & 0x1F) == 31) ? 0b100000 : 0;//+z neighborAcquireMsk |= neighborMsk; opaque = 0; notEmpty = 0; + pureFluid = 0; + partialFluid = 0; } } return neighborAcquireMsk; @@ -353,7 +365,7 @@ public class RenderDataFactory45 { long selfModel = facingForward == 1 ? A : B; long nextModel = facingForward == 1 ? B : A; - //Example thing thats just wrong but as example + this.blockMesher.putNext(((long) facingForward) |//Facing selfModel | (nextModel&(0xFFL<<56))//Apply lighting @@ -406,13 +418,17 @@ public class RenderDataFactory45 { this.blockMesher.skip(1); continue; } + + if (ModelQueries.faceOccludes(meta, (axis<<1)|(1-side))) { + this.blockMesher.skip(1); + continue; + } } //TODO: swap this out for something not getting the next entry long A = this.sectionData[idx * 2]; - //Example thing thats just wrong but as example this.blockMesher.putNext((side == 0 ? 0L : 1L) | A | ((neighborId&(0xFFL<<56))>>1) @@ -427,6 +443,155 @@ public class RenderDataFactory45 { this.blockMesher.doAuxiliaryFaceOffset = true; } + private void generateYZFluidInnerGeometry(int axis) { + for (int layer = 0; layer < 31; layer++) { + this.blockMesher.auxiliaryPosition = layer; + int cSkip = 0; + for (int other = 0; other < 32; other++) { + int pidx = axis==0 ?(layer*32+other):(other*32+layer); + int skipAmount = axis==0?32:1; + + //TODO: this needs to take into account opaqueMasks to not mesh any faces with it set + int current = this.fluidMasks[pidx]; + int next = this.fluidMasks[pidx + skipAmount]; + + int msk = (current | this.opaqueMasks[pidx]) ^ (next | this.opaqueMasks[pidx + skipAmount]); + msk &= current|next; + if (msk == 0) { + cSkip += 32; + continue; + } + + this.blockMesher.skip(cSkip); + cSkip = 0; + + int faceForwardMsk = msk & current; + int cIdx = -1; + while (msk != 0) { + int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index + int delta = index - cIdx - 1; + cIdx = index; //index--; + if (delta != 0) this.blockMesher.skip(delta); + msk &= ~Integer.lowestOneBit(msk); + + int facingForward = ((faceForwardMsk >> index) & 1); + + { + int idx = index + (pidx*32); + + int a = idx*2; + int b = (idx + skipAmount * 32) * 2; + + //Flip data with respect to facing direction + int ai = facingForward == 1 ? a : b; + int bi = facingForward == 1 ? b : a; + + long A = this.sectionData[ai]; + long Am = this.sectionData[ai+1]; + //If it isnt a fluid but contains one, + if (ModelQueries.containsFluid(Am)) { + int modelId = (int) ((A>>26)&0xFFFF); + A &= ~(0xFFFFL<<26); + int fluidId = this.modelMan.getFluidClientStateId(modelId); + A |= Integer.toUnsignedLong(fluidId)<<26; + Am = this.modelMan.getModelMetadataFromClientId(fluidId); + } + + long lighter = A; + //if (!ModelQueries.faceUsesSelfLighting(Am, facingForward|(axis*2))) {//TODO: check this is right + // lighter = this.sectionData[bi]; + //} + + this.blockMesher.putNext(((long) facingForward) |//Facing + A | + (lighter&(0xFFL<<56))//Apply lighting + ); + } + } + + this.blockMesher.endRow(); + } + this.blockMesher.finish(); + } + } + + private void generateYZFluidOuterGeometry(int axis) { + this.blockMesher.doAuxiliaryFaceOffset = false; + //Hacky generate section side faces (without check neighbor section) + for (int side = 0; side < 2; side++) {//-, + + int layer = side == 0 ? 0 : 31; + this.blockMesher.auxiliaryPosition = layer; + int cSkips = 0; + for (int other = 0; other < 32; other++) { + int pidx = axis == 0 ? (layer * 32 + other) : (other * 32 + layer); + int msk = this.fluidMasks[pidx]; + if (msk == 0) { + cSkips += 32; + continue; + } + + this.blockMesher.skip(cSkips); + cSkips = 0; + + int cIdx = -1; + while (msk != 0) { + int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index + int delta = index - cIdx - 1; + cIdx = index; //index--; + if (delta != 0) this.blockMesher.skip(delta); + msk &= ~Integer.lowestOneBit(msk); + + { + int idx = index + (pidx * 32); + + + int neighborIdx = ((axis+1)*32*32 * 2)+(side)*32*32; + long neighborId = this.neighboringFaces[neighborIdx + (other*32) + index]; + + long A = this.sectionData[idx * 2]; + long B = this.sectionData[idx * 2 + 1]; + + if (ModelQueries.containsFluid(B)) { + int modelId = (int) ((A>>26)&0xFFFF); + A &= ~(0xFFFFL<<26); + int fluidId = this.modelMan.getFluidClientStateId(modelId); + A |= Integer.toUnsignedLong(fluidId)<<26; + B = this.modelMan.getModelMetadataFromClientId(fluidId); + } + + //Check and test if can cull W.R.T neighbor + if (Mapper.getBlockId(neighborId) != 0) {//Not air + int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId)); + long meta = this.modelMan.getModelMetadataFromClientId(modelId); + if (ModelQueries.containsFluid(meta)) { + modelId = this.modelMan.getFluidClientStateId(modelId); + } + if (ModelQueries.cullsSame(B)) { + if (modelId == ((A>>26)&0xFFFF)) { + this.blockMesher.skip(1); + continue; + } + } + + if (ModelQueries.faceOccludes(meta, (axis<<1)|(1-side))) { + this.blockMesher.skip(1); + continue; + } + } + + this.blockMesher.putNext((side == 0 ? 0L : 1L) | + A | + ((neighborId&(0xFFL<<56))>>1) + ); + } + } + this.blockMesher.endRow(); + } + this.blockMesher.finish(); + } + this.blockMesher.doAuxiliaryFaceOffset = true; + } + private void generateYZNonOpaqueInnerGeometry(int axis) { //Note: think is ok to just reuse.. blockMesher this.seondaryblockMesher.doAuxiliaryFaceOffset = false; @@ -465,58 +630,19 @@ public class RenderDataFactory45 { { int idx = index + (pidx * 32); + long A = this.sectionData[idx * 2]; long B = this.sectionData[idx * 2+1]; - //This is just some garbage hack test thing - if (ModelQueries.isTranslucent(B)) { - if (axis != 0) { - this.blockMesher.putNext(0); - this.seondaryblockMesher.putNext(0); - continue; - } + //TODO: filtering - //Example thing thats just wrong but as example - long A = this.sectionData[idx * 2]; - - long MSK = 0xFFFFL<<26; - - long O = this.sectionData[(idx+skipAmount)*2]; - if ((O&MSK) != (A&MSK)) { - this.blockMesher.putNext((long) (false ? 0L : 1L) | - A | - (((0xFFL) & 0xFF) << 55) - ); - } else { - this.blockMesher.putNext(0); - } - - this.seondaryblockMesher.putNext(0); - /* - O = this.sectionData[(idx-skipAmount)*2]; - if ((O&MSK) != (A&MSK)) { - this.seondaryblockMesher.putNext((long) (true ? 0L : 1L) | - A | - (((0xFFL) & 0xFF) << 55) - ); - }*/ - continue; - } else { - //this.blockMesher.putNext(0); - //this.seondaryblockMesher.putNext(0); - //continue; - - - long A = this.sectionData[idx * 2]; - - //Example thing thats just wrong but as example - this.blockMesher.putNext((long) (false ? 0L : 1L) | - A | - (((0xFFL) & 0xFF) << 55) - ); - this.seondaryblockMesher.putNext((long) (true ? 0L : 1L) | - A | - (((0xFFL) & 0xFF) << 55) - ); - } + //Example thing thats just wrong but as example + this.blockMesher.putNext((long) (false ? 0L : 1L) | + A | + (((0xFFL) & 0xFF) << 55) + ); + this.seondaryblockMesher.putNext((long) (true ? 0L : 1L) | + A | + (((0xFFL) & 0xFF) << 55) + ); } } this.blockMesher.endRow(); @@ -534,6 +660,9 @@ public class RenderDataFactory45 { this.generateYZOpaqueInnerGeometry(axis); this.generateYZOpaqueOuterGeometry(axis); + this.generateYZFluidInnerGeometry(axis); + this.generateYZFluidOuterGeometry(axis); + //this.generateYZNonOpaqueInnerGeometry(axis); } } @@ -779,7 +908,10 @@ public class RenderDataFactory45 { this.maxY = Integer.MIN_VALUE; this.maxZ = Integer.MIN_VALUE; - Arrays.fill(this.quadCounters, (short) 0); + Arrays.fill(this.quadCounters,0); + Arrays.fill(this.opaqueMasks, 0); + Arrays.fill(this.nonOpaqueMasks, 0); + Arrays.fill(this.fluidMasks, 0); //Prepare everything int neighborMsk = this.prepareSectionData(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/NodeStore.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/NodeStore.java index 248726b6..abff2143 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/NodeStore.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/NodeStore.java @@ -198,6 +198,15 @@ public final class NodeStore { return ((this.localNodeData[id2idx(nodeId)+1]>>63)&1)!=0; } + //TODO: Implement this in node manager + public void setAllChildrenAreLeaf(int nodeId, boolean state) { + this.localNodeData[id2idx(nodeId)+2] &= ~(1L<<16); + this.localNodeData[id2idx(nodeId)+2] |= state?1L<<16:0; + } + public boolean getAllChildrenAreLeaf(int nodeId) { + return ((this.localNodeData[id2idx(nodeId)+2]>>16)&1)!=0; + } + public void markNodeGeometryInFlight(int nodeId) { this.localNodeData[id2idx(nodeId)+1] |= 1L<<59; }