diff --git a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java index 79944b67..be880e60 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -33,18 +33,6 @@ import java.util.*; //There is strict forward only dataflow //Ingest -> world engine -> raw render data -> render data public class VoxelCore { - private static final Set biomeTintableAllFaces = new HashSet<>(List.of(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES, - Blocks.TALL_GRASS, Blocks.LARGE_FERN, - Blocks.SHORT_GRASS, - - Blocks.SPRUCE_LEAVES, - Blocks.BIRCH_LEAVES, - Blocks.PINK_PETALS, - Blocks.FERN, Blocks.POTTED_FERN)); - private static final Set biomeTintableUpFace = new HashSet<>(List.of(Blocks.GRASS_BLOCK)); - private static final Set waterTint = new HashSet<>(List.of(Blocks.WATER)); - - private final WorldEngine world; private final DistanceTracker distanceTracker; private final RenderGenerationService renderGen; diff --git a/src/main/java/me/cortex/zenith/client/core/model/ModelManager.java b/src/main/java/me/cortex/zenith/client/core/model/ModelManager.java index 3627c834..f9ff7719 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/ModelManager.java +++ b/src/main/java/me/cortex/zenith/client/core/model/ModelManager.java @@ -7,6 +7,7 @@ import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.gl.GlTexture; import me.cortex.zenith.client.core.rendering.util.UploadStream; import net.minecraft.block.BlockState; +import net.minecraft.block.FluidBlock; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayers; @@ -97,6 +98,9 @@ public class ModelManager { } + //TODO: what i need to do is seperate out fluid states from blockStates + + //TODO: so need a few things, per face sizes and offsets, the sizes should be computed from the pixels and find the minimum bounding pixel // while the depth is computed from the depth buffer data public int addEntry(int blockId, BlockState blockState) { @@ -104,8 +108,9 @@ public class ModelManager { throw new IllegalArgumentException("Trying to add entry for duplicate id"); } + boolean isFluid = blockState.getBlock() instanceof FluidBlock; int modelId = -1; - var textureData = this.bakery.renderFaces(blockState, 123456); + var textureData = this.bakery.renderFaces(blockState, 123456, isFluid); {//Deduplicate same entries int possibleDuplicate = this.modelTexture2id.getInt(List.of(textureData)); if (possibleDuplicate != -1) {//Duplicate found @@ -122,9 +127,18 @@ public class ModelManager { var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock())); - var blockRenderLayer = RenderLayers.getBlockLayer(blockState); + RenderLayer blockRenderLayer = null; + if (blockState.getBlock() instanceof FluidBlock) { + blockRenderLayer = RenderLayers.getFluidLayer(blockState.getFluidState()); + } else { + blockRenderLayer = RenderLayers.getBlockLayer(blockState); + } + + int checkMode = blockRenderLayer==RenderLayer.getSolid()?TextureUtils.WRITE_CHECK_STENCIL:TextureUtils.WRITE_CHECK_ALPHA; + + //If it is the solid layer, it is _always_ going to occlude fully for all written pixels, even if they are 100% translucent, this should save alot of resources // if it is cutout it might occlude might not, need to test // if it is translucent it will _never_ occlude @@ -183,6 +197,7 @@ public class ModelManager { 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 //TODO: add a bunch of control config options for overriding/setting options of metadata for each face of each type for (int face = 5; face != -1; face--) {//In reverse order to make indexing into the metadata long easier @@ -285,6 +300,9 @@ public class ModelManager { return ((metadata>>(8*6))&2) != 0; } + public static boolean containsFluid(long metadata) { + return ((metadata>>(8*6))&8) != 0; + } @@ -316,7 +334,7 @@ public class ModelManager { public long getModelMetadata(int blockId) { int map = this.idMappings[blockId]; if (map == -1) { - throw new IllegalArgumentException("Id hasnt been computed yet"); + throw new IllegalArgumentException("Id hasnt been computed yet: " + blockId); } return this.metadataCache[map]; } @@ -324,7 +342,7 @@ public class ModelManager { public int getModelId(int blockId) { int map = this.idMappings[blockId]; if (map == -1) { - throw new IllegalArgumentException("Id hasnt been computed yet"); + throw new IllegalArgumentException("Id hasnt been computed yet: " + blockId); } return map; } diff --git a/src/main/java/me/cortex/zenith/client/core/model/ModelTextureBakery.java b/src/main/java/me/cortex/zenith/client/core/model/ModelTextureBakery.java index 96bcef4f..e51a7a81 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/ModelTextureBakery.java +++ b/src/main/java/me/cortex/zenith/client/core/model/ModelTextureBakery.java @@ -10,16 +10,27 @@ import me.cortex.zenith.client.core.gl.shader.Shader; import me.cortex.zenith.client.core.gl.shader.ShaderLoader; import me.cortex.zenith.client.core.gl.shader.ShaderType; import me.jellysquid.mods.sodium.client.gl.shader.GlShader; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.FluidBlock; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.GlUniform; import net.minecraft.client.render.*; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.fluid.FluidState; import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.random.LocalRandom; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.ColorResolver; +import net.minecraft.world.chunk.light.LightingProvider; +import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11C; @@ -86,7 +97,7 @@ public class ModelTextureBakery { //TODO: For block entities, also somehow attempt to render the default block entity, e.g. chests and stuff // cause that will result in ok looking micro details in the terrain - public ColourDepthTextureData[] renderFaces(BlockState state, long randomValue) { + public ColourDepthTextureData[] renderFaces(BlockState state, long randomValue, boolean renderFluid) { var model = MinecraftClient.getInstance() .getBakedModelManager() .getBlockModels() @@ -108,7 +119,13 @@ public class ModelTextureBakery { glBindFramebuffer(GL_FRAMEBUFFER, this.framebuffer.id); - var renderLayer = RenderLayers.getBlockLayer(state); + RenderLayer renderLayer = null; + if (!renderFluid) { + renderLayer = RenderLayers.getBlockLayer(state); + } else { + renderLayer = RenderLayers.getFluidLayer(state.getFluidState()); + } + renderLayer.startDrawing(); glEnable(GL_STENCIL_TEST); @@ -133,13 +150,10 @@ public class ModelTextureBakery { //TODO: TRANSLUCENT, must sort the quad first, or something idk } - if (!state.getFluidState().isEmpty()) { - //TODO: render fluid - } var faces = new ColourDepthTextureData[FACE_VIEWS.size()]; for (int i = 0; i < faces.length; i++) { - faces[i] = captureView(state, model, FACE_VIEWS.get(i), randomValue); + faces[i] = captureView(state, model, FACE_VIEWS.get(i), randomValue, i, renderFluid); //glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,300*(i%3),300*(i/3),300*(i%3)+256,300*(i/3)+256, GL_COLOR_BUFFER_BIT, GL_NEAREST); } @@ -155,17 +169,78 @@ public class ModelTextureBakery { return faces; } - private ColourDepthTextureData captureView(BlockState state, BakedModel model, MatrixStack stack, long randomValue) { + private ColourDepthTextureData captureView(BlockState state, BakedModel model, MatrixStack stack, long randomValue, int face, boolean renderFluid) { var vc = Tessellator.getInstance().getBuffer(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); vc.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); - renderQuads(vc, state, model, stack, randomValue); + + if (!renderFluid) { + renderQuads(vc, state, model, new MatrixStack(), randomValue); + } else { + MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() { + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return 0; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + return 0; + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + //TODO: make it so it returns air on some positions, e.g. so from UP, + @Override + public BlockState getBlockState(BlockPos pos) { + //TODO:FIXME: Dont hardcode + if (pos.equals(Direction.byId(face).getVector())) { + return Blocks.AIR.getDefaultState(); + } + return state; + } + + @Override + public FluidState getFluidState(BlockPos pos) { + if (pos.equals(Direction.byId(face).getVector())) { + return Blocks.AIR.getDefaultState().getFluidState(); + } + return state.getFluidState(); + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public int getBottomY() { + return 0; + } + }, vc, state, state.getFluidState()); + } + float[] mat = new float[4*4]; - RenderSystem.getProjectionMatrix().get(mat); + new Matrix4f(RenderSystem.getProjectionMatrix()).mul(stack.peek().getPositionMatrix()).get(mat); glUniformMatrix4fv(1, false, mat); BufferRenderer.draw(vc.end()); + glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); int[] colourData = new int[this.width*this.height]; int[] depthData = new int[this.width*this.height]; diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/Gl46FarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/Gl46FarWorldRenderer.java index 7bd6a1a6..567f803a 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/Gl46FarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/Gl46FarWorldRenderer.java @@ -1,5 +1,6 @@ package me.cortex.zenith.client.core.rendering; +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.gl.shader.Shader; @@ -109,7 +110,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { return; } - //this.models.addEntry(0, Blocks.OAK_FENCE.getDefaultState()); + //this.models.addEntry(0, Blocks.STONE.getDefaultState()); RenderLayer.getCutoutMipped().startDrawing(); @@ -169,6 +170,8 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { RenderLayer.getTranslucent().startDrawing(); glBindVertexArray(this.vao); glDisable(GL_CULL_FACE); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA); int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); @@ -178,14 +181,18 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glBindTexture(GL_TEXTURE_2D, this.models.getTextureId()); this.lodShader.bind(); + glDepthMask(false); glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, 400_000 * 4 * 5, 4, this.geometry.getSectionCount(), 0); + glDepthMask(true); glEnable(GL_CULL_FACE); glBindVertexArray(0); + glBindSampler(0, 0); GL11C.glBindTexture(GL_TEXTURE_2D, oldBoundTexture); glActiveTexture(oldActiveTexture); + RenderSystem.disableBlend(); RenderLayer.getTranslucent().endDrawing(); } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactory.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactory.java index 226c958b..4cbadf9c 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactory.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactory.java @@ -7,6 +7,7 @@ import me.cortex.zenith.common.util.MemoryBuffer; import me.cortex.zenith.common.world.WorldEngine; import me.cortex.zenith.common.world.WorldSection; import me.cortex.zenith.common.world.other.Mapper; +import net.minecraft.block.FluidBlock; import net.minecraft.client.MinecraftClient; import org.lwjgl.system.MemoryUtil; @@ -210,6 +211,25 @@ public class RenderDataFactory { return false; } + + //TODO: if the model has a fluid state, it should compute if a fluid face needs to be injected + // fluid face of type this.world.getMapper().getBlockStateFromId(self).getFluidState() and block type + // this.world.getMapper().getBlockStateFromId(self).getFluidState().getBlockState() + + //If we are a fluid and the oposing face is also a fluid need to make a closer check + if (ModelManager.containsFluid(metadata) && ModelManager.containsFluid(facingMetadata)) { + //if (this.world.getMapper().getBlockStateFromId(self).getFluidState() != this.world.getMapper().getBlockStateFromId(facingState).getFluidState()) { + // TODO: need to inject a face here of type fluid, how? i have no god damn idea, probably add an auxilery mesher just for this + //} + + //Hackfix + var selfBS = this.world.getMapper().getBlockStateFromId(self); + if (selfBS.getBlock() instanceof FluidBlock && selfBS.getFluidState().getBlockState().getBlock() == this.world.getMapper().getBlockStateFromId(facingState).getFluidState().getBlockState().getBlock()) { + return false; + } + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); long otherFlags = 0; otherFlags |= ModelManager.isTranslucent(metadata)?1L<<33:0; diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactoryOld.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactoryOld.java deleted file mode 100644 index 70eedb32..00000000 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactoryOld.java +++ /dev/null @@ -1,337 +0,0 @@ -package me.cortex.zenith.client.core.rendering.building; - -import it.unimi.dsi.fastutil.longs.LongArrayList; -import me.cortex.zenith.common.util.MemoryBuffer; -import me.cortex.zenith.client.core.util.Mesher2D; -import me.cortex.zenith.common.world.WorldEngine; -import me.cortex.zenith.common.world.WorldSection; -import me.cortex.zenith.common.world.other.Mapper; -import net.minecraft.client.MinecraftClient; -import net.minecraft.util.math.Direction; -import org.lwjgl.system.MemoryUtil; - - -public class RenderDataFactoryOld { - /* - private final Mesher2D mesher = new Mesher2D(5,15);//15 - private final LongArrayList outData = new LongArrayList(1000); - private final WorldEngine world; - private final long[] sectionCache = new long[32*32*32]; - private final long[] connectedSectionCache = new long[32*32*32]; - private final QuadEncoder encoder; - public RenderDataFactoryOld(WorldEngine world) { - this.world = world; - this.encoder = new QuadEncoder(world.getMapper(), MinecraftClient.getInstance().getBlockColors(), MinecraftClient.getInstance().world); - } - - - //TODO: MAKE a render cache that caches each WorldSection directional face generation, cause then can just pull that directly - // instead of needing to regen the entire thing - - - //section is already acquired and gets released by the parent - - //buildMask in the lower 6 bits contains the faces to build, the next 6 bits are whether the edge face builds against - // its neigbor or not (0 if it does 1 if it doesnt (0 is default behavior)) - public void generateMesh(WorldSection section, int buildMask) { - //TODO: to speed it up more, check like section.isEmpty() and stuff like that, have masks for if a slice/layer is entirly air etc - - //TODO: instead of having it check its neighbors with the same lod level, compare against 1 level lower, this will prevent cracks and seams from - // appearing between lods - - - //if (section.definitelyEmpty()) {//Fast path if its known the entire chunk is empty - // return new BuiltSectionGeometry(section.getKey(), null, null); - //} - - section.copyDataTo(this.sectionCache); - var data = this.sectionCache; - - long[] connectedData = null; - int dirId = Direction.UP.getId(); - if ((buildMask&(1<>(6+dirId))&1) == 0) { - //Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies - if (connectedData == null) { - var connectedSection = this.world.acquire(section.lvl, section.x, section.y + 1, section.z); - connectedSection.copyDataTo(this.connectedSectionCache); - connectedData = this.connectedSectionCache; - connectedSection.release(); - } - up = connectedData[WorldSection.getIndex(x, 0, z)]; - if (!Mapper.isTranslucent(up)) { - continue; - } - } - //Recodes the id to include the correct lighting - this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); - } - } - - var count = this.mesher.process(); - var array = this.mesher.getArray(); - for (int i = 0; i < count; i++) { - var quad = array[i]; - this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 1, y, quad)); - } - } - connectedData = null; - } - - dirId = Direction.EAST.getId(); - if ((buildMask&(1<>(6+dirId))&1) == 0) { - //Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies - if (connectedData == null) { - var connectedSection = this.world.acquire(section.lvl, section.x + 1, section.y, section.z); - connectedSection.copyDataTo(this.connectedSectionCache); - connectedData = this.connectedSectionCache; - connectedSection.release(); - } - up = connectedData[WorldSection.getIndex(0, y, z)]; - if (!Mapper.isTranslucent(up)) { - continue; - } - } - this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); - } - } - - var count = this.mesher.process(); - var array = this.mesher.getArray(); - for (int i = 0; i < count; i++) { - var quad = array[i]; - this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 5, x, quad)); - } - } - connectedData = null; - } - - dirId = Direction.SOUTH.getId(); - if ((buildMask&(1<>(6+dirId))&1) == 0) { - //Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies - if (connectedData == null) { - var connectedSection = this.world.acquire(section.lvl, section.x, section.y, section.z + 1); - connectedSection.copyDataTo(this.connectedSectionCache); - connectedData = this.connectedSectionCache; - connectedSection.release(); - } - up = connectedData[WorldSection.getIndex(x, y, 0)]; - if (!Mapper.isTranslucent(up)) { - continue; - } - } - this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); - } - } - - var count = this.mesher.process(); - var array = this.mesher.getArray(); - for (int i = 0; i < count; i++) { - var quad = array[i]; - this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 3, z, quad)); - } - } - connectedData = null; - } - - dirId = Direction.WEST.getId(); - if ((buildMask&(1<>(6+dirId))&1) == 0) { - //Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies - if (connectedData == null) { - var connectedSection = this.world.acquire(section.lvl, section.x - 1, section.y, section.z); - connectedSection.copyDataTo(this.connectedSectionCache); - connectedData = this.connectedSectionCache; - connectedSection.release(); - } - up = connectedData[WorldSection.getIndex(31, y, z)]; - if (!Mapper.isTranslucent(up)) { - continue; - } - } - this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); - } - } - - var count = this.mesher.process(); - var array = this.mesher.getArray(); - for (int i = 0; i < count; i++) { - var quad = array[i]; - this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 4, x, quad)); - } - } - connectedData = null; - } - - dirId = Direction.NORTH.getId(); - if ((buildMask&(1<>(6+dirId))&1) == 0) { - //Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies - if (connectedData == null) { - var connectedSection = this.world.acquire(section.lvl, section.x, section.y, section.z - 1); - connectedSection.copyDataTo(this.connectedSectionCache); - connectedData = this.connectedSectionCache; - connectedSection.release(); - } - up = connectedData[WorldSection.getIndex(x, y, 31)]; - if (!Mapper.isTranslucent(up)) { - continue; - } - } - this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); - } - } - - var count = this.mesher.process(); - var array = this.mesher.getArray(); - for (int i = 0; i < count; i++) { - var quad = array[i]; - this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 2, z, quad)); - } - } - connectedData = null; - } - - dirId = Direction.DOWN.getId(); - if ((buildMask&(1<>(6+dirId))&1) == 0) { - //Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies - if (connectedData == null) { - var connectedSection = this.world.acquire(section.lvl, section.x, section.y - 1, section.z); - connectedSection.copyDataTo(this.connectedSectionCache); - connectedData = this.connectedSectionCache; - connectedSection.release(); - } - up = connectedData[WorldSection.getIndex(x, 31, z)]; - if (!Mapper.isTranslucent(up)) { - continue; - } - } - this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); - } - } - - var count = this.mesher.process(); - var array = this.mesher.getArray(); - for (int i = 0; i < count; i++) { - var quad = array[i]; - this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 0, y, quad)); - } - } - connectedData = null; - } - - - if (this.outData.isEmpty()) { - //return new BuiltSectionGeometry(section.getKey(), null, null); - } - - var output = new MemoryBuffer(this.outData.size()*8L); - for (int i = 0; i < this.outData.size(); i++) { - MemoryUtil.memPutLong(output.address + i * 8L, this.outData.getLong(i)); - } - - this.outData.clear(); - //return new BuiltSectionGeometry(section.getKey(), output, null); - } - */ - -}