Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite facade rendering #945

Open
wants to merge 5 commits into
base: dev/1.21.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,83 +1,110 @@
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 it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.Map;
import java.util.Set;
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()) {
static void renderFacade(AddSectionGeometryEvent event) {
LongSet blockList = ConduitBundleBlockEntity.CHUNK_FACADES
.getOrDefault(SectionPos.asLong(event.getSectionOrigin()), null);

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

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);
Map<BlockPos, BlockState> facades = new Object2ObjectOpenHashMap<>();

for (long entry : blockList) {
facades.put(BlockPos.of(entry), ConduitBundleBlockEntity.FACADES.get(entry));
}

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;
}

@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)) {

var modelData = context.getRegion().getModelData(pos);

modelData = model.getModelData(context.getRegion(), pos, state, modelData);

for (var renderType : model.getRenderTypes(entry.getValue(), random, modelData)) {
VertexConsumer consumer = wrapper == null ? context.getOrCreateChunkBuffer(renderType) : wrapper;
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);
.tesselateBlock(context.getRegion(), model, state, pos, context.getPoseStack(), consumer,
true, random, 42L, OverlayTexture.NO_OVERLAY, modelData, 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 @@ -16,7 +16,6 @@
import com.enderio.conduits.api.ConduitNode;
import com.enderio.conduits.api.facade.FacadeType;
import com.enderio.conduits.api.model.ConduitCoreModelModifier;
import com.enderio.conduits.client.ConduitFacadeColor;
import com.enderio.conduits.client.model.conduit.facades.FacadeHelper;
import com.enderio.conduits.client.model.conduit.modifier.ConduitCoreModelModifiers;
import com.enderio.conduits.common.Area;
Expand All @@ -34,7 +33,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
Expand All @@ -50,7 +48,6 @@
import net.minecraft.util.RandomSource;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource;
import net.neoforged.neoforge.client.ChunkRenderTypeSet;
Expand All @@ -65,29 +62,17 @@

public class ConduitBlockModel implements IDynamicBakedModel {

private static final ChunkRenderTypeSet CUTOUT_SET = ChunkRenderTypeSet.of(RenderType.cutout());

@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand,
ModelData extraData, @Nullable RenderType renderType) {

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()
Expand Down Expand Up @@ -340,12 +325,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 CUTOUT_SET;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
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;

// 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) {
RenderSystem.recordRenderCall(() -> {
ConduitBundleBlockEntity.CHUNK_FACADES.keySet().forEach((section) -> {
Minecraft.getInstance().levelRenderer.setSectionDirty(SectionPos.x(section), SectionPos.y(section),
SectionPos.z(section));
});
});
}

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
Expand Up @@ -29,6 +29,11 @@
import com.enderio.core.common.blockentity.EnderBlockEntity;
import dev.gigaherz.graph3.Graph;
import dev.gigaherz.graph3.GraphObject;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
Expand All @@ -43,6 +48,7 @@
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
Expand Down Expand Up @@ -79,7 +85,8 @@ public class ConduitBundleBlockEntity extends EnderBlockEntity {
public static final String CONDUIT_INV_KEY = "ConduitInv";

@UseOnly(LogicalSide.CLIENT)
public static final Map<BlockPos, BlockState> FACADES = new HashMap<>();
public static final Long2ObjectMap<BlockState> FACADES = new Long2ObjectOpenHashMap<>();
public static final Long2ObjectMap<LongSet> CHUNK_FACADES = new Long2ObjectOpenHashMap<>();

private final ConduitShape shape = new ConduitShape();

Expand Down Expand Up @@ -120,9 +127,15 @@ public void updateClient() {
requestModelDataUpdate();
level.setBlocksDirty(getBlockPos(), Blocks.AIR.defaultBlockState(), getBlockState());
if (bundle.hasFacade()) {
FACADES.put(worldPosition, bundle.facade().get().defaultBlockState());
FACADES.put(worldPosition.asLong(), bundle.facade().get().defaultBlockState());
CHUNK_FACADES.computeIfAbsent(SectionPos.asLong(worldPosition), p -> new LongOpenHashSet())
.add(worldPosition.asLong());
} else {
FACADES.remove(worldPosition);
FACADES.remove(worldPosition.asLong());
LongSet chunkList = CHUNK_FACADES.getOrDefault(SectionPos.asLong(worldPosition), null);
if (chunkList != null) {
chunkList.remove(worldPosition.asLong());
}
}
}
}
Expand Down Expand Up @@ -217,7 +230,8 @@ public void onChunkUnloaded() {
ConduitSavedData savedData = ConduitSavedData.get(serverLevel);
bundle.getConduits().forEach(type -> onChunkUnloaded(savedData, type));
} else {
FACADES.remove(worldPosition);
IMS212 marked this conversation as resolved.
Show resolved Hide resolved
CHUNK_FACADES.remove(SectionPos.asLong(worldPosition));
FACADES.remove(worldPosition.asLong());
}
}

Expand All @@ -231,7 +245,7 @@ private void onChunkUnloaded(ConduitSavedData savedData, Holder<Conduit<?>> cond
public void setRemoved() {
super.setRemoved();
if (level != null && level.isClientSide) {
FACADES.remove(worldPosition);
FACADES.remove(worldPosition.asLong());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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;

/**
* This fixes the block breaking overlay when a facade is attached to use the model of the facade. Without this, it will use the model of the conduit, and be invisible in most cases.
*/
@Mixin(BlockRenderDispatcher.class)
IMS212 marked this conversation as resolved.
Show resolved Hide resolved
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.asLong(), 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"
}
Loading
Loading