Skip to content

Commit

Permalink
Improved rendering (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
maximumpower55 authored Aug 24, 2023
1 parent 4ef5be7 commit 5116eef
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 109 deletions.
6 changes: 3 additions & 3 deletions src/main/java/dev/hephaestus/glowcase/GlowcaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import dev.hephaestus.glowcase.block.entity.MailboxBlockEntity;
import dev.hephaestus.glowcase.client.GlowcaseClientProxy;
import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer.BakedBlockEntityRendererManager;
import dev.hephaestus.glowcase.client.render.block.entity.HyperlinkBlockEntityRenderer;
import dev.hephaestus.glowcase.client.render.block.entity.ItemDisplayBlockEntityRenderer;
import dev.hephaestus.glowcase.client.render.block.entity.TextBlockEntityRenderer;
import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
Expand Down Expand Up @@ -33,8 +33,8 @@ public void onInitializeClient() {
BlockEntityRendererFactories.register(Glowcase.HYPERLINK_BLOCK_ENTITY, HyperlinkBlockEntityRenderer::new);
BlockEntityRendererFactories.register(Glowcase.ITEM_DISPLAY_BLOCK_ENTITY, ItemDisplayBlockEntityRenderer::new);

WorldRenderEvents.AFTER_TRANSLUCENT.register(BakedBlockEntityRendererManager::render);
InvalidateRenderStateCallback.EVENT.register(BakedBlockEntityRendererManager::reset);
WorldRenderEvents.AFTER_TRANSLUCENT.register(BakedBlockEntityRenderer.Manager::render);
InvalidateRenderStateCallback.EVENT.register(BakedBlockEntityRenderer.Manager::reset);

HudRenderCallback.EVENT.register((context, tickDelta) -> {
MinecraftClient client = MinecraftClient.getInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.util.List;

import dev.hephaestus.glowcase.Glowcase;
import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer.BakedBlockEntityRendererManager;
import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer;

import eu.pb4.placeholders.api.ParserContext;
import eu.pb4.placeholders.api.parsers.NodeParser;
Expand All @@ -13,7 +13,6 @@
import org.jetbrains.annotations.Nullable;

import dev.hephaestus.glowcase.Glowcase;
import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer.BakedBlockEntityRendererManager;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.NbtCompound;
Expand Down Expand Up @@ -135,7 +134,7 @@ public enum ShadowType {
@Override
public void markRemoved() {
if (world != null && world.isClient) {
BakedBlockEntityRendererManager.markForRebuild(getPos());
BakedBlockEntityRenderer.Manager.markForRebuild(getPos());
}
super.markRemoved();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.logging.LogUtils;

import dev.hephaestus.glowcase.mixin.client.render.BufferBuilderAccessor;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
Expand All @@ -19,9 +21,7 @@
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.slf4j.Logger;

Expand All @@ -43,7 +43,7 @@ protected BakedBlockEntityRenderer(BlockEntityRendererFactory.Context context) {
@Override
public final void render(T entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
renderUnbaked(entity, tickDelta, matrices, vertexConsumers, light, overlay);
BakedBlockEntityRendererManager.activateRegion(entity.getPos());
Manager.activateRegion(entity.getPos());
}

/**
Expand All @@ -67,11 +67,11 @@ public final void render(T entity, float tickDelta, MatrixStack matrices, Vertex

private static record RenderRegionPos(int x, int z, @Nullable BlockPos origin) {
public RenderRegionPos(int x, int z) {
this(x, z, new BlockPos(x << BakedBlockEntityRendererManager.REGION_SHIFT, 0, z << BakedBlockEntityRendererManager.REGION_SHIFT));
this(x, z, new BlockPos(x << Manager.REGION_SHIFT, 0, z << Manager.REGION_SHIFT));
}

public RenderRegionPos(BlockPos pos) {
this(pos.getX() >> BakedBlockEntityRendererManager.REGION_SHIFT, pos.getZ() >> BakedBlockEntityRendererManager.REGION_SHIFT);
this(pos.getX() >> Manager.REGION_SHIFT, pos.getZ() >> Manager.REGION_SHIFT);
}

@Override
Expand All @@ -90,78 +90,76 @@ public int hashCode() {
}
}

public static class BakedBlockEntityRendererManager {
public static class Manager {
// 2x2 chunks size for regions
public static final int REGION_FROMCHUNK_SHIFT = 1;
public static final int REGION_SHIFT = 4 + REGION_FROMCHUNK_SHIFT;
public static final int MAX_XZ_IN_REGION = (16 << REGION_FROMCHUNK_SHIFT) - 1;
public static final int VIEW_RADIUS = 3;

private static final Map<RenderRegionPos, RegionBuffer> regions = new Object2ObjectOpenHashMap<>();
private static final ReferenceArrayList<VertexBuffer> USEABLE_VERTEX_BUFFERS = new ReferenceArrayList<>(256);

private static final Object2ReferenceMap<RenderRegionPos, RegionBuffer> regions = new Object2ReferenceOpenHashMap<>();
private static final Set<RenderRegionPos> needsRebuild = Sets.newHashSet();
private static final Map<RenderRegionPos, RegionBufferBuilder> builders = new Object2ObjectOpenHashMap<>();

private static final Matrix3f MATRIX3F_IDENTITY = new Matrix3f().identity();
private static class CachedVertexConsumerProvider implements VertexConsumerProvider {

private final Reference2ReferenceMap<RenderLayer, BufferBuilder> builders = new Reference2ReferenceOpenHashMap<>();
private final Set<RenderLayer> uploadedLayers = new ObjectOpenHashSet<>();

@Override
public VertexConsumer getBuffer(RenderLayer l) {
var builder = builders.computeIfAbsent(l, blah -> new BufferBuilder(l.getExpectedBufferSize()));
if (!builder.isBuilding()) builder.begin(l.getDrawMode(), l.getVertexFormat());
uploadedLayers.add(l);
return builder;
}

}
private static final CachedVertexConsumerProvider vcp = new CachedVertexConsumerProvider();

private static ClientWorld currentWorld = null;

private static final Logger LOGGER = LogUtils.getLogger();

private static VertexBuffer getVertexBuffer() {
if (!USEABLE_VERTEX_BUFFERS.isEmpty()) return USEABLE_VERTEX_BUFFERS.pop();
return new VertexBuffer(VertexBuffer.Usage.STATIC);
}

private static void releaseVertexBuffer(VertexBuffer buf) {
USEABLE_VERTEX_BUFFERS.add(buf);
}

private static class RegionBuffer {
private final Map<RenderLayer, VertexBuffer> layerBuffers = new Object2ObjectArrayMap<>();
private final Map<RenderLayer, VertexBuffer> uploadedLayerBuffers = new Reference2ReferenceArrayMap<>();
private final Map<RenderLayer, VertexBuffer> layerBuffers = new Reference2ReferenceOpenHashMap<>();
private final Set<RenderLayer> uploadedLayers = new ObjectOpenHashSet<>();

public void render(RenderLayer l, MatrixStack matrices, Matrix4f projectionMatrix) {
VertexBuffer buf = uploadedLayerBuffers.get(l);
VertexBuffer buf = layerBuffers.get(l);
buf.bind();
l.startDrawing();
buf.draw(matrices.peek().getPositionMatrix(), projectionMatrix, RenderSystem.getShader());
l.endDrawing();
VertexBuffer.unbind();
}

public void reset() {
uploadedLayerBuffers.clear();
uploadedLayers.clear();
}

public void upload(RenderLayer l, BufferBuilder newBuf) {
VertexBuffer buf = layerBuffers.computeIfAbsent(l, renderLayer -> new VertexBuffer(VertexBuffer.Usage.STATIC));

VertexBuffer buf = layerBuffers.computeIfAbsent(l, renderLayer -> getVertexBuffer());
buf.bind();
buf.upload(newBuf.end());
VertexBuffer.unbind();

uploadedLayerBuffers.put(l, buf);
uploadedLayers.add(l);
}

public void deallocate() {
layerBuffers.values().forEach(VertexBuffer::close);
uploadedLayerBuffers.clear();
}

public Set<RenderLayer> getAllUploadedLayers() {
return uploadedLayerBuffers.keySet();
}
}

private static class RegionBufferBuilder implements VertexConsumerProvider, Iterable<Map.Entry<RenderLayer, BufferBuilder>> {
private final Map<RenderLayer, BufferBuilder> layerBuffers = new Object2ObjectArrayMap<>();
private final Map<RenderLayer, BufferBuilder> usedLayerBuffers = new Reference2ReferenceArrayMap<>();

public void reset() {
layerBuffers.values().forEach(buf -> ((BufferBuilderAccessor) buf).invokeResetBuilding());
usedLayerBuffers.clear();
}

@Override
public VertexConsumer getBuffer(RenderLayer layer) {
return layerBuffers.compute(layer, (l, buf) -> {
if (buf == null) buf = new BufferBuilder(l.getExpectedBufferSize());
if (!buf.isBuilding()) buf.begin(l.getDrawMode(), l.getVertexFormat());
usedLayerBuffers.put(layer, buf);
return buf;
});
}

public @NotNull Iterator<Map.Entry<RenderLayer, BufferBuilder>> iterator() {
return usedLayerBuffers.entrySet().iterator();
public void release() {
layerBuffers.values().forEach(Manager::releaseVertexBuffer);
uploadedLayers.clear();
}
}

Expand Down Expand Up @@ -190,58 +188,49 @@ public static void render(WorldRenderContext wrc) {

if (!needsRebuild.isEmpty()) {
wrc.profiler().push("rebuild");
// Make builders for regions that are marked for rebuild, render and upload to RegionBuffers
Set<RenderRegionPos> rebuilt = Sets.newLinkedHashSet();
Set<RenderRegionPos> removing = Sets.newLinkedHashSet();

// Make builders for regions that are marked for rebuild, render and upload to RegionBuffers
Set<RenderRegionPos> rebuilt = Sets.newHashSet();
Set<RenderRegionPos> removing = Sets.newHashSet();
List<BlockEntity> blockEntities = new ArrayList<>();
MatrixStack bakeMatrices = new MatrixStack();
for (RenderRegionPos rrp : needsRebuild) {
rebuilt.add(rrp);
if (isVisiblePos(rrp, cam)) {
// For the current region, rebuild each render layer using the buffer builders
// Find all block entities in this region
if (currentWorld == null) {
break;
}

RegionBufferBuilder builder = builders.compute(rrp, (k, v) -> {
if (v != null) v.reset();
else v = new RegionBufferBuilder();
return v;
});

for (int chunkX = rrp.x << REGION_FROMCHUNK_SHIFT; chunkX < (rrp.x + 1) << REGION_FROMCHUNK_SHIFT; chunkX++) {
for (int chunkZ = rrp.z << REGION_FROMCHUNK_SHIFT; chunkZ < (rrp.z + 1) << REGION_FROMCHUNK_SHIFT; chunkZ++) {
blockEntities.addAll(currentWorld.getChunk(chunkX, chunkZ).getBlockEntities().values());
}
}

if (!blockEntities.isEmpty()) {
boolean bakedMaybeAnything = false;

boolean bakedAnything = false;
for (BlockEntity be : blockEntities) {
if (mc.getBlockEntityRenderDispatcher().get(be) instanceof BakedBlockEntityRenderer renderer && renderer.shouldBake(be)) {
BlockPos pos = be.getPos();
bakeMatrices.push();
bakeMatrices.translate(pos.getX() & MAX_XZ_IN_REGION, pos.getY(), pos.getZ() & MAX_XZ_IN_REGION);
try {
renderer.renderBaked(be, bakeMatrices, builder, WorldRenderer.getLightmapCoordinates(currentWorld, pos), OverlayTexture.DEFAULT_UV);
renderer.renderBaked(be, bakeMatrices, vcp, WorldRenderer.getLightmapCoordinates(currentWorld, pos), OverlayTexture.DEFAULT_UV);
bakedAnything = true;
} catch (Throwable t) {
LOGGER.error("Block entity renderer threw exception during baking : ");
t.printStackTrace();
} finally {
bakedMaybeAnything = true;
LOGGER.error("Block entity renderer threw exception during baking : ", t);
}
bakeMatrices.pop();
}
}
blockEntities.clear();

if (bakedMaybeAnything) {
if (bakedAnything) {
RegionBuffer buf = regions.computeIfAbsent(rrp, k -> new RegionBuffer());
buf.reset();

builder.forEach(layerBuilder -> buf.upload(layerBuilder.getKey(), layerBuilder.getValue()));
rebuilt.add(rrp);
vcp.uploadedLayers.removeIf(l -> {
buf.upload(l, vcp.builders.get(l));
return true;
});
} else {
removing.add(rrp);
}
Expand All @@ -251,56 +240,54 @@ public static void render(WorldRenderContext wrc) {
}
}
rebuilt.forEach(needsRebuild::remove);
removing.forEach(needsRebuild::remove);
wrc.profiler().pop();
removing.forEach(rrp -> {
RegionBuffer buf = regions.get(rrp);

if (buf != null) {
buf.deallocate();
buf.release();
regions.remove(rrp, buf);
}
});

wrc.profiler().pop();
}

if (!regions.isEmpty()) {
wrc.profiler().swap("render");
wrc.profiler().push("render");

/**
* Set the inverse view rotation matrix to the identity matrix, this fixes the fog color bleeding into the color of the rendered object at close distances,
* this isn't a complete fix since fog still looks a bit funky at far distances
* Set the fog end to a extremely high value, this is a total hack but.
* It's needed to make fog not bleed into text blocks
*/
Matrix3f originalViewRotationMatrix = RenderSystem.getInverseViewRotationMatrix();
RenderSystem.setInverseViewRotationMatrix(MATRIX3F_IDENTITY);
float originalFogEnd = RenderSystem.getShaderFogEnd();
RenderSystem.setShaderFogEnd(Float.MAX_VALUE);
// Iterate over all RegionBuffers, render visible and remove non-visible RegionBuffers
Iterator<Map.Entry<RenderRegionPos, RegionBuffer>> iterBuffers = regions.entrySet().iterator();
MatrixStack matrices = wrc.matrixStack();
matrices.push();
matrices.translate(-cam.x, -cam.y, -cam.z);
while (iterBuffers.hasNext()) {
Map.Entry<RenderRegionPos, RegionBuffer> entry = iterBuffers.next();
for (Map.Entry<RenderRegionPos, RegionBuffer> entry : regions.object2ReferenceEntrySet()) {
RenderRegionPos rrp = entry.getKey();
RegionBuffer regionBuffer = entry.getValue();
if (isVisiblePos(entry.getKey(), cam)) {
// Iterate over used render layers in the region, render them
matrices.push();
matrices.translate(rrp.origin.getX(), rrp.origin.getY(), rrp.origin.getZ());
for (RenderLayer layer : regionBuffer.getAllUploadedLayers()) {
layer.startDrawing();
regionBuffer.render(layer, matrices, wrc.projectionMatrix());
layer.endDrawing();
VertexBuffer.unbind();
}
for (RenderLayer l : regionBuffer.uploadedLayers)
regionBuffer.render(l, matrices, wrc.projectionMatrix());
matrices.pop();
} else {
regionBuffer.deallocate();
iterBuffers.remove();
regionBuffer.release();
regions.remove(rrp);
}
}
RenderSystem.setInverseViewRotationMatrix(originalViewRotationMatrix);
RenderSystem.setShaderFogEnd(originalFogEnd);
matrices.pop();

wrc.profiler().pop();
}
wrc.profiler().pop();

RenderSystem.setShaderColor(1, 1, 1, 1);

wrc.profiler().pop();
}

public static void activateRegion(BlockPos pos) {
Expand All @@ -311,11 +298,9 @@ public static void activateRegion(BlockPos pos) {
}

public static void reset() {
// Reset everything
regions.values().forEach(RegionBuffer::deallocate);
regions.values().forEach(RegionBuffer::release);
regions.clear();
needsRebuild.clear();
builders.clear();
}

public static void setWorld(ClientWorld world) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public boolean shouldBake(TextBlockEntity entity) {
public void renderUnbaked(TextBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
if (entity.renderDirty) {
entity.renderDirty = false;
BakedBlockEntityRendererManager.markForRebuild(entity.getPos());
Manager.markForRebuild(entity.getPos());
}
}

Expand Down
Loading

0 comments on commit 5116eef

Please sign in to comment.