From fb52dea6fa7c52e4474283b8e1a58eb8ab80de3e Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Tue, 6 May 2025 22:52:00 +1000 Subject: [PATCH] Finished the modelfactory stuff, is now renders block entities like shulkers --- .../core/model/BakedBlockEntityModel.java | 151 ------ .../voxy/client/core/model/ModelFactory.java | 7 +- .../model/bakery/BakedBlockEntityModel.java | 79 +++ .../{ => bakery}/BudgetBufferRenderer.java | 4 +- .../core/model/bakery/ModelTextureBakery.java | 491 +++++++++--------- .../model/bakery/ModelTextureBakery2.java | 375 ------------- .../model/bakery/ReuseVertexConsumer.java | 114 ++++ .../building/RenderDataFactory45.java | 79 --- 8 files changed, 438 insertions(+), 862 deletions(-) delete mode 100644 src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java create mode 100644 src/main/java/me/cortex/voxy/client/core/model/bakery/BakedBlockEntityModel.java rename src/main/java/me/cortex/voxy/client/core/model/{ => bakery}/BudgetBufferRenderer.java (97%) delete mode 100644 src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery2.java create mode 100644 src/main/java/me/cortex/voxy/client/core/model/bakery/ReuseVertexConsumer.java diff --git a/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java b/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java deleted file mode 100644 index a0b2e96b..00000000 --- a/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java +++ /dev/null @@ -1,151 +0,0 @@ -package me.cortex.voxy.client.core.model; - -import com.mojang.blaze3d.textures.GpuTexture; -import com.mojang.blaze3d.vertex.VertexFormat; -import me.cortex.voxy.common.Logger; -import net.minecraft.block.BlockEntityProvider; -import net.minecraft.block.BlockState; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.*; -import net.minecraft.client.texture.GlTexture; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import org.joml.Matrix4f; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; -import static org.lwjgl.opengl.GL11.glBindTexture; - -public class BakedBlockEntityModel { - private static final class BakedVertices implements VertexConsumer { - private boolean makingVertex = false; - public final RenderLayer layer; - private float cX, cY, cZ; - private int cR, cG, cB, cA; - private float cU, cV; - - private final List vertices = new ArrayList<>(); - - private BakedVertices(RenderLayer layer) { - this.layer = layer; - } - - @Override - public VertexConsumer vertex(float x, float y, float z) { - this.next(); - this.cX = x; - this.cY = y; - this.cZ = z; - this.makingVertex = true; - return this; - } - - @Override - public VertexConsumer color(int red, int green, int blue, int alpha) { - this.cR = 0;//red; - this.cG = 0;//green; - this.cB = 0;//blue; - this.cA = alpha; - return this; - } - - @Override - public VertexConsumer texture(float u, float v) { - this.cU = u; - this.cV = v; - return this; - } - - @Override - public VertexConsumer overlay(int u, int v) { - return this; - } - - @Override - public VertexConsumer light(int u, int v) { - return this; - } - - @Override - public VertexConsumer normal(float x, float y, float z) { - return this; - } - - private void next() { - if (this.makingVertex) { - this.makingVertex = false; - this.vertices.add(new int[]{ - Float.floatToIntBits(this.cX), Float.floatToIntBits(this.cY), Float.floatToIntBits(this.cZ), - this.cR, this.cG, this.cB, this.cA, - Float.floatToIntBits(this.cU), Float.floatToIntBits(this.cV)}); - } - } - - public void putInto(VertexConsumer vc) { - this.next(); - for (var vert : this.vertices) { - vc.vertex(Float.intBitsToFloat(vert[0]), Float.intBitsToFloat(vert[1]), Float.intBitsToFloat(vert[2])) - .color(vert[3], vert[4], vert[5], vert[6]) - .texture(Float.intBitsToFloat(vert[7]), Float.intBitsToFloat(vert[8])); - } - } - - public boolean isEmpty() { - return this.vertices.isEmpty(); - } - } - - private final List layers; - private BakedBlockEntityModel(List layers) { - this.layers = layers; - } - - public void renderOut(Matrix4f matrix, GpuTexture texture) { - var vc = Tessellator.getInstance(); - for (var layer : this.layers) { - if (layer.isEmpty()) continue; - if (layer.layer instanceof RenderLayer.MultiPhase mp) { - Identifier textureId = mp.phases.texture.getId().orElse(null); - if (textureId == null) { - System.err.println("ERROR: Empty texture id for layer: " + layer); - } else { - texture = MinecraftClient.getInstance().getTextureManager().getTexture(textureId).getGlTexture(); - } - } - - var bb = vc.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); - layer.putInto(bb); - var mesh = bb.endNullable(); - if (mesh!=null) - BudgetBufferRenderer.drawFast(mesh, texture, matrix); - } - } - - public static BakedBlockEntityModel bake(BlockState state) { - Map map = new HashMap<>(); - var entity = ((BlockEntityProvider)state.getBlock()).createBlockEntity(BlockPos.ORIGIN, state); - if (entity == null) { - return null; - } - var renderer = MinecraftClient.getInstance().getBlockEntityRenderDispatcher().get(entity); - entity.setWorld(MinecraftClient.getInstance().world); - if (renderer != null) { - try { - renderer.render(entity, 0.0f, new MatrixStack(), layer->map.computeIfAbsent(layer, BakedVertices::new), 0, 0, new Vec3d(0,0,0)); - } catch (Exception e) { - Logger.error("Unable to bake block entity: " + entity, e); - } - } - entity.markRemoved(); - if (map.isEmpty()) { - return null; - } - return new BakedBlockEntityModel(map.values().stream().toList()); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java index 61080ddc..ed7af43a 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java @@ -5,7 +5,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; 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.ModelTextureBakery2; +import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery; import me.cortex.voxy.client.core.rendering.util.RawDownloadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.world.other.Mapper; @@ -33,7 +33,6 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; import java.util.*; -import java.util.stream.Stream; import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE; import static org.lwjgl.opengl.GL11.*; @@ -66,7 +65,7 @@ public class ModelFactory { private final Biome DEFAULT_BIOME = MinecraftClient.getInstance().world.getRegistryManager().getOrThrow(RegistryKeys.BIOME).get(BiomeKeys.PLAINS); - public final ModelTextureBakery2 bakery; + public final ModelTextureBakery bakery; //Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the @@ -124,7 +123,7 @@ public class ModelFactory { public ModelFactory(Mapper mapper, ModelStore storage) { this.mapper = mapper; this.storage = storage; - this.bakery = new ModelTextureBakery2(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE); + this.bakery = new ModelTextureBakery(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE); this.metadataCache = new long[1<<16]; this.fluidStateLUT = new int[1<<16]; diff --git a/src/main/java/me/cortex/voxy/client/core/model/bakery/BakedBlockEntityModel.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/BakedBlockEntityModel.java new file mode 100644 index 00000000..5f722936 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/bakery/BakedBlockEntityModel.java @@ -0,0 +1,79 @@ +package me.cortex.voxy.client.core.model.bakery; + +import com.mojang.blaze3d.textures.GpuTexture; +import me.cortex.voxy.common.Logger; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.GlGpuBuffer; +import net.minecraft.client.render.*; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import org.joml.Matrix4f; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BakedBlockEntityModel { + private record LayerConsumer(RenderLayer layer, ReuseVertexConsumer consumer) {} + private final List layers; + private BakedBlockEntityModel(List layers) { + this.layers = layers; + } + + public void render(Matrix4f matrix, int texId) { + for (var layer : this.layers) { + if (layer.consumer.isEmpty()) continue; + if (layer.layer instanceof RenderLayer.MultiPhase mp) { + Identifier textureId = mp.phases.texture.getId().orElse(null); + if (textureId == null) { + Logger.error("ERROR: Empty texture id for layer: " + layer); + } else { + texId = ((net.minecraft.client.texture.GlTexture)MinecraftClient.getInstance().getTextureManager().getTexture(textureId).getGlTexture()).getGlId(); + } + } + if (texId == 0) continue; + BudgetBufferRenderer.setup(layer.consumer.getAddress(), layer.consumer.quadCount(), texId); + BudgetBufferRenderer.render(matrix); + } + } + + public void release() { + this.layers.forEach(layer->layer.consumer.free()); + } + + public static BakedBlockEntityModel bake(BlockState state) { + Map map = new HashMap<>(); + var entity = ((BlockEntityProvider)state.getBlock()).createBlockEntity(BlockPos.ORIGIN, state); + if (entity == null) { + return null; + } + var renderer = MinecraftClient.getInstance().getBlockEntityRenderDispatcher().get(entity); + entity.setWorld(MinecraftClient.getInstance().world); + if (renderer != null) { + try { + renderer.render(entity, 0.0f, new MatrixStack(), layer->map.computeIfAbsent(layer, rl -> new LayerConsumer(rl, new ReuseVertexConsumer())).consumer, 0, 0, new Vec3d(0,0,0)); + } catch (Exception e) { + Logger.error("Unable to bake block entity: " + entity, e); + } + } + entity.markRemoved(); + if (map.isEmpty()) { + return null; + } + for (var i : new ArrayList<>(map.values())) { + if (i.consumer.isEmpty()) { + map.remove(i.layer); + i.consumer.free(); + } + } + if (map.isEmpty()) { + return null; + } + return new BakedBlockEntityModel(new ArrayList<>(map.values())); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/model/BudgetBufferRenderer.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/BudgetBufferRenderer.java similarity index 97% rename from src/main/java/me/cortex/voxy/client/core/model/BudgetBufferRenderer.java rename to src/main/java/me/cortex/voxy/client/core/model/bakery/BudgetBufferRenderer.java index f627bbd6..3182585e 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/BudgetBufferRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/model/bakery/BudgetBufferRenderer.java @@ -1,4 +1,4 @@ -package me.cortex.voxy.client.core.model; +package me.cortex.voxy.client.core.model.bakery; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlStateManager; @@ -31,6 +31,8 @@ import static org.lwjgl.opengl.GL43.glBindVertexBuffer; import static org.lwjgl.opengl.GL45.*; public class BudgetBufferRenderer { + public static final int VERTEX_FORMAT_SIZE = 24; + private static final Shader bakeryShader = Shader.make() .add(ShaderType.VERTEX, "voxy:bakery/position_tex.vsh") .add(ShaderType.FRAGMENT, "voxy:bakery/position_tex.fsh") diff --git a/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery.java index e25144f4..33f6c9a1 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery.java +++ b/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery.java @@ -1,21 +1,16 @@ package me.cortex.voxy.client.core.model.bakery; -import com.mojang.blaze3d.textures.GpuTexture; -import com.mojang.blaze3d.vertex.VertexFormat; -import me.cortex.voxy.client.core.model.BakedBlockEntityModel; -import me.cortex.voxy.client.core.model.BudgetBufferRenderer; 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.*; -import net.minecraft.client.render.model.BlockStateModel; -import net.minecraft.client.util.BufferAllocator; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.RenderLayers; 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.ColorHelper; import net.minecraft.util.math.Direction; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.random.LocalRandom; @@ -26,165 +21,117 @@ import net.minecraft.world.chunk.light.LightingProvider; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import java.util.ArrayList; -import java.util.List; - 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.GL20C.glUniformMatrix4fv; import static org.lwjgl.opengl.GL30.*; -import static org.lwjgl.opengl.GL45.glBlitNamedFramebuffer; +import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfv; -//Builds a texture for each face of a model public class ModelTextureBakery { - private static final List FACE_VIEWS = new ArrayList<>(); + //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 final int width; private final int height; - private final GlViewCapture capture; - public ModelTextureBakery(int width, int height) { + this.capture = new GlViewCapture(width, height); this.width = width; this.height = height; - this.capture = new GlViewCapture(width, height); - - //This is done to help make debugging easier - FACE_VIEWS.clear(); - AddViews(); } - private static void AddViews() { - //TODO: FIXME: need to bake in the correct orientation, HOWEVER some orientations require a flipped winding order!!!! - - 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)); - 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); - } - - - - //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 void renderFacesToStream(BlockState state, long randomValue, boolean renderFluid, int streamBuffer, int streamBaseOffset) { + private void bakeBlockModel(BlockState state, RenderLayer layer) { var model = MinecraftClient.getInstance() .getBakedModelManager() .getBlockModels() .getModel(state); - BakedBlockEntityModel entityModel = state.hasBlockEntity()?BakedBlockEntityModel.bake(state):null; + boolean hasDiscard = layer == RenderLayer.getCutout() || + layer == RenderLayer.getCutoutMipped() || + layer == RenderLayer.getTripwire(); - var projection = new Matrix4f().identity().set(new float[]{ - 2,0,0,0, - 0, 2,0,0, - 0,0, -1f,0, - -1,-1,0,1, - }); + 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 originalFramebuffer = glGetInteger(GL_FRAMEBUFFER_BINDING); - - - RenderLayer renderLayer = null; - if (!renderFluid) { - renderLayer = RenderLayers.getBlockLayer(state); - } else { - renderLayer = RenderLayers.getFluidLayer(state.getFluidState()); - } - - - //TODO: figure out why calling this makes minecraft render black - //renderLayer.startDrawing(); - - - 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); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - glEnable(GL_STENCIL_TEST); - //glDepthRange(0, 1); - glDepthMask(true); - glEnable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - //glDepthFunc(GL_LESS); - - - //TODO: Find a better solution - if (renderLayer == RenderLayer.getTranslucent()) { - //Very hacky blend function to retain the effect of the applied alpha since we dont really want to apply alpha - // this is because we apply the alpha again when rendering the terrain meaning the alpha is being double applied - glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_BLEND); - } - boolean hasDiscard = renderLayer == RenderLayer.getCutout() || - renderLayer == RenderLayer.getCutoutMipped(); - - - //glBlendFunc(GL_ONE, GL_ONE); - - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilMask(0xFF); - - int[] viewdat = new int[4]; - glGetIntegerv(GL_VIEWPORT, viewdat); - - var tex = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlTexture(); - for (int i = 0; i < FACE_VIEWS.size(); i++) { - glViewport((i%3)*this.width, (i/3)*this.height, this.width, this.height); - glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id); - var transform = new Matrix4f(projection).mul(FACE_VIEWS.get(i).peek().getPositionMatrix()); - if (entityModel!=null&&!renderFluid) { - entityModel.renderOut(transform, tex); + int meta = hasDiscard?1:0; + this.vc.quad(quad, meta); + } } - this.rasterView(state, model, transform, randomValue, i, renderFluid, tex, hasDiscard); } - - glViewport(viewdat[0], viewdat[1], viewdat[2], viewdat[3]); + } + 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; + } - //renderLayer.endDrawing(); + @Override + public LightingProvider getLightingProvider() { + return null; + } - glDisable(GL_STENCIL_TEST); - glDisable(GL_BLEND); + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return 0; + } - glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); - this.capture.emitToStream(streamBuffer, streamBaseOffset); + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + return 0; + } - //var target = DefaultTerrainRenderPasses.CUTOUT.getTarget(); - //int boundFB = ((net.minecraft.client.texture.GlTexture) target.getColorAttachment()).getOrCreateFramebuffer(((GlBackend) RenderSystem.getDevice()).getFramebufferManager(), target.getDepthAttachment()); - //glBlitNamedFramebuffer(this.capture.framebuffer.id, boundFB, 0,0,16*3, 16*2, 0,0, 16*3*4,16*2*4, GL_COLOR_BUFFER_BIT, GL_NEAREST); + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + @Override + public BlockState getBlockState(BlockPos pos) { + if (shouldReturnAirForFluid(pos, face)) { + return Blocks.AIR.getDefaultState(); + } - //SOMEBODY PLEASE FUCKING EXPLAIN TO ME WHY MUST CLEAR THE FRAMEBUFFER HERE WHEN IT IS LITERALLY CLEARED AT THE START OF THE FRAME - // WITHOUT THIS, WATER DOESNT RENDER - //TODO: FIXME, WHAT THE ACTUAL FUCK - glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id); - glClearDepth(1); - glClear(GL_DEPTH_BUFFER_BIT); + //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; + } - glBindFramebuffer(GL_FRAMEBUFFER, originalFramebuffer); + @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) { @@ -193,124 +140,164 @@ public class ModelTextureBakery { return dot >= 1; } - private final BufferAllocator allocator = new BufferAllocator(786432); - private void rasterView(BlockState state, BlockStateModel model, Matrix4f transform, long randomValue, int face, boolean renderFluid, GpuTexture texture, boolean hasDiscard) { - var bb = new BufferBuilder(this.allocator, VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR) { - @Override - public void vertex(float x, float y, float z, int color, float u, float v, int overlay, int light, float normalX, float normalY, float normalZ) { - int colour = color; - colour |= hasDiscard?2:0; - if (renderFluid) { - colour = ColorHelper.getArgb(0,0,1); - } - super.vertex(x, y, z, colour, u, v, overlay, light, normalX, normalY, normalZ); - } - - @Override - public VertexConsumer color(int argb) { - return super.color(ColorHelper.getArgb(0,0,1)); - } - - @Override - public VertexConsumer color(int red, int green, int blue, int alpha) { - return super.color(0, 0, 1, 255); - } - }; - 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() { - @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(); - } - //if (pos.getY() == 1) { - // return Blocks.AIR.getDefaultState().getFluidState(); - //} - return state.getFluidState(); - } - - @Override - public int getHeight() { - return 0; - } - - @Override - public int getBottomY() { - return 0; - } - }, bb, state, state.getFluidState()); - } - - var mesh = bb.endNullable(); - if (mesh != null) - BudgetBufferRenderer.drawFast(mesh, texture, transform); - } - - private static void renderQuads(BufferBuilder builder, BlockStateModel model, MatrixStack stack, long randomValue) { - 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(randomValue))) { - var quads = part.getQuads(direction); - for (var quad : quads) { - //TODO: mark pixels that have - int meta = 1; - builder.quad(stack.peek(), quad, ((meta >> 16) & 0xff) / 255f, ((meta >> 8) & 0xff) / 255f, (meta & 0xff) / 255f, 1.0f, 0, 0); - } - } - } - } - public void free() { this.capture.free(); - this.allocator.close(); + 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 + BakedBlockEntityModel bbem = null; + if (state.hasBlockEntity()) { + bbem = BakedBlockEntityModel.bake(state); + } + + //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.isEmpty()) {//only render if there... is shit to render + + //Setup for continual emission + BudgetBufferRenderer.setup(this.vc.getAddress(), this.vc.quadCount(), 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) + .mul(VIEWS[i]); + + BudgetBufferRenderer.render(mat); + } + } + 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.isEmpty()) continue; + BudgetBufferRenderer.setup(this.vc.getAddress(), this.vc.quadCount(), 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) + .mul(VIEWS[i]); + + BudgetBufferRenderer.render(mat); + } + glBindVertexArray(0); + } + + //Render block model entity data if it exists + if (bbem != null) { + //Rerender everything again ;-; but is ok (is not) + + 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) + .mul(VIEWS[i]); + + bbem.render(mat, blockTextureId); + } + glBindVertexArray(0); + + bbem.release(); + } + + + //"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()); } } diff --git a/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery2.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery2.java deleted file mode 100644 index 28e6677d..00000000 --- a/src/main/java/me/cortex/voxy/client/core/model/bakery/ModelTextureBakery2.java +++ /dev/null @@ -1,375 +0,0 @@ -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()); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/model/bakery/ReuseVertexConsumer.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/ReuseVertexConsumer.java new file mode 100644 index 00000000..60365626 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/bakery/ReuseVertexConsumer.java @@ -0,0 +1,114 @@ +package me.cortex.voxy.client.core.model.bakery; + + +import me.cortex.voxy.common.util.MemoryBuffer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.model.BakedQuad; +import org.lwjgl.system.MemoryUtil; + +import static me.cortex.voxy.client.core.model.bakery.BudgetBufferRenderer.VERTEX_FORMAT_SIZE; + +public 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 += VERTEX_FORMAT_SIZE; 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) * VERTEX_FORMAT_SIZE < 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)+VERTEX_FORMAT_SIZE-1)/VERTEX_FORMAT_SIZE)*VERTEX_FORMAT_SIZE); + 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 - VERTEX_FORMAT_SIZE;//the thing is first time this gets incremented by FORMAT_STRIDE + return this; + } + + public void free() { + this.buffer.free(); + this.buffer = null; + } + + public boolean isEmpty() { + return this.count == 0; + } + + public int quadCount() { + if (this.count%4 != 0) throw new IllegalStateException(); + return this.count/4; + } + + public long getAddress() { + return this.buffer.address; + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java index 1c65629f..151ff72e 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java @@ -1627,83 +1627,4 @@ public class RenderDataFactory45 { public void free() { this.quadBuffer.free(); } - - - - - //Returns true if a face was placed - private boolean putFluidFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfClientModelId, int selfBlockId, long facingState, long facingMetadata, int facingClientModelId, int a, int b) { - int selfFluidClientId = this.modelMan.getFluidClientStateId(selfClientModelId); - long selfFluidMetadata = this.modelMan.getModelMetadataFromClientId(selfFluidClientId); - - int facingFluidClientId = -1; - if (ModelQueries.containsFluid(facingMetadata)) { - facingFluidClientId = this.modelMan.getFluidClientStateId(facingClientModelId); - } - - //If both of the states are the same, then dont render the fluid face - if (selfFluidClientId == facingFluidClientId) { - return false; - } - - if (facingFluidClientId != -1) { - //TODO: OPTIMIZE - if (this.world.getMapper().getBlockStateFromBlockId(selfBlockId).getBlock() == this.world.getMapper().getBlockStateFromBlockId(Mapper.getBlockId(facingState)).getBlock()) { - return false; - } - } - - - if (ModelQueries.faceOccludes(facingMetadata, opposingFace)) { - return false; - } - - //if the model has a fluid state but is not a liquid need to see if the solid state had a face rendered and that face is occluding, if so, dont render the fluid state face - if ((!ModelQueries.isFluid(metadata)) && ModelQueries.faceOccludes(metadata, face)) { - return false; - } - - - - //TODO:FIXME SOMEHOW THIS IS CRITICAL!!!!!!!!!!!!!!!!!! - // so there is one more issue need to be fixed, if water is layered ontop of eachother, the side faces depend on the water state ontop - // this has been hackfixed in the model texture bakery but a proper solution that doesnt explode the sides of the water textures needs to be done - // the issue is that the fluid rendering depends on the up state aswell not just the face state which is really really painful to account for - // e.g the sides of a full water is 8 high or something, not the full block height, this results in a gap between water layers - - - - long otherFlags = 0; - otherFlags |= ModelQueries.isTranslucent(selfFluidMetadata)?1L<<33:0; - otherFlags |= ModelQueries.isDoubleSided(selfFluidMetadata)?1L<<34:0; - mesher.put(a, b, ((long)selfFluidClientId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(selfFluidMetadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(selfFluidMetadata)?1:0)) | otherFlags); - return true; - } - - //Returns true if a face was placed - private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long metadata, int clientModelId, int selfBiome, long facingModelId, long facingMetadata, int selfLight, int facingLight, int a, int b) { - if (ModelQueries.cullsSame(metadata) && clientModelId == facingModelId) { - //If we are facing a block, and we are both the same state, dont render that face - return false; - } - - //If face can be occluded and is occluded from the facing block, then dont render the face - if (ModelQueries.faceCanBeOccluded(metadata, face) && ModelQueries.faceOccludes(facingMetadata, opposingFace)) { - return false; - } - - long otherFlags = 0; - otherFlags |= ModelQueries.isTranslucent(metadata)?1L<<33:0; - otherFlags |= ModelQueries.isDoubleSided(metadata)?1L<<34:0; - mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(metadata, face)?selfLight:facingLight))<<16) | (ModelQueries.isBiomeColoured(metadata)?(((long) Mapper.getBiomeId(selfBiome))<<24):0) | otherFlags); - return true; - } - - private static int getMeshletHoldingCount(int quads, int quadsPerMeshlet, int meshletSize) { - return ((quads+(quadsPerMeshlet-1))/quadsPerMeshlet)*meshletSize; - } - - public static int alignUp(int n, int alignment) { - return (n + alignment - 1) & -alignment; - } } \ No newline at end of file