From a3216b26eb01eea490114cd0059615955311cfcd Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:46:47 +1000 Subject: [PATCH 01/28] Wip reverse indexing --- .../client/core/rendering/GeometryManager.java | 14 +++++++++----- .../assets/zenith/shaders/lod/gl46/section.glsl | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index af55e580..dbd43c08 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -15,15 +15,19 @@ public class GeometryManager { private static final int SECTION_METADATA_SIZE = 32; - private record SectionMeta(long position, long opaqueGeometryPtr, int opaqueQuadCount, long translucentGeometryPtr, int translucentQuadCount) { + //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry + private record SectionMeta(long position, long opaqueGeometryPtr, int opaqueQuadCount, int opaquePreDataCount, long translucentGeometryPtr, int translucentQuadCount, int translucentPreDataCount) { public void writeMetadata(long ptr) { //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4; + ptr += 8; - - MemoryUtil.memPutInt(ptr, (int) this.opaqueGeometryPtr); ptr += 4; + MemoryUtil.memPutInt(ptr, (int) this.opaqueGeometryPtr + this.opaquePreDataCount); ptr += 4; MemoryUtil.memPutInt(ptr, this.opaqueQuadCount); ptr += 4; + + MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; + MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; } } @@ -40,7 +44,7 @@ public class GeometryManager { public GeometryManager() { this.sectionMetaBuffer = new GlBuffer(1L << 23, 0); - this.geometryBuffer = new BufferArena((1L << 31) - 1024, 8); + this.geometryBuffer = new BufferArena((1L << 30) - 1024, 8); this.pos2id.defaultReturnValue(-1); } @@ -52,7 +56,7 @@ public class GeometryManager { long geometryPtr = this.geometryBuffer.upload(geometry.geometryBuffer); //TODO: support translucent geometry - return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.geometryBuffer.size/8), -1,0); + return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.geometryBuffer.size/8), 0, -1,0, 0); } private void freeMeta(SectionMeta meta) { diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl index 245c6c29..3a1842b7 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl @@ -13,9 +13,9 @@ ivec3 extractPosition(SectionMeta section) { } uint extractQuadStart(SectionMeta meta) { - return meta.header.z; + return meta.drawdata.x; } uint extractQuadCount(SectionMeta meta) { - return meta.header.w; + return meta.drawdata.y; } From 48ec40c5c24c161122573a3423370f72c61c5f5b Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Wed, 24 Jan 2024 08:33:48 +1000 Subject: [PATCH 02/28] More config options --- .../cortex/zenith/client/config/ZenithConfig.java | 2 ++ .../client/config/ZenithConfigScreenFactory.java | 14 +++++++++++++- .../me/cortex/zenith/client/core/VoxelCore.java | 3 ++- .../core/rendering/AbstractFarWorldRenderer.java | 4 ++-- .../client/core/rendering/GeometryManager.java | 6 +++--- .../core/rendering/Gl46FarWorldRenderer.java | 15 +++++++++++---- .../client/core/rendering/NvFarWorldRenderer.java | 5 +++++ .../zenith/client/importers/WorldImporter.java | 2 +- 8 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/main/java/me/cortex/zenith/client/config/ZenithConfig.java b/src/main/java/me/cortex/zenith/client/config/ZenithConfig.java index 69337a83..6e964a51 100644 --- a/src/main/java/me/cortex/zenith/client/config/ZenithConfig.java +++ b/src/main/java/me/cortex/zenith/client/config/ZenithConfig.java @@ -21,6 +21,8 @@ public class ZenithConfig { public boolean enabled = true; public int qualityScale = 20; + public int maxSections = 200_000; + public int geometryBufferSize = (1<<30)/8; public int ingestThreads = 2; public int savingThreads = 10; public int renderThreads = 5; diff --git a/src/main/java/me/cortex/zenith/client/config/ZenithConfigScreenFactory.java b/src/main/java/me/cortex/zenith/client/config/ZenithConfigScreenFactory.java index ee46453c..a8924262 100644 --- a/src/main/java/me/cortex/zenith/client/config/ZenithConfigScreenFactory.java +++ b/src/main/java/me/cortex/zenith/client/config/ZenithConfigScreenFactory.java @@ -25,7 +25,7 @@ public class ZenithConfigScreenFactory implements ModMenuApi { private static Screen buildConfigScreen(Screen parent, ZenithConfig config) { ConfigBuilder builder = ConfigBuilder.create() .setParentScreen(parent) - .setTitle(Text.translatable("title.zenith.config")); + .setTitle(Text.translatable("zenith.config.title")); addGeneralCategory(builder, config); @@ -61,6 +61,18 @@ public class ZenithConfigScreenFactory implements ModMenuApi { .setDefaultValue(DEFAULT.qualityScale) .build()); + category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.general.geometryBuffer"), config.geometryBufferSize, (1<<27)/8, ((1<<31)-1)/8) + .setTooltip(Text.translatable("zenith.config.general.geometryBuffer.tooltip")) + .setSaveConsumer(val -> config.geometryBufferSize = val) + .setDefaultValue(DEFAULT.geometryBufferSize) + .build()); + + category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.general.maxSections"), config.maxSections, 100_000, 400_000) + .setTooltip(Text.translatable("zenith.config.general.maxSections.tooltip")) + .setSaveConsumer(val -> config.maxSections = val) + .setDefaultValue(DEFAULT.maxSections) + .build()); + category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.general.compression"), config.savingCompressionLevel, 1, 21) .setTooltip(Text.translatable("zenith.config.general.compression.tooltip")) .setSaveConsumer(val -> config.savingCompressionLevel = val) 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 f8870dc1..eb5d4c79 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -1,5 +1,6 @@ package me.cortex.zenith.client.core; +import me.cortex.zenith.client.Zenith; import me.cortex.zenith.client.config.ZenithConfig; import me.cortex.zenith.client.core.rendering.*; import me.cortex.zenith.client.core.rendering.building.RenderGenerationService; @@ -65,7 +66,7 @@ public class VoxelCore { //Trigger the shared index buffer loading SharedIndexBuffer.INSTANCE.id(); - this.renderer = new Gl46FarWorldRenderer(); + this.renderer = new Gl46FarWorldRenderer(ZenithConfig.CONFIG.geometryBufferSize, ZenithConfig.CONFIG.maxSections); System.out.println("Renderer initialized"); this.world = new WorldEngine(new FragmentedStorageBackendAdaptor(new File(ZenithConfig.CONFIG.storagePath)), ZenithConfig.CONFIG.ingestThreads, ZenithConfig.CONFIG.savingThreads, ZenithConfig.CONFIG.savingCompressionLevel, 5);//"storagefile.db"//"ethoslab.db" System.out.println("World engine"); diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index e67b2d5c..01b98ed0 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -50,13 +50,13 @@ public abstract class AbstractFarWorldRenderer { protected FrustumIntersection frustum; - public AbstractFarWorldRenderer() { + public AbstractFarWorldRenderer(int geometrySize, int maxSections) { this.uniformBuffer = new GlBuffer(1024, 0); //TODO: make these both dynamically sized this.stateDataBuffer = new GlBuffer((1<<16)*28, 0);//Capacity for 1<<16 entries this.biomeDataBuffer = new GlBuffer(512*4*2, 0);//capacity for 1<<9 entries this.lightDataBuffer = new GlBuffer(256*4, 0);//256 of uint - this.geometry = new GeometryManager(); + this.geometry = new GeometryManager(geometrySize*8L, maxSections); } protected abstract void setupVao(); diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index dbd43c08..e9b68c72 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -42,9 +42,9 @@ public class GeometryManager { private final BufferArena geometryBuffer; - public GeometryManager() { - this.sectionMetaBuffer = new GlBuffer(1L << 23, 0); - this.geometryBuffer = new BufferArena((1L << 30) - 1024, 8); + public GeometryManager(long geometryBufferSize, int maxSections) { + this.sectionMetaBuffer = new GlBuffer(((long) maxSections) * SECTION_METADATA_SIZE, 0); + this.geometryBuffer = new BufferArena(geometryBufferSize, 8); this.pos2id.defaultReturnValue(-1); } 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 bfabb4aa..9be5ff4d 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 @@ -18,11 +18,14 @@ import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT; import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30C.GL_R8UI; +import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; import static org.lwjgl.opengl.GL42.*; import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.GL43.*; import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; +import static org.lwjgl.opengl.GL45C.glClearNamedBufferData; import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { @@ -42,11 +45,15 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { .add(ShaderType.FRAGMENT, "zenith:lod/gl46/cull/raster.frag") .compile(); - private final GlBuffer glCommandBuffer = new GlBuffer(200_000*5*4, 0); - private final GlBuffer glVisibilityBuffer = new GlBuffer(200_000*4, 0); + private final GlBuffer glCommandBuffer; + private final GlBuffer glVisibilityBuffer; - public Gl46FarWorldRenderer() { - super(); + public Gl46FarWorldRenderer(int geometryBuffer, int maxSections) { + super(geometryBuffer, maxSections); + glCommandBuffer = new GlBuffer(maxSections*5L*4, 0); + glVisibilityBuffer = new GlBuffer(maxSections*4L, 0); + glClearNamedBufferData(glCommandBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); + glClearNamedBufferData(glVisibilityBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); setupVao(); } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java index 65669b87..0504182a 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java @@ -19,6 +19,11 @@ public class NvFarWorldRenderer extends AbstractFarWorldRenderer { .add(ShaderType.MESH, "voxelmon:lod/nvmesh/primary.mesh") .add(ShaderType.FRAGMENT, "voxelmon:lod/nvmesh/primary.frag") .compile(); + + public NvFarWorldRenderer(int geometrySize, int maxSections) { + super(geometrySize, maxSections); + } + @Override protected void setupVao() { diff --git a/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java b/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java index 1330a6f8..b2a394ec 100644 --- a/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java +++ b/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java @@ -216,7 +216,7 @@ public class WorldImporter { biomes, (bx, by, bz, state) -> { int block = 0; - int sky = 0; + int sky = 15;//since sky is inverted if (blockLight != null) { block = blockLight.get(bx, by, bz); } From dd953fc77428c70a1c6e0a704c13db4f714fbb33 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:49:55 +1000 Subject: [PATCH 03/28] Began major engineering on texturing --- build.gradle | 4 +- .../cortex/zenith/client/core/VoxelCore.java | 46 ++---- .../zenith/client/core/gl/GlBuffer.java | 5 + .../zenith/client/core/gl/GlFramebuffer.java | 3 +- .../core/model/ColourDepthTextureData.java | 4 + .../client/core/model/ModelManager.java | 37 +++++ .../client/core/model/ModelTextureBakery.java | 142 ++++++++++++++++++ .../client/core/model/TextureUtils.java | 46 ++++++ .../zenith/client/core/other/BiomeColour.java | 4 - .../client/core/other/BlockStateColour.java | 4 - .../client/core/other/ColourResolver.java | 138 ----------------- .../rendering/AbstractFarWorldRenderer.java | 52 ++----- .../core/rendering/GeometryManager.java | 2 +- .../core/rendering/Gl46FarWorldRenderer.java | 13 +- .../client/core/rendering/ModelManager.java | 5 - .../core/rendering/NvFarWorldRenderer.java | 55 ------- .../core/rendering/SharedIndexBuffer.java | 2 +- .../core/rendering/util/BufferArena.java | 2 +- .../zenith/client/core/util/DebugUtil.java | 2 +- .../zenith/shaders/lod/gl46/bindings.glsl | 18 +-- .../assets/zenith/shaders/lod/gl46/quads.vert | 11 +- 21 files changed, 273 insertions(+), 322 deletions(-) create mode 100644 src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java create mode 100644 src/main/java/me/cortex/zenith/client/core/model/ModelManager.java create mode 100644 src/main/java/me/cortex/zenith/client/core/model/ModelTextureBakery.java create mode 100644 src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java delete mode 100644 src/main/java/me/cortex/zenith/client/core/other/BiomeColour.java delete mode 100644 src/main/java/me/cortex/zenith/client/core/other/BlockStateColour.java delete mode 100644 src/main/java/me/cortex/zenith/client/core/other/ColourResolver.java delete mode 100644 src/main/java/me/cortex/zenith/client/core/rendering/ModelManager.java delete mode 100644 src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java diff --git a/build.gradle b/build.gradle index 6423647b..bca206be 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,9 @@ dependencies { modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - modImplementation "maven.modrinth:sodium:mc1.20.3-0.5.5" + //TODO: this is to eventually not need sodium installed as atm its just used for parsing shaders + modRuntimeOnly "maven.modrinth:sodium:mc1.20.3-0.5.5" + modCompileOnly "maven.modrinth:sodium:mc1.20.3-0.5.5" modImplementation("maven.modrinth:cloth-config:13.0.121+fabric") modImplementation("maven.modrinth:modmenu:9.0.0") 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 eb5d4c79..312163aa 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -1,23 +1,20 @@ package me.cortex.zenith.client.core; -import me.cortex.zenith.client.Zenith; import me.cortex.zenith.client.config.ZenithConfig; +import me.cortex.zenith.client.core.model.ModelTextureBakery; +import me.cortex.zenith.client.core.model.TextureUtils; import me.cortex.zenith.client.core.rendering.*; import me.cortex.zenith.client.core.rendering.building.RenderGenerationService; import me.cortex.zenith.client.core.util.DebugUtil; import me.cortex.zenith.common.world.WorldEngine; -import me.cortex.zenith.client.core.other.BiomeColour; -import me.cortex.zenith.client.core.other.BlockStateColour; -import me.cortex.zenith.client.core.other.ColourResolver; -import me.cortex.zenith.common.world.other.Mapper; import me.cortex.zenith.client.importers.WorldImporter; import me.cortex.zenith.common.world.storage.FragmentedStorageBackendAdaptor; import net.minecraft.block.Block; import net.minecraft.block.Blocks; +import net.minecraft.block.CropBlock; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.Direction; import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; @@ -83,39 +80,11 @@ public class VoxelCore { this.postProcessing = null;//new PostProcessing(); - this.world.getMapper().setCallbacks(this::stateUpdate, this::biomeUpdate); - - for (var state : this.world.getMapper().getStateEntries()) { - this.stateUpdate(state); - } - - for (var biome : this.world.getMapper().getBiomeEntries()) { - this.biomeUpdate(biome); - } - System.out.println("Entry updates applied"); + this.world.getMapper().setCallbacks(a->{}, a->{}); System.out.println("Voxel core initialized"); } - private void stateUpdate(Mapper.StateEntry entry) { - var state = entry.state; - int tintMsk = 0; - if (biomeTintableAllFaces.contains(state.getBlock())) { - tintMsk |= (1<<6)-1; - } - if (biomeTintableUpFace.contains(state.getBlock())) { - tintMsk |= 1<>32))); - } public void enqueueIngest(WorldChunk worldChunk) { @@ -129,7 +98,7 @@ public class VoxelCore { this.firstTime = false; } this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); - this.renderer.setupRender(frustum, camera); + //this.renderer.setupRender(frustum, camera); } public void renderOpaque(MatrixStack matrices, double cameraX, double cameraY, double cameraZ) { @@ -138,6 +107,8 @@ public class VoxelCore { DebugUtil.setPositionMatrix(matrices); matrices.pop(); + renderer.getModelManager().updateEntry(0, Blocks.FERN.getDefaultState()); + //int boundFB = GlStateManager.getBoundFramebuffer(); //this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); //this.postProcessing.bindClearFramebuffer(); @@ -150,7 +121,8 @@ public class VoxelCore { //TODO: have the renderer also render a bounding full face just like black boarders around lvl 0 // this is cause the terrain might not exist and so all the caves are visible causing hell for the // occlusion culler - this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ); + if (false) + this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ); //glBindFramebuffer(GL_FRAMEBUFFER, boundFB); diff --git a/src/main/java/me/cortex/zenith/client/core/gl/GlBuffer.java b/src/main/java/me/cortex/zenith/client/core/gl/GlBuffer.java index e27fb45e..83535e71 100644 --- a/src/main/java/me/cortex/zenith/client/core/gl/GlBuffer.java +++ b/src/main/java/me/cortex/zenith/client/core/gl/GlBuffer.java @@ -10,6 +10,11 @@ import static org.lwjgl.opengl.GL45C.glNamedBufferStorage; public class GlBuffer extends TrackedObject { public final int id; private final long size; + + public GlBuffer(long size) { + this(size, 0); + } + public GlBuffer(long size, int flags) { this.id = glCreateBuffers(); this.size = size; diff --git a/src/main/java/me/cortex/zenith/client/core/gl/GlFramebuffer.java b/src/main/java/me/cortex/zenith/client/core/gl/GlFramebuffer.java index 620a33ae..5adf2f37 100644 --- a/src/main/java/me/cortex/zenith/client/core/gl/GlFramebuffer.java +++ b/src/main/java/me/cortex/zenith/client/core/gl/GlFramebuffer.java @@ -21,10 +21,11 @@ public class GlFramebuffer extends TrackedObject { glDeleteFramebuffers(this.id); } - public void verify() { + public GlFramebuffer verify() { int code; if ((code = glCheckNamedFramebufferStatus(this.id, GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { throw new IllegalStateException("Framebuffer incomplete with error code: " + code); } + return this; } } diff --git a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java new file mode 100644 index 00000000..b3685847 --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java @@ -0,0 +1,4 @@ +package me.cortex.zenith.client.core.model; + +public record ColourDepthTextureData(int[] colour, int[] depth) { +} 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 new file mode 100644 index 00000000..04f93ca8 --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/core/model/ModelManager.java @@ -0,0 +1,37 @@ +package me.cortex.zenith.client.core.model; + +import me.cortex.zenith.client.core.gl.GlBuffer; +import net.minecraft.block.BlockState; + +//Manages the storage and updating of model states, textures and colours + +//Also has a fast long[] based metadata lookup for when the terrain mesher needs to look up the face occlusion data +public class ModelManager { + public static final int MODEL_SIZE = 64; + private final ModelTextureBakery bakery = new ModelTextureBakery(16, 16); + private final GlBuffer modelBuffer; + private final long[] metadataCache; + + public ModelManager() { + this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16)); + this.metadataCache = new long[1<<16]; + } + + public void updateEntry(int id, BlockState blockState) { + //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver + var textureData = this.bakery.renderFaces(blockState, 123456); + } + + public long getModelMetadata(int id) { + return this.metadataCache[id]; + } + + public int getBufferId() { + return this.modelBuffer.id; + } + + public void free() { + this.bakery.free(); + this.modelBuffer.free(); + } +} 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 new file mode 100644 index 00000000..b2093013 --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/core/model/ModelTextureBakery.java @@ -0,0 +1,142 @@ +package me.cortex.zenith.client.core.model; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.systems.VertexSorter; +import me.cortex.zenith.client.core.gl.GlFramebuffer; +import me.cortex.zenith.client.core.gl.GlTexture; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.*; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.RotationAxis; +import net.minecraft.util.math.random.LocalRandom; +import org.joml.Matrix4f; +import org.lwjgl.opengl.GL11C; + +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.opengl.ARBFramebufferObject.*; +import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT; +import static org.lwjgl.opengl.ARBShaderImageLoadStore.glMemoryBarrier; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate; +import static org.lwjgl.opengl.GL45C.glBlitNamedFramebuffer; +import static org.lwjgl.opengl.GL45C.glGetTextureImage; + +//Builds a texture for each face of a model +public class ModelTextureBakery { + private final int width; + private final int height; + private final GlTexture colourTex; + private final GlTexture depthTex; + private final GlFramebuffer framebuffer; + + private static final List FACE_VIEWS = new ArrayList<>(); + static { + addView(-90,0);//Direction.DOWN + addView(90,0);//Direction.UP + addView(0,180);//Direction.NORTH + addView(0,0);//Direction.SOUTH + //TODO: check these arnt the wrong way round + addView(0,90);//Direction.EAST + addView(0,270);//Direction.WEST + } + + public ModelTextureBakery(int width, int height) { + this.width = width; + this.height = height; + this.colourTex = new GlTexture().store(GL_RGBA8, 1, width, height); + this.depthTex = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width, height); + this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify(); + } + + private static void addView(float pitch, float yaw) { + var stack = new MatrixStack(); + stack.translate(0.5f,0.5f,0); + stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch)); + stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); + stack.translate(-0.5f,-0.5f,-0.5f); + FACE_VIEWS.add(stack); + } + + public ColourDepthTextureData[] renderFaces(BlockState state, long randomValue) { + var model = MinecraftClient.getInstance() + .getBakedModelManager() + .getBlockModels() + .getModel(state); + + int oldFB = GlStateManager.getBoundFramebuffer(); + var oldProjection = new Matrix4f(RenderSystem.getProjectionMatrix()); + GL11C.glViewport(0, 0, this.width, this.height); + + RenderSystem.setProjectionMatrix(new Matrix4f().identity().scale(2,2,-1f).translate(-0.5f, -0.5f, 0.0f), VertexSorter.BY_Z); + + glClearColor(0,0,0,0); + glClearDepth(1); + glBindFramebuffer(GL_FRAMEBUFFER, this.framebuffer.id); + + + RenderSystem.depthMask(true); + RenderSystem.enableBlend(); + RenderSystem.enableDepthTest(); + RenderSystem.enableCull(); + RenderSystem.depthFunc(GL_LESS); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + RenderSystem.setShader(GameRenderer::getPositionTexProgram); + + + if (!state.getFluidState().isEmpty()) { + //TODO: render fluid + } + + var renderLayer = RenderLayers.getBlockLayer(state); + if (renderLayer == RenderLayer.getTranslucent()) { + //TODO: TRANSLUCENT, must sort the quad first + } + + 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); + } + + RenderSystem.setProjectionMatrix(oldProjection, VertexSorter.BY_DISTANCE); + glBindFramebuffer(GL_FRAMEBUFFER, oldFB); + GL11C.glViewport(GlStateManager.Viewport.getX(), GlStateManager.Viewport.getY(), GlStateManager.Viewport.getWidth(), GlStateManager.Viewport.getHeight()); + + return faces; + } + + private ColourDepthTextureData captureView(BlockState state, BakedModel model, MatrixStack stack, long randomValue) { + 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); + BufferRenderer.drawWithGlobalProgram(vc.end()); + + glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); + int[] colourData = new int[this.width*this.height]; + int[] depthData = new int[this.width*this.height]; + glGetTextureImage(this.colourTex.id, 0, GL_RGBA, GL_UNSIGNED_BYTE, colourData); + glGetTextureImage(this.depthTex.id, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, depthData); + return new ColourDepthTextureData(colourData, depthData); + } + + private static void renderQuads(BufferBuilder builder, BlockState state, BakedModel model, MatrixStack stack, long randomValue) { + for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) { + var quads = model.getQuads(state, direction, new LocalRandom(randomValue)); + for (var quad : quads) { + builder.quad(stack.peek(), quad, 0, 0, 0, 0, 0); + } + } + } + + public void free() { + this.framebuffer.free(); + this.colourTex.free(); + this.depthTex.free(); + } +} diff --git a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java new file mode 100644 index 00000000..4d574e6a --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java @@ -0,0 +1,46 @@ +package me.cortex.zenith.client.core.model; + +//Texturing utils to manipulate data from the model bakery +public class TextureUtils { + //Returns if any pixels are not fully transparent or fully translucent + public static boolean hasAlpha(ColourDepthTextureData texture) { + for (int pixel : texture.colour()) { + int alpha = (pixel>>24)&0xFF; + if (alpha != 0 && alpha != 255) { + return true; + } + } + return false; + } + + public static boolean isSolid(ColourDepthTextureData texture) { + for (int pixel : texture.colour()) { + if (((pixel>>24)&0xFF) != 255) { + return false; + } + } + return true; + } + + public static boolean isFullyCovered(ColourDepthTextureData texture) { + for (int pixel : texture.colour()) { + if (((pixel>>24)&0xFF) == 0) { + return false; + } + } + return true; + } + + + public static int computeDepth(ColourDepthTextureData texture, int mode) { + final var colourData = texture.colour(); + final var depthData = texture.depth(); + for (int i = 0; i < colourData.length; i++) { + if ((colourData[0]&0xFF)==0) { + continue; + } + int depth = depthData[0]&0xffffff; + } + return 0; + } +} diff --git a/src/main/java/me/cortex/zenith/client/core/other/BiomeColour.java b/src/main/java/me/cortex/zenith/client/core/other/BiomeColour.java deleted file mode 100644 index 118ca331..00000000 --- a/src/main/java/me/cortex/zenith/client/core/other/BiomeColour.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.cortex.zenith.client.core.other; - -public record BiomeColour(int id, int foliageColour, int waterColour) { -} diff --git a/src/main/java/me/cortex/zenith/client/core/other/BlockStateColour.java b/src/main/java/me/cortex/zenith/client/core/other/BlockStateColour.java deleted file mode 100644 index 12310827..00000000 --- a/src/main/java/me/cortex/zenith/client/core/other/BlockStateColour.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.cortex.zenith.client.core.other; - -public record BlockStateColour(int id, int biomeTintMsk, int[] faceColours) { -} diff --git a/src/main/java/me/cortex/zenith/client/core/other/ColourResolver.java b/src/main/java/me/cortex/zenith/client/core/other/ColourResolver.java deleted file mode 100644 index a3ace29b..00000000 --- a/src/main/java/me/cortex/zenith/client/core/other/ColourResolver.java +++ /dev/null @@ -1,138 +0,0 @@ -package me.cortex.zenith.client.core.other; - -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.model.BakedQuad; -import net.minecraft.client.texture.NativeImage; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.random.LocalRandom; - -public class ColourResolver { - //TODO: sample from multiple random values and avg it - public static int[] resolveColour(BlockState state) { - return resolveColour(state, 1234567890L); - } - - //The way this works is it takes the and computes its colour, it then computes the area of the quad and the normal direction - // it adds each area and colour to a per direcition colour - // for non specific axis dimensions it takes the normal of each quad computes the dot between it and each of the directions - // and averages that - // if the colour doesnt exist for a specific axis set it to the average of the other axis and or make it translucent - - //TODO: fixme: finish - public static int[] resolveColour(BlockState state, long randomValue) { - if (state == Blocks.AIR.getDefaultState()) { - return new int[6]; - } - int[][] builder = new int[6][5]; - var random = new LocalRandom(randomValue); - if (state.getFluidState().isEmpty()) { - for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) { - var quads = MinecraftClient.getInstance() - .getBakedModelManager() - .getBlockModels() - .getModel(state) - .getQuads(state, direction, random); - for (var quad : quads) { - long weightColour = resolveQuadColour(quad); - int colour = (int) weightColour; - int weight = (int) (weightColour>>32); - if (direction == null) { - //TODO: apply normal multiplication to weight - for (int i = 0; i < 6; i++) { - builder[i][0] += weight; - builder[i][4] += weight * ((colour>>>24)&0xFF); - builder[i][3] += weight * ((colour>>>16)&0xFF); - builder[i][2] += weight * ((colour>>>8)&0xFF); - builder[i][1] += weight * ((colour>>>0)&0xFF); - } - } else { - builder[direction.getId()][0] += weight; - builder[direction.getId()][4] += weight*((colour>>>24)&0xFF); - builder[direction.getId()][3] += weight*((colour>>>16)&0xFF); - builder[direction.getId()][2] += weight*((colour>>>8)&0xFF); - builder[direction.getId()][1] += weight*((colour>>>0)&0xFF); - } - } - } - } else { - //TODO FIXME: need to account for both the fluid and block state at the same time - //FIXME: make it not hacky and use the fluid handler thing from fabric - - long weightColour = resolveNI(MinecraftClient.getInstance().getBakedModelManager().getBlockModels().getModelParticleSprite(state).getContents().image); - for (int i = 0; i < 6; i++) { - builder[i][0] = 1; - builder[i][1] += (weightColour>>0)&0xFF; - builder[i][2] += (weightColour>>8)&0xFF; - builder[i][3] += (weightColour>>16)&0xFF; - builder[i][4] += (weightColour>>24)&0xFF; - } - } - - int[] out = new int[6]; - for (int i = 0; i < 6; i++) { - int c = builder[i][0]; - if (c == 0) { - continue; - } - int r = builder[i][4]/c; - int g = builder[i][3]/c; - int b = builder[i][2]/c; - int a = builder[i][1]/c; - out[i] = (r<<24)|(g<<16)|(b<<8)|a; - } - return out; - } - - private static long resolveQuadColour(BakedQuad quad) { - return resolveNI(quad.getSprite().getContents().image); - } - - private static long resolveNI(NativeImage image) { - int r = 0; - int g = 0; - int b = 0; - int a = 0; - int count = 0; - for (int y = 0; y < image.getHeight(); y++) { - for (int x = 0; x < image.getWidth(); x++) { - int colour = image.getColor(x, y); - if (((colour >>> 24)&0xFF) == 0) { - continue; - } - r += (colour >>> 0) & 0xFF; - g += (colour >>> 8) & 0xFF; - b += (colour >>> 16) & 0xFF; - a += (colour >>> 24) & 0xFF; - count++; - } - } - if (count == 0) { - return 0; - } - - r /= count; - g /= count; - b /= count; - a /= count; - - int colour = (r<<24)|(g<<16)|(b<<8)|a; - - return Integer.toUnsignedLong(colour)|(((long)count)<<32); - } - - - public static long resolveBiomeColour(String biomeId) { - var biome = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(new Identifier(biomeId)); - if (biome == null) { - System.err.println("Biome: " + biomeId + " doesnt exist in registry!"); - return 0; - } - int ARGBFoliage = biome.getFoliageColor(); - int ARGBWater = biome.getWaterColor(); - return Integer.toUnsignedLong(((ARGBFoliage&0xFFFFFF)<<8)|(ARGBFoliage>>>24)) | (Integer.toUnsignedLong(((ARGBWater&0xFFFFFF)<<8)|(ARGBWater>>>24))<<32); - } -} diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index 01b98ed0..91a208d3 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -4,10 +4,9 @@ package me.cortex.zenith.client.core.rendering; // could maybe tosomething else import me.cortex.zenith.client.core.gl.GlBuffer; +import me.cortex.zenith.client.core.model.ModelManager; import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.util.UploadStream; -import me.cortex.zenith.client.core.other.BiomeColour; -import me.cortex.zenith.client.core.other.BlockStateColour; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; @@ -16,7 +15,6 @@ import org.joml.FrustumIntersection; import org.lwjgl.system.MemoryUtil; import java.util.List; -import java.util.concurrent.ConcurrentLinkedDeque; import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect; import static org.lwjgl.opengl.GL30.*; @@ -35,14 +33,9 @@ public abstract class AbstractFarWorldRenderer { protected final GlBuffer uniformBuffer; protected final GeometryManager geometry; - - private final ConcurrentLinkedDeque stateUpdateQueue = new ConcurrentLinkedDeque<>(); - private final ConcurrentLinkedDeque biomeUpdateQueue = new ConcurrentLinkedDeque<>(); - protected final GlBuffer stateDataBuffer; - protected final GlBuffer biomeDataBuffer; + protected final ModelManager models; protected final GlBuffer lightDataBuffer; - //Current camera base level section position protected int sx; protected int sy; @@ -51,12 +44,10 @@ public abstract class AbstractFarWorldRenderer { protected FrustumIntersection frustum; public AbstractFarWorldRenderer(int geometrySize, int maxSections) { - this.uniformBuffer = new GlBuffer(1024, 0); - //TODO: make these both dynamically sized - this.stateDataBuffer = new GlBuffer((1<<16)*28, 0);//Capacity for 1<<16 entries - this.biomeDataBuffer = new GlBuffer(512*4*2, 0);//capacity for 1<<9 entries - this.lightDataBuffer = new GlBuffer(256*4, 0);//256 of uint + this.uniformBuffer = new GlBuffer(1024); + this.lightDataBuffer = new GlBuffer(256*4);//256 of uint this.geometry = new GeometryManager(geometrySize*8L, maxSections); + this.models = new ModelManager(); } protected abstract void setupVao(); @@ -89,36 +80,10 @@ public abstract class AbstractFarWorldRenderer { //Upload any new geometry this.geometry.uploadResults(); - - //Upload any block state changes - while (!this.stateUpdateQueue.isEmpty()) { - var stateUpdate = this.stateUpdateQueue.pop(); - long ptr = UploadStream.INSTANCE.upload(this.stateDataBuffer, stateUpdate.id()*28L, 28); - MemoryUtil.memPutInt(ptr, stateUpdate.biomeTintMsk()); ptr+=4; - for (int faceColour : stateUpdate.faceColours()) { - MemoryUtil.memPutInt(ptr, faceColour); ptr+=4; - } - } - - //Upload any biome changes - while (!this.biomeUpdateQueue.isEmpty()) { - var biomeUpdate = this.biomeUpdateQueue.pop(); - long ptr = UploadStream.INSTANCE.upload(this.biomeDataBuffer, biomeUpdate.id()*8L, 8); - MemoryUtil.memPutInt(ptr, biomeUpdate.foliageColour()); ptr+=4; - MemoryUtil.memPutInt(ptr, biomeUpdate.waterColour()); ptr+=4; - } } public abstract void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz); - public void enqueueUpdate(BlockStateColour stateColour) { - this.stateUpdateQueue.add(stateColour); - } - - public void enqueueUpdate(BiomeColour biomeColour) { - this.biomeUpdateQueue.add(biomeColour); - } - public void enqueueResult(BuiltSectionGeometry result) { this.geometry.enqueueResult(result); } @@ -129,10 +94,13 @@ public abstract class AbstractFarWorldRenderer { public void shutdown() { glDeleteVertexArrays(this.vao); + this.models.free(); this.geometry.free(); this.uniformBuffer.free(); - this.stateDataBuffer.free(); - this.biomeDataBuffer.free(); this.lightDataBuffer.free(); } + + public ModelManager getModelManager() { + return this.models; + } } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index e9b68c72..07cc84f1 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -43,7 +43,7 @@ public class GeometryManager { public GeometryManager(long geometryBufferSize, int maxSections) { - this.sectionMetaBuffer = new GlBuffer(((long) maxSections) * SECTION_METADATA_SIZE, 0); + this.sectionMetaBuffer = new GlBuffer(((long) maxSections) * SECTION_METADATA_SIZE); this.geometryBuffer = new BufferArena(geometryBufferSize, 8); this.pos2id.defaultReturnValue(-1); } 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 9be5ff4d..5d6ff069 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 @@ -50,10 +50,10 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { public Gl46FarWorldRenderer(int geometryBuffer, int maxSections) { super(geometryBuffer, maxSections); - glCommandBuffer = new GlBuffer(maxSections*5L*4, 0); - glVisibilityBuffer = new GlBuffer(maxSections*4L, 0); - glClearNamedBufferData(glCommandBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); - glClearNamedBufferData(glVisibilityBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); + this.glCommandBuffer = new GlBuffer(maxSections*5L*4); + this.glVisibilityBuffer = new GlBuffer(maxSections*4L); + glClearNamedBufferData(this.glCommandBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); + glClearNamedBufferData(this.glVisibilityBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); setupVao(); } @@ -67,9 +67,8 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.glCommandBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.geometry.metaId()); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.glVisibilityBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.stateDataBuffer.id);//State LUT - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.biomeDataBuffer.id);//Biome LUT - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.lightDataBuffer.id);//Lighting LUT + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.models.getBufferId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.lightDataBuffer.id);//Lighting LUT glBindVertexArray(0); } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/ModelManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/ModelManager.java deleted file mode 100644 index 9525432a..00000000 --- a/src/main/java/me/cortex/zenith/client/core/rendering/ModelManager.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.cortex.zenith.client.core.rendering; - -//Manages the storage and updating of model states, textures and colours -public class ModelManager { -} diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java deleted file mode 100644 index 0504182a..00000000 --- a/src/main/java/me/cortex/zenith/client/core/rendering/NvFarWorldRenderer.java +++ /dev/null @@ -1,55 +0,0 @@ -package me.cortex.zenith.client.core.rendering; - -import me.cortex.zenith.client.core.gl.shader.Shader; -import me.cortex.zenith.client.core.gl.shader.ShaderType; -import me.cortex.zenith.client.core.rendering.util.UploadStream; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.util.math.MatrixStack; - -import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect; -import static org.lwjgl.opengl.GL30.glBindVertexArray; -import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; - -//TODO: make this a 2 phase culling system -// first phase renders the terrain, in the terrain task shader it also checks if the section was not visible in the frustum but now is -// and then renders it and marks it as being in the frustum -public class NvFarWorldRenderer extends AbstractFarWorldRenderer { - private final Shader primaryTerrainRaster = Shader.make() - .add(ShaderType.TASK, "voxelmon:lod/nvmesh/primary.task") - .add(ShaderType.MESH, "voxelmon:lod/nvmesh/primary.mesh") - .add(ShaderType.FRAGMENT, "voxelmon:lod/nvmesh/primary.frag") - .compile(); - - public NvFarWorldRenderer(int geometrySize, int maxSections) { - super(geometrySize, maxSections); - } - - @Override - protected void setupVao() { - - } - - @Override - public void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz) { - if (this.geometry.getSectionCount() == 0) { - return; - } - RenderLayer.getCutoutMipped().startDrawing(); - - UploadStream.INSTANCE.commit(); - - glBindVertexArray(this.vao); - this.primaryTerrainRaster.bind(); - glDrawMeshTasksNV(0, this.geometry.getSectionCount()); - glBindVertexArray(0); - - - RenderLayer.getCutoutMipped().endDrawing(); - } - - @Override - public void shutdown() { - super.shutdown(); - - } -} diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/SharedIndexBuffer.java b/src/main/java/me/cortex/zenith/client/core/rendering/SharedIndexBuffer.java index f3c07a07..201b0d2c 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/SharedIndexBuffer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/SharedIndexBuffer.java @@ -14,7 +14,7 @@ public class SharedIndexBuffer { private final GlBuffer indexBuffer; public SharedIndexBuffer() { - this.indexBuffer = new GlBuffer((1<<16)*6*2 + 6*2*3, 0); + this.indexBuffer = new GlBuffer((1<<16)*6*2 + 6*2*3); var quadIndexBuff = IndexUtil.generateQuadIndicesShort(16380); var cubeBuff = generateCubeIndexBuffer(); diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/util/BufferArena.java b/src/main/java/me/cortex/zenith/client/core/rendering/util/BufferArena.java index 6f31f977..d03c1c71 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/util/BufferArena.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/util/BufferArena.java @@ -18,7 +18,7 @@ public class BufferArena { } this.size = capacity; this.elementSize = elementSize; - this.buffer = new GlBuffer(capacity, 0); + this.buffer = new GlBuffer(capacity); this.allocationMap.setLimit(capacity/elementSize); } diff --git a/src/main/java/me/cortex/zenith/client/core/util/DebugUtil.java b/src/main/java/me/cortex/zenith/client/core/util/DebugUtil.java index 30d3bfef..751c0554 100644 --- a/src/main/java/me/cortex/zenith/client/core/util/DebugUtil.java +++ b/src/main/java/me/cortex/zenith/client/core/util/DebugUtil.java @@ -7,7 +7,7 @@ import net.minecraft.util.math.Box; import org.joml.Matrix4f; public class DebugUtil { - private static Matrix4f positionMatrix = new Matrix4f().identity(); + public static Matrix4f positionMatrix = new Matrix4f().identity(); public static void setPositionMatrix(MatrixStack stack) { positionMatrix = new Matrix4f(stack.peek().getPositionMatrix()); } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index 4bf335e5..fdbd7d43 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -11,16 +11,10 @@ layout(binding = 0, std140) uniform SceneUniform { uint frameId; }; -struct State { - uint biomeTintMsk; +struct BlockModel { uint faceColours[6]; }; -struct Biome { - uint foliage; - uint water; -}; - struct SectionMeta { uvec4 header; uvec4 drawdata; @@ -57,15 +51,11 @@ layout(binding = 4, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer { uint visibilityData[]; }; -layout(binding = 5, std430) readonly restrict buffer StateBuffer { - State stateData[]; +layout(binding = 5, std430) readonly restrict buffer ModelBuffer { + BlockModel modelData[]; }; -layout(binding = 6, std430) readonly restrict buffer BiomeBuffer { - Biome biomeData[]; -}; - -layout(binding = 7, std430) readonly restrict buffer LightingBuffer { +layout(binding = 6, std430) readonly restrict buffer LightingBuffer { uint lightData[]; }; diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index 22c061a2..1c815da5 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -63,20 +63,11 @@ void main() { uint stateId = extractStateId(quad); uint biomeId = extractBiomeId(quad); - State stateInfo = stateData[stateId]; + BlockModel stateInfo = modelData[stateId]; colour = uint2vec4RGBA(stateInfo.faceColours[face]); colour *= getLighting(extractLightId(quad)); - if (((stateInfo.biomeTintMsk>>face)&1) == 1) { - vec4 biomeColour = uint2vec4RGBA(biomeData[biomeId].foliage); - colour *= biomeColour; - } - //Apply water tint - if (((stateInfo.biomeTintMsk>>6)&1) == 1) { - colour *= vec4(0.247, 0.463, 0.894, 1); - } - //Apply face tint if (face == 0) { colour.xyz *= vec3(0.75, 0.75, 0.75); From c2f771244886d9d893e14ff1e3db66b1ccb1b73b Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 25 Jan 2024 08:44:50 +1000 Subject: [PATCH 04/28] More work on texturing and planning --- .../cortex/zenith/client/core/VoxelCore.java | 4 +- .../client/core/model/ModelManager.java | 42 +++++++++++++++++++ .../client/core/model/ModelTextureBakery.java | 4 +- .../client/core/model/TextureUtils.java | 41 +++++++++++++----- src/main/resources/zenith.accesswidener | 3 +- 5 files changed, 79 insertions(+), 15 deletions(-) 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 312163aa..9d713523 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -12,6 +12,7 @@ import me.cortex.zenith.common.world.storage.FragmentedStorageBackendAdaptor; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.block.CropBlock; +import net.minecraft.block.SnowBlock; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; import net.minecraft.client.util.math.MatrixStack; @@ -107,7 +108,8 @@ public class VoxelCore { DebugUtil.setPositionMatrix(matrices); matrices.pop(); - renderer.getModelManager().updateEntry(0, Blocks.FERN.getDefaultState()); + //this.renderer.getModelManager().updateEntry(0, Blocks.COMPARATOR.getDefaultState()); + this.renderer.getModelManager().updateEntry(0, Blocks.OAK_LEAVES.getDefaultState()); //int boundFB = GlStateManager.getBoundFramebuffer(); //this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); 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 04f93ca8..179c408a 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 @@ -2,6 +2,9 @@ package me.cortex.zenith.client.core.model; import me.cortex.zenith.client.core.gl.GlBuffer; import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.registry.Registries; //Manages the storage and updating of model states, textures and colours @@ -10,6 +13,29 @@ public class ModelManager { public static final int MODEL_SIZE = 64; private final ModelTextureBakery bakery = new ModelTextureBakery(16, 16); private final GlBuffer modelBuffer; + + + //The Meta-cache contains critical information needed for meshing, colour provider bit, per-face = is empty, has alpha, is solid, full width, full height + // alpha means that some pixels have alpha values and belong in the translucent rendering layer, + // is empty means that the face is air/shouldent be rendered as there is nothing there + // is solid means that every pixel is fully opaque + // full width, height, is if the blockmodel dimentions occupy a full block, e.g. comparator, some faces do some dont and some only in a specific axis + + //FIXME: the issue is e.g. leaves are translucent but the alpha value is used to colour the leaves, so a block can have alpha but still be only made up of transparent or opaque pixels + // will need to find a way to send this info to the shader via the material, if it is in the opaque phase render as transparent with blending shiz + + //TODO: ADD an occlusion mask that can be queried (16x16 pixels takes up 4 longs) this mask shows what pixels are exactly occluded at the edge of the block + // so that full block occlusion can work nicely + + + //TODO: what might work maybe, is that all the transparent pixels should be set to the average of the other pixels + // that way the block is always "fully occluding" (if the block model doesnt cover the entire thing), maybe + // this has some issues with quad merging + //TODO: ACTUALLY, full out all the transparent pixels that are _within_ the bounding box of the model + // this will mean that when quad merging and rendering, the transparent pixels of the block where there shouldent be + // might still work??? + + // this has an issue with scaffolding i believe tho, so maybe make it a probability to render??? idk private final long[] metadataCache; public ModelManager() { @@ -18,8 +44,24 @@ public class ModelManager { } public void updateEntry(int id, BlockState blockState) { + var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock())); + //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver var textureData = this.bakery.renderFaces(blockState, 123456); + int depth = TextureUtils.computeDepth(textureData[0], TextureUtils.DEPTH_MODE_AVG); + + int aaaa = 1; + + + //Model data contains, the quad size and offset of each face and whether the face needs to be resolved with a colour modifier + // sourced from the quad data and reverse indexed into the section data (meaning there will be a maxiumum number of colours) + // can possibly also put texture coords if needed + //Supplying the quad size and offset means that much more accurate rendering quads are rendered and stuff like snow layers will look correct + // the size/offset of the corners of the quads will only be applied to the corner quads of the merged quads with adjustment to the UV to ensure textures are not alignned weirdly + // the other axis offset is always applied and means that the models will look more correct even when merged into a large quad (which will have alot of overdraw) + //TODO: need to make an option for like leaves to be fully opaque as by default they are not!!!! + + } public long getModelMetadata(int id) { 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 b2093013..edcf1e3e 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 @@ -56,7 +56,7 @@ public class ModelTextureBakery { private static void addView(float pitch, float yaw) { var stack = new MatrixStack(); - stack.translate(0.5f,0.5f,0); + stack.translate(0.5f,0.5f,0.5f); stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch)); stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); stack.translate(-0.5f,-0.5f,-0.5f); @@ -106,7 +106,7 @@ public class ModelTextureBakery { RenderSystem.setProjectionMatrix(oldProjection, VertexSorter.BY_DISTANCE); glBindFramebuffer(GL_FRAMEBUFFER, oldFB); GL11C.glViewport(GlStateManager.Viewport.getX(), GlStateManager.Viewport.getY(), GlStateManager.Viewport.getWidth(), GlStateManager.Viewport.getHeight()); - + glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,0,0,256,256, GL_COLOR_BUFFER_BIT, GL_NEAREST); return faces; } diff --git a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java index 4d574e6a..84de9b92 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java +++ b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java @@ -1,5 +1,7 @@ package me.cortex.zenith.client.core.model; +import java.util.Map; + //Texturing utils to manipulate data from the model bakery public class TextureUtils { //Returns if any pixels are not fully transparent or fully translucent @@ -22,25 +24,42 @@ public class TextureUtils { return true; } - public static boolean isFullyCovered(ColourDepthTextureData texture) { - for (int pixel : texture.colour()) { - if (((pixel>>24)&0xFF) == 0) { - return false; - } - } - return true; - } - + public static final int DEPTH_MODE_AVG = 1; + public static final int DEPTH_MODE_MAX = 2; + public static final int DEPTH_MODE_MIN = 3; + //Computes depth info based on written pixel data public static int computeDepth(ColourDepthTextureData texture, int mode) { final var colourData = texture.colour(); final var depthData = texture.depth(); + long a = 0; + long b = 0; + if (mode == DEPTH_MODE_MIN) { + a = Long.MAX_VALUE; + } for (int i = 0; i < colourData.length; i++) { if ((colourData[0]&0xFF)==0) { continue; } - int depth = depthData[0]&0xffffff; + int depth = depthData[0]>>>8; + if (mode == DEPTH_MODE_AVG) { + a++; + b += depth; + } else if (mode == DEPTH_MODE_MAX) { + a = Math.max(a, depth); + } else if (mode == DEPTH_MODE_MIN) { + a = Math.min(a, depth); + } } - return 0; + + if (mode == DEPTH_MODE_AVG) { + if (a == 0) { + return -1; + } + return (int) (b/a); + } else if (mode == DEPTH_MODE_MAX || mode == DEPTH_MODE_MIN) { + return (int) a; + } + throw new IllegalArgumentException(); } } diff --git a/src/main/resources/zenith.accesswidener b/src/main/resources/zenith.accesswidener index 02186f2a..7d5ae33e 100644 --- a/src/main/resources/zenith.accesswidener +++ b/src/main/resources/zenith.accesswidener @@ -2,4 +2,5 @@ accessWidener v1 named accessible field net/minecraft/client/texture/SpriteContents image Lnet/minecraft/client/texture/NativeImage; accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection; -accessible field net/minecraft/client/render/LightmapTextureManager texture Lnet/minecraft/client/texture/NativeImageBackedTexture; \ No newline at end of file +accessible field net/minecraft/client/render/LightmapTextureManager texture Lnet/minecraft/client/texture/NativeImageBackedTexture; +accessible field net/minecraft/client/color/block/BlockColors providers Lnet/minecraft/util/collection/IdList; \ No newline at end of file From a391f34ab72f7df3b486f854b9d446949c06eb6c Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:46:16 +1000 Subject: [PATCH 05/28] Textures almost working, fixed up the mesher being a dumbass --- .../cortex/zenith/client/core/VoxelCore.java | 8 +-- .../client/core/model/ModelManager.java | 59 ++++++++++++++++++- .../rendering/AbstractFarWorldRenderer.java | 2 +- .../core/rendering/Gl46FarWorldRenderer.java | 40 +++++++------ .../zenith/client/core/util/Mesher2D.java | 41 ++++++++----- .../zenith/shaders/lod/gl46/bindings.glsl | 4 ++ .../assets/zenith/shaders/lod/gl46/quads.frag | 7 ++- .../assets/zenith/shaders/lod/gl46/quads.vert | 18 ++---- 8 files changed, 121 insertions(+), 58 deletions(-) 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 9d713523..bc2bf13a 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -99,7 +99,7 @@ public class VoxelCore { this.firstTime = false; } this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); - //this.renderer.setupRender(frustum, camera); + this.renderer.setupRender(frustum, camera); } public void renderOpaque(MatrixStack matrices, double cameraX, double cameraY, double cameraZ) { @@ -109,7 +109,7 @@ public class VoxelCore { matrices.pop(); //this.renderer.getModelManager().updateEntry(0, Blocks.COMPARATOR.getDefaultState()); - this.renderer.getModelManager().updateEntry(0, Blocks.OAK_LEAVES.getDefaultState()); + //this.renderer.getModelManager().updateEntry(0, Blocks.OAK_LEAVES.getDefaultState()); //int boundFB = GlStateManager.getBoundFramebuffer(); //this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); @@ -123,8 +123,8 @@ public class VoxelCore { //TODO: have the renderer also render a bounding full face just like black boarders around lvl 0 // this is cause the terrain might not exist and so all the caves are visible causing hell for the // occlusion culler - if (false) - this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ); + + this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ); //glBindFramebuffer(GL_FRAMEBUFFER, boundFB); 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 179c408a..2165e331 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 @@ -1,19 +1,40 @@ package me.cortex.zenith.client.core.model; +import com.mojang.blaze3d.platform.GlStateManager; import me.cortex.zenith.client.core.gl.GlBuffer; +import me.cortex.zenith.client.core.gl.GlTexture; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.color.block.BlockColors; import net.minecraft.registry.Registries; +import org.lwjgl.opengl.GL45C; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL11C.GL_NEAREST; +import static org.lwjgl.opengl.GL11C.GL_NEAREST_MIPMAP_LINEAR; +import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MAX_LOD; +import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MIN_LOD; +import static org.lwjgl.opengl.GL33.glDeleteSamplers; +import static org.lwjgl.opengl.GL33.glGenSamplers; +import static org.lwjgl.opengl.GL33C.glSamplerParameteri; +import static org.lwjgl.opengl.GL45C.glTextureSubImage2D; //Manages the storage and updating of model states, textures and colours //Also has a fast long[] based metadata lookup for when the terrain mesher needs to look up the face occlusion data + +//TODO: support more than 65535 states, what should actually happen is a blockstate is registered, the model data is generated, then compared +// to all other models already loaded, if it is a duplicate, create a mapping from the id to the already loaded id, this will help with meshing aswell +// as leaves and such will be able to be merged public class ModelManager { public static final int MODEL_SIZE = 64; - private final ModelTextureBakery bakery = new ModelTextureBakery(16, 16); + private final ModelTextureBakery bakery; private final GlBuffer modelBuffer; + private final GlTexture textures; + private final int blockSampler = glGenSamplers(); + //Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the + // section buffer reverse indexing //The Meta-cache contains critical information needed for meshing, colour provider bit, per-face = is empty, has alpha, is solid, full width, full height // alpha means that some pixels have alpha values and belong in the translucent rendering layer, @@ -38,9 +59,24 @@ public class ModelManager { // this has an issue with scaffolding i believe tho, so maybe make it a probability to render??? idk private final long[] metadataCache; - public ModelManager() { + //Provides a map from id -> model id as multiple ids might have the same internal model id + private final int[] idMappings; + private final int modelTextureSize; + + public ModelManager(int modelTextureSize) { + this.modelTextureSize = modelTextureSize; + this.bakery = new ModelTextureBakery(modelTextureSize, modelTextureSize); this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16)); + //TODO: figure out how to do mipping :blobfox_pineapple: + this.textures = new GlTexture().store(GL_RGBA8, 1, modelTextureSize*3*256,modelTextureSize*2*256); this.metadataCache = new long[1<<16]; + this.idMappings = new int[1<<16]; + + + glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); + glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_LOD, 0); + glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAX_LOD, 4); } public void updateEntry(int id, BlockState blockState) { @@ -64,6 +100,16 @@ public class ModelManager { } + private void putTextures(int id, ColourDepthTextureData[] textures) { + int X = (id&0xFF) * this.modelTextureSize*3; + int Y = ((id>>8)&0xFF) * this.modelTextureSize*2; + for (int subTex = 0; subTex < 6; subTex++) { + + + //glTextureSubImage2D(this.textures.id, 0, ); + } + } + public long getModelMetadata(int id) { return this.metadataCache[id]; } @@ -72,8 +118,17 @@ public class ModelManager { return this.modelBuffer.id; } + public int getTextureId() { + return this.textures.id; + } + public int getSamplerId() { + return this.blockSampler; + } + public void free() { this.bakery.free(); this.modelBuffer.free(); + this.textures.free(); + glDeleteSamplers(this.blockSampler); } } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index 91a208d3..b2b41dae 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -47,7 +47,7 @@ public abstract class AbstractFarWorldRenderer { this.uniformBuffer = new GlBuffer(1024); this.lightDataBuffer = new GlBuffer(256*4);//256 of uint this.geometry = new GeometryManager(geometrySize*8L, maxSections); - this.models = new ModelManager(); + this.models = new ModelManager(16); } protected abstract void setupVao(); 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 5d6ff069..2cd8b431 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; @@ -10,6 +11,8 @@ import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; import org.joml.Vector3f; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL45C; import org.lwjgl.system.MemoryUtil; import java.util.List; @@ -17,6 +20,7 @@ import java.util.List; import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT; +import static org.lwjgl.opengl.GL11.glGetInteger; import static org.lwjgl.opengl.GL30.glBindVertexArray; import static org.lwjgl.opengl.GL30C.GL_R8UI; import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; @@ -77,6 +81,8 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { return; } RenderLayer.getCutoutMipped().startDrawing(); + int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); + int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); //RenderSystem.enableBlend(); //RenderSystem.defaultBlendFunc(); @@ -85,32 +91,23 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { UploadStream.INSTANCE.commit(); glBindVertexArray(this.vao); + + + //Bind the texture atlas + glBindSampler(0, this.models.getSamplerId()); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this.models.getTextureId()); + + this.commandGen.bind(); glDispatchCompute((this.geometry.getSectionCount() + 127) / 128, 1, 1); glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT); this.lodShader.bind(); - if (false) {//Bloody intel gpus - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, 1000, 0); - - //int count = this.geometry.getSectionCount()/1000; - //for (int i = 0; i < 10; i++) { - // glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, i*1000L*20, 1000, 0); - //} - //int rem = this.geometry.getSectionCount() - (count*1000); - //if (rem != 0) { - // glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, count*1000L*20, rem, 0); - //} - - } else { - //TODO: swap to a multidraw indirect counted - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, this.geometry.getSectionCount(), 0); - } + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, this.geometry.getSectionCount(), 0); //ARBIndirectParameters.glMultiDrawElementsIndirectCountARB( glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); - //TODO: add gpu occlusion culling here (after the lod drawing) (maybe, finish the rest of the PoC first) - cullShader.bind(); glColorMask(false, false, false, false); @@ -124,8 +121,13 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glColorMask(true, true, true, true); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - glBindVertexArray(0); + //TODO: need to do temporal rasterization here + + glBindVertexArray(0); + glBindSampler(0, 0); + GL11C.glBindTexture(GL_TEXTURE_2D, oldBoundTexture); + glActiveTexture(oldActiveTexture); RenderLayer.getCutoutMipped().endDrawing(); } diff --git a/src/main/java/me/cortex/zenith/client/core/util/Mesher2D.java b/src/main/java/me/cortex/zenith/client/core/util/Mesher2D.java index 8977f476..08461b3b 100644 --- a/src/main/java/me/cortex/zenith/client/core/util/Mesher2D.java +++ b/src/main/java/me/cortex/zenith/client/core/util/Mesher2D.java @@ -81,7 +81,7 @@ public class Mesher2D { while (ex || ez) { //Expand in the x direction if (ex) { - if (endX + 1 > this.maxSize || endX+1 == (1 << this.size) - 1) { + if (endX - x >= this.maxSize || endX >= (1 << this.size) - 1) { ex = false; } } @@ -96,7 +96,7 @@ public class Mesher2D { endX++; } if (ez) { - if (endZ + 1 > this.maxSize || endZ+1 == (1<= this.maxSize || endZ >= (1< #import -layout(location = 0) out flat vec4 colour; +layout(location = 0) out vec2 uv; uint extractLodLevel() { return uint(gl_BaseInstance)>>29; @@ -41,7 +41,8 @@ void main() { cornerIdx ^= 1; } - ivec2 size = extractSize(quad) * ivec2((cornerIdx>>1)&1, cornerIdx&1) * (1<>1)&1, cornerIdx&1); + ivec2 size = sizePreLod * (1<>1)&1)+10),1); //uint i = uint(quad>>32); From 77b5c9173713ac5cc4dd7db8764834e0f34821d7 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 25 Jan 2024 14:36:53 +1000 Subject: [PATCH 06/28] Textures work --- .../cortex/zenith/client/core/VoxelCore.java | 7 +++++ .../client/core/model/ModelManager.java | 12 ++++++-- .../client/core/model/ModelTextureBakery.java | 29 ++++++++++--------- .../core/rendering/Gl46FarWorldRenderer.java | 4 +++ .../assets/zenith/shaders/lod/gl46/quads.frag | 8 +++-- .../assets/zenith/shaders/lod/gl46/quads.vert | 17 +++++++++++ 6 files changed, 60 insertions(+), 17 deletions(-) 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 bc2bf13a..1a798377 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -84,6 +84,12 @@ public class VoxelCore { this.world.getMapper().setCallbacks(a->{}, a->{}); System.out.println("Voxel core initialized"); + + + for (var state : this.world.getMapper().getStateEntries()) { + this.renderer.getModelManager().updateEntry(state.id, state.state); + } + //this.renderer.getModelManager().updateEntry(0, Blocks.GRASS_BLOCK.getDefaultState()); } @@ -107,6 +113,7 @@ public class VoxelCore { matrices.translate(-cameraX, -cameraY, -cameraZ); DebugUtil.setPositionMatrix(matrices); matrices.pop(); + //this.renderer.getModelManager().updateEntry(0, Blocks.DIRT_PATH.getDefaultState()); //this.renderer.getModelManager().updateEntry(0, Blocks.COMPARATOR.getDefaultState()); //this.renderer.getModelManager().updateEntry(0, Blocks.OAK_LEAVES.getDefaultState()); 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 2165e331..9fd5a253 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 @@ -1,5 +1,6 @@ package me.cortex.zenith.client.core.model; +import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.platform.GlStateManager; import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.gl.GlTexture; @@ -88,6 +89,8 @@ public class ModelManager { int aaaa = 1; + this.putTextures(id, textureData); + //Model data contains, the quad size and offset of each face and whether the face needs to be resolved with a colour modifier // sourced from the quad data and reverse indexed into the section data (meaning there will be a maxiumum number of colours) @@ -104,9 +107,14 @@ public class ModelManager { int X = (id&0xFF) * this.modelTextureSize*3; int Y = ((id>>8)&0xFF) * this.modelTextureSize*2; for (int subTex = 0; subTex < 6; subTex++) { + int x = X + (subTex%3)*this.modelTextureSize; + int y = Y + (subTex/3)*this.modelTextureSize; - - //glTextureSubImage2D(this.textures.id, 0, ); + GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); + glTextureSubImage2D(this.textures.id, 0, x, y, this.modelTextureSize, this.modelTextureSize, GL_RGBA, GL_UNSIGNED_BYTE, textures[subTex].colour()); } } 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 edcf1e3e..3987c7db 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 @@ -37,13 +37,13 @@ public class ModelTextureBakery { private static final List FACE_VIEWS = new ArrayList<>(); static { - addView(-90,0);//Direction.DOWN - addView(90,0);//Direction.UP - addView(0,180);//Direction.NORTH - addView(0,0);//Direction.SOUTH + addView(-90,0, 0);//Direction.DOWN + addView(90,0, 0);//Direction.UP + addView(0,180, 0);//Direction.NORTH + addView(0,0, 0);//Direction.SOUTH //TODO: check these arnt the wrong way round - addView(0,90);//Direction.EAST - addView(0,270);//Direction.WEST + addView(0,90, -90);//Direction.EAST + addView(0,270, -90);//Direction.WEST } public ModelTextureBakery(int width, int height) { @@ -54,9 +54,10 @@ public class ModelTextureBakery { this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify(); } - private static void addView(float pitch, float yaw) { + private static void addView(float pitch, float yaw, float rotation) { var stack = new MatrixStack(); stack.translate(0.5f,0.5f,0.5f); + stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotation)); stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch)); stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); stack.translate(-0.5f,-0.5f,-0.5f); @@ -80,6 +81,11 @@ public class ModelTextureBakery { glBindFramebuffer(GL_FRAMEBUFFER, this.framebuffer.id); + var renderLayer = RenderLayers.getBlockLayer(state); + if (renderLayer == RenderLayer.getTranslucent()) { + //TODO: TRANSLUCENT, must sort the quad first + } + renderLayer.startDrawing(); RenderSystem.depthMask(true); RenderSystem.enableBlend(); RenderSystem.enableDepthTest(); @@ -93,20 +99,17 @@ public class ModelTextureBakery { //TODO: render fluid } - var renderLayer = RenderLayers.getBlockLayer(state); - if (renderLayer == RenderLayer.getTranslucent()) { - //TODO: TRANSLUCENT, must sort the quad first - } - 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); } + renderLayer.endDrawing(); + RenderSystem.setProjectionMatrix(oldProjection, VertexSorter.BY_DISTANCE); glBindFramebuffer(GL_FRAMEBUFFER, oldFB); GL11C.glViewport(GlStateManager.Viewport.getX(), GlStateManager.Viewport.getY(), GlStateManager.Viewport.getWidth(), GlStateManager.Viewport.getHeight()); - glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,0,0,256,256, GL_COLOR_BUFFER_BIT, GL_NEAREST); + //glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,0,0,256,256, GL_COLOR_BUFFER_BIT, GL_NEAREST); return faces; } 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 2cd8b431..cf9f5bb7 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 @@ -7,6 +7,7 @@ import me.cortex.zenith.client.core.gl.shader.Shader; import me.cortex.zenith.client.core.gl.shader.ShaderType; import me.cortex.zenith.client.core.rendering.util.UploadStream; import me.cortex.zenith.client.mixin.joml.AccessFrustumIntersection; +import net.minecraft.block.Blocks; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; @@ -80,6 +81,9 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { if (this.geometry.getSectionCount() == 0) { return; } + + //this.getModelManager().updateEntry(this.frameId%(1<<15), Blocks.STONE.getDefaultState()); + RenderLayer.getCutoutMipped().startDrawing(); int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag index c27e93b4..a2a4f69c 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag @@ -1,9 +1,13 @@ #version 460 core layout(binding = 0) uniform sampler2D blockModelAtlas; + layout(location = 0) in vec2 uv; +layout(location = 1) in flat vec2 baseUV; +layout(location = 2) in flat vec4 colourTinting; + layout(location = 0) out vec4 outColour; void main() { - vec2 uv = mod(uv, vec2(1)); + vec2 uv = mod(uv, vec2(1))*(1f/(vec2(3,2)*256f)); - outColour = texture(blockModelAtlas, uv); + outColour = texture(blockModelAtlas, uv + baseUV) * colourTinting; } \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index 3110efc4..f71f3a0d 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -5,6 +5,8 @@ #import layout(location = 0) out vec2 uv; +layout(location = 1) out flat vec2 baseUV; +layout(location = 2) out flat vec4 colourTinting; uint extractLodLevel() { return uint(gl_BaseInstance)>>29; @@ -66,6 +68,21 @@ void main() { uint biomeId = extractBiomeId(quad); uv = vec2(sizePreLod); + + vec2 modelUV = vec2(stateId&0xFF, (stateId>>8)&0xFF)*(1f/(256f)); + + //TODO: make the face orientated by 2x3 so that division is not a integer div and modulo isnt needed + // as these are very slow ops + baseUV = modelUV + (vec2(face%3, face/3) * (1f/(vec2(3,2)*256f))); + + colourTinting = getLighting(extractLightId(quad)); + //Apply face tint + if (face == 0) { + colourTinting.xyz *= vec3(0.75, 0.75, 0.75); + } else if (face != 1) { + colourTinting.xyz *= vec3((float(face-2)/4)*0.6 + 0.4); + } + } //gl_Position = MVP * vec4(vec3(((cornerIdx)&1)+10,10,((cornerIdx>>1)&1)+10),1); //uint i = uint(quad>>32); From dd401ed15b00e5173388053e53d0b48bca8e6f2e Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:01:25 +1000 Subject: [PATCH 07/28] Began rewriting of the render build subsystem --- .../client/core/model/ModelManager.java | 3 + .../rendering/AbstractFarWorldRenderer.java | 3 +- .../core/rendering/GeometryManager.java | 13 +- .../client/core/rendering/RenderTracker.java | 23 +- .../core/rendering/building/BuiltSection.java | 29 ++ .../building/BuiltSectionGeometry.java | 31 +- .../rendering/building/RenderDataFactory.java | 308 +--------------- .../building/RenderDataFactoryOld.java | 335 ++++++++++++++++++ .../building/RenderGenerationService.java | 8 +- 9 files changed, 405 insertions(+), 348 deletions(-) create mode 100644 src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java create mode 100644 src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactoryOld.java 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 9fd5a253..956c330b 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 @@ -37,6 +37,9 @@ public class ModelManager { //Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the // section buffer reverse indexing + //model data also contains if a face should be randomly rotated,flipped etc to get rid of moire effect + // this would be done in the fragment shader + //The Meta-cache contains critical information needed for meshing, colour provider bit, per-face = is empty, has alpha, is solid, full width, full height // alpha means that some pixels have alpha values and belong in the translucent rendering layer, // is empty means that the face is air/shouldent be rendered as there is nothing there diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index b2b41dae..e3146be5 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -5,6 +5,7 @@ package me.cortex.zenith.client.core.rendering; import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.model.ModelManager; +import me.cortex.zenith.client.core.rendering.building.BuiltSection; import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.util.UploadStream; import net.minecraft.client.MinecraftClient; @@ -84,7 +85,7 @@ public abstract class AbstractFarWorldRenderer { public abstract void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz); - public void enqueueResult(BuiltSectionGeometry result) { + public void enqueueResult(BuiltSection result) { this.geometry.enqueueResult(result); } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index 07cc84f1..79bfa9a5 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.cortex.zenith.client.core.gl.GlBuffer; +import me.cortex.zenith.client.core.rendering.building.BuiltSection; import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.util.BufferArena; import me.cortex.zenith.client.core.rendering.util.UploadStream; @@ -31,7 +32,7 @@ public class GeometryManager { } } - private final ConcurrentLinkedDeque buildResults = new ConcurrentLinkedDeque<>(); + private final ConcurrentLinkedDeque buildResults = new ConcurrentLinkedDeque<>(); private int sectionCount = 0; private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap(); @@ -48,15 +49,15 @@ public class GeometryManager { this.pos2id.defaultReturnValue(-1); } - public void enqueueResult(BuiltSectionGeometry sectionGeometry) { + public void enqueueResult(BuiltSection sectionGeometry) { this.buildResults.add(sectionGeometry); } - private SectionMeta createMeta(BuiltSectionGeometry geometry) { - long geometryPtr = this.geometryBuffer.upload(geometry.geometryBuffer); + private SectionMeta createMeta(BuiltSection geometry) { + long geometryPtr = this.geometryBuffer.upload(geometry.buffer); //TODO: support translucent geometry - return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.geometryBuffer.size/8), 0, -1,0, 0); + return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.buffer.size/8), 0, -1,0, 0); } private void freeMeta(SectionMeta meta) { @@ -71,7 +72,7 @@ public class GeometryManager { void uploadResults() { while (!this.buildResults.isEmpty()) { var result = this.buildResults.pop(); - boolean isDelete = result.geometryBuffer == null && result.translucentGeometryBuffer == null; + boolean isDelete = result.buffer == null && result.translucentGeometryBuffer == null; if (isDelete) { int id = -1; if ((id = this.pos2id.remove(result.position)) != -1) { diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java b/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java index bfe125ff..71606601 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java @@ -1,5 +1,6 @@ package me.cortex.zenith.client.core.rendering; +import me.cortex.zenith.client.core.rendering.building.BuiltSection; import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.building.RenderGenerationService; import me.cortex.zenith.common.world.WorldEngine; @@ -41,7 +42,7 @@ public class RenderTracker { //Removes a lvl 0 section from the world renderer public void remLvl0(int x, int y, int z) { this.activeSections.remove(WorldEngine.getWorldSectionId(0, x, y, z)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(0, x, y, z), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(0, x, y, z), null, null)); this.renderGen.removeTask(0, x, y, z); } @@ -63,14 +64,14 @@ public class RenderTracker { this.renderGen.enqueueTask(lvl, x, y, z, this::shouldStillBuild, this::getBuildFlagsOrAbort); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), null, null)); this.renderGen.removeTask(lvl-1, (x<<1), (y<<1), (z<<1)); @@ -95,7 +96,7 @@ public class RenderTracker { this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), O); this.activeSections.remove(WorldEngine.getWorldSectionId(lvl, x, y, z)); - this.renderer.enqueueResult(new BuiltSectionGeometry(lvl, x, y, z, null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z), null, null)); this.renderGen.removeTask(lvl, x, y, z); this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1), (z<<1), this::shouldStillBuild, this::getBuildFlagsOrAbort); @@ -134,7 +135,7 @@ public class RenderTracker { //called by the RenderGenerationService about built geometry, the RenderTracker checks if it can use the result (e.g. the LoD hasnt changed/still correct etc) // and dispatches it to the renderer // it also batch collects the geometry sections until all the geometry for an operation is collected, then it executes the operation, its removes flickering - public void processBuildResult(BuiltSectionGeometry section) { + public void processBuildResult(BuiltSection section) { //Check that we still want the section if (this.activeSections.containsKey(section.position)) { this.renderer.enqueueResult(section); diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java new file mode 100644 index 00000000..92c95990 --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java @@ -0,0 +1,29 @@ +package me.cortex.zenith.client.core.rendering.building; + +import java.util.Objects; + +//TODO: also have an AABB size stored +public final class BuiltSection { + public final long position; + public final BuiltSectionGeometry opaque; + public final BuiltSectionGeometry translucent; + + public BuiltSection(long position, BuiltSectionGeometry opaque, BuiltSectionGeometry translucent) { + this.position = position; + this.opaque = opaque; + this.translucent = translucent; + } + + public BuiltSection clone() { + return new BuiltSection(this.position, this.opaque != null ? this.opaque.clone() : null, this.translucent != null ? this.translucent.clone() : null); + } + + public void free() { + if (this.opaque != null) { + this.opaque.free(); + } + if (this.translucent != null) { + this.translucent.free(); + } + } +} diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java index 83112958..e9165c4d 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java @@ -1,32 +1,21 @@ package me.cortex.zenith.client.core.rendering.building; import me.cortex.zenith.common.util.MemoryBuffer; -import me.cortex.zenith.common.world.WorldEngine; -public class BuiltSectionGeometry { - public final long position; - public final MemoryBuffer geometryBuffer; - public final MemoryBuffer translucentGeometryBuffer; +import java.util.Arrays; - public BuiltSectionGeometry(int lvl, int x, int y, int z, MemoryBuffer geometryBuffer, MemoryBuffer translucentGeometryBuffer) { - this(WorldEngine.getWorldSectionId(lvl, x, y, z), geometryBuffer, translucentGeometryBuffer); - } - public BuiltSectionGeometry(long position, MemoryBuffer geometryBuffer, MemoryBuffer translucentGeometryBuffer) { - this.position = position; - this.geometryBuffer = geometryBuffer; - this.translucentGeometryBuffer = translucentGeometryBuffer; +/** + * @param startOffsets Will be converted to ending offsets when doing data computation + */ +public record BuiltSectionGeometry(MemoryBuffer buffer, short[] startOffsets) { + + public BuiltSectionGeometry clone() { + return new BuiltSectionGeometry(this.buffer != null ? this.buffer.copy() : null, Arrays.copyOf(this.startOffsets, this.startOffsets.length)); } public void free() { - if (this.geometryBuffer != null) { - this.geometryBuffer.free(); + if (this.buffer != null) { + this.buffer.free(); } - if (this.translucentGeometryBuffer != null) { - this.translucentGeometryBuffer.free(); - } - } - - public BuiltSectionGeometry clone() { - return new BuiltSectionGeometry(this.position, this.geometryBuffer!=null?this.geometryBuffer.copy():null, this.translucentGeometryBuffer!=null?this.translucentGeometryBuffer.copy():null); } } 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 720ae5d0..ab042db4 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 @@ -1,23 +1,15 @@ 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 RenderDataFactory { - private final Mesher2D mesher = new Mesher2D(5,15);//15 - private final LongArrayList outData = new LongArrayList(1000); private final WorldEngine world; + private final QuadEncoder encoder; private final long[] sectionCache = new long[32*32*32]; private final long[] connectedSectionCache = new long[32*32*32]; - private final QuadEncoder encoder; public RenderDataFactory(WorldEngine world) { this.world = world; this.encoder = new QuadEncoder(world.getMapper(), MinecraftClient.getInstance().getBlockColors(), MinecraftClient.getInstance().world); @@ -32,304 +24,10 @@ public class RenderDataFactory { //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 BuiltSectionGeometry 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); - //} - + public BuiltSection generateMesh(WorldSection section, int buildMask) { 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); + return new BuiltSection(section.getKey(), null, null); } } 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 new file mode 100644 index 00000000..b2de5790 --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactoryOld.java @@ -0,0 +1,335 @@ +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); + } + +} diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java index a090bc66..7c361e6e 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java @@ -22,9 +22,9 @@ public class RenderGenerationService { private final Semaphore taskCounter = new Semaphore(0); private final WorldEngine world; - private final Consumer resultConsumer; + private final Consumer resultConsumer; - public RenderGenerationService(WorldEngine world, int workers, Consumer consumer) { + public RenderGenerationService(WorldEngine world, int workers, Consumer consumer) { this.world = world; this.resultConsumer = consumer; this.workers = new Thread[workers]; @@ -36,7 +36,7 @@ public class RenderGenerationService { } } - private final ConcurrentHashMap renderCache = new ConcurrentHashMap<>(1000,0.75f,10); + private final ConcurrentHashMap renderCache = new ConcurrentHashMap<>(1000,0.75f,10); //TODO: add a generated render data cache private void renderWorker() { @@ -156,6 +156,6 @@ public class RenderGenerationService { while (!this.taskQueue.isEmpty()) { this.taskQueue.removeFirst(); } - this.renderCache.values().forEach(BuiltSectionGeometry::free); + this.renderCache.values().forEach(BuiltSection::free); } } From 7e1c910b96bdc02c642f0ffe1e977c93670450d6 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Fri, 26 Jan 2024 10:35:54 +1000 Subject: [PATCH 08/28] Boop --- .../cortex/zenith/client/core/VoxelCore.java | 8 +- .../core/rendering/GeometryManager.java | 81 +++++++++---------- .../rendering/building/RenderDataFactory.java | 6 +- 3 files changed, 51 insertions(+), 44 deletions(-) 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 1a798377..c83fb545 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -101,10 +101,14 @@ public class VoxelCore { boolean firstTime = true; public void renderSetup(Frustum frustum, Camera camera) { if (this.firstTime) { - this.distanceTracker.init(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); + //this.distanceTracker.init(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); this.firstTime = false; + + this.renderTracker.addLvl0(0,0,0); + + } - this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); + //this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); this.renderer.setupRender(frustum, camera); } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index 79bfa9a5..5846d6e0 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -15,25 +15,7 @@ import java.util.concurrent.ConcurrentLinkedDeque; public class GeometryManager { private static final int SECTION_METADATA_SIZE = 32; - - //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry - private record SectionMeta(long position, long opaqueGeometryPtr, int opaqueQuadCount, int opaquePreDataCount, long translucentGeometryPtr, int translucentQuadCount, int translucentPreDataCount) { - public void writeMetadata(long ptr) { - //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints - MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; - MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4; - ptr += 8; - - MemoryUtil.memPutInt(ptr, (int) this.opaqueGeometryPtr + this.opaquePreDataCount); ptr += 4; - MemoryUtil.memPutInt(ptr, this.opaqueQuadCount); ptr += 4; - - MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; - MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; - } - } - private final ConcurrentLinkedDeque buildResults = new ConcurrentLinkedDeque<>(); - private int sectionCount = 0; private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap(); private final LongArrayList id2pos = new LongArrayList(); @@ -42,37 +24,16 @@ public class GeometryManager { private final GlBuffer sectionMetaBuffer; private final BufferArena geometryBuffer; - public GeometryManager(long geometryBufferSize, int maxSections) { this.sectionMetaBuffer = new GlBuffer(((long) maxSections) * SECTION_METADATA_SIZE); this.geometryBuffer = new BufferArena(geometryBufferSize, 8); this.pos2id.defaultReturnValue(-1); } - public void enqueueResult(BuiltSection sectionGeometry) { - this.buildResults.add(sectionGeometry); - } - - private SectionMeta createMeta(BuiltSection geometry) { - long geometryPtr = this.geometryBuffer.upload(geometry.buffer); - - //TODO: support translucent geometry - return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.buffer.size/8), 0, -1,0, 0); - } - - private void freeMeta(SectionMeta meta) { - if (meta.opaqueGeometryPtr != -1) { - this.geometryBuffer.free(meta.opaqueGeometryPtr); - } - if (meta.translucentGeometryPtr != -1) { - this.geometryBuffer.free(meta.translucentGeometryPtr); - } - } - void uploadResults() { while (!this.buildResults.isEmpty()) { var result = this.buildResults.pop(); - boolean isDelete = result.buffer == null && result.translucentGeometryBuffer == null; + boolean isDelete = result.opaque == null && result.translucent == null; if (isDelete) { int id = -1; if ((id = this.pos2id.remove(result.position)) != -1) { @@ -165,9 +126,47 @@ public class GeometryManager { return this.sectionMetaBuffer.id; } - public float getGeometryBufferUsage() { return this.geometryBuffer.usage(); } + + + //TODO: pack the offsets of each axis so that implicit face culling can work + //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry + private record SectionMeta(long position, int aabb, long opaqueGeometryPtr, int opaqueQuadCount, int opaquePreDataCount, long translucentGeometryPtr, int translucentQuadCount, int translucentPreDataCount) { + public void writeMetadata(long ptr) { + //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints + MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; + MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4; + ptr += 8; + + MemoryUtil.memPutInt(ptr, (int) this.opaqueGeometryPtr + this.opaquePreDataCount); ptr += 4; + MemoryUtil.memPutInt(ptr, this.opaqueQuadCount); ptr += 4; + + MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; + MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; + } + } + + private SectionMeta createMeta(BuiltSection geometry) { + long geometryPtr = this.geometryBuffer.upload(geometry.opaque.buffer()); + + //TODO: support translucent geometry + return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0, -1,0, 0); + } + + private void freeMeta(SectionMeta meta) { + if (meta.opaqueGeometryPtr != -1) { + this.geometryBuffer.free(meta.opaqueGeometryPtr); + } + if (meta.translucentGeometryPtr != -1) { + this.geometryBuffer.free(meta.translucentGeometryPtr); + } + } + + public void enqueueResult(BuiltSection sectionGeometry) { + this.buildResults.add(sectionGeometry); + } + } 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 ab042db4..8b7564e5 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 @@ -1,8 +1,10 @@ package me.cortex.zenith.client.core.rendering.building; +import me.cortex.zenith.common.util.MemoryBuffer; import me.cortex.zenith.common.world.WorldEngine; import me.cortex.zenith.common.world.WorldSection; import net.minecraft.client.MinecraftClient; +import org.lwjgl.system.MemoryUtil; public class RenderDataFactory { @@ -27,7 +29,9 @@ public class RenderDataFactory { public BuiltSection generateMesh(WorldSection section, int buildMask) { section.copyDataTo(this.sectionCache); - return new BuiltSection(section.getKey(), null, null); + var output = new MemoryBuffer(8*1); + + return new BuiltSection(section.getKey(), new BuiltSectionGeometry(output, new short[0]), null); } } From 99acc7226f1a7ecae865d70c8a18030f9885db42 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:17:16 +1000 Subject: [PATCH 09/28] Small change --- .../client/core/rendering/GeometryManager.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index 5846d6e0..67d84435 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -106,6 +106,10 @@ public class GeometryManager { } } + public void enqueueResult(BuiltSection sectionGeometry) { + this.buildResults.add(sectionGeometry); + } + public int getSectionCount() { return this.sectionCount; } @@ -131,10 +135,14 @@ public class GeometryManager { } + //========================================================================================================================================================================================= + //========================================================================================================================================================================================= + //========================================================================================================================================================================================= + //TODO: pack the offsets of each axis so that implicit face culling can work //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry - private record SectionMeta(long position, int aabb, long opaqueGeometryPtr, int opaqueQuadCount, int opaquePreDataCount, long translucentGeometryPtr, int translucentQuadCount, int translucentPreDataCount) { + private record SectionMeta(long position, int aabb, int opaqueGeometryPtr, int opaqueQuadCount, int opaquePreDataCount, int translucentGeometryPtr, int translucentQuadCount, int translucentPreDataCount) { public void writeMetadata(long ptr) { //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; @@ -150,7 +158,7 @@ public class GeometryManager { } private SectionMeta createMeta(BuiltSection geometry) { - long geometryPtr = this.geometryBuffer.upload(geometry.opaque.buffer()); + int geometryPtr = (int) this.geometryBuffer.upload(geometry.opaque.buffer()); //TODO: support translucent geometry return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0, -1,0, 0); @@ -165,8 +173,4 @@ public class GeometryManager { } } - public void enqueueResult(BuiltSection sectionGeometry) { - this.buildResults.add(sectionGeometry); - } - } From 8ca73145632b223a09d6ca781cd64e242305a584 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sat, 27 Jan 2024 00:22:29 +1000 Subject: [PATCH 10/28] Made the fragmented storage replicate mappings to all dbs, this should mean that repair after corruption is 30x more likly --- .../FragmentedStorageBackendAdaptor.java | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java b/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java index ffbad877..27b89084 100644 --- a/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java +++ b/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java @@ -1,6 +1,9 @@ package me.cortex.zenith.common.world.storage; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import me.cortex.zenith.common.world.storage.lmdb.LMDBStorageBackend; import net.minecraft.util.math.random.RandomSeed; @@ -8,6 +11,7 @@ import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.util.Arrays; //Segments the section data into multiple dbs public class FragmentedStorageBackendAdaptor extends StorageBackend { @@ -57,12 +61,67 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend { @Override public void putIdMapping(int id, ByteBuffer data) { - this.backends[0].putIdMapping(id, data); + //Replicate the mappings over all the dbs to mean the chance of recovery in case of corruption is 30x + for (var backend : this.backends) { + backend.putIdMapping(id, data); + } + } + + private record EqualingArray(byte[] bytes) { + @Override + public boolean equals(Object obj) { + return Arrays.equals(this.bytes, ((EqualingArray)obj).bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.bytes); + } } @Override public Int2ObjectOpenHashMap getIdMappingsData() { - return this.backends[0].getIdMappingsData(); + Object2IntOpenHashMap> verification = new Object2IntOpenHashMap<>(); + Int2ObjectOpenHashMap any = null; + for (var backend : this.backends) { + var mappings = backend.getIdMappingsData(); + if (mappings.isEmpty()) { + //TODO: log a warning and attempt to replicate the data the other fragments + continue; + } + var repackaged = new Int2ObjectOpenHashMap(mappings.size()); + for (var entry : mappings.int2ObjectEntrySet()) { + repackaged.put(entry.getIntKey(), new EqualingArray(entry.getValue())); + } + verification.addTo(repackaged, 1); + any = repackaged; + } + if (verification.size() != 1) { + System.err.println("Error id mapping not matching across all fragments, attempting to recover"); + Object2IntMap.Entry> maxEntry = null; + for (var entry : verification.object2IntEntrySet()) { + if (maxEntry == null) { maxEntry = entry; } + else { + if (maxEntry.getIntValue() < entry.getIntValue()) { + maxEntry = entry; + } + } + } + + var mapping = maxEntry.getKey(); + + var out = new Int2ObjectOpenHashMap(mapping.size()); + for (var entry : mapping.int2ObjectEntrySet()) { + out.put(entry.getIntKey(), entry.getValue().bytes); + } + return out; + } else { + var out = new Int2ObjectOpenHashMap(any.size()); + for (var entry : any.int2ObjectEntrySet()) { + out.put(entry.getIntKey(), entry.getValue().bytes); + } + return out; + } } @Override From 1eeb8c069c07524713dd32ed1d081517666c77ce Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sat, 27 Jan 2024 01:32:16 +1000 Subject: [PATCH 11/28] Fixed woopsie --- .../common/world/storage/FragmentedStorageBackendAdaptor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java b/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java index 27b89084..dca4c3cb 100644 --- a/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java +++ b/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java @@ -96,6 +96,10 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend { verification.addTo(repackaged, 1); any = repackaged; } + if (any == null) { + return new Int2ObjectOpenHashMap<>(); + } + if (verification.size() != 1) { System.err.println("Error id mapping not matching across all fragments, attempting to recover"); Object2IntMap.Entry> maxEntry = null; From 3f6647af7079432901b558a5406b651fdc339627 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sat, 27 Jan 2024 01:32:50 +1000 Subject: [PATCH 12/28] Continued work on block models --- .../cortex/zenith/client/core/VoxelCore.java | 6 +- .../core/model/ColourDepthTextureData.java | 13 ++ .../client/core/model/ModelManager.java | 119 ++++++++++++++++-- .../client/core/model/ModelTextureBakery.java | 43 +++++-- .../client/core/model/TextureUtils.java | 53 ++++++-- .../core/rendering/Gl46FarWorldRenderer.java | 5 +- .../zenith/shaders/bakery/position_tex.fsh | 8 ++ .../zenith/shaders/bakery/position_tex.vsh | 12 ++ 8 files changed, 216 insertions(+), 43 deletions(-) create mode 100644 src/main/resources/assets/zenith/shaders/bakery/position_tex.fsh create mode 100644 src/main/resources/assets/zenith/shaders/bakery/position_tex.vsh 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 c83fb545..0d85d1e0 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -1,8 +1,6 @@ package me.cortex.zenith.client.core; import me.cortex.zenith.client.config.ZenithConfig; -import me.cortex.zenith.client.core.model.ModelTextureBakery; -import me.cortex.zenith.client.core.model.TextureUtils; import me.cortex.zenith.client.core.rendering.*; import me.cortex.zenith.client.core.rendering.building.RenderGenerationService; import me.cortex.zenith.client.core.util.DebugUtil; @@ -11,8 +9,6 @@ import me.cortex.zenith.client.importers.WorldImporter; import me.cortex.zenith.common.world.storage.FragmentedStorageBackendAdaptor; import net.minecraft.block.Block; import net.minecraft.block.Blocks; -import net.minecraft.block.CropBlock; -import net.minecraft.block.SnowBlock; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; import net.minecraft.client.util.math.MatrixStack; @@ -87,7 +83,7 @@ public class VoxelCore { for (var state : this.world.getMapper().getStateEntries()) { - this.renderer.getModelManager().updateEntry(state.id, state.state); + this.renderer.getModelManager().addEntry(state.id, state.state); } //this.renderer.getModelManager().updateEntry(0, Blocks.GRASS_BLOCK.getDefaultState()); } diff --git a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java index b3685847..ca19f3f0 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java +++ b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java @@ -1,4 +1,17 @@ package me.cortex.zenith.client.core.model; +import java.util.Arrays; + public record ColourDepthTextureData(int[] colour, int[] depth) { + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + var other = ((ColourDepthTextureData)obj); + return Arrays.equals(other.colour, this.colour) && Arrays.equals(other.depth, this.depth); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.colour) ^ Arrays.hashCode(this.depth); + } } 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 956c330b..91041202 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 @@ -2,13 +2,18 @@ package me.cortex.zenith.client.core.model; import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.platform.GlStateManager; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.gl.GlTexture; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.RenderLayers; import net.minecraft.registry.Registries; -import org.lwjgl.opengl.GL45C; +import net.minecraft.util.math.Direction; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11C.GL_NEAREST; @@ -34,6 +39,8 @@ public class ModelManager { private final GlTexture textures; private final int blockSampler = glGenSamplers(); + private final int modelTextureSize; + //Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the // section buffer reverse indexing @@ -65,7 +72,7 @@ public class ModelManager { //Provides a map from id -> model id as multiple ids might have the same internal model id private final int[] idMappings; - private final int modelTextureSize; + private final Object2IntOpenHashMap> modelTexture2id = new Object2IntOpenHashMap<>(); public ModelManager(int modelTextureSize) { this.modelTextureSize = modelTextureSize; @@ -75,24 +82,59 @@ public class ModelManager { this.textures = new GlTexture().store(GL_RGBA8, 1, modelTextureSize*3*256,modelTextureSize*2*256); this.metadataCache = new long[1<<16]; this.idMappings = new int[1<<16]; + Arrays.fill(this.idMappings, -1); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_LOD, 0); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAX_LOD, 4); + + this.modelTexture2id.defaultReturnValue(-1); } - public void updateEntry(int id, BlockState blockState) { + + //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) { + if (this.idMappings[blockId] != -1) { + throw new IllegalArgumentException("Trying to add entry for duplicate id"); + } + + int modelId = -1; + var textureData = this.bakery.renderFaces(blockState, 123456); + {//Deduplicate same entries + int possibleDuplicate = this.modelTexture2id.getInt(List.of(textureData)); + if (possibleDuplicate != -1) {//Duplicate found + this.idMappings[blockId] = possibleDuplicate; + return possibleDuplicate; + } else {//Not a duplicate so create a new entry + modelId = this.modelTexture2id.size(); + this.idMappings[blockId] = modelId; + this.modelTexture2id.put(List.of(textureData), modelId); + } + } + this.putTextures(modelId, textureData); + var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock())); + var blockRenderLayer = RenderLayers.getBlockLayer(blockState); + //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 + + //NOTE: this is excluding fluid states + //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver - var textureData = this.bakery.renderFaces(blockState, 123456); - int depth = TextureUtils.computeDepth(textureData[0], TextureUtils.DEPTH_MODE_AVG); + var sizes = this.computeModelDepth(textureData); - int aaaa = 1; - - this.putTextures(id, textureData); + for (int face = 0; face < 6; face++) { + if (sizes[face] == -1) {//Face is empty, so ignore + continue; + } + //TODO: combine all the methods into a single + //boolean fullyOccluding = TextureUtils.hasAlpha() + } //Model data contains, the quad size and offset of each face and whether the face needs to be resolved with a colour modifier @@ -104,6 +146,60 @@ public class ModelManager { //TODO: need to make an option for like leaves to be fully opaque as by default they are not!!!! + + + + + + + + + //Models data is computed per face, the axis offset of the face is computed from the depth component of the rasterized data + // the size and offset of the face data is computed from the remaining pixels that where actually rastered (e.g. depth != 1.0) + // (note this might be able to be optimized for cuttout layers where it automatically tries to squish as much as possible) + // solid layer renders it as black so might need to add a bitset in the model data of whether the face is rendering + // in discard or solid mode maybe? + + + + + + + + + return modelId; + } + + private int[] computeModelDepth(ColourDepthTextureData[] textures) { + int[] res = new int[6]; + for (var dir : Direction.values()) { + var data = textures[dir.getId()]; + float fd = TextureUtils.computeDepth(data, TextureUtils.DEPTH_MODE_MIN);//Compute the min float depth, smaller means closer to the camera, range 0-1 + int depth = Math.round(fd * this.modelTextureSize); + //If fd is -1, it means that there was nothing rendered on that face and it should be discarded + if (fd < -0.1) { + res[dir.ordinal()] = -1; + } else { + res[dir.ordinal()] = depth; + } + } + return res; + } + + public long getModelMetadata(int blockId) { + int map = this.idMappings[blockId]; + if (map == -1) { + throw new IllegalArgumentException("Id hasnt been computed yet"); + } + return this.metadataCache[map]; + } + + public int getModelId(int blockId) { + int map = this.idMappings[blockId]; + if (map == -1) { + throw new IllegalArgumentException("Id hasnt been computed yet"); + } + return map; } private void putTextures(int id, ColourDepthTextureData[] textures) { @@ -121,10 +217,6 @@ public class ModelManager { } } - public long getModelMetadata(int id) { - return this.metadataCache[id]; - } - public int getBufferId() { return this.modelBuffer.id; } @@ -132,6 +224,7 @@ public class ModelManager { public int getTextureId() { return this.textures.id; } + public int getSamplerId() { return this.blockSampler; } 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 3987c7db..ca646945 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 @@ -1,15 +1,22 @@ package me.cortex.zenith.client.core.model; +import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.VertexSorter; import me.cortex.zenith.client.core.gl.GlFramebuffer; import me.cortex.zenith.client.core.gl.GlTexture; +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.BlockState; 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.util.Identifier; import net.minecraft.util.math.Direction; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.random.LocalRandom; @@ -24,6 +31,8 @@ import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BI import static org.lwjgl.opengl.ARBShaderImageLoadStore.glMemoryBarrier; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate; +import static org.lwjgl.opengl.GL20C.glUniformMatrix4fv; +import static org.lwjgl.opengl.GL20C.glUseProgram; import static org.lwjgl.opengl.GL45C.glBlitNamedFramebuffer; import static org.lwjgl.opengl.GL45C.glGetTextureImage; @@ -34,6 +43,10 @@ public class ModelTextureBakery { private final GlTexture colourTex; private final GlTexture depthTex; private final GlFramebuffer framebuffer; + private final Shader rasterShader = Shader.make() + .add(ShaderType.VERTEX, "zenith:bakery/position_tex.vsh") + .add(ShaderType.FRAGMENT, "zenith:bakery/position_tex.fsh") + .compile(); private static final List FACE_VIEWS = new ArrayList<>(); static { @@ -42,8 +55,8 @@ public class ModelTextureBakery { addView(0,180, 0);//Direction.NORTH addView(0,0, 0);//Direction.SOUTH //TODO: check these arnt the wrong way round - addView(0,90, -90);//Direction.EAST - addView(0,270, -90);//Direction.WEST + addView(0,90, 270);//Direction.EAST + addView(0,270, 270);//Direction.WEST } public ModelTextureBakery(int width, int height) { @@ -64,6 +77,9 @@ public class ModelTextureBakery { FACE_VIEWS.add(stack); } + + //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) { var model = MinecraftClient.getInstance() .getBakedModelManager() @@ -82,9 +98,7 @@ public class ModelTextureBakery { var renderLayer = RenderLayers.getBlockLayer(state); - if (renderLayer == RenderLayer.getTranslucent()) { - //TODO: TRANSLUCENT, must sort the quad first - } + renderLayer.startDrawing(); RenderSystem.depthMask(true); RenderSystem.enableBlend(); @@ -92,8 +106,16 @@ public class ModelTextureBakery { RenderSystem.enableCull(); RenderSystem.depthFunc(GL_LESS); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - RenderSystem.setShader(GameRenderer::getPositionTexProgram); + //TODO: bind the required uniforms and + this.rasterShader.bind(); + RenderSystem.bindTexture(RenderSystem.getShaderTexture(0)); + GlUniform.uniform1(0, 0); + RenderSystem.activeTexture(GlConst.GL_TEXTURE0); + + if (renderLayer == RenderLayer.getTranslucent()) { + //TODO: TRANSLUCENT, must sort the quad first, or something idk + } if (!state.getFluidState().isEmpty()) { //TODO: render fluid @@ -109,7 +131,7 @@ public class ModelTextureBakery { RenderSystem.setProjectionMatrix(oldProjection, VertexSorter.BY_DISTANCE); glBindFramebuffer(GL_FRAMEBUFFER, oldFB); GL11C.glViewport(GlStateManager.Viewport.getX(), GlStateManager.Viewport.getY(), GlStateManager.Viewport.getWidth(), GlStateManager.Viewport.getHeight()); - //glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,0,0,256,256, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,0,0,256,256, GL_COLOR_BUFFER_BIT, GL_NEAREST); return faces; } @@ -118,7 +140,11 @@ public class ModelTextureBakery { 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); - BufferRenderer.drawWithGlobalProgram(vc.end()); + + float[] mat = new float[4*4]; + new Matrix4f(RenderSystem.getModelViewMatrix()).mul(RenderSystem.getProjectionMatrix()).get(mat); + glUniformMatrix4fv(1, false, mat); + BufferRenderer.draw(vc.end()); glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); int[] colourData = new int[this.width*this.height]; @@ -141,5 +167,6 @@ public class ModelTextureBakery { this.framebuffer.free(); this.colourTex.free(); this.depthTex.free(); + this.rasterShader.free(); } } diff --git a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java index 84de9b92..326d5380 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java +++ b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java @@ -4,15 +4,27 @@ import java.util.Map; //Texturing utils to manipulate data from the model bakery public class TextureUtils { - //Returns if any pixels are not fully transparent or fully translucent - public static boolean hasAlpha(ColourDepthTextureData texture) { - for (int pixel : texture.colour()) { + //Returns a bitset of + public static int computeColourData(ColourDepthTextureData texture) { + final var colour = texture.colour(); + int bitset = 0b101; + for (int i = 0; i < colour.length && bitset != 0b010; i++) { + int pixel = colour[i]; int alpha = (pixel>>24)&0xFF; - if (alpha != 0 && alpha != 255) { - return true; - } + bitset |= (alpha != 0 && alpha != 255)?2:0;//Test if the pixel is translucent (has alpha) + bitset &= (alpha != 0)?3:7;// test if the pixel is not empty (assumes that if alpha is 0 it wasnt written to!!) FIXME: THIS MIGHT NOT BE CORRECT + bitset &= alpha != 255?6:7;// test if the pixel is anything but solid, (occlusion culling stuff) } - return false; + return bitset; + } + + //Returns the number of non pixels not written to + public static int getNonWrittenPixels(ColourDepthTextureData texture) { + int count = 0; + for (int pixel : texture.depth()) { + count += (((pixel>>8)&0xFFFFFF) == 0xFFFFFF)?1:0; + } + return count; } public static boolean isSolid(ColourDepthTextureData texture) { @@ -29,7 +41,7 @@ public class TextureUtils { public static final int DEPTH_MODE_MIN = 3; //Computes depth info based on written pixel data - public static int computeDepth(ColourDepthTextureData texture, int mode) { + public static float computeDepth(ColourDepthTextureData texture, int mode) { final var colourData = texture.colour(); final var depthData = texture.depth(); long a = 0; @@ -37,11 +49,14 @@ public class TextureUtils { if (mode == DEPTH_MODE_MIN) { a = Long.MAX_VALUE; } + if (mode == DEPTH_MODE_MAX) { + a = Long.MIN_VALUE; + } for (int i = 0; i < colourData.length; i++) { - if ((colourData[0]&0xFF)==0) { + if ((colourData[i]&0xFF)==0) { continue; } - int depth = depthData[0]>>>8; + int depth = depthData[i]>>>8; if (mode == DEPTH_MODE_AVG) { a++; b += depth; @@ -56,10 +71,22 @@ public class TextureUtils { if (a == 0) { return -1; } - return (int) (b/a); - } else if (mode == DEPTH_MODE_MAX || mode == DEPTH_MODE_MIN) { - return (int) a; + return u2fdepth((int) (b/a)); + } else if (mode == DEPTH_MODE_MAX) { + if (a == Long.MIN_VALUE) { + return -1; + } + return u2fdepth((int) a); + } else if (mode == DEPTH_MODE_MIN) { + if (a == Long.MAX_VALUE) { + return -1; + } + return u2fdepth((int) a); } throw new IllegalArgumentException(); } + + private static float u2fdepth(int depth) { + return (((float)depth)/(float)(1<<24)); + } } 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 cf9f5bb7..86f6c7b5 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,6 +1,5 @@ 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; @@ -13,7 +12,6 @@ import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; import org.joml.Vector3f; import org.lwjgl.opengl.GL11C; -import org.lwjgl.opengl.GL45C; import org.lwjgl.system.MemoryUtil; import java.util.List; @@ -31,7 +29,6 @@ import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.GL43.*; import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; import static org.lwjgl.opengl.GL45C.glClearNamedBufferData; -import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { private final Shader commandGen = Shader.make() @@ -82,7 +79,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { return; } - //this.getModelManager().updateEntry(this.frameId%(1<<15), Blocks.STONE.getDefaultState()); + //this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.OAK_BUTTON.getDefaultState()); RenderLayer.getCutoutMipped().startDrawing(); int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); diff --git a/src/main/resources/assets/zenith/shaders/bakery/position_tex.fsh b/src/main/resources/assets/zenith/shaders/bakery/position_tex.fsh new file mode 100644 index 00000000..340a9f9c --- /dev/null +++ b/src/main/resources/assets/zenith/shaders/bakery/position_tex.fsh @@ -0,0 +1,8 @@ +#version 430 + +layout(location=0) uniform sampler2D tex; +in vec2 texCoord; +out vec4 colour; +void main() { + colour = texture(tex, texCoord); +} diff --git a/src/main/resources/assets/zenith/shaders/bakery/position_tex.vsh b/src/main/resources/assets/zenith/shaders/bakery/position_tex.vsh new file mode 100644 index 00000000..5429d546 --- /dev/null +++ b/src/main/resources/assets/zenith/shaders/bakery/position_tex.vsh @@ -0,0 +1,12 @@ +#version 430 + +in vec3 pos; +in vec2 uv; + +layout(location=1) uniform mat4 transform; +out vec2 texCoord; + +void main() { + gl_Position = transform * vec4(pos, 1.0); + texCoord = uv; +} From 66be05369cc8aa42bc7893578d115a0d1c864b54 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 28 Jan 2024 02:59:03 +1000 Subject: [PATCH 13/28] God i hate opengl --- .../core/model/ColourDepthTextureData.java | 4 +- .../client/core/model/ModelManager.java | 55 +++++++---- .../client/core/model/ModelTextureBakery.java | 24 ++++- .../client/core/model/TextureUtils.java | 91 ++++++++++++++++++- .../core/rendering/Gl46FarWorldRenderer.java | 7 +- 5 files changed, 152 insertions(+), 29 deletions(-) diff --git a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java index ca19f3f0..c9c3d689 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java +++ b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java @@ -2,7 +2,7 @@ package me.cortex.zenith.client.core.model; import java.util.Arrays; -public record ColourDepthTextureData(int[] colour, int[] depth) { +public record ColourDepthTextureData(int[] colour, int[] depth, int width, int height) { @Override public boolean equals(Object obj) { if (obj == null) return false; @@ -12,6 +12,6 @@ public record ColourDepthTextureData(int[] colour, int[] depth) { @Override public int hashCode() { - return Arrays.hashCode(this.colour) ^ Arrays.hashCode(this.depth); + return (this.width * 312337173 * (Arrays.hashCode(this.colour) ^ Arrays.hashCode(this.depth))) ^ this.height; } } 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 91041202..ab021137 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 net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayers; import net.minecraft.registry.Registries; import net.minecraft.util.math.Direction; @@ -97,9 +98,9 @@ public class ModelManager { //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) { - if (this.idMappings[blockId] != -1) { - throw new IllegalArgumentException("Trying to add entry for duplicate id"); - } + //if (this.idMappings[blockId] != -1) { + // throw new IllegalArgumentException("Trying to add entry for duplicate id"); + //} int modelId = -1; var textureData = this.bakery.renderFaces(blockState, 123456); @@ -107,7 +108,8 @@ public class ModelManager { int possibleDuplicate = this.modelTexture2id.getInt(List.of(textureData)); if (possibleDuplicate != -1) {//Duplicate found this.idMappings[blockId] = possibleDuplicate; - return possibleDuplicate; + modelId = possibleDuplicate; + //return possibleDuplicate; } else {//Not a duplicate so create a new entry modelId = this.modelTexture2id.size(); this.idMappings[blockId] = modelId; @@ -119,22 +121,15 @@ public class ModelManager { var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock())); var 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 //NOTE: this is excluding fluid states - //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver - var sizes = this.computeModelDepth(textureData); - for (int face = 0; face < 6; face++) { - if (sizes[face] == -1) {//Face is empty, so ignore - continue; - } - //TODO: combine all the methods into a single - //boolean fullyOccluding = TextureUtils.hasAlpha() - } //Model data contains, the quad size and offset of each face and whether the face needs to be resolved with a colour modifier @@ -162,7 +157,20 @@ public class ModelManager { + //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver + var sizes = this.computeModelDepth(textureData, checkMode); + for (int face = 0; face < 6; face++) { + if (sizes[face] < -0.1) {//Face is empty, so ignore + continue; + } + //TODO: replace this with a more intelligent method that + // if using solid use TextureUtils.computeBounds() with depth testing + // if using translucent or transparent compare if alpha is 0 + var faceSize = TextureUtils.computeBounds(textureData[face], checkMode); + + int eee = 0; + } @@ -170,17 +178,30 @@ public class ModelManager { return modelId; } - private int[] computeModelDepth(ColourDepthTextureData[] textures) { - int[] res = new int[6]; + + + + + + + + + + + + + + private float[] computeModelDepth(ColourDepthTextureData[] textures, int checkMode) { + float[] res = new float[6]; for (var dir : Direction.values()) { var data = textures[dir.getId()]; - float fd = TextureUtils.computeDepth(data, TextureUtils.DEPTH_MODE_MIN);//Compute the min float depth, smaller means closer to the camera, range 0-1 + float fd = TextureUtils.computeDepth(data, TextureUtils.DEPTH_MODE_MIN, checkMode);//Compute the min float depth, smaller means closer to the camera, range 0-1 int depth = Math.round(fd * this.modelTextureSize); //If fd is -1, it means that there was nothing rendered on that face and it should be discarded if (fd < -0.1) { res[dir.ordinal()] = -1; } else { - res[dir.ordinal()] = depth; + res[dir.ordinal()] = ((float) depth)/this.modelTextureSize; } } return res; 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 ca646945..8e03671b 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 @@ -90,7 +90,12 @@ public class ModelTextureBakery { var oldProjection = new Matrix4f(RenderSystem.getProjectionMatrix()); GL11C.glViewport(0, 0, this.width, this.height); - RenderSystem.setProjectionMatrix(new Matrix4f().identity().scale(2,2,-1f).translate(-0.5f, -0.5f, 0.0f), VertexSorter.BY_Z); + RenderSystem.setProjectionMatrix(new Matrix4f().identity().set(new float[]{ + 2,0,0,0, + 0, 2,0,0, + 0,0, -1f,0, + -1,-1,0,1, + }), VertexSorter.BY_Z); glClearColor(0,0,0,0); glClearDepth(1); @@ -100,6 +105,8 @@ public class ModelTextureBakery { var renderLayer = RenderLayers.getBlockLayer(state); renderLayer.startDrawing(); + glEnable(GL_STENCIL_TEST); + glDepthRange(0, 1); RenderSystem.depthMask(true); RenderSystem.enableBlend(); RenderSystem.enableDepthTest(); @@ -107,7 +114,10 @@ public class ModelTextureBakery { RenderSystem.depthFunc(GL_LESS); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - //TODO: bind the required uniforms and + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilMask(0xFF); + this.rasterShader.bind(); RenderSystem.bindTexture(RenderSystem.getShaderTexture(0)); GlUniform.uniform1(0, 0); @@ -124,14 +134,18 @@ public class ModelTextureBakery { 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); + //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); } renderLayer.endDrawing(); + glDisable(GL_STENCIL_TEST); RenderSystem.setProjectionMatrix(oldProjection, VertexSorter.BY_DISTANCE); glBindFramebuffer(GL_FRAMEBUFFER, oldFB); GL11C.glViewport(GlStateManager.Viewport.getX(), GlStateManager.Viewport.getY(), GlStateManager.Viewport.getWidth(), GlStateManager.Viewport.getHeight()); - glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,0,0,256,256, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + //TODO: FIXME: fully revert the state of opengl + return faces; } @@ -142,7 +156,7 @@ public class ModelTextureBakery { renderQuads(vc, state, model, stack, randomValue); float[] mat = new float[4*4]; - new Matrix4f(RenderSystem.getModelViewMatrix()).mul(RenderSystem.getProjectionMatrix()).get(mat); + RenderSystem.getProjectionMatrix().get(mat); glUniformMatrix4fv(1, false, mat); BufferRenderer.draw(vc.end()); @@ -151,7 +165,7 @@ public class ModelTextureBakery { int[] depthData = new int[this.width*this.height]; glGetTextureImage(this.colourTex.id, 0, GL_RGBA, GL_UNSIGNED_BYTE, colourData); glGetTextureImage(this.depthTex.id, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, depthData); - return new ColourDepthTextureData(colourData, depthData); + return new ColourDepthTextureData(colourData, depthData, this.width, this.height); } private static void renderQuads(BufferBuilder builder, BlockState state, BakedModel model, MatrixStack stack, long randomValue) { diff --git a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java index 326d5380..c72358fb 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java +++ b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java @@ -22,7 +22,7 @@ public class TextureUtils { public static int getNonWrittenPixels(ColourDepthTextureData texture) { int count = 0; for (int pixel : texture.depth()) { - count += (((pixel>>8)&0xFFFFFF) == 0xFFFFFF)?1:0; + count += ((pixel&0xFF) == 0)?1:0; } return count; } @@ -36,12 +36,27 @@ public class TextureUtils { return true; } + public static final int WRITE_CHECK_STENCIL = 1; + public static final int WRITE_CHECK_DEPTH = 2; + public static final int WRITE_CHECK_ALPHA = 3; + private static boolean wasPixelWritten(ColourDepthTextureData data, int mode, int index) { + if (mode == WRITE_CHECK_STENCIL) { + return (data.depth()[index]&0xFF)!=0; + } else if (mode == WRITE_CHECK_DEPTH) { + return (data.depth()[index]>>>8)!=((1<<24)-1); + } else if (mode == WRITE_CHECK_ALPHA) { + return ((data.colour()[index]>>>24)&0xff)!=0; + } + throw new IllegalArgumentException(); + } + public static final int DEPTH_MODE_AVG = 1; public static final int DEPTH_MODE_MAX = 2; public static final int DEPTH_MODE_MIN = 3; + //Computes depth info based on written pixel data - public static float computeDepth(ColourDepthTextureData texture, int mode) { + public static float computeDepth(ColourDepthTextureData texture, int mode, int checkMode) { final var colourData = texture.colour(); final var depthData = texture.depth(); long a = 0; @@ -53,7 +68,7 @@ public class TextureUtils { a = Long.MIN_VALUE; } for (int i = 0; i < colourData.length; i++) { - if ((colourData[i]&0xFF)==0) { + if (!wasPixelWritten(texture, checkMode, i)) { continue; } int depth = depthData[i]>>>8; @@ -87,6 +102,74 @@ public class TextureUtils { } private static float u2fdepth(int depth) { - return (((float)depth)/(float)(1<<24)); + float depthF = (float) ((double)depth/((1<<24)-1)); + //https://registry.khronos.org/OpenGL-Refpages/gl4/html/glDepthRange.xhtml + // due to this and the unsigned bullshit, i believe the depth value needs to get multiplied by 2 + depthF *= 2; + if (depthF > 1.00001f) { + throw new IllegalArgumentException("Depth greater than 1"); + } + return depthF; + } + + + //NOTE: data goes from bottom left to top right (x first then y) + public static int[] computeBounds(ColourDepthTextureData data, int checkMode) { + final var depth = data.depth(); + //Compute x bounds first + int minX = 0; + minXCheck: + do { + for (int y = 0; y < data.height(); y++) { + int idx = minX + (y * data.width()); + if (wasPixelWritten(data, checkMode, idx)) { + break minXCheck;//pixel was written too so break from loop + } + } + minX++; + } while (minX != data.width()); + + int maxX = data.width()-1; + maxXCheck: + do { + for (int y = data.height()-1; y!=-1; y--) { + int idx = maxX + (y * data.width()); + if (wasPixelWritten(data, checkMode, idx)) { + break maxXCheck;//pixel was written too so break from loop + } + } + maxX--; + } while (maxX != -1); + maxX++; + + + //Compute y bounds + int minY = 0; + minYCheck: + do { + for (int x = 0; x < data.width(); x++) { + int idx = (minY * data.height()) + x; + if (wasPixelWritten(data, checkMode, idx)) { + break minYCheck;//pixel was written too + } + } + minY++; + } while (minY != data.height()); + + + int maxY = data.height()-1; + maxYCheck: + do { + for (int x = data.width()-1; x!=-1; x--) { + int idx = (maxY * data.height()) + x; + if (wasPixelWritten(data, checkMode, idx)) { + break maxYCheck;//pixel was written too so break from loop + } + } + maxY--; + } while (maxY != -1); + maxY++; + + return new int[]{minX, maxX, minY, maxY}; } } 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 86f6c7b5..ec1cc68c 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 @@ -7,8 +7,11 @@ import me.cortex.zenith.client.core.gl.shader.ShaderType; import me.cortex.zenith.client.core.rendering.util.UploadStream; import me.cortex.zenith.client.mixin.joml.AccessFrustumIntersection; import net.minecraft.block.Blocks; +import net.minecraft.block.WallMountedBlock; +import net.minecraft.block.enums.BlockFace; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Direction; import org.joml.Matrix4f; import org.joml.Vector3f; import org.lwjgl.opengl.GL11C; @@ -75,11 +78,13 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { } public void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz) { + this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.OAK_BUTTON.getDefaultState().with(WallMountedBlock.FACE, BlockFace.FLOOR).with(WallMountedBlock.FACING, Direction.SOUTH)); + //this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.HEAVY_WEIGHTED_PRESSURE_PLATE.getDefaultState()); + //this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.COMPARATOR.getDefaultState()); if (this.geometry.getSectionCount() == 0) { return; } - //this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.OAK_BUTTON.getDefaultState()); RenderLayer.getCutoutMipped().startDrawing(); int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); From 2e5eca6f24849d717e80b4ee8d6ede90cb67a1a3 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 28 Jan 2024 15:13:04 +1000 Subject: [PATCH 14/28] Mostly finished Model data i think --- .../client/core/model/ModelManager.java | 61 ++++++++++++++++--- .../client/core/model/TextureUtils.java | 24 ++------ .../core/rendering/GeometryManager.java | 16 ++--- .../core/rendering/Gl46FarWorldRenderer.java | 3 - .../rendering/building/RenderDataFactory.java | 8 ++- 5 files changed, 74 insertions(+), 38 deletions(-) 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 ab021137..8356bc34 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 @@ -5,12 +5,14 @@ import com.mojang.blaze3d.platform.GlStateManager; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; 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.client.MinecraftClient; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayers; import net.minecraft.registry.Registries; import net.minecraft.util.math.Direction; +import org.lwjgl.system.MemoryUtil; import java.util.ArrayList; import java.util.Arrays; @@ -155,23 +157,68 @@ public class ModelManager { // solid layer renders it as black so might need to add a bitset in the model data of whether the face is rendering // in discard or solid mode maybe? + long uploadPtr = UploadStream.INSTANCE.upload(this.modelBuffer, (long) modelId * MODEL_SIZE, MODEL_SIZE); + //TODO: implement + boolean hasBiomeColourResolver = false; + //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver var sizes = this.computeModelDepth(textureData, checkMode); - for (int face = 0; face < 6; face++) { - if (sizes[face] < -0.1) {//Face is empty, so ignore + //Each face gets 1 byte, with the top 2 bytes being for whatever + long metadata = hasBiomeColourResolver?1:0; + metadata |= blockRenderLayer == RenderLayer.getTranslucent()?2:0; + + //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 + long faceUploadPtr = uploadPtr + 4L * face;//Each face gets 4 bytes worth of data + metadata <<= 8; + float offset = sizes[face]; + if (offset < -0.1) {//Face is empty, so ignore + metadata |= 0xFF;//Mark the face as non-existent + //Set to -1 as safepoint + MemoryUtil.memPutLong(faceUploadPtr, -1); continue; } - //TODO: replace this with a more intelligent method that - // if using solid use TextureUtils.computeBounds() with depth testing - // if using translucent or transparent compare if alpha is 0 var faceSize = TextureUtils.computeBounds(textureData[face], checkMode); - int eee = 0; - } + boolean faceCoversFullBlock = faceSize[0] == 0 && faceSize[2] == 0 && + faceSize[1] == (this.modelTextureSize-1) && faceSize[3] == (this.modelTextureSize-1); + metadata |= faceCoversFullBlock?2:0; + + //TODO: add alot of config options for the following + boolean occludesFace = true; + occludesFace &= blockRenderLayer != RenderLayer.getTranslucent();//If its translucent, it doesnt occlude + + //TODO: make this an option, basicly if the face is really close, it occludes otherwise it doesnt + occludesFace &= offset < 0.1;//If the face is rendered far away from the other face, then it doesnt occlude + + if (occludesFace) { + int writeCount = TextureUtils.getWrittenPixelCount(textureData[face], checkMode); + occludesFace &= ((float)writeCount)/(this.modelTextureSize * this.modelTextureSize) > 0.9;// only occlude if the face covers more than 90% of the face + } + metadata |= occludesFace?1:0; + + + //Scale face size from 0->this.modelTextureSize-1 to 0->15 + for (int i = 0; i < 4; i++) { + faceSize[i] = Math.round((((float)faceSize[i])/(this.modelTextureSize-1))*15); + } + + int faceModelData = 0; + faceModelData |= faceSize[0] | (faceSize[1]<<4) | (faceSize[2]<<8) | (faceSize[3]<<12); + faceModelData |= Math.round(offset*63);//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model + //Still have 11 bits free + + MemoryUtil.memPutInt(faceUploadPtr, faceModelData); + } + this.metadataCache[modelId] = metadata; + + uploadPtr += 4*6; + //Have 40 bytes free for remaining model data + // todo: put in like the render layer type ig? along with colour resolver info diff --git a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java index c72358fb..2c828379 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java +++ b/src/main/java/me/cortex/zenith/client/core/model/TextureUtils.java @@ -4,25 +4,11 @@ import java.util.Map; //Texturing utils to manipulate data from the model bakery public class TextureUtils { - //Returns a bitset of - public static int computeColourData(ColourDepthTextureData texture) { - final var colour = texture.colour(); - int bitset = 0b101; - for (int i = 0; i < colour.length && bitset != 0b010; i++) { - int pixel = colour[i]; - int alpha = (pixel>>24)&0xFF; - bitset |= (alpha != 0 && alpha != 255)?2:0;//Test if the pixel is translucent (has alpha) - bitset &= (alpha != 0)?3:7;// test if the pixel is not empty (assumes that if alpha is 0 it wasnt written to!!) FIXME: THIS MIGHT NOT BE CORRECT - bitset &= alpha != 255?6:7;// test if the pixel is anything but solid, (occlusion culling stuff) - } - return bitset; - } - //Returns the number of non pixels not written to - public static int getNonWrittenPixels(ColourDepthTextureData texture) { + public static int getWrittenPixelCount(ColourDepthTextureData texture, int checkMode) { int count = 0; - for (int pixel : texture.depth()) { - count += ((pixel&0xFF) == 0)?1:0; + for (int i = 0; i < texture.colour().length; i++) { + count += wasPixelWritten(texture, checkMode, i)?1:0; } return count; } @@ -140,7 +126,7 @@ public class TextureUtils { } maxX--; } while (maxX != -1); - maxX++; + //maxX++; //Compute y bounds @@ -168,7 +154,7 @@ public class TextureUtils { } maxY--; } while (maxY != -1); - maxY++; + //maxY++; return new int[]{minX, maxX, minY, maxY}; } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index 67d84435..ecfdbd00 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -142,18 +142,19 @@ public class GeometryManager { //TODO: pack the offsets of each axis so that implicit face culling can work //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry - private record SectionMeta(long position, int aabb, int opaqueGeometryPtr, int opaqueQuadCount, int opaquePreDataCount, int translucentGeometryPtr, int translucentQuadCount, int translucentPreDataCount) { + private record SectionMeta(long position, int aabb, int opaqueGeometryPtr, int count, int translucentGeometryPtr) { public void writeMetadata(long ptr) { //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4; - ptr += 8; + MemoryUtil.memPutInt(ptr, (int) this.aabb); ptr += 4; + ptr += 4; - MemoryUtil.memPutInt(ptr, (int) this.opaqueGeometryPtr + this.opaquePreDataCount); ptr += 4; - MemoryUtil.memPutInt(ptr, this.opaqueQuadCount); ptr += 4; + MemoryUtil.memPutInt(ptr, this.count); ptr += 4; + //MemoryUtil.memPutInt(ptr, this.opaqueQuadCount); ptr += 4; - MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; - MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; + //MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; + //MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; } } @@ -161,7 +162,8 @@ public class GeometryManager { int geometryPtr = (int) this.geometryBuffer.upload(geometry.opaque.buffer()); //TODO: support translucent geometry - return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0, -1,0, 0); + //return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0, -1,0, 0); + return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0); } private void freeMeta(SectionMeta meta) { 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 ec1cc68c..85de7dc8 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 @@ -78,9 +78,6 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { } public void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz) { - this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.OAK_BUTTON.getDefaultState().with(WallMountedBlock.FACE, BlockFace.FLOOR).with(WallMountedBlock.FACING, Direction.SOUTH)); - //this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.HEAVY_WEIGHTED_PRESSURE_PLATE.getDefaultState()); - //this.getModelManager().addEntry(this.frameId%(1<<15), Blocks.COMPARATOR.getDefaultState()); if (this.geometry.getSectionCount() == 0) { return; } 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 8b7564e5..78a66133 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 @@ -29,9 +29,13 @@ public class RenderDataFactory { public BuiltSection generateMesh(WorldSection section, int buildMask) { section.copyDataTo(this.sectionCache); - var output = new MemoryBuffer(8*1); + //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 - return new BuiltSection(section.getKey(), new BuiltSectionGeometry(output, new short[0]), null); + var buff = new MemoryBuffer(8*1); + + return new BuiltSection(section.getKey(), new BuiltSectionGeometry(buff, new short[0]), null); } } From 81e98c8daf6eb0f94abd1366c2c3e2a9aadf9d0d Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:02:40 +1000 Subject: [PATCH 15/28] Almost all works --- .../cortex/zenith/client/core/VoxelCore.java | 2 +- .../client/core/model/ModelManager.java | 22 ++-- .../core/rendering/GeometryManager.java | 4 +- .../core/rendering/Gl46FarWorldRenderer.java | 4 +- .../rendering/building/RenderDataFactory.java | 12 +++ .../zenith/shaders/lod/gl46/bindings.glsl | 11 +- .../zenith/shaders/lod/gl46/block_model.glsl | 9 ++ .../zenith/shaders/lod/gl46/quad_format.glsl | 2 +- .../assets/zenith/shaders/lod/gl46/quads.frag | 8 +- .../assets/zenith/shaders/lod/gl46/quads.vert | 100 +++++++++--------- .../zenith/shaders/lod/gl46/section.glsl | 14 +-- 11 files changed, 113 insertions(+), 75 deletions(-) create mode 100644 src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl 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 0d85d1e0..f5fef648 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -100,7 +100,7 @@ public class VoxelCore { //this.distanceTracker.init(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); this.firstTime = false; - this.renderTracker.addLvl0(0,0,0); + this.renderTracker.addLvl0(0,6,0); } 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 8356bc34..e122f701 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 @@ -100,9 +100,9 @@ public class ModelManager { //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) { - //if (this.idMappings[blockId] != -1) { - // throw new IllegalArgumentException("Trying to add entry for duplicate id"); - //} + if (this.idMappings[blockId] != -1) { + throw new IllegalArgumentException("Trying to add entry for duplicate id"); + } int modelId = -1; var textureData = this.bakery.renderFaces(blockState, 123456); @@ -111,7 +111,7 @@ public class ModelManager { if (possibleDuplicate != -1) {//Duplicate found this.idMappings[blockId] = possibleDuplicate; modelId = possibleDuplicate; - //return possibleDuplicate; + return possibleDuplicate; } else {//Not a duplicate so create a new entry modelId = this.modelTexture2id.size(); this.idMappings[blockId] = modelId; @@ -163,6 +163,16 @@ public class ModelManager { //TODO: implement boolean hasBiomeColourResolver = false; + + //TODO: THIS, note this can be tested for in 2 ways, re render the model with quad culling disabled and see if the result + // is the same, (if yes then needs double sided quads) + // another way to test it is if e.g. up and down havent got anything rendered but the sides do (e.g. all plants etc) + boolean needsDoubleSidedQuads = false; + + //TODO: special case stuff like vines and glow lichen, where it can be represented by a single double sided quad + // since that would help alot with perf of lots of vines + + //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver var sizes = this.computeModelDepth(textureData, checkMode); @@ -209,10 +219,10 @@ public class ModelManager { int faceModelData = 0; faceModelData |= faceSize[0] | (faceSize[1]<<4) | (faceSize[2]<<8) | (faceSize[3]<<12); - faceModelData |= Math.round(offset*63);//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model + faceModelData |= Math.round(offset*63)<<16;//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model //Still have 11 bits free - MemoryUtil.memPutInt(faceUploadPtr, faceModelData); + MemoryUtil.memPutInt(faceUploadPtr, 0); } this.metadataCache[modelId] = metadata; diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index ecfdbd00..cd7307e4 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -150,8 +150,8 @@ public class GeometryManager { MemoryUtil.memPutInt(ptr, (int) this.aabb); ptr += 4; ptr += 4; + MemoryUtil.memPutInt(ptr, this.opaqueGeometryPtr); ptr += 4; MemoryUtil.memPutInt(ptr, this.count); ptr += 4; - //MemoryUtil.memPutInt(ptr, this.opaqueQuadCount); ptr += 4; //MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; //MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; @@ -163,7 +163,7 @@ public class GeometryManager { //TODO: support translucent geometry //return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0, -1,0, 0); - return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0); + return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), -1); } private void freeMeta(SectionMeta meta) { 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 85de7dc8..4822d8cd 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 @@ -107,12 +107,14 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT); this.lodShader.bind(); + glDisable(GL_CULL_FACE); glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, this.geometry.getSectionCount(), 0); + glEnable(GL_CULL_FACE); //ARBIndirectParameters.glMultiDrawElementsIndirectCountARB( glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); - cullShader.bind(); + this.cullShader.bind(); glColorMask(false, false, false, false); glDepthMask(false); 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 78a66133..844afead 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 @@ -34,8 +34,20 @@ public class RenderDataFactory { // this stops e.g. multiple layers of glass (and ocean) from having 3000 layers of quads etc var buff = new MemoryBuffer(8*1); + //MemoryUtil.memPutLong(buff.address, encodeRaw(1, 0,2,0,0,0,515,0, 0));//92 + //MemoryUtil.memPutLong(buff.address+8, encodeRaw(2, 1,1,0,0,0,515,0, 0));//92 + //MemoryUtil.memPutLong(buff.address+16, encodeRaw(3, 0,0,0,0,0,515,0, 0));//92 + //MemoryUtil.memPutLong(buff.address+24, encodeRaw(4, 0,2,0,0,0,515,0, 0));//92 + //MemoryUtil.memPutLong(buff.address+32, encodeRaw(5, 0,2,0,0,0,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address, encodeRaw(2, 1,1,0,0,0,515,0, 0));//92 + //MemoryUtil.memPutLong(buff.address, encodeRaw(3, 0,0,0,0,0,515,0, 0));//92 return new BuiltSection(section.getKey(), new BuiltSectionGeometry(buff, new short[0]), null); } + + private static long encodeRaw(int face, int width, int height, int x, int y, int z, int blockId, int biomeId, int lightId) { + return ((long)face) | (((long) width)<<3) | (((long) height)<<7) | (((long) z)<<11) | (((long) y)<<16) | (((long) x)<<21) | (((long) blockId)<<26) | (((long) biomeId)<<46) | (((long) lightId)<<55); + } + } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index 1a8f01b6..b52fba54 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -12,12 +12,17 @@ layout(binding = 0, std140) uniform SceneUniform { }; struct BlockModel { - uint faceColours[6]; + uint faceData[6]; + uint _pad[10]; }; struct SectionMeta { - uvec4 header; - uvec4 drawdata; + uint posA; + uint posB; + uint AABB; + uint _padA; + uint ptr; + uint cnt; }; //TODO: see if making the stride 2*4*4 bytes or something cause you get that 16 byte write diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl new file mode 100644 index 00000000..0c2588c7 --- /dev/null +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl @@ -0,0 +1,9 @@ +//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off +// but im going to assume that since we are dealing with huge render distances, this shouldent matter that much +float extractFaceIndentation(uint faceData) { + return float((faceData>>16)&63)/63; +} + +vec4 extractFaceSizes(uint faceData) { + return (vec4(faceData&0xF, (faceData>>4)&0xF, (faceData>>8)&0xF, (faceData>>12)&0xF)/16)+vec4(0,1f/16,0,1f/16); +} \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quad_format.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/quad_format.glsl index 2867eaba..51978518 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quad_format.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quad_format.glsl @@ -17,7 +17,7 @@ uint extractFace(uint64_t quad) { } uint extractStateId(uint64_t quad) { - return Eu32(quad, 20, 26); + return Eu32(quad, 16, 26); } uint extractBiomeId(uint64_t quad) { diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag index a2a4f69c..f1e2c3db 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag @@ -4,10 +4,14 @@ layout(binding = 0) uniform sampler2D blockModelAtlas; layout(location = 0) in vec2 uv; layout(location = 1) in flat vec2 baseUV; layout(location = 2) in flat vec4 colourTinting; +layout(location = 3) in flat int discardAlpha; layout(location = 0) out vec4 outColour; void main() { vec2 uv = mod(uv, vec2(1))*(1f/(vec2(3,2)*256f)); - - outColour = texture(blockModelAtlas, uv + baseUV) * colourTinting; + vec4 colour = texture(blockModelAtlas, uv + baseUV); + if (discardAlpha == 1 && colour.a == 0.0f) { + discard; + } + outColour = colour * colourTinting; } \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index f71f3a0d..b855e4ac 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -3,10 +3,13 @@ #import #import +#import +#line 8 layout(location = 0) out vec2 uv; layout(location = 1) out flat vec2 baseUV; layout(location = 2) out flat vec4 colourTinting; +layout(location = 3) out flat int discardAlpha; uint extractLodLevel() { return uint(gl_BaseInstance)>>29; @@ -22,59 +25,69 @@ vec4 uint2vec4RGBA(uint colour) { return vec4((uvec4(colour)>>uvec4(24,16,8,0))&uvec4(0xFF))/255; } +//Gets the face offset with respect to the face direction (e.g. some will be + some will be -) +float getFaceOffset(BlockModel model, uint face) { + float offset = extractFaceIndentation(model.faceData[face]); + return offset * (1-((int(face)&1)*2)); +} + +vec2 getFaceSizeOffset(BlockModel model, uint face, uint corner) { + vec4 faceOffsetsSizes = extractFaceSizes(model.faceData[face]); + return mix(faceOffsetsSizes.xz, -(1-faceOffsetsSizes.yw), bvec2(((corner>>1)&1)==1, (corner&1)==1)); +} + +//TODO: add a mechanism so that some quads can ignore backface culling +// this would help alot with stuff like crops as they would look kinda weird i think, +// same with flowers etc void main() { int cornerIdx = gl_VertexID&3; - Quad quad = quadData[uint(gl_VertexID)>>2]; vec3 innerPos = extractPos(quad); - uint face = extractFace(quad); + uint modelId = extractStateId(quad); + BlockModel model = modelData[modelId]; + + //Change the ordering due to backface culling + //NOTE: when rendering, backface culling is disabled as we simply dispatch calls for each face + // this has the advantage of having "unassigned" geometry, that is geometry where the backface isnt culled + //if (face == 0 || (face>>1 != 0 && (face&1)==1)) { + // cornerIdx ^= 1; + //} uint lodLevel = extractLodLevel(); ivec3 lodCorner = ((extractRelativeLodPos()<>1)&1, cornerIdx&1)); + vec2 size = (quadSize + faceOffset) * (1<>1) == 0) {//Up/down + offset = offset.xzy; } - if ((face>>1) == 0) { - cornerIdx ^= 1; + //Not needed, here for readability + //if ((face>>1) == 1) {//north/south + // offset = offset.xyz; + //} + if ((face>>1) == 2) {//west/east + offset = offset.zxy; } - ivec2 sizePreLod = extractSize(quad) * ivec2((cornerIdx>>1)&1, cornerIdx&1); - ivec2 size = sizePreLod * (1<>1; - if (axis == 0) { - pos.xz += size; - pos.y += (face&1)<>8)&0xFF)*(1f/(256f)); + //Compute the uv coordinates + vec2 modelUV = vec2(modelId&0xFF, (modelId>>8)&0xFF)*(1f/(256f)); //TODO: make the face orientated by 2x3 so that division is not a integer div and modulo isnt needed // as these are very slow ops baseUV = modelUV + (vec2(face%3, face/3) * (1f/(vec2(3,2)*256f))); + uv = quadSize + faceOffset;//Add in the face offset for 0,0 uv + discardAlpha = 0; + + //Compute lighting colourTinting = getLighting(extractLightId(quad)); //Apply face tint if (face == 0) { @@ -82,21 +95,4 @@ void main() { } else if (face != 1) { colourTinting.xyz *= vec3((float(face-2)/4)*0.6 + 0.4); } - -} -//gl_Position = MVP * vec4(vec3(((cornerIdx)&1)+10,10,((cornerIdx>>1)&1)+10),1); -//uint i = uint(quad>>32); -//uint i = uint(gl_BaseInstance); -//i ^= i>>16; -//i *= 124128573; -//i ^= i>>16; -//i *= 4211346123; -//i ^= i>>16; -//i *= 462312435; -//i ^= i>>16; -//i *= 542354341; -//i ^= i>>16; - -//uint i = uint(lodLevel+12)*215387625; -//colour *= vec4(vec3(float((uint(i)>>2)&7)/7,float((uint(i)>>5)&7)/7,float((uint(i)>>8)&7)/7)*0.7+0.3,1); -//colour = vec4(vec3(float((uint(i)>>2)&7)/7,float((uint(i)>>5)&7)/7,float((uint(i)>>8)&7)/7),1); \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl index 3a1842b7..5aab92a5 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/section.glsl @@ -1,21 +1,21 @@ uint extractDetail(SectionMeta section) { - return section.header.x>>28; + return section.posA>>28; } ivec3 extractPosition(SectionMeta section) { - int y = ((int(section.header.x)<<4)>>24); - int x = (int(section.header.y)<<4)>>8; - int z = int((section.header.x&((1<<20)-1))<<4); - z |= int(section.header.y>>28); + int y = ((int(section.posA)<<4)>>24); + int x = (int(section.posB)<<4)>>8; + int z = int((section.posA&((1<<20)-1))<<4); + z |= int(section.posB>>28); z <<= 8; z >>= 8; return ivec3(x,y,z); } uint extractQuadStart(SectionMeta meta) { - return meta.drawdata.x; + return meta.ptr; } uint extractQuadCount(SectionMeta meta) { - return meta.drawdata.y; + return meta.cnt; } From 002005a6a23787b3051c395f6f862fbedd370eee Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:02:52 +1000 Subject: [PATCH 16/28] e --- .../java/me/cortex/zenith/client/core/model/ModelManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e122f701..2cf57a71 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 @@ -222,7 +222,7 @@ public class ModelManager { faceModelData |= Math.round(offset*63)<<16;//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model //Still have 11 bits free - MemoryUtil.memPutInt(faceUploadPtr, 0); + MemoryUtil.memPutInt(faceUploadPtr, faceModelData); } this.metadataCache[modelId] = metadata; From 205f043f2e6bdb1bf619821bc6ea70cf08207449 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:53:03 +1000 Subject: [PATCH 17/28] It works --- .../zenith/client/core/model/ModelManager.java | 2 +- .../rendering/building/RenderDataFactory.java | 17 +++++++++-------- .../assets/zenith/shaders/lod/gl46/quads.vert | 8 ++++---- 3 files changed, 14 insertions(+), 13 deletions(-) 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 2cf57a71..3b1fac57 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 @@ -188,7 +188,7 @@ public class ModelManager { if (offset < -0.1) {//Face is empty, so ignore metadata |= 0xFF;//Mark the face as non-existent //Set to -1 as safepoint - MemoryUtil.memPutLong(faceUploadPtr, -1); + MemoryUtil.memPutInt(faceUploadPtr, -1); continue; } var faceSize = TextureUtils.computeBounds(textureData[face], checkMode); 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 844afead..9e904667 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 @@ -33,15 +33,16 @@ public class RenderDataFactory { // 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 - var buff = new MemoryBuffer(8*1); - //MemoryUtil.memPutLong(buff.address, encodeRaw(1, 0,2,0,0,0,515,0, 0));//92 - //MemoryUtil.memPutLong(buff.address+8, encodeRaw(2, 1,1,0,0,0,515,0, 0));//92 - //MemoryUtil.memPutLong(buff.address+16, encodeRaw(3, 0,0,0,0,0,515,0, 0));//92 - //MemoryUtil.memPutLong(buff.address+24, encodeRaw(4, 0,2,0,0,0,515,0, 0));//92 - //MemoryUtil.memPutLong(buff.address+32, encodeRaw(5, 0,2,0,0,0,515,0, 0));//92 + var buff = new MemoryBuffer(8*8); + MemoryUtil.memPutLong(buff.address, encodeRaw(2, 0,0,0,0,0,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+8, encodeRaw(3, 0,0,0,0,0,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+16, encodeRaw(4, 0,2,0,0,0,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+24, encodeRaw(5, 0,2,0,0,0,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+32, encodeRaw(2, 0,0,0,0,1,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+40, encodeRaw(3, 0,0,0,0,1,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+48, encodeRaw(2, 0,0,0,0,2,515,0, 0));//92 + MemoryUtil.memPutLong(buff.address+56, encodeRaw(3, 0,0,0,0,2,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address, encodeRaw(2, 1,1,0,0,0,515,0, 0));//92 - //MemoryUtil.memPutLong(buff.address, encodeRaw(3, 0,0,0,0,0,515,0, 0));//92 return new BuiltSection(section.getKey(), new BuiltSectionGeometry(buff, new short[0]), null); } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index b855e4ac..789bb8c6 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -26,7 +26,7 @@ vec4 uint2vec4RGBA(uint colour) { } //Gets the face offset with respect to the face direction (e.g. some will be + some will be -) -float getFaceOffset(BlockModel model, uint face) { +float getDepthOffset(BlockModel model, uint face) { float offset = extractFaceIndentation(model.faceData[face]); return offset * (1-((int(face)&1)*2)); } @@ -62,7 +62,7 @@ void main() { vec2 quadSize = vec2(extractSize(quad) * ivec2((cornerIdx>>1)&1, cornerIdx&1)); vec2 size = (quadSize + faceOffset) * (1<>1) == 0) {//Up/down offset = offset.xzy; @@ -83,9 +83,9 @@ void main() { //TODO: make the face orientated by 2x3 so that division is not a integer div and modulo isnt needed // as these are very slow ops baseUV = modelUV + (vec2(face%3, face/3) * (1f/(vec2(3,2)*256f))); - uv = quadSize + faceOffset;//Add in the face offset for 0,0 uv + uv = quadSize;// + faceOffset;//Add in the face offset for 0,0 uv - discardAlpha = 0; + discardAlpha = 1; //Compute lighting colourTinting = getLighting(extractLightId(quad)); From c4efd2920691978b88ef63fae8c77660c27cd3fe Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 28 Jan 2024 22:14:04 +1000 Subject: [PATCH 18/28] It works even more :3 --- .../zenith/client/core/DistanceTracker.java | 20 ++-- .../cortex/zenith/client/core/VoxelCore.java | 10 +- .../client/core/model/ModelManager.java | 31 ++++++ .../core/rendering/building/QuadEncoder.java | 2 + .../rendering/building/RenderDataFactory.java | 96 +++++++++++++++++-- .../building/RenderDataFactoryOld.java | 2 + .../building/RenderGenerationService.java | 7 +- .../mixin/sodium/MixinSodiumWorldRender.java | 19 ++++ .../zenith/common/world/other/Mapper.java | 18 ++-- .../zenith/shaders/lod/gl46/bindings.glsl | 2 + src/main/resources/zenith.mixins.json | 3 +- 11 files changed, 173 insertions(+), 37 deletions(-) create mode 100644 src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java diff --git a/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java b/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java index 4ec07d34..3300157e 100644 --- a/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java +++ b/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java @@ -28,15 +28,17 @@ public class DistanceTracker { this.minYSection = MinecraftClient.getInstance().world.getBottomSectionCoord()/2; this.maxYSection = MinecraftClient.getInstance().world.getTopSectionCoord()/2; - this.rings[0] = new TransitionRing2D(5, MinecraftClient.getInstance().options.getViewDistance().getValue()/2, (x, z)->{ - for (int y = this.minYSection; y <= this.maxYSection; y++) { - this.tracker.remLvl0(x, y, z); - } - }, (x, z) -> { - for (int y = this.minYSection; y <= this.maxYSection; y++) { - this.tracker.addLvl0(x, y, z); - } - }); + if (false) { + this.rings[0] = new TransitionRing2D(5, MinecraftClient.getInstance().options.getViewDistance().getValue() / 2, (x, z) -> { + for (int y = this.minYSection; y <= this.maxYSection; y++) { + this.tracker.remLvl0(x, y, z); + } + }, (x, z) -> { + for (int y = this.minYSection; y <= this.maxYSection; y++) { + this.tracker.addLvl0(x, y, z); + } + }); + } //The rings 0+ start at 64 vanilla rd, no matter what the game is set at, that is if the game is set to 32 rd // there will still be 32 chunks untill the first lod drop 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 f5fef648..3a15ab1d 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -66,7 +66,7 @@ public class VoxelCore { System.out.println("World engine"); this.renderTracker = new RenderTracker(this.world, this.renderer); - this.renderGen = new RenderGenerationService(this.world,ZenithConfig.CONFIG.renderThreads, this.renderTracker::processBuildResult); + this.renderGen = new RenderGenerationService(this.world, this.renderer.getModelManager(), ZenithConfig.CONFIG.renderThreads, this.renderTracker::processBuildResult); this.world.setDirtyCallback(this.renderTracker::sectionUpdated); this.renderTracker.setRenderGen(this.renderGen); System.out.println("Render tracker and generator initialized"); @@ -97,14 +97,10 @@ public class VoxelCore { boolean firstTime = true; public void renderSetup(Frustum frustum, Camera camera) { if (this.firstTime) { - //this.distanceTracker.init(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); + this.distanceTracker.init(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); this.firstTime = false; - - this.renderTracker.addLvl0(0,6,0); - - } - //this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); + this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); this.renderer.setupRender(frustum, camera); } 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 3b1fac57..a9fe5e84 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 @@ -212,6 +212,15 @@ public class ModelManager { metadata |= occludesFace?1:0; + + boolean canBeOccluded = true; + //TODO: make this an option on how far/close + canBeOccluded &= offset < 0.3;//If the face is rendered far away from the other face, then it cant be occluded + + metadata |= canBeOccluded?4:0; + + + //Scale face size from 0->this.modelTextureSize-1 to 0->15 for (int i = 0; i < 4; i++) { faceSize[i] = Math.round((((float)faceSize[i])/(this.modelTextureSize-1))*15); @@ -236,6 +245,28 @@ public class ModelManager { } + public static boolean faceExists(long metadata, int face) { + return ((metadata>>(8*face))&0xFF)!=0xFF; + } + + public static boolean faceCanBeOccluded(long metadata, int face) { + return ((metadata>>(8*face))&0b100)==0b100; + } + + public static boolean faceOccludes(long metadata, int face) { + return faceExists(metadata, face) && ((metadata>>(8*face))&0b1)==0b1; + } + + public static boolean isColoured(long metadata) { + //TODO: THIS + return false; + } + + public static boolean isTranslucent(long metadata) { + //TODO: THIS + return false; + } + diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/QuadEncoder.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/QuadEncoder.java index a75b5a07..4bc206f0 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/QuadEncoder.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/QuadEncoder.java @@ -1,6 +1,8 @@ package me.cortex.zenith.client.core.rendering.building; +import me.cortex.zenith.client.core.model.ModelManager; +import me.cortex.zenith.client.core.rendering.GeometryManager; import me.cortex.zenith.client.core.util.Mesher2D; import me.cortex.zenith.common.world.other.Mapper; import net.minecraft.client.color.block.BlockColors; 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 9e904667..f412aecf 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 @@ -1,19 +1,27 @@ package me.cortex.zenith.client.core.rendering.building; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import me.cortex.zenith.client.core.model.ModelManager; +import me.cortex.zenith.client.core.util.Mesher2D; 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.client.MinecraftClient; import org.lwjgl.system.MemoryUtil; +import java.util.Map; + public class RenderDataFactory { private final WorldEngine world; + private final ModelManager modelMan; private final QuadEncoder encoder; private final long[] sectionCache = new long[32*32*32]; private final long[] connectedSectionCache = new long[32*32*32]; - public RenderDataFactory(WorldEngine world) { + public RenderDataFactory(WorldEngine world, ModelManager modelManager) { this.world = world; + this.modelMan = modelManager; this.encoder = new QuadEncoder(world.getMapper(), MinecraftClient.getInstance().getBlockColors(), MinecraftClient.getInstance().world); } @@ -33,15 +41,83 @@ public class RenderDataFactory { // 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 - var buff = new MemoryBuffer(8*8); - MemoryUtil.memPutLong(buff.address, encodeRaw(2, 0,0,0,0,0,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+8, encodeRaw(3, 0,0,0,0,0,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+16, encodeRaw(4, 0,2,0,0,0,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+24, encodeRaw(5, 0,2,0,0,0,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+32, encodeRaw(2, 0,0,0,0,1,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+40, encodeRaw(3, 0,0,0,0,1,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+48, encodeRaw(2, 0,0,0,0,2,515,0, 0));//92 - MemoryUtil.memPutLong(buff.address+56, encodeRaw(3, 0,0,0,0,2,515,0, 0));//92 + + Mesher2D mesher = new Mesher2D(5,15); + + LongArrayList outData = new LongArrayList(1000); + + //Up direction + + for (int y = 0; y < 32; y++) { + mesher.reset(); + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long metadata = this.modelMan.getModelMetadata(selfBlockId); + + //If the model doesnt have a face, then just skip it + if (!ModelManager.faceExists(metadata, 1)) { + continue; + } + + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (y == 31) { + + } else { + facingState = this.sectionCache[WorldSection.getIndex(x, y+1, z)]; + } + + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, 1) && ModelManager.faceOccludes(facingMetadata, 0)) { + continue; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + + mesher.put(x, z, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); + } + } + + //TODO: encode translucents and double sided quads to different global buffers + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(1, y, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); + //outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(1, y, quad)) | (1<<26)); + } + } + + + + + + //var buff = new MemoryBuffer(8*8); + //MemoryUtil.memPutLong(buff.address, encodeRaw(2, 0,1,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+8, encodeRaw(3, 0,1,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+16, encodeRaw(4, 1,2,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+24, encodeRaw(5, 1,2,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+32, encodeRaw(2, 0,1,0,0,1,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+40, encodeRaw(3, 0,1,0,0,1,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+48, encodeRaw(2, 0,1,0,0,2,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+56, encodeRaw(3, 0,1,0,0,2,159,0, 0));//92 515 + if (outData.isEmpty()) { + return new BuiltSection(section.getKey(), null, null); + } + + //outData.add(encodeRaw(3, 0,1,0,0,0,159,0, 0)); + var buff = new MemoryBuffer(outData.size()*8L); + long ptr = buff.address; + for (long data : outData) { + MemoryUtil.memPutLong(ptr, data); ptr+=8; + } return new BuiltSection(section.getKey(), new BuiltSectionGeometry(buff, new short[0]), null); } 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 index b2de5790..70eedb32 100644 --- 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 @@ -12,6 +12,7 @@ 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; @@ -331,5 +332,6 @@ public class RenderDataFactoryOld { this.outData.clear(); //return new BuiltSectionGeometry(section.getKey(), output, null); } + */ } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java index 7c361e6e..2ebf190c 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderGenerationService.java @@ -1,6 +1,7 @@ package me.cortex.zenith.client.core.rendering.building; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import me.cortex.zenith.client.core.model.ModelManager; import me.cortex.zenith.common.world.WorldEngine; import me.cortex.zenith.common.world.WorldSection; @@ -22,10 +23,12 @@ public class RenderGenerationService { private final Semaphore taskCounter = new Semaphore(0); private final WorldEngine world; + private final ModelManager modelManager; private final Consumer resultConsumer; - public RenderGenerationService(WorldEngine world, int workers, Consumer consumer) { + public RenderGenerationService(WorldEngine world, ModelManager modelManager, int workers, Consumer consumer) { this.world = world; + this.modelManager = modelManager; this.resultConsumer = consumer; this.workers = new Thread[workers]; for (int i = 0; i < workers; i++) { @@ -41,7 +44,7 @@ public class RenderGenerationService { //TODO: add a generated render data cache private void renderWorker() { //Thread local instance of the factory - var factory = new RenderDataFactory(this.world); + var factory = new RenderDataFactory(this.world, this.modelManager); while (this.running) { this.taskCounter.acquireUninterruptibly(); if (!this.running) break; diff --git a/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java b/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java new file mode 100644 index 00000000..be7ae1e8 --- /dev/null +++ b/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java @@ -0,0 +1,19 @@ +package me.cortex.zenith.client.mixin.sodium; + +import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; +import me.jellysquid.mods.sodium.client.render.chunk.occlusion.OcclusionCuller; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.util.math.MatrixStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = SodiumWorldRenderer.class, remap = false) +public class MixinSodiumWorldRender { + @Inject(method = "drawChunkLayer", at = @At("HEAD"), cancellable = true) + private void cancelRender(RenderLayer renderLayer, MatrixStack matrixStack, double x, double y, double z, CallbackInfo ci) { + ci.cancel(); + } +} diff --git a/src/main/java/me/cortex/zenith/common/world/other/Mapper.java b/src/main/java/me/cortex/zenith/common/world/other/Mapper.java index 8e94130a..4dea991a 100644 --- a/src/main/java/me/cortex/zenith/common/world/other/Mapper.java +++ b/src/main/java/me/cortex/zenith/common/world/other/Mapper.java @@ -51,18 +51,20 @@ public class Mapper { } - public static boolean isTranslucent(long id) { - //Atm hardcode to air - return ((id>>27)&((1<<20)-1)) == 0; - } - public static boolean isAir(long id) { return ((id>>27)&((1<<20)-1)) == 0; } - public static boolean shouldRenderFace(int dirId, long self, long other) { - //TODO: fixme make it be with respect to the type itself e.g. water, glass etc - return isTranslucent(other); + public static int getBlockId(long id) { + return (int) ((id>>27)&((1<<20)-1)); + } + + public static int getBiomeId(long id) { + return (int) ((id>>47)&0x1FF); + } + + public static int getLightId(long id) { + return (int) ((id>>56)&0xFF); } public void setCallbacks(Consumer stateCallback, Consumer biomeCallback) { diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index b52fba54..da13de92 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -23,6 +23,8 @@ struct SectionMeta { uint _padA; uint ptr; uint cnt; + uint _padB; + uint _padC; }; //TODO: see if making the stride 2*4*4 bytes or something cause you get that 16 byte write diff --git a/src/main/resources/zenith.mixins.json b/src/main/resources/zenith.mixins.json index 93bb39f8..11b2dd81 100644 --- a/src/main/resources/zenith.mixins.json +++ b/src/main/resources/zenith.mixins.json @@ -15,6 +15,7 @@ "defaultRequire": 1 }, "mixins": [ - "sodium.MixinOcclusionCuller" + "sodium.MixinOcclusionCuller", + "sodium.MixinSodiumWorldRender" ] } From 6302d3db1e4bfb1db844b4c02c197ed5a37b33e0 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:18:16 +1000 Subject: [PATCH 19/28] Lots of fixes --- .../cortex/zenith/client/core/VoxelCore.java | 1 + .../client/core/model/ModelTextureBakery.java | 26 +- .../core/rendering/Gl46FarWorldRenderer.java | 23 +- .../rendering/building/RenderDataFactory.java | 256 +++++++++++++++++- .../zenith/common/world/other/Mapper.java | 8 + .../zenith/shaders/lod/gl46/bindings.glsl | 13 +- .../zenith/shaders/lod/gl46/cmdgen.comp | 10 +- .../assets/zenith/shaders/lod/gl46/quads.frag | 2 + .../assets/zenith/shaders/lod/gl46/quads.vert | 4 +- 9 files changed, 308 insertions(+), 35 deletions(-) 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 3a15ab1d..87a60391 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -99,6 +99,7 @@ public class VoxelCore { if (this.firstTime) { this.distanceTracker.init(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); this.firstTime = false; + //this.renderTracker.addLvl0(0,6,0); } this.distanceTracker.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); this.renderer.setupRender(frustum, camera); 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 8e03671b..96bcef4f 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 @@ -49,15 +49,7 @@ public class ModelTextureBakery { .compile(); private static final List FACE_VIEWS = new ArrayList<>(); - static { - addView(-90,0, 0);//Direction.DOWN - addView(90,0, 0);//Direction.UP - addView(0,180, 0);//Direction.NORTH - addView(0,0, 0);//Direction.SOUTH - //TODO: check these arnt the wrong way round - addView(0,90, 270);//Direction.EAST - addView(0,270, 270);//Direction.WEST - } + public ModelTextureBakery(int width, int height) { this.width = width; @@ -65,9 +57,23 @@ public class ModelTextureBakery { this.colourTex = new GlTexture().store(GL_RGBA8, 1, width, height); this.depthTex = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width, height); this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify(); + + //This is done to help make debugging easier + FACE_VIEWS.clear(); + AddViews(); } - private static void addView(float pitch, float yaw, float rotation) { + private static void AddViews() { + addView(-90,0, 0, false);//Direction.DOWN + addView(90,0, 0, false);//Direction.UP + addView(0,180, 0, true);//Direction.NORTH + addView(0,0, 0, false);//Direction.SOUTH + //TODO: check these arnt the wrong way round + addView(0,90, 270, false);//Direction.EAST + addView(0,270, 270, false);//Direction.WEST + } + + private static void addView(float pitch, float yaw, float rotation, boolean flipX) { var stack = new MatrixStack(); stack.translate(0.5f,0.5f,0.5f); stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotation)); 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 4822d8cd..f2e4c127 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 @@ -14,11 +14,14 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.Direction; import org.joml.Matrix4f; import org.joml.Vector3f; +import org.lwjgl.opengl.ARBIndirectParameters; import org.lwjgl.opengl.GL11C; import org.lwjgl.system.MemoryUtil; import java.util.List; +import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB; +import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB; import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT; @@ -51,11 +54,13 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { .compile(); private final GlBuffer glCommandBuffer; + private final GlBuffer glCommandCountBuffer; private final GlBuffer glVisibilityBuffer; public Gl46FarWorldRenderer(int geometryBuffer, int maxSections) { super(geometryBuffer, maxSections); this.glCommandBuffer = new GlBuffer(maxSections*5L*4); + this.glCommandCountBuffer = new GlBuffer(4*2); this.glVisibilityBuffer = new GlBuffer(maxSections*4L); glClearNamedBufferData(this.glCommandBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); glClearNamedBufferData(this.glVisibilityBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); @@ -66,14 +71,16 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { protected void setupVao() { glBindVertexArray(this.vao); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.glCommandBuffer.id); + glBindBuffer(GL_PARAMETER_BUFFER_ARB, this.glCommandCountBuffer.id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId()); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.glCommandBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.geometry.metaId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.glVisibilityBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.models.getBufferId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.lightDataBuffer.id);//Lighting LUT + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.glCommandCountBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometry.metaId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.glVisibilityBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.lightDataBuffer.id);//Lighting LUT glBindVertexArray(0); } @@ -82,6 +89,8 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { return; } + //this.models.addEntry(0, Blocks.OAK_FENCE.getDefaultState()); + RenderLayer.getCutoutMipped().startDrawing(); int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); @@ -101,16 +110,15 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, this.models.getTextureId()); - + glClearNamedBufferData(this.glCommandCountBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[1]); this.commandGen.bind(); glDispatchCompute((this.geometry.getSectionCount() + 127) / 128, 1, 1); glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT); this.lodShader.bind(); glDisable(GL_CULL_FACE); - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, this.geometry.getSectionCount(), 0); + glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, 0, (int) (this.geometry.getSectionCount()*4.4), 0); glEnable(GL_CULL_FACE); - //ARBIndirectParameters.glMultiDrawElementsIndirectCountARB( glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); @@ -164,6 +172,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { this.cullShader.free(); this.glCommandBuffer.free(); this.glVisibilityBuffer.free(); + this.glCommandCountBuffer.free(); } @Override 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 f412aecf..cc8181fe 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.Blocks; import net.minecraft.client.MinecraftClient; import org.lwjgl.system.MemoryUtil; @@ -47,7 +48,6 @@ public class RenderDataFactory { LongArrayList outData = new LongArrayList(1000); //Up direction - for (int y = 0; y < 32; y++) { mesher.reset(); for (int x = 0; x < 32; x++) { @@ -91,7 +91,195 @@ public class RenderDataFactory { int quad = array[i]; long data = mesher.getDataFromQuad(quad); outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(1, y, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); - //outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(1, y, quad)) | (1<<26)); + } + } + + + //North direction + for (int z = 0; z < 32; z++) { + mesher.reset(); + for (int x = 0; x < 32; x++) { + for (int y = 0; y < 32; y++) { + long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long metadata = this.modelMan.getModelMetadata(selfBlockId); + + //If the model doesnt have a face, then just skip it + if (!ModelManager.faceExists(metadata, 2)) { + continue; + } + + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (z == 0) { + + } else { + facingState = this.sectionCache[WorldSection.getIndex(x, y, z-1)]; + } + + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, 2) && ModelManager.faceOccludes(facingMetadata, 3)) { + continue; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + + mesher.put(x, y, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); + } + } + + //TODO: encode translucents and double sided quads to different global buffers + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(2, z, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); + } + } + + //South direction + for (int z = 0; z < 32; z++) { + mesher.reset(); + for (int x = 0; x < 32; x++) { + for (int y = 0; y < 32; y++) { + long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long metadata = this.modelMan.getModelMetadata(selfBlockId); + + //If the model doesnt have a face, then just skip it + if (!ModelManager.faceExists(metadata, 3)) { + continue; + } + + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (z == 31) { + + } else { + facingState = this.sectionCache[WorldSection.getIndex(x, y, z+1)]; + } + + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, 3) && ModelManager.faceOccludes(facingMetadata, 2)) { + continue; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + + mesher.put(x, y, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); + } + } + + //TODO: encode translucents and double sided quads to different global buffers + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(3, z, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); + } + } + + //West direction + for (int x = 0; x < 32; x++) { + mesher.reset(); + for (int y = 0; y < 32; y++) { + for (int z = 0; z < 32; z++) { + long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long metadata = this.modelMan.getModelMetadata(selfBlockId); + + //If the model doesnt have a face, then just skip it + if (!ModelManager.faceExists(metadata, 2)) { + continue; + } + + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (x == 0) { + + } else { + facingState = this.sectionCache[WorldSection.getIndex(x-1, y, z)]; + } + + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, 4) && ModelManager.faceOccludes(facingMetadata, 5)) { + continue; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + + mesher.put(y, z, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); + } + } + + //TODO: encode translucents and double sided quads to different global buffers + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(4, x, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); + } + } + + //East direction + for (int x = 0; x < 32; x++) { + mesher.reset(); + for (int y = 0; y < 32; y++) { + for (int z = 0; z < 32; z++) { + long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long metadata = this.modelMan.getModelMetadata(selfBlockId); + + //If the model doesnt have a face, then just skip it + if (!ModelManager.faceExists(metadata, 2)) { + continue; + } + + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (x == 31) { + + } else { + facingState = this.sectionCache[WorldSection.getIndex(x+1, y, z)]; + } + + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, 5) && ModelManager.faceOccludes(facingMetadata, 4)) { + continue; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + + mesher.put(y, z, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); + } + } + + //TODO: encode translucents and double sided quads to different global buffers + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(5, x, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); } } @@ -111,8 +299,18 @@ public class RenderDataFactory { if (outData.isEmpty()) { return new BuiltSection(section.getKey(), null, null); } + //outData.clear(); + + //int modelId = this.modelMan.getModelId(this.world.getMapper().getIdFromBlockState(Blocks.OAK_FENCE.getDefaultState())); + int modelId = this.modelMan.getModelId(this.world.getMapper().getIdFromBlockState(Blocks.OAK_FENCE.getDefaultState())); + + //outData.add(encodeRaw(0, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(1, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(2, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(3, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(4, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(5, 0,0,0,0,0, modelId,0, 0)); - //outData.add(encodeRaw(3, 0,1,0,0,0,159,0, 0)); var buff = new MemoryBuffer(outData.size()*8L); long ptr = buff.address; for (long data : outData) { @@ -127,4 +325,56 @@ public class RenderDataFactory { return ((long)face) | (((long) width)<<3) | (((long) height)<<7) | (((long) z)<<11) | (((long) y)<<16) | (((long) x)<<21) | (((long) blockId)<<26) | (((long) biomeId)<<46) | (((long) lightId)<<55); } + + /* + private void generateMeshForAxis() { + + //Up direction + for (int y = 0; y < 32; y++) { + mesher.reset(); + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long metadata = this.modelMan.getModelMetadata(selfBlockId); + + //If the model doesnt have a face, then just skip it + if (!ModelManager.faceExists(metadata, 1)) { + continue; + } + + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (y == 31) { + + } else { + facingState = this.sectionCache[WorldSection.getIndex(x, y+1, z)]; + } + + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, 1) && ModelManager.faceOccludes(facingMetadata, 0)) { + continue; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + + mesher.put(x, z, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); + } + } + + //TODO: encode translucents and double sided quads to different global buffers + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(1, y, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); + } + } + }*/ + } diff --git a/src/main/java/me/cortex/zenith/common/world/other/Mapper.java b/src/main/java/me/cortex/zenith/common/world/other/Mapper.java index 4dea991a..1e4b8037 100644 --- a/src/main/java/me/cortex/zenith/common/world/other/Mapper.java +++ b/src/main/java/me/cortex/zenith/common/world/other/Mapper.java @@ -185,6 +185,14 @@ public class Mapper { return this.blockId2stateEntry.get(blockId).state; } + public int getIdFromBlockState(BlockState state) { + var entry = this.block2stateEntry.get(state); + if (entry == null) { + return -1; + } + return entry.id; + } + //TODO: fixme: synchronize access to this.blockId2stateEntry public StateEntry[] getStateEntries() { var set = new ArrayList<>(this.blockId2stateEntry); diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index da13de92..e923999b 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -49,22 +49,27 @@ layout(binding = 2, std430) writeonly restrict buffer DrawBuffer { DrawCommand cmdBuffer[]; }; -layout(binding = 3, std430) readonly restrict buffer SectionBuffer { +layout(binding = 3, std430) restrict buffer DrawCommandCountBuffer { + uint opaqueDrawCount; + uint translucentDrawCount; +}; + +layout(binding = 4, std430) readonly restrict buffer SectionBuffer { SectionMeta sectionData[]; }; #ifndef VISIBILITY_ACCESS #define VISIBILITY_ACCESS readonly #endif -layout(binding = 4, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer { +layout(binding = 5, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer { uint visibilityData[]; }; -layout(binding = 5, std430) readonly restrict buffer ModelBuffer { +layout(binding = 6, std430) readonly restrict buffer ModelBuffer { BlockModel modelData[]; }; -layout(binding = 6, std430) readonly restrict buffer LightingBuffer { +layout(binding = 7, std430) readonly restrict buffer LightingBuffer { uint lightData[]; }; diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp index 1df241c0..4d0dfc55 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp @@ -61,14 +61,6 @@ void main() { cmd.firstIndex = 0; cmd.baseVertex = int(extractQuadStart(meta))<<2; cmd.baseInstance = encodeLocalLodPos(detail, ipos); - cmdBuffer[gl_GlobalInvocationID.x] = cmd; - } else { - DrawCommand cmd; - cmd.count = 0; - cmd.instanceCount = 0; - cmd.firstIndex = 0; - cmd.baseVertex = 0; - cmd.baseInstance = 0; - cmdBuffer[gl_GlobalInvocationID.x] = cmd; + cmdBuffer[atomicAdd(opaqueDrawCount, 1)] = cmd; } } \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag index f1e2c3db..0f7db1de 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag @@ -14,4 +14,6 @@ void main() { discard; } outColour = colour * colourTinting; + + //outColour = vec4(uv + baseUV, 0, 1); } \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index 789bb8c6..297021ed 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -83,9 +83,9 @@ void main() { //TODO: make the face orientated by 2x3 so that division is not a integer div and modulo isnt needed // as these are very slow ops baseUV = modelUV + (vec2(face%3, face/3) * (1f/(vec2(3,2)*256f))); - uv = quadSize;// + faceOffset;//Add in the face offset for 0,0 uv + uv = quadSize + faceOffset;//Add in the face offset for 0,0 uv - discardAlpha = 1; + discardAlpha = 0; //Compute lighting colourTinting = getLighting(extractLightId(quad)); From d499a19d4eef9112d8b62eb0e5862bae55dbc972 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:50:46 +1000 Subject: [PATCH 20/28] Added alpha discard flag to faces --- .../client/core/model/ModelManager.java | 37 ++++++++++++++----- .../rendering/AbstractFarWorldRenderer.java | 2 +- .../core/rendering/Gl46FarWorldRenderer.java | 1 + .../zenith/shaders/lod/gl46/bindings.glsl | 3 +- .../zenith/shaders/lod/gl46/block_model.glsl | 4 ++ .../assets/zenith/shaders/lod/gl46/quads.frag | 2 +- .../assets/zenith/shaders/lod/gl46/quads.vert | 17 +++++---- 7 files changed, 46 insertions(+), 20 deletions(-) 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 a9fe5e84..d6fa3119 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 @@ -160,25 +160,29 @@ public class ModelManager { long uploadPtr = UploadStream.INSTANCE.upload(this.modelBuffer, (long) modelId * MODEL_SIZE, MODEL_SIZE); - //TODO: implement + //TODO: implement; + // TODO: if it has a constant colour instead... idk why (apparently for things like spruce leaves)?? but premultiply the texture data by the constant colour boolean hasBiomeColourResolver = false; + + //TODO: special case stuff like vines and glow lichen, where it can be represented by a single double sided quad + // since that would help alot with perf of lots of vines, can be done by having one of the faces just not exist and the other be in no occlusion mode + + var sizes = this.computeModelDepth(textureData, checkMode); + //TODO: THIS, note this can be tested for in 2 ways, re render the model with quad culling disabled and see if the result // is the same, (if yes then needs double sided quads) // another way to test it is if e.g. up and down havent got anything rendered but the sides do (e.g. all plants etc) - boolean needsDoubleSidedQuads = false; - - //TODO: special case stuff like vines and glow lichen, where it can be represented by a single double sided quad - // since that would help alot with perf of lots of vines + boolean needsDoubleSidedQuads = (sizes[0] < -0.1 && sizes[1] < -0.1) || (sizes[2] < -0.1 && sizes[3] < -0.1) || (sizes[4] < -0.1 && sizes[5] < -0.1); - //This also checks if there is a block colour resolver for the given blockstate and marks that the block has a resolver - var sizes = this.computeModelDepth(textureData, checkMode); //Each face gets 1 byte, with the top 2 bytes being for whatever - long metadata = hasBiomeColourResolver?1:0; + long metadata = 0; + metadata |= hasBiomeColourResolver?1:0; metadata |= blockRenderLayer == RenderLayer.getTranslucent()?2:0; + metadata |= needsDoubleSidedQuads?4:0; //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 @@ -192,6 +196,7 @@ public class ModelManager { continue; } var faceSize = TextureUtils.computeBounds(textureData[face], checkMode); + int writeCount = TextureUtils.getWrittenPixelCount(textureData[face], checkMode); boolean faceCoversFullBlock = faceSize[0] == 0 && faceSize[2] == 0 && faceSize[1] == (this.modelTextureSize-1) && faceSize[3] == (this.modelTextureSize-1); @@ -206,7 +211,6 @@ public class ModelManager { occludesFace &= offset < 0.1;//If the face is rendered far away from the other face, then it doesnt occlude if (occludesFace) { - int writeCount = TextureUtils.getWrittenPixelCount(textureData[face], checkMode); occludesFace &= ((float)writeCount)/(this.modelTextureSize * this.modelTextureSize) > 0.9;// only occlude if the face covers more than 90% of the face } metadata |= occludesFace?1:0; @@ -231,6 +235,14 @@ public class ModelManager { faceModelData |= Math.round(offset*63)<<16;//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model //Still have 11 bits free + //Stuff like fences are solid, however they have extra side piece that mean it needs to have discard on + int area = (faceSize[1]-faceSize[0]+1) * (faceSize[3]-faceSize[2]+1); + boolean needsAlphaDiscard = ((float)writeCount)/area<0.9;//If the amount of area covered by written pixels is less than a threashold, disable discard as its not needed + + needsAlphaDiscard |= blockRenderLayer != RenderLayer.getSolid(); + + faceModelData |= needsAlphaDiscard?1<<22:0; + MemoryUtil.memPutInt(faceUploadPtr, faceModelData); } this.metadataCache[modelId] = metadata; @@ -238,6 +250,9 @@ public class ModelManager { uploadPtr += 4*6; //Have 40 bytes free for remaining model data // todo: put in like the render layer type ig? along with colour resolver info + int modelFlags = 0; + //modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha + MemoryUtil.memPutInt(uploadPtr, modelFlags); @@ -344,4 +359,8 @@ public class ModelManager { this.textures.free(); glDeleteSamplers(this.blockSampler); } + + public void addDebugInfo(List info) { + + } } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index e3146be5..9c634147 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -90,7 +90,7 @@ public abstract class AbstractFarWorldRenderer { } public void addDebugData(List debug) { - + this.models.addDebugInfo(debug); } public void shutdown() { 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 f2e4c127..16745612 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 @@ -177,6 +177,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { @Override public void addDebugData(List debug) { + super.addDebugData(debug); debug.add("Geometry buffer usage: " + ((float)Math.round((this.geometry.getGeometryBufferUsage()*100000))/1000) + "%"); debug.add("Render Sections: " + this.geometry.getSectionCount()); } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index e923999b..97cafa50 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -13,7 +13,8 @@ layout(binding = 0, std140) uniform SceneUniform { struct BlockModel { uint faceData[6]; - uint _pad[10]; + uint flagsA; + uint _pad[9]; }; struct SectionMeta { diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl index 0c2588c7..9a8198e4 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl @@ -6,4 +6,8 @@ float extractFaceIndentation(uint faceData) { vec4 extractFaceSizes(uint faceData) { return (vec4(faceData&0xF, (faceData>>4)&0xF, (faceData>>8)&0xF, (faceData>>12)&0xF)/16)+vec4(0,1f/16,0,1f/16); +} + +uint faceHasAlphaCuttout(uint faceData) { + return (faceData>>22)&1; } \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag index 0f7db1de..208cbb02 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.frag @@ -4,7 +4,7 @@ layout(binding = 0) uniform sampler2D blockModelAtlas; layout(location = 0) in vec2 uv; layout(location = 1) in flat vec2 baseUV; layout(location = 2) in flat vec4 colourTinting; -layout(location = 3) in flat int discardAlpha; +layout(location = 3) in flat uint discardAlpha; layout(location = 0) out vec4 outColour; void main() { diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index 297021ed..1eebdb24 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -9,7 +9,7 @@ layout(location = 0) out vec2 uv; layout(location = 1) out flat vec2 baseUV; layout(location = 2) out flat vec4 colourTinting; -layout(location = 3) out flat int discardAlpha; +layout(location = 3) out flat uint discardAlpha; uint extractLodLevel() { return uint(gl_BaseInstance)>>29; @@ -26,13 +26,13 @@ vec4 uint2vec4RGBA(uint colour) { } //Gets the face offset with respect to the face direction (e.g. some will be + some will be -) -float getDepthOffset(BlockModel model, uint face) { - float offset = extractFaceIndentation(model.faceData[face]); +float getDepthOffset(uint faceData, uint face) { + float offset = extractFaceIndentation(faceData); return offset * (1-((int(face)&1)*2)); } -vec2 getFaceSizeOffset(BlockModel model, uint face, uint corner) { - vec4 faceOffsetsSizes = extractFaceSizes(model.faceData[face]); +vec2 getFaceSizeOffset(uint faceData, uint corner) { + vec4 faceOffsetsSizes = extractFaceSizes(faceData); return mix(faceOffsetsSizes.xz, -(1-faceOffsetsSizes.yw), bvec2(((corner>>1)&1)==1, (corner&1)==1)); } @@ -46,6 +46,7 @@ void main() { uint face = extractFace(quad); uint modelId = extractStateId(quad); BlockModel model = modelData[modelId]; + uint faceData = model.faceData[face]; //Change the ordering due to backface culling //NOTE: when rendering, backface culling is disabled as we simply dispatch calls for each face @@ -58,11 +59,11 @@ void main() { ivec3 lodCorner = ((extractRelativeLodPos()<>1)&1, cornerIdx&1)); vec2 size = (quadSize + faceOffset) * (1<>1) == 0) {//Up/down offset = offset.xzy; @@ -85,7 +86,7 @@ void main() { baseUV = modelUV + (vec2(face%3, face/3) * (1f/(vec2(3,2)*256f))); uv = quadSize + faceOffset;//Add in the face offset for 0,0 uv - discardAlpha = 0; + discardAlpha = faceHasAlphaCuttout(faceData); //Compute lighting colourTinting = getLighting(extractLightId(quad)); From 787dc88c43728ddd9e93bd1bbc37cde70eeded33 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:35:09 +1000 Subject: [PATCH 21/28] Finished GeometryManager and added AABB raster support --- .../client/core/model/ModelManager.java | 2 +- .../rendering/AbstractFarWorldRenderer.java | 1 - .../core/rendering/GeometryManager.java | 30 ++++++---------- .../client/core/rendering/RenderTracker.java | 21 ++++++----- .../core/rendering/building/BuiltSection.java | 36 +++++++++++++------ .../building/BuiltSectionGeometry.java | 21 ----------- .../rendering/building/RenderDataFactory.java | 4 +-- .../zenith/shaders/lod/gl46/bindings.glsl | 8 ++--- .../zenith/shaders/lod/gl46/cmdgen.comp | 6 ++-- .../zenith/shaders/lod/gl46/cull/raster.vert | 9 +++-- .../zenith/shaders/lod/gl46/section.glsl | 8 +++-- 11 files changed, 69 insertions(+), 77 deletions(-) delete mode 100644 src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java 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 d6fa3119..86fb24a5 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 @@ -361,6 +361,6 @@ public class ModelManager { } public void addDebugInfo(List info) { - + info.add("BlockModels registered: " + this.modelTexture2id.size() + "/" + (1<<16)); } } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index 9c634147..fba5ec49 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -6,7 +6,6 @@ package me.cortex.zenith.client.core.rendering; import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.model.ModelManager; import me.cortex.zenith.client.core.rendering.building.BuiltSection; -import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.util.UploadStream; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Camera; diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java index cd7307e4..956296b6 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/GeometryManager.java @@ -5,7 +5,6 @@ import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.rendering.building.BuiltSection; -import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.util.BufferArena; import me.cortex.zenith.client.core.rendering.util.UploadStream; import org.lwjgl.system.MemoryUtil; @@ -33,7 +32,7 @@ public class GeometryManager { void uploadResults() { while (!this.buildResults.isEmpty()) { var result = this.buildResults.pop(); - boolean isDelete = result.opaque == null && result.translucent == null; + boolean isDelete = result.geometryBuffer == null; if (isDelete) { int id = -1; if ((id = this.pos2id.remove(result.position)) != -1) { @@ -142,36 +141,29 @@ public class GeometryManager { //TODO: pack the offsets of each axis so that implicit face culling can work //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry - private record SectionMeta(long position, int aabb, int opaqueGeometryPtr, int count, int translucentGeometryPtr) { + private record SectionMeta(long position, int aabb, int geometryPtr, int size, int[] offsets) { public void writeMetadata(long ptr) { //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4; MemoryUtil.memPutInt(ptr, (int) this.aabb); ptr += 4; - ptr += 4; + MemoryUtil.memPutInt(ptr, this.geometryPtr + this.offsets[0]); ptr += 4; - MemoryUtil.memPutInt(ptr, this.opaqueGeometryPtr); ptr += 4; - MemoryUtil.memPutInt(ptr, this.count); ptr += 4; - - //MemoryUtil.memPutInt(ptr, (int) this.translucentGeometryPtr + this.translucentPreDataCount); ptr += 4; - //MemoryUtil.memPutInt(ptr, this.translucentQuadCount); ptr += 4; + MemoryUtil.memPutInt(ptr, (this.offsets[1]-this.offsets[0])|((this.offsets[2]-this.offsets[1])<<16)); ptr += 4; + MemoryUtil.memPutInt(ptr, (this.offsets[3]-this.offsets[2])|((this.offsets[4]-this.offsets[3])<<16)); ptr += 4; + MemoryUtil.memPutInt(ptr, (this.offsets[5]-this.offsets[4])|((this.offsets[6]-this.offsets[5])<<16)); ptr += 4; + MemoryUtil.memPutInt(ptr, (this.offsets[7]-this.offsets[6])|((this.size -this.offsets[7])<<16)); ptr += 4; } } private SectionMeta createMeta(BuiltSection geometry) { - int geometryPtr = (int) this.geometryBuffer.upload(geometry.opaque.buffer()); - - //TODO: support translucent geometry - //return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), 0, -1,0, 0); - return new SectionMeta(geometry.position, 0, geometryPtr, (int) (geometry.opaque.buffer().size/8), -1); + int geometryPtr = (int) this.geometryBuffer.upload(geometry.geometryBuffer); + return new SectionMeta(geometry.position, geometry.aabb, geometryPtr, (int) (geometry.geometryBuffer.size/8), geometry.offsets); } private void freeMeta(SectionMeta meta) { - if (meta.opaqueGeometryPtr != -1) { - this.geometryBuffer.free(meta.opaqueGeometryPtr); - } - if (meta.translucentGeometryPtr != -1) { - this.geometryBuffer.free(meta.translucentGeometryPtr); + if (meta.geometryPtr != -1) { + this.geometryBuffer.free(meta.geometryPtr); } } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java b/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java index 71606601..fa7fbb76 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/RenderTracker.java @@ -1,7 +1,6 @@ package me.cortex.zenith.client.core.rendering; import me.cortex.zenith.client.core.rendering.building.BuiltSection; -import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry; import me.cortex.zenith.client.core.rendering.building.RenderGenerationService; import me.cortex.zenith.common.world.WorldEngine; import me.cortex.zenith.common.world.WorldSection; @@ -42,7 +41,7 @@ public class RenderTracker { //Removes a lvl 0 section from the world renderer public void remLvl0(int x, int y, int z) { this.activeSections.remove(WorldEngine.getWorldSectionId(0, x, y, z)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(0, x, y, z), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(0, x, y, z))); this.renderGen.removeTask(0, x, y, z); } @@ -64,14 +63,14 @@ public class RenderTracker { this.renderGen.enqueueTask(lvl, x, y, z, this::shouldStillBuild, this::getBuildFlagsOrAbort); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)), null, null)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)))); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1))); this.renderGen.removeTask(lvl-1, (x<<1), (y<<1), (z<<1)); @@ -96,7 +95,7 @@ public class RenderTracker { this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), O); this.activeSections.remove(WorldEngine.getWorldSectionId(lvl, x, y, z)); - this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z), null, null)); + this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z))); this.renderGen.removeTask(lvl, x, y, z); this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1), (z<<1), this::shouldStillBuild, this::getBuildFlagsOrAbort); diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java index 92c95990..03c01a59 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSection.java @@ -1,29 +1,43 @@ package me.cortex.zenith.client.core.rendering.building; +import me.cortex.zenith.common.util.MemoryBuffer; + +import java.util.Arrays; import java.util.Objects; //TODO: also have an AABB size stored public final class BuiltSection { public final long position; - public final BuiltSectionGeometry opaque; - public final BuiltSectionGeometry translucent; + public final int aabb; + public final MemoryBuffer geometryBuffer; + public final int[] offsets; - public BuiltSection(long position, BuiltSectionGeometry opaque, BuiltSectionGeometry translucent) { + public BuiltSection(long position) { + this(position, -1, null, null); + } + + public BuiltSection(long position, int aabb, MemoryBuffer geometryBuffer, int[] offsets) { this.position = position; - this.opaque = opaque; - this.translucent = translucent; + this.aabb = aabb; + this.geometryBuffer = geometryBuffer; + this.offsets = offsets; + if (offsets != null) { + for (int i = 0; i < offsets.length-1; i++) { + int delta = offsets[i+1] - offsets[i]; + if (delta<0||delta>=(1<<16)) { + throw new IllegalArgumentException("Offsets out of range"); + } + } + } } public BuiltSection clone() { - return new BuiltSection(this.position, this.opaque != null ? this.opaque.clone() : null, this.translucent != null ? this.translucent.clone() : null); + return new BuiltSection(this.position, this.aabb, this.geometryBuffer!=null?this.geometryBuffer.copy():null, this.offsets!=null?Arrays.copyOf(this.offsets, this.offsets.length):null); } public void free() { - if (this.opaque != null) { - this.opaque.free(); - } - if (this.translucent != null) { - this.translucent.free(); + if (this.geometryBuffer != null) { + this.geometryBuffer.free(); } } } diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java deleted file mode 100644 index e9165c4d..00000000 --- a/src/main/java/me/cortex/zenith/client/core/rendering/building/BuiltSectionGeometry.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.cortex.zenith.client.core.rendering.building; - -import me.cortex.zenith.common.util.MemoryBuffer; - -import java.util.Arrays; - -/** - * @param startOffsets Will be converted to ending offsets when doing data computation - */ -public record BuiltSectionGeometry(MemoryBuffer buffer, short[] startOffsets) { - - public BuiltSectionGeometry clone() { - return new BuiltSectionGeometry(this.buffer != null ? this.buffer.copy() : null, Arrays.copyOf(this.startOffsets, this.startOffsets.length)); - } - - public void free() { - if (this.buffer != null) { - this.buffer.free(); - } - } -} 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 cc8181fe..80a34718 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 @@ -297,7 +297,7 @@ public class RenderDataFactory { //MemoryUtil.memPutLong(buff.address+48, encodeRaw(2, 0,1,0,0,2,159,0, 0));//92 515 //MemoryUtil.memPutLong(buff.address+56, encodeRaw(3, 0,1,0,0,2,159,0, 0));//92 515 if (outData.isEmpty()) { - return new BuiltSection(section.getKey(), null, null); + return new BuiltSection(section.getKey()); } //outData.clear(); @@ -317,7 +317,7 @@ public class RenderDataFactory { MemoryUtil.memPutLong(ptr, data); ptr+=8; } - return new BuiltSection(section.getKey(), new BuiltSectionGeometry(buff, new short[0]), null); + return new BuiltSection(section.getKey(), (31<<15)|(31<<20)|(31<<25), buff, new int[]{0, outData.size(), outData.size(), outData.size(), outData.size(), outData.size(), outData.size(), outData.size()}); } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index 97cafa50..37a2d72b 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -21,11 +21,11 @@ struct SectionMeta { uint posA; uint posB; uint AABB; - uint _padA; uint ptr; - uint cnt; - uint _padB; - uint _padC; + uint cntA; + uint cntB; + uint cntC; + uint cntD; }; //TODO: see if making the stride 2*4*4 bytes or something cause you get that 16 byte write diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp index 4d0dfc55..3ddb5f6a 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp @@ -55,11 +55,13 @@ void main() { } if (shouldRender) { + uint basePtr = extractQuadStart(meta); + DrawCommand cmd; - cmd.count = extractQuadCount(meta) * 6; + cmd.count = (meta.cntA&0xFFFF) * 6; cmd.instanceCount = 1; cmd.firstIndex = 0; - cmd.baseVertex = int(extractQuadStart(meta))<<2; + cmd.baseVertex = int(basePtr)<<2; cmd.baseInstance = encodeLocalLodPos(detail, ipos); cmdBuffer[atomicAdd(opaqueDrawCount, 1)] = cmd; } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert index 4ac89195..0d0980de 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert @@ -14,12 +14,15 @@ void main() { uint detail = extractDetail(section); ivec3 ipos = extractPosition(section); + ivec3 aabbOffset = extractAABBOffset(section); + ivec3 size = extractAABBSize(section); //Transform ipos with respect to the vertex corner - ipos += ivec3(gl_VertexID&1, (gl_VertexID>>2)&1, (gl_VertexID>>1)&1); + ivec3 pos = (((ipos<>2)&1, (gl_VertexID>>1)&1)*size)*(1<>ivec3(0,5,10))&31; +} + +ivec3 extractAABBSize(SectionMeta meta) { + return ((ivec3(meta.AABB)>>ivec3(15,20,25))&31)+1;//The size is + 1 cause its always at least 1x1x1 } From 4d28a5a8c98add691e298712caad056e8272f3bb Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:12:50 +1000 Subject: [PATCH 22/28] Fixed directional culling and added AABB raster --- .../client/core/model/ModelManager.java | 7 +- .../rendering/building/RenderDataFactory.java | 338 +++++++++++++----- .../zenith/shaders/lod/gl46/cmdgen.comp | 90 ++++- 3 files changed, 325 insertions(+), 110 deletions(-) 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 86fb24a5..3627c834 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 @@ -277,9 +277,12 @@ public class ModelManager { return false; } + public static boolean isDoubleSided(long metadata) { + return ((metadata>>(8*6))&4) != 0; + } + public static boolean isTranslucent(long metadata) { - //TODO: THIS - return false; + return ((metadata>>(8*6))&2) != 0; } 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 80a34718..226c958b 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,19 +7,32 @@ 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.Blocks; import net.minecraft.client.MinecraftClient; import org.lwjgl.system.MemoryUtil; -import java.util.Map; - public class RenderDataFactory { private final WorldEngine world; private final ModelManager modelMan; private final QuadEncoder encoder; + + private final Mesher2D negativeMesher = new Mesher2D(5, 15); + private final Mesher2D positiveMesher = new Mesher2D(5, 15); + private final long[] sectionCache = new long[32*32*32]; private final long[] connectedSectionCache = new long[32*32*32]; + + private final LongArrayList doubleSidedQuadCollector = new LongArrayList(); + private final LongArrayList translucentQuadCollector = new LongArrayList(); + private final LongArrayList[] directionalQuadCollectors = new LongArrayList[]{new LongArrayList(), new LongArrayList(), new LongArrayList(), new LongArrayList(), new LongArrayList(), new LongArrayList()}; + + + private int minX; + private int minY; + private int minZ; + private int maxX; + private int maxY; + private int maxZ; public RenderDataFactory(WorldEngine world, ModelManager modelManager) { this.world = world; this.modelMan = modelManager; @@ -37,12 +50,234 @@ public class RenderDataFactory { // its neigbor or not (0 if it does 1 if it doesnt (0 is default behavior)) public BuiltSection generateMesh(WorldSection section, int buildMask) { section.copyDataTo(this.sectionCache); + this.translucentQuadCollector.clear(); + this.doubleSidedQuadCollector.clear(); + for (var collector : this.directionalQuadCollectors) { + collector.clear(); + } + this.minX = Integer.MAX_VALUE; + this.minY = Integer.MAX_VALUE; + this.minZ = Integer.MAX_VALUE; + this.maxX = Integer.MIN_VALUE; + this.maxY = Integer.MIN_VALUE; + this.maxZ = Integer.MIN_VALUE; //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 + this.generateMeshForAxis(section, 0);//Direction.Axis.Y + this.generateMeshForAxis(section, 1);//Direction.Axis.Z + this.generateMeshForAxis(section, 2);//Direction.Axis.X + + int quadCount = this.doubleSidedQuadCollector.size() + this.translucentQuadCollector.size(); + for (var collector : this.directionalQuadCollectors) { + quadCount += collector.size(); + } + + if (quadCount == 0) { + return new BuiltSection(section.getKey()); + } + + var buff = new MemoryBuffer(quadCount*8L); + long ptr = buff.address; + int[] offsets = new int[8]; + int coff = 0; + + //Ordering is: translucent, double sided quads, directional quads + offsets[0] = coff; + for (long data : this.translucentQuadCollector) { + MemoryUtil.memPutLong(ptr + ((coff++)*8L), data); + } + + offsets[1] = coff; + for (long data : this.doubleSidedQuadCollector) { + MemoryUtil.memPutLong(ptr + ((coff++)*8L), data); + } + + for (int face = 0; face < 6; face++) { + offsets[face+2] = coff; + for (long data : this.directionalQuadCollectors[face]) { + MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data); + } + } + + int aabb = 0; + aabb |= this.minX; + aabb |= this.minY<<5; + aabb |= this.minZ<<10; + aabb |= (this.maxX-this.minX)<<15; + aabb |= (this.maxY-this.minY)<<20; + aabb |= (this.maxZ-this.minZ)<<25; + + return new BuiltSection(section.getKey(), aabb, buff, offsets); + } + + + private void generateMeshForAxis(WorldSection section, int axisId) { + int aX = axisId==2?1:0; + int aY = axisId==0?1:0; + int aZ = axisId==1?1:0; + + //Note the way the connectedSectionCache works is that it reuses the section cache because we know we dont need the connectedSection + // when we are on the other direction + boolean obtainedOppositeSection0 = false; + boolean obtainedOppositeSection31 = false; + + + for (int primary = 0; primary < 32; primary++) { + this.negativeMesher.reset(); + this.positiveMesher.reset(); + + for (int a = 0; a < 32; a++) { + for (int b = 0; b < 32; b++) { + int x = axisId==2?primary:a; + int y = axisId==0?primary:(axisId==1?b:a); + int z = axisId==1?primary:b; + long self = this.sectionCache[WorldSection.getIndex(x,y,z)]; + if (Mapper.isAir(self)) continue; + + int selfBlockId = Mapper.getBlockId(self); + long selfMetadata = this.modelMan.getModelMetadata(selfBlockId); + + boolean putFace = false; + + //Branch into 2 paths, the + direction and -direction, doing it at once makes it much faster as it halves the number of loops + + if (ModelManager.faceExists(selfMetadata, axisId<<1)) {//- direction + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (primary == 0) { + if (!obtainedOppositeSection0) { + var connectedSection = this.world.acquire(section.lvl, section.x - aX, section.y - aY, section.z - aZ); + connectedSection.copyDataTo(this.connectedSectionCache); + connectedSection.release(); + obtainedOppositeSection0 = true; + } + facingState = this.connectedSectionCache[WorldSection.getIndex(x*(1-aX)+(31*aX), y*(1-aY)+(31*aY), z*(1-aZ)+(31*aZ))]; + } else { + facingState = this.sectionCache[WorldSection.getIndex(x-aX, y-aY, z-aZ)]; + } + + putFace |= this.putFaceIfCan(this.negativeMesher, (axisId<<1), (axisId<<1)|1, self, selfMetadata, selfBlockId, facingState, a, b); + } + if (ModelManager.faceExists(selfMetadata, axisId<<1)) {//+ direction + long facingState = Mapper.AIR; + //Need to access the other connecting section + if (primary == 31) { + if (!obtainedOppositeSection31) { + var connectedSection = this.world.acquire(section.lvl, section.x + aX, section.y + aY, section.z + aZ); + connectedSection.copyDataTo(this.connectedSectionCache); + connectedSection.release(); + obtainedOppositeSection31 = true; + } + facingState = this.connectedSectionCache[WorldSection.getIndex(x*(1-aX), y*(1-aY), z*(1-aZ))]; + } else { + facingState = this.sectionCache[WorldSection.getIndex(x+aX, y+aY, z+aZ)]; + } + + putFace |= this.putFaceIfCan(this.positiveMesher, (axisId<<1)|1, (axisId<<1), self, selfMetadata, selfBlockId, facingState, a, b); + } + + if (putFace) { + this.minX = Math.min(this.minX, x); + this.minY = Math.min(this.minY, y); + this.minZ = Math.min(this.minZ, z); + this.maxX = Math.max(this.maxX, x); + this.maxY = Math.max(this.maxY, y); + this.maxZ = Math.max(this.maxZ, z); + } + } + } + + processMeshedFace(this.negativeMesher, axisId<<1, primary, this.directionalQuadCollectors[(axisId<<1)]); + processMeshedFace(this.positiveMesher, (axisId<<1)|1, primary, this.directionalQuadCollectors[(axisId<<1)|1]); + } + } + + //Returns true if a face was placed + private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) { + long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); + + //If face can be occluded and is occluded from the facing block, then dont render the face + if (ModelManager.faceCanBeOccluded(metadata, face) && ModelManager.faceOccludes(facingMetadata, opposingFace)) { + return false; + } + + if (ModelManager.isTranslucent(metadata) && selfBlockId == Mapper.getBlockId(facingState)) { + //If we are facing a block, and are translucent and it is the same block as us, cull the quad + return false; + } + + int clientModelId = this.modelMan.getModelId(selfBlockId); + long otherFlags = 0; + otherFlags |= ModelManager.isTranslucent(metadata)?1L<<33:0; + otherFlags |= ModelManager.isDoubleSided(metadata)?1L<<34:0; + mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24) | otherFlags); + return true; + } + + private void processMeshedFace(Mesher2D mesher, int face, int otherAxis, LongArrayList axisOutputGeometry) { + //TODO: encode translucents and double sided quads to different global buffers + + int count = mesher.process(); + var array = mesher.getArray(); + for (int i = 0; i < count; i++) { + int quad = array[i]; + long data = mesher.getDataFromQuad(quad); + long encodedQuad = Integer.toUnsignedLong(QuadEncoder.encodePosition(face, otherAxis, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46); + + + if ((data&(1L<<33))!=0) { + this.translucentQuadCollector.add(encodedQuad); + } else if ((data&(1L<<34))!=0) { + this.doubleSidedQuadCollector.add(encodedQuad); + } else { + axisOutputGeometry.add(encodedQuad); + } + } + } +} + + +/* + private static long encodeRaw(int face, int width, int height, int x, int y, int z, int blockId, int biomeId, int lightId) { + return ((long)face) | (((long) width)<<3) | (((long) height)<<7) | (((long) z)<<11) | (((long) y)<<16) | (((long) x)<<21) | (((long) blockId)<<26) | (((long) biomeId)<<46) | (((long) lightId)<<55); + } + */ + + +/* + + //outData.clear(); + + //var buff = new MemoryBuffer(8*8); + //MemoryUtil.memPutLong(buff.address, encodeRaw(2, 0,1,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+8, encodeRaw(3, 0,1,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+16, encodeRaw(4, 1,2,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+24, encodeRaw(5, 1,2,0,0,0,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+32, encodeRaw(2, 0,1,0,0,1,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+40, encodeRaw(3, 0,1,0,0,1,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+48, encodeRaw(2, 0,1,0,0,2,159,0, 0));//92 515 + //MemoryUtil.memPutLong(buff.address+56, encodeRaw(3, 0,1,0,0,2,159,0, 0));//92 515 + + //int modelId = this.modelMan.getModelId(this.world.getMapper().getIdFromBlockState(Blocks.OAK_FENCE.getDefaultState())); + //int modelId = this.modelMan.getModelId(this.world.getMapper().getIdFromBlockState(Blocks.OAK_FENCE.getDefaultState())); + + //outData.add(encodeRaw(0, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(1, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(2, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(3, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(4, 0,0,0,0,0, modelId,0, 0)); + //outData.add(encodeRaw(5, 0,0,0,0,0, modelId,0, 0)); + */ + + + +/* + + Mesher2D mesher = new Mesher2D(5,15); LongArrayList outData = new LongArrayList(1000); @@ -282,99 +517,4 @@ public class RenderDataFactory { outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(5, x, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); } } - - - - - - //var buff = new MemoryBuffer(8*8); - //MemoryUtil.memPutLong(buff.address, encodeRaw(2, 0,1,0,0,0,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+8, encodeRaw(3, 0,1,0,0,0,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+16, encodeRaw(4, 1,2,0,0,0,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+24, encodeRaw(5, 1,2,0,0,0,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+32, encodeRaw(2, 0,1,0,0,1,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+40, encodeRaw(3, 0,1,0,0,1,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+48, encodeRaw(2, 0,1,0,0,2,159,0, 0));//92 515 - //MemoryUtil.memPutLong(buff.address+56, encodeRaw(3, 0,1,0,0,2,159,0, 0));//92 515 - if (outData.isEmpty()) { - return new BuiltSection(section.getKey()); - } - //outData.clear(); - - //int modelId = this.modelMan.getModelId(this.world.getMapper().getIdFromBlockState(Blocks.OAK_FENCE.getDefaultState())); - int modelId = this.modelMan.getModelId(this.world.getMapper().getIdFromBlockState(Blocks.OAK_FENCE.getDefaultState())); - - //outData.add(encodeRaw(0, 0,0,0,0,0, modelId,0, 0)); - //outData.add(encodeRaw(1, 0,0,0,0,0, modelId,0, 0)); - //outData.add(encodeRaw(2, 0,0,0,0,0, modelId,0, 0)); - //outData.add(encodeRaw(3, 0,0,0,0,0, modelId,0, 0)); - //outData.add(encodeRaw(4, 0,0,0,0,0, modelId,0, 0)); - //outData.add(encodeRaw(5, 0,0,0,0,0, modelId,0, 0)); - - var buff = new MemoryBuffer(outData.size()*8L); - long ptr = buff.address; - for (long data : outData) { - MemoryUtil.memPutLong(ptr, data); ptr+=8; - } - - return new BuiltSection(section.getKey(), (31<<15)|(31<<20)|(31<<25), buff, new int[]{0, outData.size(), outData.size(), outData.size(), outData.size(), outData.size(), outData.size(), outData.size()}); - } - - - private static long encodeRaw(int face, int width, int height, int x, int y, int z, int blockId, int biomeId, int lightId) { - return ((long)face) | (((long) width)<<3) | (((long) height)<<7) | (((long) z)<<11) | (((long) y)<<16) | (((long) x)<<21) | (((long) blockId)<<26) | (((long) biomeId)<<46) | (((long) lightId)<<55); - } - - - /* - private void generateMeshForAxis() { - - //Up direction - for (int y = 0; y < 32; y++) { - mesher.reset(); - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - long self = this.sectionCache[WorldSection.getIndex(x, y, z)]; - if (Mapper.isAir(self)) continue; - - int selfBlockId = Mapper.getBlockId(self); - long metadata = this.modelMan.getModelMetadata(selfBlockId); - - //If the model doesnt have a face, then just skip it - if (!ModelManager.faceExists(metadata, 1)) { - continue; - } - - long facingState = Mapper.AIR; - //Need to access the other connecting section - if (y == 31) { - - } else { - facingState = this.sectionCache[WorldSection.getIndex(x, y+1, z)]; - } - - long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); - - //If face can be occluded and is occluded from the facing block, then dont render the face - if (ModelManager.faceCanBeOccluded(metadata, 1) && ModelManager.faceOccludes(facingMetadata, 0)) { - continue; - } - - int clientModelId = this.modelMan.getModelId(selfBlockId); - - mesher.put(x, z, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24)); - } - } - - //TODO: encode translucents and double sided quads to different global buffers - int count = mesher.process(); - var array = mesher.getArray(); - for (int i = 0; i < count; i++) { - int quad = array[i]; - long data = mesher.getDataFromQuad(quad); - outData.add(Integer.toUnsignedLong(QuadEncoder.encodePosition(1, y, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46)); - } - } - }*/ - -} + */ \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp index 3ddb5f6a..3ee62f7b 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp @@ -7,6 +7,7 @@ layout(local_size_x = 128, local_size_y = 1, local_size_x = 1) in; #import #import #import +#line 11 //https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_shader_16bit_storage.txt // adds support for uint8_t which can use for compact visibility buffer @@ -27,7 +28,16 @@ uint encodeLocalLodPos(uint detail, ivec3 pos) { } -//TODO: swap to a multidraw indirect counted +//Note: if i want reverse indexing i need to use the index buffer offset to offset +void writeCmd(uint idx, uint encodedPos, uint offset, uint quadCount) { + DrawCommand cmd; + cmd.count = quadCount * 6; + cmd.instanceCount = 1; + cmd.firstIndex = 0; + cmd.baseVertex = int(offset)<<2; + cmd.baseInstance = encodedPos; + cmdBuffer[idx] = cmd; +} void main() { if (gl_GlobalInvocationID.x >= sectionCount) { @@ -55,14 +65,76 @@ void main() { } if (shouldRender) { - uint basePtr = extractQuadStart(meta); + uint encodedPos = encodeLocalLodPos(detail, ipos); + uint ptr = extractQuadStart(meta); + ivec3 relative = ipos-(baseSectionPos>>detail); - DrawCommand cmd; - cmd.count = (meta.cntA&0xFFFF) * 6; - cmd.instanceCount = 1; - cmd.firstIndex = 0; - cmd.baseVertex = int(basePtr)<<2; - cmd.baseInstance = encodeLocalLodPos(detail, ipos); - cmdBuffer[atomicAdd(opaqueDrawCount, 1)] = cmd; + + + //TODO:FIXME: Figure out why these are in such a weird order + uint msk = 0; + msk |= uint(relative.y>-1)<<0; + msk |= uint(relative.y<1 )<<1; + msk |= uint(relative.z>-1)<<2; + msk |= uint(relative.z<1 )<<3; + msk |= uint(relative.x>-1)<<4; + msk |= uint(relative.x<1 )<<5; + + + uint cmdPtr = atomicAdd(opaqueDrawCount, bitCount(msk)+1); + + + uint count = 0; + //Translucency + count = meta.cntA&0xFFFF; + + ptr += count; + + //Double sided quads + count = (meta.cntA>>16)&0xFFFF; + writeCmd(cmdPtr++, encodedPos, ptr, count); + ptr += count; + + //Down + count = (meta.cntB)&0xFFFF; + if ((msk&(1<<0))!=0) { + writeCmd(cmdPtr++, encodedPos, ptr, count); + } + ptr += count; + + //Up + count = (meta.cntB>>16)&0xFFFF; + if ((msk&(1<<1))!=0) { + writeCmd(cmdPtr++, encodedPos, ptr, count); + } + ptr += count; + + //North + count = (meta.cntC)&0xFFFF; + if ((msk&(1<<2))!=0) { + writeCmd(cmdPtr++, encodedPos, ptr, count); + } + ptr += count; + + //South + count = (meta.cntC>>16)&0xFFFF; + if ((msk&(1<<3))!=0) { + writeCmd(cmdPtr++, encodedPos, ptr, count); + } + ptr += count; + + //West + count = (meta.cntD)&0xFFFF; + if ((msk&(1<<4))!=0) { + writeCmd(cmdPtr++, encodedPos, ptr, count); + } + ptr += count; + + //East + count = (meta.cntD>>16)&0xFFFF; + if ((msk&(1<<5))!=0) { + writeCmd(cmdPtr++, encodedPos, ptr, count); + } + ptr += count; } } \ No newline at end of file From c2c622226b1c8383417bb5635dd89f1c1bda7895 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:39:48 +1000 Subject: [PATCH 23/28] Hacked in translucency rendering without sorting --- .../cortex/zenith/client/core/VoxelCore.java | 3 + .../rendering/AbstractFarWorldRenderer.java | 2 + .../core/rendering/Gl46FarWorldRenderer.java | 65 +++++++++++++------ .../zenith/shaders/lod/gl46/cmdgen.comp | 5 +- 4 files changed, 55 insertions(+), 20 deletions(-) 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 87a60391..79944b67 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -133,6 +133,9 @@ public class VoxelCore { //glBindFramebuffer(GL_FRAMEBUFFER, boundFB); //this.postProcessing.renderPost(boundFB); + + //We can render the translucent directly after as it is the furthest translucent objects + this.renderer.renderFarAwayTranslucent(); } public void addDebugInfo(List debug) { diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index fba5ec49..b8b9cf26 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -84,6 +84,8 @@ public abstract class AbstractFarWorldRenderer { public abstract void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz); + public abstract void renderFarAwayTranslucent(); + public void enqueueResult(BuiltSection result) { this.geometry.enqueueResult(result); } 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 16745612..7bd6a1a6 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 @@ -59,7 +59,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { public Gl46FarWorldRenderer(int geometryBuffer, int maxSections) { super(geometryBuffer, maxSections); - this.glCommandBuffer = new GlBuffer(maxSections*5L*4); + this.glCommandBuffer = new GlBuffer(maxSections*5L*4 * 6); this.glCommandCountBuffer = new GlBuffer(4*2); this.glVisibilityBuffer = new GlBuffer(maxSections*4L); glClearNamedBufferData(this.glCommandBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, new int[1]); @@ -84,6 +84,26 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glBindVertexArray(0); } + //FIXME: dont do something like this as it breaks multiviewport mods + private int frameId = 0; + private void updateUniformBuffer(MatrixStack stack, double cx, double cy, double cz) { + long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size()); + var mat = new Matrix4f(RenderSystem.getProjectionMatrix()).mul(stack.peek().getPositionMatrix()); + var innerTranslation = new Vector3f((float) (cx-(this.sx<<5)), (float) (cy-(this.sy<<5)), (float) (cz-(this.sz<<5))); + mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z); + mat.getToAddress(ptr); ptr += 4*4*4; + MemoryUtil.memPutInt(ptr, this.sx); ptr += 4; + MemoryUtil.memPutInt(ptr, this.sy); ptr += 4; + MemoryUtil.memPutInt(ptr, this.sz); ptr += 4; + MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4; + var planes = ((AccessFrustumIntersection)this.frustum).getPlanes(); + for (var plane : planes) { + plane.getToAddress(ptr); ptr += 4*4; + } + innerTranslation.getToAddress(ptr); ptr += 4*3; + MemoryUtil.memPutInt(ptr, this.frameId++); ptr += 4; + } + public void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz) { if (this.geometry.getSectionCount() == 0) { return; @@ -144,26 +164,33 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { RenderLayer.getCutoutMipped().endDrawing(); } - //FIXME: dont do something like this as it breaks multiviewport mods - private int frameId = 0; - private void updateUniformBuffer(MatrixStack stack, double cx, double cy, double cz) { - long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size()); - var mat = new Matrix4f(RenderSystem.getProjectionMatrix()).mul(stack.peek().getPositionMatrix()); - var innerTranslation = new Vector3f((float) (cx-(this.sx<<5)), (float) (cy-(this.sy<<5)), (float) (cz-(this.sz<<5))); - mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z); - mat.getToAddress(ptr); ptr += 4*4*4; - MemoryUtil.memPutInt(ptr, this.sx); ptr += 4; - MemoryUtil.memPutInt(ptr, this.sy); ptr += 4; - MemoryUtil.memPutInt(ptr, this.sz); ptr += 4; - MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4; - var planes = ((AccessFrustumIntersection)this.frustum).getPlanes(); - for (var plane : planes) { - plane.getToAddress(ptr); ptr += 4*4; - } - innerTranslation.getToAddress(ptr); ptr += 4*3; - MemoryUtil.memPutInt(ptr, this.frameId++); ptr += 4; + @Override + public void renderFarAwayTranslucent() { + RenderLayer.getTranslucent().startDrawing(); + glBindVertexArray(this.vao); + glDisable(GL_CULL_FACE); + + int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); + int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); + + glBindSampler(0, this.models.getSamplerId()); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this.models.getTextureId()); + this.lodShader.bind(); + + glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, 400_000 * 4 * 5, 4, this.geometry.getSectionCount(), 0); + + glEnable(GL_CULL_FACE); + glBindVertexArray(0); + + glBindSampler(0, 0); + GL11C.glBindTexture(GL_TEXTURE_2D, oldBoundTexture); + glActiveTexture(oldActiveTexture); + + RenderLayer.getTranslucent().endDrawing(); } + @Override public void shutdown() { super.shutdown(); diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp index 3ee62f7b..2224fe4e 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cmdgen.comp @@ -87,7 +87,10 @@ void main() { uint count = 0; //Translucency count = meta.cntA&0xFFFF; - + if (count != 0) { + uint translucentCommandPtr = atomicAdd(translucentDrawCount, 1) + 400000;//FIXME: dont hardcode this offset + writeCmd(translucentCommandPtr, encodedPos, ptr, count); + } ptr += count; //Double sided quads From 3cb90f44fcf6ce0e96682024a07070fde618544f Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:47:35 +1000 Subject: [PATCH 24/28] Fuck fluids --- .../cortex/zenith/client/core/VoxelCore.java | 12 - .../client/core/model/ModelManager.java | 26 +- .../client/core/model/ModelTextureBakery.java | 93 ++++- .../core/rendering/Gl46FarWorldRenderer.java | 9 +- .../rendering/building/RenderDataFactory.java | 20 ++ .../building/RenderDataFactoryOld.java | 337 ------------------ 6 files changed, 134 insertions(+), 363 deletions(-) delete mode 100644 src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactoryOld.java 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); - } - */ - -} From e33b8deb325611236337baa7d1ed4704eac24451 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:17:26 +1000 Subject: [PATCH 25/28] Most things work? --- .../cortex/zenith/client/core/VoxelCore.java | 6 +- .../client/core/model/ModelManager.java | 170 ++++++++++++++---- .../client/core/model/ModelTextureBakery.java | 20 ++- .../rendering/building/RenderDataFactory.java | 2 +- .../zenith/shaders/lod/gl46/bindings.glsl | 3 +- .../assets/zenith/shaders/lod/gl46/quads.vert | 6 + 6 files changed, 163 insertions(+), 44 deletions(-) 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 be880e60..0e300dc3 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -67,13 +67,17 @@ public class VoxelCore { this.world.getMapper().setCallbacks(a->{}, a->{}); - System.out.println("Voxel core initialized"); + + ////Resave the db incase it failed a recovery + //this.world.getMapper().forceResaveStates(); for (var state : this.world.getMapper().getStateEntries()) { this.renderer.getModelManager().addEntry(state.id, state.state); } //this.renderer.getModelManager().updateEntry(0, Blocks.GRASS_BLOCK.getDefaultState()); + + System.out.println("Voxel core initialized"); } 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 f9ff7719..f6e66e8a 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 @@ -8,11 +8,23 @@ 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.block.entity.BlockEntity; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.color.block.BlockColorProvider; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayers; +import net.minecraft.fluid.FluidState; import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.biome.ColorResolver; +import net.minecraft.world.chunk.light.LightingProvider; +import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; import java.util.ArrayList; @@ -98,6 +110,7 @@ public class ModelManager { } + //TODO: what i need to do is seperate out fluid states from blockStates @@ -127,6 +140,7 @@ public class ModelManager { var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock())); + RenderLayer blockRenderLayer = null; if (blockState.getBlock() instanceof FluidBlock) { blockRenderLayer = RenderLayers.getFluidLayer(blockState.getFluidState()); @@ -139,37 +153,6 @@ public class ModelManager { - //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 - - //NOTE: this is excluding fluid states - - - - - //Model data contains, the quad size and offset of each face and whether the face needs to be resolved with a colour modifier - // sourced from the quad data and reverse indexed into the section data (meaning there will be a maxiumum number of colours) - // can possibly also put texture coords if needed - //Supplying the quad size and offset means that much more accurate rendering quads are rendered and stuff like snow layers will look correct - // the size/offset of the corners of the quads will only be applied to the corner quads of the merged quads with adjustment to the UV to ensure textures are not alignned weirdly - // the other axis offset is always applied and means that the models will look more correct even when merged into a large quad (which will have alot of overdraw) - //TODO: need to make an option for like leaves to be fully opaque as by default they are not!!!! - - - - - - - - - - - //Models data is computed per face, the axis offset of the face is computed from the depth component of the rasterized data - // the size and offset of the face data is computed from the remaining pixels that where actually rastered (e.g. depth != 1.0) - // (note this might be able to be optimized for cuttout layers where it automatically tries to squish as much as possible) - // solid layer renders it as black so might need to add a bitset in the model data of whether the face is rendering - // in discard or solid mode maybe? long uploadPtr = UploadStream.INSTANCE.upload(this.modelBuffer, (long) modelId * MODEL_SIZE, MODEL_SIZE); @@ -177,6 +160,9 @@ public class ModelManager { //TODO: implement; // TODO: if it has a constant colour instead... idk why (apparently for things like spruce leaves)?? but premultiply the texture data by the constant colour boolean hasBiomeColourResolver = false; + if (colourProvider != null) { + hasBiomeColourResolver = isBiomeDependentColour(colourProvider, blockState); + } @@ -266,14 +252,131 @@ public class ModelManager { //Have 40 bytes free for remaining model data // todo: put in like the render layer type ig? along with colour resolver info int modelFlags = 0; + modelFlags |= colourProvider != null?1:0; + modelFlags |= hasBiomeColourResolver?2:0;//Basicly whether to use the next int as a colour or as a base index/id into a colour buffer for biome dependent colours + + //modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha MemoryUtil.memPutInt(uploadPtr, modelFlags); - + //Temporary override to always be non biome specific + if (colourProvider != null && ((!hasBiomeColourResolver) || true)) { + Biome defaultBiome = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS); + MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, defaultBiome)|0xFF000000); + } else { + MemoryUtil.memPutInt(uploadPtr + 4, -1);//Set the default to nothing so that its faster on the gpu + } return modelId; } + //TODO: add a method to detect biome dependent colours (can do by detecting if getColor is ever called) + // if it is, need to add it to a list and mark it as biome colour dependent or something then the shader + // will either use the uint as an index or a direct colour multiplier + private static int captureColourConstant(BlockColorProvider colorProvider, BlockState state, Biome biome) { + return colorProvider.getColor(state, new BlockRenderView() { + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0; + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return 0; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + return colorResolver.getColor(biome, 0, 0); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return state; + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return state.getFluidState(); + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public int getBottomY() { + return 0; + } + }, BlockPos.ORIGIN, 0); + } + + private static boolean isBiomeDependentColour(BlockColorProvider colorProvider, BlockState state) { + boolean[] biomeDependent = new boolean[1]; + colorProvider.getColor(state, new BlockRenderView() { + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0; + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return 0; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + biomeDependent[0] = true; + return 0; + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return state; + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return state.getFluidState(); + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public int getBottomY() { + return 0; + } + }, BlockPos.ORIGIN, 0); + return biomeDependent[0]; + } + + public static boolean faceExists(long metadata, int face) { return ((metadata>>(8*face))&0xFF)!=0xFF; @@ -304,6 +407,9 @@ public class ModelManager { return ((metadata>>(8*6))&8) != 0; } + public static boolean isBiomeColoured(long metadata) { + return ((metadata>>(8*6))&1) != 0; + } 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 e51a7a81..afe4bf7a 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,10 +10,7 @@ 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.*; import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.GlUniform; @@ -146,11 +143,6 @@ public class ModelTextureBakery { GlUniform.uniform1(0, 0); RenderSystem.activeTexture(GlConst.GL_TEXTURE0); - if (renderLayer == RenderLayer.getTranslucent()) { - //TODO: TRANSLUCENT, must sort the quad first, or something idk - } - - 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, i, renderFluid); @@ -235,12 +227,22 @@ public class ModelTextureBakery { } + float[] mat = new float[4*4]; new Matrix4f(RenderSystem.getProjectionMatrix()).mul(stack.peek().getPositionMatrix()).get(mat); glUniformMatrix4fv(1, false, mat); BufferRenderer.draw(vc.end()); + if (state.hasBlockEntity()) { + //TODO: finish BlockEntity raster + //var entity = ((BlockEntityProvider)state).createBlockEntity(BlockPos.ORIGIN, state); + //var renderer = MinecraftClient.getInstance().getBlockEntityRenderDispatcher().get(entity); + //renderer.render(); + //entity.markRemoved(); + } + + 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/building/RenderDataFactory.java b/src/main/java/me/cortex/zenith/client/core/rendering/building/RenderDataFactory.java index 4cbadf9c..80e527cd 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 @@ -234,7 +234,7 @@ public class RenderDataFactory { long otherFlags = 0; otherFlags |= ModelManager.isTranslucent(metadata)?1L<<33:0; otherFlags |= ModelManager.isDoubleSided(metadata)?1L<<34:0; - mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | (((long) Mapper.getBiomeId(self))<<24) | otherFlags); + mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelManager.isBiomeColoured(metadata)?1:0)) | otherFlags); return true; } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl index 37a2d72b..608d2583 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/bindings.glsl @@ -14,7 +14,8 @@ layout(binding = 0, std140) uniform SceneUniform { struct BlockModel { uint faceData[6]; uint flagsA; - uint _pad[9]; + uint colourTint; + uint _pad[8]; }; struct SectionMeta { diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert index 1eebdb24..b087ddca 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/quads.vert @@ -90,10 +90,16 @@ void main() { //Compute lighting colourTinting = getLighting(extractLightId(quad)); + + //Apply model colour tinting + colourTinting *= uint2vec4RGBA(model.colourTint).yzwx; + //Apply face tint if (face == 0) { colourTinting.xyz *= vec3(0.75, 0.75, 0.75); } else if (face != 1) { colourTinting.xyz *= vec3((float(face-2)/4)*0.6 + 0.4); } + + } \ No newline at end of file From 23c8c9a072436cd0177e4c8549b00a21cbde66c2 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:31:24 +1000 Subject: [PATCH 26/28] Fully works --- gradle.properties | 2 +- .../me/cortex/zenith/client/core/VoxelCore.java | 2 +- .../zenith/client/core/model/ModelManager.java | 2 ++ .../core/rendering/AbstractFarWorldRenderer.java | 16 +++++++++++++++- .../zenith/shaders/lod/gl46/block_model.glsl | 4 ++++ .../zenith/shaders/lod/gl46/cull/raster.vert | 3 ++- .../assets/zenith/shaders/lod/gl46/quads.frag | 3 +++ .../assets/zenith/shaders/lod/gl46/quads.vert | 10 +++++++--- 8 files changed, 35 insertions(+), 7 deletions(-) diff --git a/gradle.properties b/gradle.properties index 720494b1..1b3adb35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ yarn_mappings=1.20.4+build.1 loader_version=0.15.0 # Mod Properties -mod_version = 0.0.2-alpha +mod_version = 0.0.3-alpha maven_group = me.cortex archives_base_name = zenith 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 0e300dc3..28a6535f 100644 --- a/src/main/java/me/cortex/zenith/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/zenith/client/core/VoxelCore.java @@ -65,7 +65,7 @@ public class VoxelCore { this.postProcessing = null;//new PostProcessing(); - this.world.getMapper().setCallbacks(a->{}, a->{}); + this.world.getMapper().setCallbacks(this.renderer::addBlockState, a->{}); ////Resave the db incase it failed a recovery 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 f6e66e8a..07b8e440 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 @@ -244,6 +244,8 @@ public class ModelManager { faceModelData |= needsAlphaDiscard?1<<22:0; + faceModelData |= (!faceCoversFullBlock)?1<<23:0; + MemoryUtil.memPutInt(faceUploadPtr, faceModelData); } this.metadataCache[modelId] = metadata; diff --git a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java index b8b9cf26..a409529a 100644 --- a/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/zenith/client/core/rendering/AbstractFarWorldRenderer.java @@ -7,6 +7,8 @@ import me.cortex.zenith.client.core.gl.GlBuffer; import me.cortex.zenith.client.core.model.ModelManager; import me.cortex.zenith.client.core.rendering.building.BuiltSection; import me.cortex.zenith.client.core.rendering.util.UploadStream; +import me.cortex.zenith.common.world.other.Mapper; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; @@ -15,6 +17,8 @@ import org.joml.FrustumIntersection; import org.lwjgl.system.MemoryUtil; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentLinkedDeque; import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect; import static org.lwjgl.opengl.GL30.*; @@ -43,6 +47,7 @@ public abstract class AbstractFarWorldRenderer { protected FrustumIntersection frustum; + private final ConcurrentLinkedDeque blockStateUpdates = new ConcurrentLinkedDeque<>(); public AbstractFarWorldRenderer(int geometrySize, int maxSections) { this.uniformBuffer = new GlBuffer(1024); this.lightDataBuffer = new GlBuffer(256*4);//256 of uint @@ -59,7 +64,6 @@ public abstract class AbstractFarWorldRenderer { this.sy = camera.getBlockPos().getY() >> 5; this.sz = camera.getBlockPos().getZ() >> 5; - //TODO: move this to a render function that is only called // once per frame when using multi viewport mods //it shouldent matter if its called multiple times a frame however, as its synced with fences @@ -80,6 +84,12 @@ public abstract class AbstractFarWorldRenderer { //Upload any new geometry this.geometry.uploadResults(); + + //Do any BlockChanges + while (!this.blockStateUpdates.isEmpty()) { + var update = this.blockStateUpdates.pop(); + this.models.addEntry(update.id, update.state); + } } public abstract void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz); @@ -90,6 +100,10 @@ public abstract class AbstractFarWorldRenderer { this.geometry.enqueueResult(result); } + public void addBlockState(Mapper.StateEntry entry) { + this.blockStateUpdates.add(entry); + } + public void addDebugData(List debug) { this.models.addDebugInfo(debug); } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl b/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl index 9a8198e4..b87b101c 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/block_model.glsl @@ -10,4 +10,8 @@ vec4 extractFaceSizes(uint faceData) { uint faceHasAlphaCuttout(uint faceData) { return (faceData>>22)&1; +} + +uint faceHasAlphaCuttoutOverride(uint faceData) { + return (faceData>>23)&1; } \ No newline at end of file diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert index 0d0980de..42c6a5e7 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert @@ -20,7 +20,8 @@ void main() { //Transform ipos with respect to the vertex corner ivec3 pos = (((ipos<>2)&1, (gl_VertexID>>1)&1)*size)*(1<>2)&1, (gl_VertexID>>1)&1)*(size+1))*(1<>1)&1, cornerIdx&1)); - vec2 size = (quadSize + faceOffset) * (1<>1)&1, cornerIdx&1)); + vec2 size = (respectiveQuadSize + faceOffset) * (1< 1 + discardAlpha |= uint(any(greaterThan(quadSize, ivec2(1)))) & faceHasAlphaCuttoutOverride(faceData); + //Compute lighting colourTinting = getLighting(extractLightId(quad)); From 10bb73adf6da636a360d62f0cf6d9a1c9e2e93df Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Tue, 30 Jan 2024 11:13:48 +1000 Subject: [PATCH 27/28] Tweeks and fixes --- .../zenith/client/core/DistanceTracker.java | 2 +- .../client/core/model/ModelManager.java | 24 +++++++++++++++++-- .../core/rendering/Gl46FarWorldRenderer.java | 6 ++--- .../client/importers/WorldImporter.java | 2 +- .../mixin/sodium/MixinSodiumWorldRender.java | 2 +- .../zenith/shaders/lod/gl46/cull/raster.vert | 2 +- 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java b/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java index 3300157e..951bba63 100644 --- a/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java +++ b/src/main/java/me/cortex/zenith/client/core/DistanceTracker.java @@ -28,7 +28,7 @@ public class DistanceTracker { this.minYSection = MinecraftClient.getInstance().world.getBottomSectionCoord()/2; this.maxYSection = MinecraftClient.getInstance().world.getTopSectionCoord()/2; - if (false) { + if (true) { this.rings[0] = new TransitionRing2D(5, MinecraftClient.getInstance().options.getViewDistance().getValue() / 2, (x, z) -> { for (int y = this.minYSection; y <= this.maxYSection; y++) { this.tracker.remLvl0(x, y, z); 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 07b8e440..913aa1b9 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 @@ -439,12 +439,32 @@ public class ModelManager { return res; } + //TODO:FIXME: if the model is not already in the cache for some reason it renders black, need to figure out why public long getModelMetadata(int blockId) { - int map = this.idMappings[blockId]; + int map = 0; + while ((map = this.idMappings[blockId]) == -1) { + Thread.onSpinWait(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + if (map == -1) { throw new IllegalArgumentException("Id hasnt been computed yet: " + blockId); } - return this.metadataCache[map]; + long meta = 0; + + while ((meta = this.metadataCache[map]) == 0) { + Thread.onSpinWait(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return meta; } public int getModelId(int blockId) { 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 567f803a..3369d4cf 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 @@ -115,7 +115,6 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { RenderLayer.getCutoutMipped().startDrawing(); int oldActiveTexture = glGetInteger(GL_ACTIVE_TEXTURE); - int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); //RenderSystem.enableBlend(); //RenderSystem.defaultBlendFunc(); @@ -127,8 +126,9 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { //Bind the texture atlas - glBindSampler(0, this.models.getSamplerId()); glActiveTexture(GL_TEXTURE0); + int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); + glBindSampler(0, this.models.getSamplerId()); glBindTexture(GL_TEXTURE_2D, this.models.getTextureId()); glClearNamedBufferData(this.glCommandCountBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[1]); @@ -174,10 +174,10 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { 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); glBindSampler(0, this.models.getSamplerId()); glActiveTexture(GL_TEXTURE0); + int oldBoundTexture = glGetInteger(GL_TEXTURE_BINDING_2D); glBindTexture(GL_TEXTURE_2D, this.models.getTextureId()); this.lodShader.bind(); diff --git a/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java b/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java index b2a394ec..1330a6f8 100644 --- a/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java +++ b/src/main/java/me/cortex/zenith/client/importers/WorldImporter.java @@ -216,7 +216,7 @@ public class WorldImporter { biomes, (bx, by, bz, state) -> { int block = 0; - int sky = 15;//since sky is inverted + int sky = 0; if (blockLight != null) { block = blockLight.get(bx, by, bz); } diff --git a/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java b/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java index be7ae1e8..503371cb 100644 --- a/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java +++ b/src/main/java/me/cortex/zenith/client/mixin/sodium/MixinSodiumWorldRender.java @@ -14,6 +14,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class MixinSodiumWorldRender { @Inject(method = "drawChunkLayer", at = @At("HEAD"), cancellable = true) private void cancelRender(RenderLayer renderLayer, MatrixStack matrixStack, double x, double y, double z, CallbackInfo ci) { - ci.cancel(); + //ci.cancel(); } } diff --git a/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert b/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert index 42c6a5e7..d0e62e03 100644 --- a/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert +++ b/src/main/resources/assets/zenith/shaders/lod/gl46/cull/raster.vert @@ -21,7 +21,7 @@ void main() { ivec3 pos = (((ipos<>2)&1, (gl_VertexID>>1)&1)*(size+1))*(1<>2)&1, (gl_VertexID>>1)&1)*(size+2))*(1< Date: Tue, 30 Jan 2024 14:37:06 +1000 Subject: [PATCH 28/28] E --- .../core/model/ColourDepthTextureData.java | 5 +++++ .../client/core/model/ModelManager.java | 22 +++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java index c9c3d689..5b07d55c 100644 --- a/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java +++ b/src/main/java/me/cortex/zenith/client/core/model/ColourDepthTextureData.java @@ -14,4 +14,9 @@ public record ColourDepthTextureData(int[] colour, int[] depth, int width, int h public int hashCode() { return (this.width * 312337173 * (Arrays.hashCode(this.colour) ^ Arrays.hashCode(this.depth))) ^ this.height; } + + @Override + public ColourDepthTextureData clone() { + return new ColourDepthTextureData(Arrays.copyOf(this.colour, this.colour.length), Arrays.copyOf(this.depth, this.depth.length), this.width, this.height); + } } 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 913aa1b9..a783ea7f 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 @@ -30,6 +30,7 @@ import org.lwjgl.system.MemoryUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11C.GL_NEAREST; @@ -97,7 +98,7 @@ public class ModelManager { //TODO: figure out how to do mipping :blobfox_pineapple: this.textures = new GlTexture().store(GL_RGBA8, 1, modelTextureSize*3*256,modelTextureSize*2*256); this.metadataCache = new long[1<<16]; - this.idMappings = new int[1<<16]; + this.idMappings = new int[1<<20];//Max of 1 million blockstates mapping to 65k model states Arrays.fill(this.idMappings, -1); @@ -133,10 +134,9 @@ public class ModelManager { } else {//Not a duplicate so create a new entry modelId = this.modelTexture2id.size(); this.idMappings[blockId] = modelId; - this.modelTexture2id.put(List.of(textureData), modelId); + this.modelTexture2id.put(Stream.of(textureData).map(ColourDepthTextureData::clone).toList(), modelId); } } - this.putTextures(modelId, textureData); var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock())); @@ -261,14 +261,22 @@ public class ModelManager { //modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha MemoryUtil.memPutInt(uploadPtr, modelFlags); //Temporary override to always be non biome specific - if (colourProvider != null && ((!hasBiomeColourResolver) || true)) { + if (colourProvider == null) { + MemoryUtil.memPutInt(uploadPtr + 4, -1);//Set the default to nothing so that its faster on the gpu + } else if ((!hasBiomeColourResolver) || true) { Biome defaultBiome = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS); MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, defaultBiome)|0xFF000000); } else { - MemoryUtil.memPutInt(uploadPtr + 4, -1);//Set the default to nothing so that its faster on the gpu + //Populate the list of biomes for the model state } + //Note: if the layer isSolid then need to fill all the points in the texture where alpha == 0 with the average colour + // of the surrounding blocks but only within the computed face size bounds + //TODO + + + this.putTextures(modelId, textureData); return modelId; } @@ -445,7 +453,7 @@ public class ModelManager { while ((map = this.idMappings[blockId]) == -1) { Thread.onSpinWait(); try { - Thread.sleep(100); + Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -459,7 +467,7 @@ public class ModelManager { while ((meta = this.metadataCache[map]) == 0) { Thread.onSpinWait(); try { - Thread.sleep(100); + Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); }