work on chunk outline renderer

This commit is contained in:
mcrcortex
2025-05-01 11:20:55 +10:00
parent dd46c43c73
commit d12a0f1ba4
6 changed files with 124 additions and 6 deletions

View File

@@ -12,6 +12,9 @@ public class GlTexture extends TrackedObject {
public final int id;
private final int type;
private int format;
private int width;
private int height;
private int layers;
public GlTexture() {
this(GL_TEXTURE_2D);
}
@@ -34,6 +37,9 @@ public class GlTexture extends TrackedObject {
this.format = format;
if (this.type == GL_TEXTURE_2D) {
glTextureStorage2D(this.id, levels, format, width, height);
this.width = width;
this.height = height;
this.layers = layers;
} else {
throw new IllegalStateException("Unknown texture type");
}
@@ -55,4 +61,16 @@ public class GlTexture extends TrackedObject {
public GlTexture name(String name) {
return GlDebug.name(name, this);
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
}
public int getLayers() {
return this.layers;
}
}

View File

@@ -111,6 +111,7 @@ public class ModelTextureBakery {
glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id);
//TODO: make use glClearNamedFramebuffer* to reduce/remove state change
glClearColor(0,0,0,0);
glClearDepth(1);
glClearStencil(0);

View File

@@ -3,19 +3,30 @@ package me.cortex.voxy.client.core.rendering;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import net.minecraft.util.math.MathHelper;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import org.lwjgl.system.MemoryUtil;
import static me.cortex.voxy.client.core.rendering.PrintfDebugUtil.PRINTF_processor;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL30.glBindBufferBase;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfv;
//This is a render subsystem, its very simple in what it does
// it renders an AABB around loaded chunks, thats it
@@ -25,12 +36,24 @@ public class ChunkBoundRenderer {
private final GlBuffer uniformBuffer = new GlBuffer(128);
private final Long2IntOpenHashMap chunk2idx = new Long2IntOpenHashMap(MAX_CHUNK_COUNT);
private final long[] idx2chunk = new long[MAX_CHUNK_COUNT];
private final Shader rasterShader = Shader.makeAuto()
.add(ShaderType.VERTEX, "voxy:chunkoutline/outline.vsh")
.add(ShaderType.FRAGMENT, "voxy:chunkoutline/outline.fsh")
.compile()
.ssbo(0, this.uniformBuffer)
.ssbo(1, this.chunkPosBuffer);
private GlTexture depthBuffer = new GlTexture().store(GL_DEPTH_COMPONENT24, 1, 128, 128);
private final GlFramebuffer frameBuffer = new GlFramebuffer().bind(GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
public ChunkBoundRenderer() {
this.chunk2idx.defaultReturnValue(-1);
}
public void addChunk(long pos) {
if (this.chunk2idx.size() >= MAX_CHUNK_COUNT) {
throw new IllegalStateException("At capacity");
}
if (this.chunk2idx.containsKey(pos)) {
throw new IllegalArgumentException("Chunk already in map");
}
@@ -75,22 +98,62 @@ public class ChunkBoundRenderer {
//Bind and render, changing as little gl state as possible so that the caller may configure how it wants to render
public void render(Viewport<?> viewport) {
if (this.depthBuffer.getWidth() != viewport.width || this.depthBuffer.getHeight() != viewport.height) {
this.depthBuffer.free();
this.depthBuffer = new GlTexture().store(GL_DEPTH_COMPONENT24, 1, viewport.width, viewport.height);
this.frameBuffer.bind(GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
}
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128);
viewport.MVP.getToAddress(ptr); ptr += 4*4*4;
viewport.section.getToAddress(ptr); ptr += 4*4;
viewport.innerTranslation.getToAddress(ptr); ptr += 4*4;
long matPtr = ptr; ptr += 4*4*4;
{//This is recomputed to be in chunk section space not worldsection
int sx = MathHelper.floor(viewport.cameraX) >> 4;
int sy = MathHelper.floor(viewport.cameraY) >> 4;
int sz = MathHelper.floor(viewport.cameraZ) >> 4;
new Vector3i(sx, sy, sz).getToAddress(ptr); ptr += 4*4;
viewport.MVP.translate(
-(float) (viewport.cameraX - (sx << 4)),
-(float) (viewport.cameraY - (sy << 4)),
-(float) (viewport.cameraZ - (sz << 4)),
new Matrix4f()).getToAddress(matPtr);
}
UploadStream.INSTANCE.commit();
//TODO: NOTE: need to reverse the winding order since we want the back faces of the AABB, not the front
//this.cullShader.bind();
{
glFrontFace(GL_CW);//Reverse winding order
//"reverse depth buffer" it goes from 0->1 where 1 is far away
glClearNamedFramebufferfv(this.frameBuffer.id, GL_DEPTH, 0, new float[]{0});
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
}
glBindVertexArray(RenderService.STATIC_VAO);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.chunkPosBuffer.id);
glBindFramebuffer(GL_FRAMEBUFFER, this.frameBuffer.id);
this.rasterShader.bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
glDrawElementsInstanced(GL_TRIANGLES, 6*2*3, GL_UNSIGNED_BYTE, SharedIndexBuffer.CUBE_INDEX_OFFSET, this.chunk2idx.size());
{
glFrontFace(GL_CCW);//Restore winding order
glDepthFunc(GL_LEQUAL);
//TODO: check this is correct
glEnable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
}
}
public void free() {
this.depthBuffer.free();
this.frameBuffer.free();
this.rasterShader.free();
this.uniformBuffer.free();
this.chunkPosBuffer.free();
}

View File

@@ -2,6 +2,7 @@ package me.cortex.voxy.client.mixin.sodium;
import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.commonImpl.VoxyCommon;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import net.minecraft.client.world.ClientWorld;
@@ -18,12 +19,22 @@ public class MixinRenderSectionManager {
@Inject(method = "onChunkAdded", at = @At("HEAD"))
private void voxy$trackChunkAdd(int x, int z, CallbackInfo ci) {
if (this.level.worldRenderer != null) {
var system = ((IGetVoxyRenderSystem)(this.level.worldRenderer)).getVoxyRenderSystem();
if (system != null) {
}
}
}
@Inject(method = "onChunkRemoved", at = @At("HEAD"))
private void voxy$trackChunkRemove(int x, int z, CallbackInfo ci) {
if (this.level.worldRenderer != null) {
var system = ((IGetVoxyRenderSystem)(this.level.worldRenderer)).getVoxyRenderSystem();
if (system != null) {
}
}
}
@Inject(method = "onChunkRemoved", at = @At("HEAD"))

View File

@@ -0,0 +1,4 @@
#version 460 core
layout(early_fragment_tests) in;
void main() {}

View File

@@ -0,0 +1,21 @@
#version 450
layout(location = 0) out vec2 uv;
layout(binding = 0, std140) uniform SceneUniform {
mat4 MVP;
ivec4 section;
};
layout(binding = 1, std430) restrict readonly buffer ChunkPosBuffer {
ivec2[] chunkPos;
};
void main() {
ivec3 cubeCornerI = ivec3(gl_VertexID&1, (gl_VertexID>>2)&1, (gl_VertexID>>1)&1);
cubeCornerI.xz += chunkPos[gl_InstanceID];
//Expand the y height to be big (will be +- 8192)
//TODO: make it W.R.T world height and offsets
cubeCornerI.y = cubeCornerI.y*1024-512;
cubeCornerI -= section.xyz;
gl_Position = MVP * vec4(vec3(cubeCornerI*16), 1);
}