Skip to content

Commit

Permalink
Rewrite facade rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
IMS212 committed Dec 30, 2024
1 parent cb5d016 commit 99716ac
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -1,83 +1,100 @@
package com.enderio.conduits.client;

import com.enderio.conduits.client.model.conduit.facades.FacadeHelper;
import com.enderio.conduits.common.conduit.block.ConduitBundleBlock;
import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.Map;

import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.util.FastColor;
import net.minecraft.core.SectionPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.neoforged.neoforge.client.event.AddSectionGeometryEvent;
import net.neoforged.neoforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.pipeline.VertexConsumerWrapper;

@EventBusSubscriber(bus = EventBusSubscriber.Bus.GAME, value = Dist.CLIENT)
public class ConduitFacadeRendering {

private static final ThreadLocal<RandomSource> RANDOM = ThreadLocal.withInitial(() -> new SingleThreadedRandomSource(42L));

@SubscribeEvent
static void renderFacade(RenderLevelStageEvent event) {
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRIPWIRE_BLOCKS || FacadeHelper.areFacadesVisible()) {
return;
}
static void renderFacade(AddSectionGeometryEvent event) {
Map<BlockPos, BlockState> facades = new Object2ObjectOpenHashMap<>();

for (Map.Entry<BlockPos, BlockState> entry : ConduitBundleBlockEntity.FACADES.entrySet()) {
ClientLevel level = Minecraft.getInstance().level;
if (!level.isLoaded(entry.getKey())) {
return;
if (SectionPos.of(entry.getKey()).equals(SectionPos.of(event.getSectionOrigin()))) {
facades.put(entry.getKey(), entry.getValue());
}
if (level.getBlockState(entry.getKey()).getBlock() instanceof ConduitBundleBlock) {
if (entry.getValue() == null) {
continue;
}
}

if (facades.isEmpty()) return;

event.addRenderer(new FacadeRenderer(facades, FacadeHelper.areFacadesVisible()));
}

private static class FacadeRenderer implements AddSectionGeometryEvent.AdditionalSectionRenderer {
private final Map<BlockPos, BlockState> facades;
private final boolean opaque;

public FacadeRenderer(Map<BlockPos, BlockState> facades, boolean opaque) {
this.facades = facades;
this.opaque = opaque;
}

var baseConsumer = Minecraft.getInstance()
.renderBuffers()
.bufferSource()
.getBuffer(Sheets.translucentCullBlockSheet());
var wrappedConsumer = new VertexConsumerWrapper(baseConsumer) {
@Override
public VertexConsumer setColor(int r, int g, int b, int a) {
super.setColor(r, g, b, 85);
return this;
}
};

var cameraPos = event.getCamera().getPosition();
event.getPoseStack().pushPose();
event.getPoseStack()
.translate(entry.getKey().getX() - cameraPos.x, entry.getKey().getY() - cameraPos.y,
entry.getKey().getZ() - cameraPos.z);
@Override
public void render(AddSectionGeometryEvent.SectionRenderingContext context) {
VertexConsumerWrapper wrapper = opaque ? null : new AlphaWrapper(context);

RandomSource random = RANDOM.get();

for (Map.Entry<BlockPos, BlockState> entry : facades.entrySet()) {
context.getPoseStack().pushPose();
context.getPoseStack()
.translate(entry.getKey().getX() & 15,
entry.getKey().getY() & 15,
entry.getKey().getZ() & 15);

var state = entry.getValue();
var pos = entry.getKey();

random.setSeed(42L);

var model = Minecraft.getInstance()
.getModelManager()
.getBlockModelShaper()
.getBlockModel(entry.getValue());
int color = Minecraft.getInstance().getBlockColors().getColor(entry.getValue(), level, entry.getKey());
for (var renderType : model.getRenderTypes(entry.getValue(), RandomSource.create(), ModelData.EMPTY)) {
Minecraft.getInstance()
.getBlockRenderer()
.getModelRenderer()
.renderModel(event.getPoseStack().last(), wrappedConsumer, entry.getValue(), model,
FastColor.ARGB32.red(color) / 255.0F, FastColor.ARGB32.green(color) / 255.0F,
FastColor.ARGB32.blue(color) / 255.0F,
LightTexture.pack(level.getBrightness(LightLayer.BLOCK, entry.getKey()),
level.getBrightness(LightLayer.SKY, entry.getKey())),
OverlayTexture.NO_OVERLAY,
model.getModelData(level, entry.getKey(), entry.getValue(), ModelData.EMPTY),
renderType);
.getModelManager()
.getBlockModelShaper()
.getBlockModel(entry.getValue());

for (var renderType : model.getRenderTypes(entry.getValue(), random, ModelData.EMPTY)) {
VertexConsumer consumer = wrapper == null ? context.getOrCreateChunkBuffer(renderType) : wrapper;
Minecraft.getInstance().getBlockRenderer().getModelRenderer().tesselateBlock(context.getRegion(),
model, state, pos, context.getPoseStack(),
consumer, true, random,
42L, OverlayTexture.NO_OVERLAY,
ModelData.EMPTY, renderType);
}
Minecraft.getInstance().renderBuffers().bufferSource().endBatch(Sheets.translucentCullBlockSheet());
event.getPoseStack().popPose();

context.getPoseStack().popPose();
}
}

private static class AlphaWrapper extends VertexConsumerWrapper {
public AlphaWrapper(AddSectionGeometryEvent.SectionRenderingContext context) {
super(context.getOrCreateChunkBuffer(RenderType.translucent()));
}

@Override
public VertexConsumer setColor(int r, int g, int b, int a) {
super.setColor(r, g, b, 85);
return this;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,28 +71,14 @@ public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction

List<BakedQuad> quads = new ArrayList<>();
ConduitBundle conduitBundle = extraData.get(ConduitBundleBlockEntity.BUNDLE_MODEL_PROPERTY);
ModelData data = extraData.get(ConduitBundleBlockEntity.FACADE_MODEL_DATA);

if (conduitBundle != null) {
if (FacadeHelper.areFacadesVisible()) {
IQuadTransformer transformer = quad -> quad.tintIndex = ConduitFacadeColor
.moveTintIndex(quad.getTintIndex());
Optional<Block> facadeOpt = conduitBundle.facade();
if (facadeOpt.isPresent()) {
BlockState facade = facadeOpt.get().defaultBlockState();
var model = Minecraft.getInstance().getBlockRenderer().getBlockModel(facade);
var facadeQuads = model.getQuads(facade, side, rand, data, renderType);

if (renderType != null && model.getRenderTypes(facade, rand, data).contains(renderType)) {
quads.addAll(transformer.process(facadeQuads));
}
}

// If the facade should hide the conduits, escape early.
if (conduitBundle.hasFacade()) {
boolean areConduitsHidden = conduitBundle.facadeType()
.map(FacadeType::doesHideConduits)
.orElse(false);
.map(FacadeType::doesHideConduits)
.orElse(false);

if (areConduitsHidden) {
return quads;
Expand Down Expand Up @@ -340,12 +326,7 @@ public ItemOverrides getOverrides() {
@Override
public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand,
@NotNull ModelData data) {
ChunkRenderTypeSet facadeRenderTypes = data.get(ConduitBundleBlockEntity.FACADE_RENDERTYPE);
ChunkRenderTypeSet renderTypes = ChunkRenderTypeSet.of(RenderType.cutout());
if (facadeRenderTypes != null) {
renderTypes = ChunkRenderTypeSet.union(renderTypes, facadeRenderTypes);
}
return renderTypes;
return ChunkRenderTypeSet.of(RenderType.cutout());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
package com.enderio.conduits.client.model.conduit.facades;

import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.core.SectionPos;

import java.util.HashSet;
import java.util.Set;

// TODO: In future, support hiding specific conduit types too.
public class FacadeHelper {

private static boolean FACADES_VISIBLE = true;

public static void setFacadesVisible(boolean visible) {
if (visible != FACADES_VISIBLE) {
Set<SectionPos> facadeSections = new HashSet<>();

ConduitBundleBlockEntity.FACADES.keySet().forEach((pos) -> facadeSections.add(SectionPos.of(pos)));

RenderSystem.recordRenderCall(() -> {
facadeSections.forEach((section) -> {
Minecraft.getInstance().levelRenderer.setSectionDirty(section.x(), section.y(), section.z());
});
});
}

FACADES_VISIBLE = visible;
}

public static boolean areFacadesVisible() {
return FACADES_VISIBLE;
}

public static void rebuildChunkMeshes() {
var minecraft = Minecraft.getInstance();

if (minecraft.levelRenderer.viewArea == null) {
return;
}

for (var section : minecraft.levelRenderer.viewArea.sections) {
section.setDirty(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public static void onEquipmentChanged(LivingEquipmentChangeEvent event) {
ItemStack offHand = event.getEntity().getItemBySlot(EquipmentSlot.OFFHAND);
FacadeHelper.setFacadesVisible(
!mainHand.is(EIOTags.Items.HIDE_FACADES) && !offHand.is(EIOTags.Items.HIDE_FACADES));

FacadeHelper.rebuildChunkMeshes();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.enderio.conduits.mixin;

import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(BlockRenderDispatcher.class)
public class BlockRenderDispatcherMixin {
@WrapOperation(method = "renderBreakingTexture(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/BlockAndTintGetter;Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Lnet/neoforged/neoforge/client/model/data/ModelData;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/BlockModelShaper;getBlockModel(Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/client/resources/model/BakedModel;"))
public BakedModel enderio$checkFacades(BlockModelShaper instance, BlockState state, Operation<BakedModel> original, BlockState localState, BlockPos pos, BlockAndTintGetter level) {
BlockState facadeState = ConduitBundleBlockEntity.FACADES.getOrDefault(pos, null);

return original.call(instance, facadeState == null ? state : facadeState);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ description="The conduits module for Ender IO"
displayURL="https://enderio.com/"
logoFile="logo.png"

[[mixins]]
config="enderioconduits.mixins.json"

[[dependencies.enderio_conduits]]
modId="minecraft"
type="required"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"required" : true,
"package" : "com.enderio.conduits.mixin",
"compatibilityLevel" : "JAVA_17",
"client" : [
"BlockRenderDispatcherMixin"
],
"minVersion" : "0.8"
}

0 comments on commit 99716ac

Please sign in to comment.