rewrote the render side of texture bakery, is very significantly better
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package me.cortex.voxy.client.core.gl;
|
||||
|
||||
import me.cortex.voxy.common.util.TrackedObject;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.lwjgl.opengl.GL45C.*;
|
||||
|
||||
public class GlVertexArray extends TrackedObject {
|
||||
public final int id;
|
||||
private int[] indices = new int[0];
|
||||
private int stride;
|
||||
public GlVertexArray() {
|
||||
this.id = glCreateVertexArrays();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
this.free0();
|
||||
glDeleteVertexArrays(this.id);
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
glBindVertexArray(this.id);
|
||||
}
|
||||
|
||||
public GlVertexArray bindBuffer(int buffer) {
|
||||
//TODO: optimization, use glVertexArrayVertexBuffers
|
||||
for (int index : this.indices) {
|
||||
glVertexArrayVertexBuffer(this.id, index, buffer, 0, this.stride);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GlVertexArray bindElementBuffer(int buffer) {
|
||||
glVertexArrayElementBuffer(this.id, buffer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GlVertexArray setStride(int stride) {
|
||||
this.stride = stride;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GlVertexArray setI(int index, int type, int count, int offset) {
|
||||
this.addIndex(index);
|
||||
glEnableVertexArrayAttrib(this.id, index);
|
||||
glVertexArrayAttribIFormat(this.id, index, count, type, offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GlVertexArray setF(int index, int type, int count, int offset) {
|
||||
return this.setF(index, type, count, false, offset);
|
||||
}
|
||||
|
||||
public GlVertexArray setF(int index, int type, int count, boolean normalize, int offset) {
|
||||
this.addIndex(index);
|
||||
glEnableVertexArrayAttrib(this.id, index);
|
||||
glVertexArrayAttribFormat(this.id, index, count, type, normalize, offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addIndex(int index) {
|
||||
for (int i : this.indices) {
|
||||
if (i == index) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.indices = Arrays.copyOf(this.indices, this.indices.length+1);
|
||||
this.indices[this.indices.length-1] = index;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ public class BakedBlockEntityModel {
|
||||
layer.putInto(bb);
|
||||
var mesh = bb.endNullable();
|
||||
if (mesh!=null)
|
||||
BudgetBufferRenderer.draw(mesh, texture, matrix);
|
||||
BudgetBufferRenderer.drawFast(mesh, texture, matrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,54 +8,103 @@ import com.mojang.blaze3d.systems.RenderPass;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.textures.GpuTexture;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.GlVertexArray;
|
||||
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.SharedIndexBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
||||
import net.minecraft.client.gl.*;
|
||||
import net.minecraft.client.render.BuiltBuffer;
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import static org.lwjgl.opengl.ARBVertexArrayObject.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
|
||||
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||
import static org.lwjgl.opengl.GL43.glBindVertexBuffer;
|
||||
import static org.lwjgl.opengl.GL45.*;
|
||||
|
||||
public class BudgetBufferRenderer {
|
||||
public static final RenderPipeline RENDERER_THING = RenderPipeline.builder()
|
||||
.withLocation(Identifier.of("voxy","bakery/position_tex"))
|
||||
.withVertexShader(Identifier.of("voxy","bakery/position_tex"))
|
||||
.withFragmentShader(Identifier.of("voxy","bakery/position_tex"))
|
||||
.withUniform("transform", UniformType.MATRIX4X4)
|
||||
.withSampler("tex")
|
||||
.withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST)
|
||||
.withVertexFormat(VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.QUADS)
|
||||
.build();
|
||||
private static final Shader bakeryShader = Shader.make()
|
||||
.add(ShaderType.VERTEX, "voxy:bakery/position_tex.vsh")
|
||||
.add(ShaderType.FRAGMENT, "voxy:bakery/position_tex.fsh")
|
||||
.compile();
|
||||
|
||||
|
||||
private static final GlBuffer indexBuffer;
|
||||
static {
|
||||
var i = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS);
|
||||
int id = ((GlGpuBuffer) i.getIndexBuffer(4096*3*2)).id;
|
||||
if (i.getIndexType() != VertexFormat.IndexType.SHORT) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
indexBuffer = new GlBuffer(3*2*2*4096);
|
||||
glCopyNamedBufferSubData(id, indexBuffer.id, 0, 0, 3*2*2*4096);
|
||||
}
|
||||
|
||||
private static final int STRIDE = 24;
|
||||
private static final GlVertexArray VA = new GlVertexArray()
|
||||
.setStride(STRIDE)
|
||||
.setF(0, GL_FLOAT, 3, 0)//pos
|
||||
.setI(1, GL_INT, 1, 4 * 3)//metadata
|
||||
.setF(2, GL_FLOAT, 2, 4 * 4)//UV
|
||||
.bindElementBuffer(indexBuffer.id);
|
||||
|
||||
private static GlBuffer immediateBuffer;
|
||||
private static int quadCount;
|
||||
public static void drawFast(BuiltBuffer buffer, GpuTexture tex, Matrix4f matrix) {
|
||||
if (buffer.getDrawParameters().mode() != VertexFormat.DrawMode.QUADS) {
|
||||
throw new IllegalStateException("Fast only supports quads");
|
||||
}
|
||||
|
||||
var buff = buffer.getBuffer();
|
||||
int size = buff.remaining();
|
||||
if (size%STRIDE != 0) throw new IllegalStateException();
|
||||
size /= STRIDE;
|
||||
if (size%4 != 0) throw new IllegalStateException();
|
||||
size /= 4;
|
||||
setup(MemoryUtil.memAddress(buff), size, ((net.minecraft.client.texture.GlTexture)tex).getGlId());
|
||||
buffer.close();
|
||||
|
||||
render(matrix);
|
||||
}
|
||||
|
||||
public static void setup(long dataPtr, int quads, int texId) {
|
||||
if (quads == 0) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public static void draw(BuiltBuffer buffer, GpuTexture tex, Matrix4f matrix) {
|
||||
//Fuz the gpu sampler state
|
||||
GlStateManager._activeTexture(GlConst.GL_TEXTURE0);
|
||||
GlStateManager._bindTexture(0);
|
||||
GlStateManager._activeTexture(GlConst.GL_TEXTURE1);
|
||||
GlStateManager._bindTexture(0);
|
||||
GlStateManager._activeTexture(GlConst.GL_TEXTURE2);
|
||||
GlStateManager._bindTexture(0);
|
||||
GlStateManager._activeTexture(GlConst.GL_TEXTURE2+1);
|
||||
GlStateManager._bindTexture(0);
|
||||
|
||||
RenderSystem.ShapeIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(buffer.getDrawParameters().mode());
|
||||
GpuBuffer gpuBuffer = buffer.getDrawParameters().format().uploadImmediateVertexBuffer(buffer.getBuffer());
|
||||
quadCount = quads;
|
||||
|
||||
var res = (GlResourceManager)RenderSystem.getDevice()
|
||||
.createCommandEncoder();
|
||||
res.currentProgram = null;
|
||||
res.currentPipeline = null;
|
||||
try (RenderPass renderPass = new RenderPassImpl(res, false)) {
|
||||
renderPass.setPipeline(RENDERER_THING);
|
||||
renderPass.setVertexBuffer(0, gpuBuffer);
|
||||
|
||||
renderPass.bindSampler("tex", tex);
|
||||
renderPass.setUniform("transform", matrix);
|
||||
|
||||
renderPass.setIndexBuffer(shapeIndexBuffer.getIndexBuffer(buffer.getDrawParameters().indexCount()), shapeIndexBuffer.getIndexType());
|
||||
renderPass.drawIndexed(0, buffer.getDrawParameters().indexCount());
|
||||
long size = quads * 4L * STRIDE;
|
||||
if (immediateBuffer == null || immediateBuffer.size()<size) {
|
||||
if (immediateBuffer != null) {
|
||||
immediateBuffer.free();
|
||||
}
|
||||
immediateBuffer = new GlBuffer(size*2L);//This also accounts for when immediateBuffer == null
|
||||
VA.bindBuffer(immediateBuffer.id);
|
||||
}
|
||||
//gpuBuffer.close();
|
||||
buffer.close();
|
||||
res.currentProgram = null;
|
||||
res.currentPipeline = null;
|
||||
long ptr = UploadStream.INSTANCE.upload(immediateBuffer, 0, size);
|
||||
MemoryUtil.memCopy(dataPtr, ptr, size);
|
||||
UploadStream.INSTANCE.commit();
|
||||
|
||||
bakeryShader.bind();
|
||||
VA.bind();
|
||||
glBindSampler(0, 0);
|
||||
glBindTextureUnit(0, texId);
|
||||
}
|
||||
|
||||
public static void render(Matrix4f matrix) {
|
||||
glUniformMatrix4fv(1, false, matrix.get(new float[16]));
|
||||
glDrawElements(GL_TRIANGLES, quadCount * 2 * 3, GL_UNSIGNED_SHORT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@ import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
|
||||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
||||
import static org.lwjgl.opengl.GL11.glGetInteger;
|
||||
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
|
||||
import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER;
|
||||
import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_BINDING;
|
||||
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
|
||||
import static org.lwjgl.opengl.GL30C.glBindFramebuffer;
|
||||
import static org.lwjgl.opengl.GL45.glBlitNamedFramebuffer;
|
||||
|
||||
public class ModelBakerySubsystem {
|
||||
@@ -79,6 +83,8 @@ public class ModelBakerySubsystem {
|
||||
Integer i = this.blockIdQueue.poll();
|
||||
int j = 0;
|
||||
if (i != null) {
|
||||
int fbBinding = glGetInteger(GL_FRAMEBUFFER_BINDING);
|
||||
|
||||
do {
|
||||
this.factory.addEntry(i);
|
||||
j++;
|
||||
@@ -86,6 +92,8 @@ public class ModelBakerySubsystem {
|
||||
break;
|
||||
i = this.blockIdQueue.poll();
|
||||
} while (i != null);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbBinding);//This is done here as stops needing to set then unset the fb in the thing 1000x
|
||||
}
|
||||
this.blockIdCount.addAndGet(-j);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import me.cortex.voxy.client.core.gl.Capabilities;
|
||||
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery;
|
||||
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery2;
|
||||
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.common.Logger;
|
||||
@@ -67,7 +68,7 @@ public class ModelFactory {
|
||||
|
||||
private final Biome DEFAULT_BIOME = MinecraftClient.getInstance().world.getRegistryManager().getOrThrow(RegistryKeys.BIOME).get(BiomeKeys.PLAINS);
|
||||
|
||||
public final ModelTextureBakery bakery;
|
||||
public final ModelTextureBakery2 bakery;
|
||||
|
||||
|
||||
//Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the
|
||||
@@ -125,7 +126,7 @@ public class ModelFactory {
|
||||
public ModelFactory(Mapper mapper, ModelStore storage) {
|
||||
this.mapper = mapper;
|
||||
this.storage = storage;
|
||||
this.bakery = new ModelTextureBakery(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
|
||||
this.bakery = new ModelTextureBakery2(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
|
||||
|
||||
this.metadataCache = new long[1<<16];
|
||||
this.fluidStateLUT = new int[1<<16];
|
||||
@@ -194,7 +195,7 @@ public class ModelFactory {
|
||||
}
|
||||
this.resultJobs.add(()->processTextureBakeResult(blockId, blockState, textureData));
|
||||
});
|
||||
this.bakery.renderFacesToStream(blockState, 123456, isFluid, this.downstream.getBufferId(), allocation);
|
||||
this.bakery.renderToStream(blockState, this.downstream.getBufferId(), allocation);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,13 @@ 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 static org.lwjgl.opengl.ARBDirectStateAccess.glClearNamedFramebufferfv;
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL11.GL_STENCIL_INDEX;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
import static org.lwjgl.opengl.GL43.*;
|
||||
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfi;
|
||||
|
||||
public class GlViewCapture {
|
||||
private final int width;
|
||||
@@ -57,6 +59,11 @@ public class GlViewCapture {
|
||||
glDispatchCompute(3, 2, 1);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
glClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, new float[]{0,0,0,0});
|
||||
glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
|
||||
}
|
||||
|
||||
public void free() {
|
||||
this.framebuffer.free();
|
||||
this.colourTex.free();
|
||||
|
||||
@@ -219,6 +219,8 @@ public class ModelTextureBakery {
|
||||
if (!renderFluid) {
|
||||
//TODO: need to do 2 variants for quads, one which have coloured, ones that dont, might be able to pull a spare bit
|
||||
// at the end whether or not a pixel should be mixed with texture
|
||||
|
||||
//TODO: CACHE THE BUILT MODEL AND REUSE AND JUST RENDER FROM DIFFERENT VIEWS
|
||||
renderQuads(bb, model, new MatrixStack(), randomValue);
|
||||
} else {
|
||||
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
|
||||
@@ -291,7 +293,7 @@ public class ModelTextureBakery {
|
||||
|
||||
var mesh = bb.endNullable();
|
||||
if (mesh != null)
|
||||
BudgetBufferRenderer.draw(mesh, texture, transform);
|
||||
BudgetBufferRenderer.drawFast(mesh, texture, transform);
|
||||
}
|
||||
|
||||
private static void renderQuads(BufferBuilder builder, BlockStateModel model, MatrixStack stack, long randomValue) {
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
package me.cortex.voxy.client.core.model.bakery;
|
||||
|
||||
import me.cortex.voxy.client.core.model.BudgetBufferRenderer;
|
||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||
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.render.RenderLayer;
|
||||
import net.minecraft.client.render.RenderLayers;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.render.model.BlockStateModel;
|
||||
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.system.MemoryUtil;
|
||||
|
||||
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.GL11C.GL_DEPTH;
|
||||
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfv;
|
||||
|
||||
public class ModelTextureBakery2 {
|
||||
//Note: the first bit of metadata is if alpha discard is enabled
|
||||
private static final Matrix4f[] VIEWS = new Matrix4f[6];
|
||||
|
||||
private final GlViewCapture capture;
|
||||
private final ReuseVertexConsumer vc = new ReuseVertexConsumer();
|
||||
private static final int FORMAT_STRIDE = 24;
|
||||
private static final class ReuseVertexConsumer implements VertexConsumer {
|
||||
private MemoryBuffer buffer = new MemoryBuffer(8192);
|
||||
private long ptr;
|
||||
private int count;
|
||||
|
||||
public ReuseVertexConsumer() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReuseVertexConsumer vertex(float x, float y, float z) {
|
||||
this.ensureCanPut();
|
||||
this.ptr += FORMAT_STRIDE; this.count++; //Goto next vertex
|
||||
MemoryUtil.memPutFloat(this.ptr, x);
|
||||
MemoryUtil.memPutFloat(this.ptr + 4, y);
|
||||
MemoryUtil.memPutFloat(this.ptr + 8, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReuseVertexConsumer meta(int metadata) {
|
||||
MemoryUtil.memPutInt(this.ptr + 12, metadata);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReuseVertexConsumer color(int red, int green, int blue, int alpha) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReuseVertexConsumer texture(float u, float v) {
|
||||
MemoryUtil.memPutFloat(this.ptr + 16, u);
|
||||
MemoryUtil.memPutFloat(this.ptr + 20, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReuseVertexConsumer overlay(int u, int v) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReuseVertexConsumer light(int u, int v) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReuseVertexConsumer normal(float x, float y, float z) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReuseVertexConsumer quad(BakedQuad quad, int metadata) {
|
||||
int[] data = quad.vertexData();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float x = Float.intBitsToFloat(data[i * 8]);
|
||||
float y = Float.intBitsToFloat(data[i * 8 + 1]);
|
||||
float z = Float.intBitsToFloat(data[i * 8 + 2]);
|
||||
this.vertex(x,y,z);
|
||||
float u = Float.intBitsToFloat(data[i * 8 + 4]);
|
||||
float v = Float.intBitsToFloat(data[i * 8 + 5]);
|
||||
this.texture(u,v);
|
||||
|
||||
this.meta(metadata);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ensureCanPut() {
|
||||
if ((long) (this.count + 1) * FORMAT_STRIDE < this.buffer.size) {
|
||||
return;
|
||||
}
|
||||
long offset = this.buffer.address-this.ptr;
|
||||
//1.5x the size
|
||||
var newBuffer = new MemoryBuffer((((int)(this.buffer.size*1.5)+FORMAT_STRIDE-1)/FORMAT_STRIDE)*FORMAT_STRIDE);
|
||||
this.buffer.cpyTo(newBuffer.address);
|
||||
this.buffer.free();
|
||||
this.buffer = newBuffer;
|
||||
this.ptr = offset + newBuffer.address;
|
||||
}
|
||||
|
||||
public ReuseVertexConsumer reset() {
|
||||
this.count = 0;
|
||||
this.ptr = this.buffer.address - FORMAT_STRIDE;//the thing is first time this gets incremented by FORMAT_STRIDE
|
||||
return this;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
this.buffer.free();
|
||||
this.buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
public ModelTextureBakery2(int width, int height) {
|
||||
this.capture = new GlViewCapture(width, height);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
private void bakeBlockModel(BlockState state, RenderLayer layer) {
|
||||
var model = MinecraftClient.getInstance()
|
||||
.getBakedModelManager()
|
||||
.getBlockModels()
|
||||
.getModel(state);
|
||||
|
||||
boolean hasDiscard = layer == RenderLayer.getCutout() ||
|
||||
layer == RenderLayer.getCutoutMipped() ||
|
||||
layer == RenderLayer.getTripwire();
|
||||
|
||||
for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) {
|
||||
for (var part : model.getParts(new LocalRandom(42L))) {
|
||||
var quads = part.getQuads(direction);
|
||||
for (var quad : quads) {
|
||||
//TODO: add meta specifiying quad has a tint
|
||||
|
||||
int meta = hasDiscard?1:0;
|
||||
this.vc.quad(quad, meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void bakeFluidState(BlockState state, int face) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
if (shouldReturnAirForFluid(pos, face)) {
|
||||
return Blocks.AIR.getDefaultState();
|
||||
}
|
||||
|
||||
//Fixme:
|
||||
// This makes it so that the top face of water is always air, if this is commented out
|
||||
// the up block will be a liquid state which makes the sides full
|
||||
// if this is uncommented, that issue is fixed but e.g. stacking water layers ontop of eachother
|
||||
// doesnt fill the side of the block
|
||||
|
||||
//if (pos.getY() == 1) {
|
||||
// return Blocks.AIR.getDefaultState();
|
||||
//}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
if (shouldReturnAirForFluid(pos, face)) {
|
||||
return Blocks.AIR.getDefaultState().getFluidState();
|
||||
}
|
||||
|
||||
return state.getFluidState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBottomY() {
|
||||
return 0;
|
||||
}
|
||||
}, this.vc, state, state.getFluidState());
|
||||
}
|
||||
|
||||
private static boolean shouldReturnAirForFluid(BlockPos pos, int face) {
|
||||
var fv = Direction.byIndex(face).getVector();
|
||||
int dot = fv.getX()*pos.getX() + fv.getY()*pos.getY() + fv.getZ()*pos.getZ();
|
||||
return dot >= 1;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
this.capture.free();
|
||||
this.vc.free();
|
||||
}
|
||||
|
||||
|
||||
public void renderToStream(BlockState state, int streamBuffer, int streamOffset) {
|
||||
this.capture.clear();
|
||||
boolean isBlock = true;
|
||||
RenderLayer layer;
|
||||
if (state.getBlock() instanceof FluidBlock) {
|
||||
layer = RenderLayers.getFluidLayer(state.getFluidState());
|
||||
isBlock = false;
|
||||
} else {
|
||||
layer = RenderLayers.getBlockLayer(state);
|
||||
}
|
||||
|
||||
//TODO: support block model entities
|
||||
//state.hasBlockEntity()
|
||||
|
||||
//Setup GL state
|
||||
int[] viewdat = new int[4];
|
||||
int blockTextureId;
|
||||
|
||||
{
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
if (layer == RenderLayer.getTranslucent()) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
glStencilFunc(GL_ALWAYS, 1, 0xFF);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, viewdat);//TODO: faster way todo this, or just use main framebuffer resolution
|
||||
|
||||
//Bind the capture framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id);
|
||||
|
||||
var tex = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlTexture();
|
||||
blockTextureId = ((net.minecraft.client.texture.GlTexture)tex).getGlId();
|
||||
}
|
||||
|
||||
//TODO: fastpath for blocks
|
||||
if (isBlock) {
|
||||
this.vc.reset();
|
||||
this.bakeBlockModel(state, layer);
|
||||
if (this.vc.count != 0) {//only render if there... is shit to render
|
||||
if (this.vc.count % 4 != 0) throw new IllegalStateException();
|
||||
|
||||
//Setup for continual emission
|
||||
BudgetBufferRenderer.setup(this.vc.buffer.address, this.vc.count / 4, blockTextureId);//note: this.vc.buffer.address NOT this.vc.ptr
|
||||
|
||||
var mat = new Matrix4f();
|
||||
for (int i = 0; i < VIEWS.length; i++) {
|
||||
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
|
||||
|
||||
//The projection matrix
|
||||
mat.set(2, 0, 0, 0,
|
||||
0, 2, 0, 0,
|
||||
0, 0, -1f, 0,
|
||||
-1, -1, 0, 1);
|
||||
|
||||
BudgetBufferRenderer.render(mat.mul(VIEWS[i]));
|
||||
}
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
} else {//Is fluid, slow path :(
|
||||
if (!(state.getBlock() instanceof FluidBlock)) throw new IllegalStateException();
|
||||
|
||||
var mat = new Matrix4f();
|
||||
for (int i = 0; i < VIEWS.length; i++) {
|
||||
this.vc.reset();
|
||||
this.bakeFluidState(state, i);
|
||||
if (this.vc.count == 0) continue;
|
||||
BudgetBufferRenderer.setup(this.vc.buffer.address, this.vc.count / 4, blockTextureId);
|
||||
|
||||
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
|
||||
|
||||
//The projection matrix
|
||||
mat.set(2, 0, 0, 0,
|
||||
0, 2, 0, 0,
|
||||
0, 0, -1f, 0,
|
||||
-1, -1, 0, 1);
|
||||
|
||||
BudgetBufferRenderer.render(mat.mul(VIEWS[i]));
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
//"Restore" gl state
|
||||
glViewport(viewdat[0], viewdat[1], viewdat[2], viewdat[3]);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
//Finish and download
|
||||
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
|
||||
this.capture.emitToStream(streamBuffer, streamOffset);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id);
|
||||
glClearDepth(1);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static {
|
||||
//TODO: FIXME: need to bake in the correct orientation, HOWEVER some orientations require a flipped winding order!!!!
|
||||
|
||||
addView(0, -90,0, 0, false);//Direction.DOWN
|
||||
addView(1, 90,0, 0, false);//Direction.UP
|
||||
addView(2, 0,180, 0, true);//Direction.NORTH
|
||||
addView(3, 0,0, 0, false);//Direction.SOUTH
|
||||
//TODO: check these arnt the wrong way round
|
||||
addView(4, 0,90, 270, false);//Direction.EAST
|
||||
addView(5, 0,270, 270, false);//Direction.WEST
|
||||
}
|
||||
|
||||
private static void addView(int i, 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));
|
||||
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch));
|
||||
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
|
||||
stack.translate(-0.5f,-0.5f,-0.5f);
|
||||
VIEWS[i] = new Matrix4f(stack.peek().getPositionMatrix());
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
|
||||
this.renderGen = new RenderGenerationService(world, this.modelService, serviceThreadPool,
|
||||
this.geometryUpdateQueue::push, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets,
|
||||
()->this.geometryUpdateQueue.count()<1000);
|
||||
()->this.geometryUpdateQueue.count()<7000);
|
||||
|
||||
router.setCallbacks(this.renderGen::enqueueTask, section -> {
|
||||
section.acquire();
|
||||
@@ -142,7 +142,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
|
||||
//if (this.modelService.getProcessingCount() < 750)
|
||||
{//Very bad hack to try control things
|
||||
this.geometryUpdateQueue.consumeNano(1_000_000 - (System.nanoTime() - frameStart));
|
||||
this.geometryUpdateQueue.consumeNano(1_500_000 - (System.nanoTime() - frameStart));
|
||||
}
|
||||
|
||||
this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here??
|
||||
|
||||
@@ -206,7 +206,7 @@ public class ServiceThreadPool {
|
||||
if (service == null) {
|
||||
Logger.warn("No available jobs, sleeping releasing returning");
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
Thread.sleep((long) (200*Math.random()+5));
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -173,21 +173,26 @@ public class ActiveSectionTracker {
|
||||
sec = section;
|
||||
}
|
||||
}
|
||||
|
||||
WorldSection aa = null;
|
||||
if (sec != null) {
|
||||
long stamp2 = this.lruLock.writeLock();
|
||||
WorldSection a = this.lruSecondaryCache.put(section.key, section);
|
||||
if (a != null) {
|
||||
throw new IllegalStateException("duplicate sections in cache is impossible");
|
||||
}
|
||||
//If cache is bigger than its ment to be, remove the least recently used and free it
|
||||
if (this.lruSize < this.lruSecondaryCache.size()) {
|
||||
aa = this.lruSecondaryCache.removeFirst();
|
||||
}
|
||||
this.lruLock.unlockWrite(stamp2);
|
||||
|
||||
}
|
||||
|
||||
lock.unlockWrite(stamp);
|
||||
|
||||
if (sec != null) {
|
||||
stamp = this.lruLock.writeLock();
|
||||
|
||||
WorldSection a = this.lruSecondaryCache.put(section.key, section);
|
||||
//If cache is bigger than its ment to be, remove the least recently used and free it
|
||||
if (a == null && this.lruSize < this.lruSecondaryCache.size()) {
|
||||
a = this.lruSecondaryCache.removeFirst();
|
||||
}
|
||||
this.lruLock.unlockWrite(stamp);
|
||||
|
||||
if (a != null) {
|
||||
a._releaseArray();
|
||||
}
|
||||
if (aa != null) {
|
||||
aa._releaseArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#version 430
|
||||
|
||||
layout(location=0) uniform sampler2D tex;
|
||||
in vec2 texCoord;
|
||||
in flat uint metadata;
|
||||
in vec2 texCoord;
|
||||
out vec4 colour;
|
||||
|
||||
void main() {
|
||||
colour = texture(tex, texCoord)*(metadata&1);
|
||||
if (colour.a <0.0001f) {
|
||||
colour = texture(tex, texCoord);
|
||||
if (colour.a < 0.0001f && ((metadata&1u)!=0)) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#version 430
|
||||
|
||||
layout(location=0) in vec3 pos;
|
||||
layout(location=1) in vec2 uv;
|
||||
layout(location=2) in vec4 _metadata;
|
||||
layout(location=1) in uint _metadata;
|
||||
layout(location=2) in vec2 uv;
|
||||
|
||||
uniform mat4 transform;
|
||||
layout(location=1) uniform mat4 transform;
|
||||
out vec2 texCoord;
|
||||
out flat uint metadata;
|
||||
|
||||
void main() {
|
||||
uvec4 meta = uvec4(_metadata*255);
|
||||
metadata = (meta.r<<16)|(meta.g<<8)|(meta.b);
|
||||
metadata = _metadata;
|
||||
|
||||
gl_Position = transform * vec4(pos, 1.0);
|
||||
texCoord = uv;
|
||||
|
||||
Reference in New Issue
Block a user