This commit is contained in:
mcrcortex
2025-08-11 23:15:08 +10:00
parent 11f7041df0
commit 634d187c11
19 changed files with 300 additions and 100 deletions

View File

@@ -17,29 +17,29 @@ import java.util.function.Function;
public class VoxyClient implements ClientModInitializer { public class VoxyClient implements ClientModInitializer {
private static final HashSet<String> FREX = new HashSet<>(); private static final HashSet<String> FREX = new HashSet<>();
public static void initVoxyClient() {
Capabilities.init();//Ensure clinit is called
boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters;
if (systemSupported) {
SharedIndexBuffer.INSTANCE.id();
BudgetBufferRenderer.init();
VoxyCommon.setInstanceFactory(VoxyClientInstance::new);
if (!Capabilities.INSTANCE.subgroup) {
Logger.warn("GPU does not support subgroup operations, expect some performance degradation");
}
} else {
Logger.error("Voxy is unsupported on your system.");
}
}
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ClientLifecycleEvents.CLIENT_STARTED.register(client->{
Capabilities.init();//Ensure clinit is called
boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters;
if (systemSupported) {
SharedIndexBuffer.INSTANCE.id();
BudgetBufferRenderer.init();
VoxyCommon.setInstanceFactory(VoxyClientInstance::new);
if (!Capabilities.INSTANCE.subgroup) {
Logger.warn("GPU does not support subgroup operations, expect some performance degradation");
}
} else {
Logger.error("Voxy is unsupported on your system.");
}
});
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
if (VoxyCommon.isAvailable()) { if (VoxyCommon.isAvailable()) {
dispatcher.register(VoxyCommands.register()); dispatcher.register(VoxyCommands.register());

View File

@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core;
import me.cortex.voxy.client.RenderStatistics; import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.client.TimingStatistics;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager; import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser; import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
@@ -60,6 +61,9 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
this.traversal = traversal; this.traversal = traversal;
} }
//Allows pipelines to configure model baking system
public void setupExtraModelBakeryData(ModelBakerySubsystem modelService) {}
public final void setSectionRenderer(AbstractSectionRenderer<?,?> sectionRenderer) {//Stupid java ordering not allowing something pre super public final void setSectionRenderer(AbstractSectionRenderer<?,?> sectionRenderer) {//Stupid java ordering not allowing something pre super
if (this.sectionRenderer != null) throw new IllegalStateException(); if (this.sectionRenderer != null) throw new IllegalStateException();
this.sectionRenderer = sectionRenderer; this.sectionRenderer = sectionRenderer;
@@ -67,12 +71,12 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
protected abstract int setup(Viewport<?> viewport, int sourceFramebuffer); protected abstract int setup(Viewport<?> viewport, int sourceFramebuffer);
protected abstract void postOpaquePreTranslucent(Viewport<?> viewport); protected abstract void postOpaquePreTranslucent(Viewport<?> viewport);
protected void finish(Viewport<?> viewport, Matrix4fc mcProjection, int sourceFrameBuffer) { protected void finish(Viewport<?> viewport, int sourceFrameBuffer) {
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer);
} }
public void runPipeline(Viewport<?> viewport, Matrix4fc mcProjection, int sourceFrameBuffer) { public void runPipeline(Viewport<?> viewport, int sourceFrameBuffer) {
int depthTexture = this.setup(viewport, sourceFrameBuffer); int depthTexture = this.setup(viewport, sourceFrameBuffer);
var rs = ((AbstractSectionRenderer)this.sectionRenderer); var rs = ((AbstractSectionRenderer)this.sectionRenderer);
@@ -85,7 +89,7 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
rs.renderTranslucent(viewport); rs.renderTranslucent(viewport);
this.finish(viewport, mcProjection, sourceFrameBuffer); this.finish(viewport, sourceFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer);
} }
@@ -186,8 +190,9 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
RenderStatistics.addDebug(debug); RenderStatistics.addDebug(debug);
} }
public abstract void bindOpaqueFramebuffer(); //Binds the framebuffer and any other bindings needed for rendering
public abstract void bindTranslucentFramebuffer(); public abstract void setupAndBindOpaque(Viewport<?> viewport);
public abstract void setupAndBindTranslucent(Viewport<?> viewport);
//null means dont transform the shader //null means dont transform the shader
@@ -199,4 +204,5 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
public String patchTranslucentShader(AbstractSectionRenderer<?,?> renderer, String input) { public String patchTranslucentShader(AbstractSectionRenderer<?,?> renderer, String input) {
return null; return null;
} }
} }

View File

@@ -102,7 +102,7 @@ public class NormalRenderPipeline extends AbstractRenderPipeline {
} }
@Override @Override
protected void finish(Viewport<?> viewport, Matrix4fc mcProjection, int sourceFrameBuffer) { protected void finish(Viewport<?> viewport, int sourceFrameBuffer) {
this.finalBlit.bind(); this.finalBlit.bind();
if (this.useEnvFog) { if (this.useEnvFog) {
float start = viewport.fogParameters.environmentalStart(); float start = viewport.fogParameters.environmentalStart();
@@ -113,18 +113,18 @@ public class NormalRenderPipeline extends AbstractRenderPipeline {
} }
glBindTextureUnit(3, this.colourSSAOTex.id); glBindTextureUnit(3, this.colourSSAOTex.id);
AbstractRenderPipeline.transformBlitDepth(this.finalBlit, this.fb.getDepthTex().id, sourceFrameBuffer, viewport, new Matrix4f(mcProjection).mul(viewport.modelView)); AbstractRenderPipeline.transformBlitDepth(this.finalBlit, this.fb.getDepthTex().id, sourceFrameBuffer, viewport, new Matrix4f(viewport.vanillaProjection).mul(viewport.modelView));
//glBlitNamedFramebuffer(this.fbSSAO.id, sourceFrameBuffer, 0,0, viewport.width, viewport.height, 0,0, viewport.width, viewport.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); //glBlitNamedFramebuffer(this.fbSSAO.id, sourceFrameBuffer, 0,0, viewport.width, viewport.height, 0,0, viewport.width, viewport.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
} }
@Override @Override
public void bindOpaqueFramebuffer() { public void setupAndBindOpaque(Viewport<?> viewport) {
this.fb.bind(); this.fb.bind();
} }
@Override @Override
public void bindTranslucentFramebuffer() { public void setupAndBindTranslucent(Viewport<?> viewport) {
glBindFramebuffer(GL_FRAMEBUFFER, this.fbSSAO.id); glBindFramebuffer(GL_FRAMEBUFFER, this.fbSSAO.id);
} }

View File

@@ -26,12 +26,17 @@ import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.thread.ServiceThreadPool; import me.cortex.voxy.common.thread.ServiceThreadPool;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.commonImpl.VoxyCommon;
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices; import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
import net.caffeinemc.mods.sodium.client.util.FogParameters; import net.caffeinemc.mods.sodium.client.util.FogParameters;
import net.irisshaders.iris.Iris;
import net.irisshaders.iris.pipeline.programs.SodiumShader;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Matrix4fc; import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -39,9 +44,12 @@ import java.util.List;
import static org.lwjgl.opengl.GL11.GL_VIEWPORT; import static org.lwjgl.opengl.GL11.GL_VIEWPORT;
import static org.lwjgl.opengl.GL11.glGetIntegerv; import static org.lwjgl.opengl.GL11.glGetIntegerv;
import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING; import static org.lwjgl.opengl.GL30.glBindBufferRange;
import static org.lwjgl.opengl.GL30C.glBindFramebuffer; import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL32.glGetInteger64i;
import static org.lwjgl.opengl.GL33.glBindSampler; import static org.lwjgl.opengl.GL33.glBindSampler;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL43C.*;
public class VoxyRenderSystem { public class VoxyRenderSystem {
private final WorldEngine worldIn; private final WorldEngine worldIn;
@@ -71,6 +79,13 @@ public class VoxyRenderSystem {
//Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more //Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more
// than timeout, we keep the world acquired // than timeout, we keep the world acquired
world.acquireRef(); world.acquireRef();
//Fking HATE EVERYTHING AAAAAAAAAAAAAAAA
int[] oldBufferBindings = new int[10];
for (int i = 0; i < oldBufferBindings.length; i++) {
oldBufferBindings[i] = glGetIntegeri(GL_SHADER_STORAGE_BUFFER_BINDING, i);
}
try { try {
//wait for opengl to be finished, this should hopefully ensure all memory allocations are free //wait for opengl to be finished, this should hopefully ensure all memory allocations are free
glFinish(); glFinish();
@@ -100,6 +115,7 @@ public class VoxyRenderSystem {
} }
this.pipeline = RenderPipelineFactory.createPipeline(this.nodeManager, this.nodeCleaner, this.traversal, this::frexStillHasWork); this.pipeline = RenderPipelineFactory.createPipeline(this.nodeManager, this.nodeCleaner, this.traversal, this::frexStillHasWork);
this.pipeline.setupExtraModelBakeryData(this.modelService);//Configure the model service
var sectionRenderer = createSectionRenderer(this.pipeline, this.modelService.getStore(), this.geometryData); var sectionRenderer = createSectionRenderer(this.pipeline, this.modelService.getStore(), this.geometryData);
this.pipeline.setSectionRenderer(sectionRenderer); this.pipeline.setSectionRenderer(sectionRenderer);
this.viewportSelector = new ViewportSelector<>(sectionRenderer::createViewport); this.viewportSelector = new ViewportSelector<>(sectionRenderer::createViewport);
@@ -109,7 +125,7 @@ public class VoxyRenderSystem {
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5; int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5;
//Do some very cheeky stuff for MiB //Do some very cheeky stuff for MiB
if (false) { if (VoxyCommon.IS_MINE_IN_ABYSS) {//TODO: make this somehow configurable
minSec = -8; minSec = -8;
maxSec = 7; maxSec = 7;
} }
@@ -125,32 +141,63 @@ public class VoxyRenderSystem {
this.chunkBoundRenderer = new ChunkBoundRenderer(); this.chunkBoundRenderer = new ChunkBoundRenderer();
Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'"); Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'");
} catch (RuntimeException e) { } catch (RuntimeException e) {
world.releaseRef();//If something goes wrong, we must release the world first world.releaseRef();//If something goes wrong, we must release the world first
throw e; throw e;
} }
for (int i = 0; i < oldBufferBindings.length; i++) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, oldBufferBindings[i]);
}
} }
public void renderOpaque(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) {
if (IrisUtil.irisShadowActive()) {
return;
}
TimingStatistics.resetSamplers();
public Viewport<?> setupViewport(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) {
//Do some very cheeky stuff for MiB //Do some very cheeky stuff for MiB
if (false) { if (VoxyCommon.IS_MINE_IN_ABYSS) {
int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; int sector = (((int)Math.floor(cameraX)>>4)+512)>>10;
cameraX -= sector<<14;//10+4 cameraX -= sector<<14;//10+4
cameraY += (16+(256-32-sector*30))*16; cameraY += (16+(256-32-sector*30))*16;
} }
var projection = computeProjectionMat(matrices.projection());//RenderSystem.getProjectionMatrix();
//var projection = new Matrix4f(matrices.projection());
int[] dims = new int[4];
glGetIntegerv(GL_VIEWPORT, dims);
var viewport = this.getViewport();
viewport
.setVanillaProjection(matrices.projection())
.setProjection(projection)
.setModelView(new Matrix4f(matrices.modelView()))
.setCamera(cameraX, cameraY, cameraZ)
.setScreenSize(dims[2], dims[3])
.setFogParameters(fogParameters)
.update();
viewport.frameId++;
return viewport;
}
public void renderOpaque(Viewport<?> viewport) {
if (IrisUtil.irisShadowActive()) {
return;
}
TimingStatistics.resetSamplers();
long startTime = System.nanoTime(); long startTime = System.nanoTime();
TimingStatistics.all.start(); TimingStatistics.all.start();
TimingStatistics.main.start(); TimingStatistics.main.start();
//TODO: optimize
int[] oldBufferBindings = new int[10];
for (int i = 0; i < oldBufferBindings.length; i++) {
oldBufferBindings[i] = glGetIntegeri(GL_SHADER_STORAGE_BUFFER_BINDING, i);
}
int oldFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); int oldFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
int boundFB = oldFB; int boundFB = oldFB;
@@ -162,21 +209,6 @@ public class VoxyRenderSystem {
//this.autoBalanceSubDivSize(); //this.autoBalanceSubDivSize();
var projection = computeProjectionMat(matrices.projection());//RenderSystem.getProjectionMatrix();
//var projection = new Matrix4f(matrices.projection());
int[] dims = new int[4];
glGetIntegerv(GL_VIEWPORT, dims);
var viewport = this.getViewport();
viewport
.setProjection(projection)
.setModelView(new Matrix4f(matrices.modelView()))
.setCamera(cameraX, cameraY, cameraZ)
.setScreenSize(dims[2], dims[3])
.setFogParameters(fogParameters)
.update();
viewport.frameId++;
TimingStatistics.E.start(); TimingStatistics.E.start();
this.chunkBoundRenderer.render(viewport); this.chunkBoundRenderer.render(viewport);
@@ -184,7 +216,7 @@ public class VoxyRenderSystem {
//The entire rendering pipeline (excluding the chunkbound thing) //The entire rendering pipeline (excluding the chunkbound thing)
this.pipeline.runPipeline(viewport, matrices.projection(), boundFB); this.pipeline.runPipeline(viewport, boundFB);
TimingStatistics.main.stop(); TimingStatistics.main.stop();
@@ -197,7 +229,7 @@ public class VoxyRenderSystem {
//Tick upload stream (this is ok to do here as upload ticking is just memory management) //Tick upload stream (this is ok to do here as upload ticking is just memory management)
UploadStream.INSTANCE.tick(); UploadStream.INSTANCE.tick();
while (this.renderDistanceTracker.setCenterAndProcess(cameraX, cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed while (this.renderDistanceTracker.setCenterAndProcess(viewport.cameraX, viewport.cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed
//Done here as is allows less gl state resetup //Done here as is allows less gl state resetup
this.modelService.tick(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000)); this.modelService.tick(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000));
@@ -207,22 +239,29 @@ public class VoxyRenderSystem {
glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB); glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB);
{//Reset state manager stuffs {//Reset state manager stuffs
glUseProgram(0);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
GlStateManager._glBindVertexArray(0);//Clear binding GlStateManager._glBindVertexArray(0);//Clear binding
GlStateManager._activeTexture(GlConst.GL_TEXTURE0);
GlStateManager._bindTexture(0);
glBindSampler(0, 0);
GlStateManager._activeTexture(GlConst.GL_TEXTURE1); GlStateManager._activeTexture(GlConst.GL_TEXTURE1);
GlStateManager._bindTexture(0); for (int i = 0; i < 16; i++) {
glBindSampler(1, 0); GlStateManager._activeTexture(GlConst.GL_TEXTURE0+i);
GlStateManager._bindTexture(0);
glBindSampler(i, 0);
}
GlStateManager._activeTexture(GlConst.GL_TEXTURE2); IrisUtil.clearIrisSamplers();//Thanks iris (sigh)
GlStateManager._bindTexture(0);
glBindSampler(2, 0); //TODO: should/needto actually restore all of these, not just clear them
//Clear all the bindings
for (int i = 0; i < oldBufferBindings.length; i++) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, oldBufferBindings[i]);
}
//((SodiumShader) Iris.getPipelineManager().getPipelineNullable().getSodiumPrograms().getProgram(DefaultTerrainRenderPasses.CUTOUT).getInterface()).setupState(DefaultTerrainRenderPasses.CUTOUT, fogParameters);
} }
TimingStatistics.all.stop(); TimingStatistics.all.stop();
/* /*
@@ -375,7 +414,7 @@ public class VoxyRenderSystem {
//Limit to available dedicated memory if possible //Limit to available dedicated memory if possible
if (Capabilities.INSTANCE.canQueryGpuMemory) { if (Capabilities.INSTANCE.canQueryGpuMemory) {
//512mb less than avalible, //512mb less than avalible,
long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 1024*1024*1024; long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - (long)(1.5*1024*1024*1024);//1.5gb vram buffer
// Give a minimum of 512 mb requirement // Give a minimum of 512 mb requirement
limit = Math.max(512*1024*1024, limit); limit = Math.max(512*1024*1024, limit);

View File

@@ -1,6 +1,7 @@
package me.cortex.voxy.client.core.model; package me.cortex.voxy.client.core.model;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet; import it.unimi.dsi.fastutil.objects.ObjectSet;
@@ -118,6 +119,7 @@ public class ModelFactory {
public final Deque<Runnable> resultJobs = new ArrayDeque<>(); public final Deque<Runnable> resultJobs = new ArrayDeque<>();
private Object2IntMap<BlockState> customBlockStateIdMapping;
//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!! //TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!!
// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving // this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving
@@ -141,6 +143,10 @@ public class ModelFactory {
this.downstream.tick(); this.downstream.tick();
} }
public void setCustomBlockStateMapping(Object2IntMap<BlockState> mapping) {
this.customBlockStateIdMapping = mapping;
}
public boolean addEntry(int blockId) { public boolean addEntry(int blockId) {
if (this.idMappings[blockId] != -1) { if (this.idMappings[blockId] != -1) {
return false; return false;
@@ -456,6 +462,16 @@ public class ModelFactory {
MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, blockState, biome)|0xFF000000); clrUploadPtr += 4; MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, blockState, biome)|0xFF000000); clrUploadPtr += 4;
} }
} }
uploadPtr += 4;
//have 32 bytes of free space after here
//install the custom mapping id if it exists
if (this.customBlockStateIdMapping != null && this.customBlockStateIdMapping.containsKey(blockState)) {
MemoryUtil.memPutInt(uploadPtr, this.customBlockStateIdMapping.getInt(blockState));
} else {
MemoryUtil.memPutInt(uploadPtr, 0);
} uploadPtr += 4;
//Note: if the layer isSolid then need to fill all the points in the texture where alpha == 0 with the average colour //Note: if the layer isSolid then need to fill all the points in the texture where alpha == 0 with the average colour

View File

@@ -27,6 +27,7 @@ public abstract class Viewport <A extends Viewport<A>> {
public int width; public int width;
public int height; public int height;
public int frameId; public int frameId;
public Matrix4f vanillaProjection = new Matrix4f();
public Matrix4f projection; public Matrix4f projection;
public Matrix4f modelView; public Matrix4f modelView;
public final FrustumIntersection frustum = new FrustumIntersection(); public final FrustumIntersection frustum = new FrustumIntersection();
@@ -59,6 +60,11 @@ public abstract class Viewport <A extends Viewport<A>> {
this.depthBoundingBuffer.free(); this.depthBoundingBuffer.free();
} }
public A setVanillaProjection(Matrix4fc projection) {
this.vanillaProjection.set(projection);
return (A) this;
}
public A setProjection(Matrix4f projection) { public A setProjection(Matrix4f projection) {
this.projection = projection; this.projection = projection;
return (A) this; return (A) this;

View File

@@ -102,20 +102,34 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
String opaqueFrag = pipeline.patchOpaqueShader(this, frag); String opaqueFrag = pipeline.patchOpaqueShader(this, frag);
opaqueFrag = opaqueFrag==null?frag:opaqueFrag; opaqueFrag = opaqueFrag==null?frag:opaqueFrag;
this.terrainShader = builder.clone() //TODO: find a more robust/nicer way todo this
.addSource(ShaderType.FRAGMENT, opaqueFrag) this.terrainShader = tryCompilePatchedOrNormal(builder, opaqueFrag, frag);
.compile();
String translucentFrag = pipeline.patchTranslucentShader(this, frag); String translucentFrag = pipeline.patchTranslucentShader(this, frag);
if (translucentFrag == null) { if (translucentFrag != null) {
this.translucentTerrainShader = builder.clone() this.translucentTerrainShader = tryCompilePatchedOrNormal(builder, translucentFrag, frag);
.addSource(ShaderType.FRAGMENT, opaqueFrag)
.compile();
} else { } else {
this.translucentTerrainShader = this.terrainShader; this.translucentTerrainShader = this.terrainShader;
} }
} }
private static Shader tryCompilePatchedOrNormal(Shader.Builder<?> builder, String shader, String original) {
boolean patched = shader != original;//This is the correct comparison type (reference)
try {
return builder.clone()
.defineIf("PATCHED_SHADER", patched)
.addSource(ShaderType.FRAGMENT, shader)
.compile();
} catch (RuntimeException e) {
if (patched) {
Logger.error("Failed to compile shader patch, using normal pipeline to prevent errors", e);
return tryCompilePatchedOrNormal(builder, original, original);
} else {
throw e;
}
}
}
private void uploadUniformBuffer(MDICViewport viewport) { private void uploadUniformBuffer(MDICViewport viewport) {
long ptr = UploadStream.INSTANCE.upload(this.uniform, 0, 1024); long ptr = UploadStream.INSTANCE.upload(this.uniform, 0, 1024);
@@ -153,12 +167,12 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
private void renderTerrain(MDICViewport viewport, long indirectOffset, long drawCountOffset, int maxDrawCount) { private void renderTerrain(MDICViewport viewport, long indirectOffset, long drawCountOffset, int maxDrawCount) {
//RenderLayer.getCutoutMipped().startDrawing(); //RenderLayer.getCutoutMipped().startDrawing();
this.pipeline.bindOpaqueFramebuffer();
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.terrainShader.bind(); this.terrainShader.bind();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
this.pipeline.setupAndBindOpaque(viewport);
this.bindRenderingBuffers(viewport); this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
@@ -187,7 +201,6 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
@Override @Override
public void renderTranslucent(MDICViewport viewport) { public void renderTranslucent(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
this.pipeline.bindTranslucentFramebuffer();
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
@@ -196,6 +209,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.translucentTerrainShader.bind(); this.translucentTerrainShader.bind();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
this.pipeline.setupAndBindTranslucent(viewport);
this.bindRenderingBuffers(viewport); this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed

View File

@@ -41,6 +41,10 @@ public class UploadStream {
data.cpyTo(this.upload(buffer, destOffset, data.size)); data.cpyTo(this.upload(buffer, destOffset, data.size));
} }
public long uploadTo(GlBuffer buffer) {
return this.upload(buffer, 0, buffer.size());
}
public long upload(GlBuffer buffer, long destOffset, long size) { public long upload(GlBuffer buffer, long destOffset, long size) {
long addr = this.rawUploadAddress((int) size); long addr = this.rawUploadAddress((int) size);

View File

@@ -1,10 +1,11 @@
package me.cortex.voxy.client.core.util; package me.cortex.voxy.client.core.util;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.irisshaders.iris.gl.IrisRenderSystem;
import net.irisshaders.iris.shadows.ShadowRenderer; import net.irisshaders.iris.shadows.ShadowRenderer;
public class IrisUtil { public class IrisUtil {
private static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris"); public static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris");
private static boolean irisShadowActive0() { private static boolean irisShadowActive0() {
@@ -14,4 +15,14 @@ public class IrisUtil {
public static boolean irisShadowActive() { public static boolean irisShadowActive() {
return IRIS_INSTALLED && irisShadowActive0(); return IRIS_INSTALLED && irisShadowActive0();
} }
public static void clearIrisSamplers() {
if (IRIS_INSTALLED) clearIrisSamplers0();
}
private static void clearIrisSamplers0() {
for (int i = 0; i < 16; i++) {
IrisRenderSystem.bindSamplerToUnit(i, 0);
}
}
} }

View File

@@ -0,0 +1,23 @@
package me.cortex.voxy.client.mixin.minecraft;
import com.mojang.blaze3d.shaders.ShaderType;
import com.mojang.blaze3d.systems.RenderSystem;
import me.cortex.voxy.client.VoxyClient;
import net.minecraft.util.Identifier;
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.callback.CallbackInfo;
import java.util.function.BiFunction;
//Thanks iris for making me need todo this ;-; _irritater_
@Mixin(RenderSystem.class)
public class MixinRenderSystem {
//We need to inject before iris to initalize our systems
@Inject(method = "initRenderer", order = 900, remap = false, at = @At("RETURN"))
private static void voxy$injectInit(long windowHandle, int debugVerbosity, boolean sync, BiFunction<Identifier, ShaderType, String> shaderSourceGetter, boolean renderDebugLabels, CallbackInfo ci) {
VoxyClient.initVoxyClient();
}
}

View File

@@ -18,7 +18,7 @@ public class MixinRenderPipeline {
private void voxy$injectRender(TerrainRenderPass pass, Viewport frustum, FogParameters fogParameters, ChunkRenderMatrices crm, double px, double py, double pz, CallbackInfo ci) { private void voxy$injectRender(TerrainRenderPass pass, Viewport frustum, FogParameters fogParameters, ChunkRenderMatrices crm, double px, double py, double pz, CallbackInfo ci) {
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem(); var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
if (renderer != null) { if (renderer != null) {
renderer.renderOpaque(crm, fogParameters, px, py, pz); renderer.renderOpaque(renderer.setupViewport(crm, fogParameters, px, py, pz));
} }
} }
} }

View File

@@ -23,7 +23,7 @@ public class MixinDefaultChunkRenderer {
if (renderPass == DefaultTerrainRenderPasses.CUTOUT) { if (renderPass == DefaultTerrainRenderPasses.CUTOUT) {
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem(); var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
if (renderer != null) { if (renderer != null) {
renderer.renderOpaque(matrices, fogParameters, camera.x, camera.y, camera.z); renderer.renderOpaque(renderer.setupViewport(matrices, fogParameters, camera.x, camera.y, camera.z));
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem; import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.client.core.VoxyRenderSystem; import me.cortex.voxy.client.core.VoxyRenderSystem;
import me.cortex.voxy.common.world.service.VoxelIngestService; import me.cortex.voxy.common.world.service.VoxelIngestService;
import me.cortex.voxy.commonImpl.VoxyCommon;
import net.caffeinemc.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.client.gl.device.CommandList;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
@@ -94,7 +95,7 @@ public class MixinRenderSectionManager {
} }
int x = instance.getChunkX(), y = instance.getChunkY(), z = instance.getChunkZ(); int x = instance.getChunkX(), y = instance.getChunkY(), z = instance.getChunkZ();
//Do some very cheeky stuff for MiB //Do some very cheeky stuff for MiB
if (false) { if (VoxyCommon.IS_MINE_IN_ABYSS) {
int sector = (x+512)>>10; int sector = (x+512)>>10;
x-=sector<<10; x-=sector<<10;
y+=16+(256-32-sector*30); y+=16+(256-32-sector*30);

View File

@@ -2,6 +2,7 @@ package me.cortex.voxy.common.world;
import me.cortex.voxy.common.voxelization.VoxelizedSection; import me.cortex.voxy.common.voxelization.VoxelizedSection;
import me.cortex.voxy.common.world.other.Mapper; import me.cortex.voxy.common.world.other.Mapper;
import me.cortex.voxy.commonImpl.VoxyCommon;
import static me.cortex.voxy.common.world.WorldEngine.*; import static me.cortex.voxy.common.world.WorldEngine.*;
@@ -12,7 +13,7 @@ public class WorldUpdater {
public static void insertUpdate(WorldEngine into, VoxelizedSection section) {//TODO: add a bitset of levels to update and if it should force update public static void insertUpdate(WorldEngine into, VoxelizedSection section) {//TODO: add a bitset of levels to update and if it should force update
//Do some very cheeky stuff for MiB //Do some very cheeky stuff for MiB
if (false) { if (VoxyCommon.IS_MINE_IN_ABYSS) {
int sector = (section.x+512)>>10; int sector = (section.x+512)>>10;
section.setPosition(section.x-(sector<<10), section.y+16+(256-32-sector*30), section.z);//Note sector size mult is 30 because the top chunk is replicated (and so is bottom chunk) section.setPosition(section.x-(sector<<10), section.y+16+(256-32-sector*30), section.z);//Note sector size mult is 30 because the top chunk is replicated (and so is bottom chunk)
} }

View File

@@ -85,4 +85,7 @@ public class VoxyCommon implements ModInitializer {
public static boolean isAvailable() { public static boolean isAvailable() {
return FACTORY != null; return FACTORY != null;
} }
public static final boolean IS_MINE_IN_ABYSS = false;
} }

View File

@@ -2,7 +2,8 @@ struct BlockModel {
uint faceData[6]; uint faceData[6];
uint flagsA; uint flagsA;
uint colourTint; uint colourTint;
uint _pad[8]; uint customId;
uint _pad[7];
}; };
//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off //TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off

View File

@@ -17,9 +17,18 @@ layout(location = 1) in flat uvec4 interData;
#ifdef DEBUG_RENDER #ifdef DEBUG_RENDER
layout(location = 7) in flat uint quadDebug; layout(location = 7) in flat uint quadDebug;
#endif #endif
layout(location = 0) out vec4 outColour;
#ifndef PATCHED_SHADER
layout(location = 0) out vec4 outColour;
#else
//Bind the model buffer and import the model system as we need it
#define MODEL_BUFFER_BINDING 3
#import <voxy:lod/block_model.glsl>
#endif
#import <voxy:lod/gl46/bindings.glsl> #import <voxy:lod/gl46/bindings.glsl>
vec4 uint2vec4RGBA(uint colour) { vec4 uint2vec4RGBA(uint colour) {
@@ -38,17 +47,23 @@ bool useCutout() {
return (interData.x&1u)==1u; return (interData.x&1u)==1u;
} }
vec4 computeColour(vec4 colour) { uint getFace() {
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha #ifndef PATCHED_SHADER
if (useTinting() && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) { return (interData.w>>8)&7u;
colour *= uint2vec4RGBA(interData.z).yzwx; #else
} return (interData.y>>8)&7u;
return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255); #endif
} }
#ifdef PATCHED_SHADER
vec2 getLightmap() {
//return clamp(vec2(interData.y&0xFu, (interData.y>>4)&0xFu)/16, vec2(4.0f/255), vec2(252.0f/255));
return vec2(interData.y&0xFu, (interData.y>>4)&0xFu)/15;
}
#endif
uint getFace() { uint getModelId() {
return (interData.w>>8)&7u; return interData.x>>16;
} }
vec2 getBaseUV() { vec2 getBaseUV() {
@@ -58,6 +73,37 @@ vec2 getBaseUV() {
return modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0))); return modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
} }
#ifdef PATCHED_SHADER
struct VoxyFragmentParameters {
//TODO: pass in derivative data
vec4 sampledColour;
vec2 tile;
vec2 uv;
uint face;
uint modelId;
vec2 lightMap;
vec4 tinting;
uint customId;//Same as iris's modelId
};
void voxy_emitFragment(VoxyFragmentParameters parameters);
#else
vec4 computeColour(vec2 texturePos, vec4 colour) {
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
if (useTinting()) {
vec4 tintTest = texture(blockModelAtlas, texturePos, -2);
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
colour *= uint2vec4RGBA(interData.z).yzwx;
}
}
return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255);
}
#endif
void main() { void main() {
//Tile is the tile we are in //Tile is the tile we are in
vec2 tile; vec2 tile;
@@ -92,11 +138,10 @@ void main() {
#endif #endif
} }
colour = computeColour(colour); #ifndef PATCHED_SHADER
colour = computeColour(texPos, colour);
outColour = colour; outColour = colour;
#ifdef DEBUG_RENDER #ifdef DEBUG_RENDER
uint hash = quadDebug*1231421+123141; uint hash = quadDebug*1231421+123141;
hash ^= hash>>16; hash ^= hash>>16;
@@ -105,6 +150,21 @@ void main() {
hash = hash * 1827364925 + 123325621; hash = hash * 1827364925 + 123325621;
outColour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 1); outColour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 1);
#endif #endif
#else
uint modelId = getModelId();
BlockModel model = modelData[modelId];
vec4 tint = vec4(1);
if (useTinting()) {
vec4 tintTest = texture(blockModelAtlas, texPos, -2);
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
tint = uint2vec4RGBA(interData.z).yzwx;
}
}
voxy_emitFragment(VoxyFragmentParameters(colour, tile, texPos, getFace(), modelId, getLightmap().yx, tint, model.customId));
#endif
} }
@@ -132,3 +192,4 @@ colour = textureGrad(blockModelAtlas, texPos, dx, dy);
//#else //#else
//colour = texture(blockModelAtlas, texPos); //colour = texture(blockModelAtlas, texPos);
//#endif //#endif

View File

@@ -129,6 +129,7 @@ void main() {
flags |= uint(!modelHasMipmaps(model))<<1; flags |= uint(!modelHasMipmaps(model))<<1;
//Compute lighting //Compute lighting
uint lighting = extractLightId(quad);
vec4 tinting = getLighting(extractLightId(quad)); vec4 tinting = getLighting(extractLightId(quad));
//Apply model colour tinting //Apply model colour tinting
@@ -143,6 +144,9 @@ void main() {
conditionalTinting = tintColour; conditionalTinting = tintColour;
} }
setSizeAndFlags(modelId, flags, quadSize);
#ifndef PATCHED_SHADER
uint addin = 0; uint addin = 0;
if (!isTranslucent) { if (!isTranslucent) {
tinting.w = 0.0; tinting.w = 0.0;
@@ -167,8 +171,11 @@ void main() {
} }
} }
setSizeAndFlags(modelId, flags, quadSize);
setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8)); setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8));
#else
interData.y = lighting|(face<<8);
interData.z = tintColour;
#endif
} }

View File

@@ -3,6 +3,14 @@
"package": "me.cortex.voxy.client.mixin", "package": "me.cortex.voxy.client.mixin",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"client": [ "client": [
"iris.CustomUniformsAccessor",
"iris.IrisRenderingPipelineAccessor",
"iris.MixinIrisRenderingPipeline",
"iris.MixinIrisSamplers",
"iris.MixinMatrixUniforms",
"iris.MixinProgramSet",
"iris.MixinShaderPackSourceNames",
"iris.MixinStandardMacros",
"minecraft.MixinClientChunkManager", "minecraft.MixinClientChunkManager",
"minecraft.MixinClientCommonNetworkHandler", "minecraft.MixinClientCommonNetworkHandler",
"minecraft.MixinClientLoginNetworkHandler", "minecraft.MixinClientLoginNetworkHandler",
@@ -10,6 +18,7 @@
"minecraft.MixinFogRenderer", "minecraft.MixinFogRenderer",
"minecraft.MixinGlDebug", "minecraft.MixinGlDebug",
"minecraft.MixinMinecraftClient", "minecraft.MixinMinecraftClient",
"minecraft.MixinRenderSystem",
"minecraft.MixinThreadExecutor", "minecraft.MixinThreadExecutor",
"minecraft.MixinWindow", "minecraft.MixinWindow",
"minecraft.MixinWorldRenderer", "minecraft.MixinWorldRenderer",
@@ -20,7 +29,5 @@
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
}, }
"mixins": [
]
} }