Heavily optimize infested leaves rendering
This commit is contained in:
parent
9b8363e9e4
commit
910d0adcd1
|
|
@ -38,12 +38,8 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.fml.loading.FMLEnvironment;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity;
|
||||
import thedarkcolour.exdeorum.client.RenderUtil;
|
||||
import thedarkcolour.exdeorum.config.EConfig;
|
||||
import thedarkcolour.exdeorum.registry.EBlockEntities;
|
||||
import thedarkcolour.exdeorum.registry.EBlocks;
|
||||
|
||||
|
|
@ -115,7 +111,6 @@ public class InfestedLeavesBlock extends LeavesBlock implements EntityBlock {
|
|||
|
||||
@Override
|
||||
public RenderShape getRenderShape(BlockState pState) {
|
||||
if (FMLEnvironment.dist == Dist.DEDICATED_SERVER) return RenderShape.MODEL;
|
||||
return (EConfig.CLIENT_SPEC.isLoaded() && EConfig.CLIENT.useFastInfestedLeaves.get()) || RenderUtil.IRIS_ACCESS.areShadersEnabled() ? RenderShape.MODEL : RenderShape.INVISIBLE;
|
||||
return pState.getValue(FULLY_INFESTED) ? RenderShape.MODEL : RenderShape.INVISIBLE;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,17 +19,19 @@
|
|||
package thedarkcolour.exdeorum.client;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
|
||||
import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState;
|
||||
import net.minecraft.client.renderer.BiomeColors;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.util.FastColor;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.level.FoliageColor;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.fml.ModList;
|
||||
|
|
@ -39,6 +41,8 @@ import net.neoforged.neoforge.client.event.*;
|
|||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import thedarkcolour.exdeorum.ExDeorum;
|
||||
import thedarkcolour.exdeorum.asm.ASMHooks;
|
||||
import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity;
|
||||
import thedarkcolour.exdeorum.client.model.InfestedLeavesBakedModel;
|
||||
import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen;
|
||||
import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen;
|
||||
import thedarkcolour.exdeorum.client.ter.*;
|
||||
|
|
@ -46,11 +50,10 @@ import thedarkcolour.exdeorum.compat.ModIds;
|
|||
import thedarkcolour.exdeorum.config.EConfig;
|
||||
import thedarkcolour.exdeorum.recipe.RecipeUtil;
|
||||
import thedarkcolour.exdeorum.registry.EBlockEntities;
|
||||
import thedarkcolour.exdeorum.registry.EBlocks;
|
||||
import thedarkcolour.exdeorum.registry.EFluids;
|
||||
import thedarkcolour.exdeorum.registry.EMenus;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ClientHandler {
|
||||
// Used for the composting recipe category in JEI
|
||||
public static final ModelResourceLocation OAK_BARREL_COMPOSTING = new ModelResourceLocation(ExDeorum.loc("item/oak_barrel_composting"), ModelResourceLocation.STANDALONE_VARIANT);
|
||||
|
|
@ -64,9 +67,10 @@ public class ClientHandler {
|
|||
modBus.addListener(ClientHandler::clientSetup);
|
||||
modBus.addListener(ClientHandler::registerMenuScreens);
|
||||
modBus.addListener(ClientHandler::registerRenderers);
|
||||
modBus.addListener(ClientHandler::registerShaders);
|
||||
modBus.addListener(ClientHandler::addClientReloadListeners);
|
||||
modBus.addListener(ClientHandler::onConfigChanged);
|
||||
modBus.addListener(ClientHandler::onModelBake);
|
||||
modBus.addListener(ClientHandler::addBlockColorHandler);
|
||||
fmlBus.addListener(ClientHandler::onPlayerRespawn);
|
||||
fmlBus.addListener(ClientHandler::onPlayerLogout);
|
||||
fmlBus.addListener(ClientHandler::onScreenOpen);
|
||||
|
|
@ -124,17 +128,6 @@ public class ClientHandler {
|
|||
event.registerBlockEntityRenderer(EBlockEntities.COMPRESSED_SIEVE.get(), ctx -> new CompressedSieveRenderer<>(0.5625f, 16f));
|
||||
}
|
||||
|
||||
private static void registerShaders(RegisterShadersEvent event) {
|
||||
try {
|
||||
// NEW_ENTITY is BLOCK except it also uses UV1 (overlay coordinates)
|
||||
event.registerShader(new ShaderInstance(event.getResourceProvider(), ExDeorum.loc("rendertype_tinted_cutout_mipped"), DefaultVertexFormat.NEW_ENTITY), instance -> {
|
||||
RenderUtil.renderTypeTintedCutoutMippedShader = instance;
|
||||
});
|
||||
} catch (IOException e) {
|
||||
ExDeorum.LOGGER.error("Unable to load tinted shader", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets Ex Deorum world type as default
|
||||
private static void onScreenOpen(ScreenEvent.Opening event) {
|
||||
if (event.getNewScreen() instanceof CreateWorldScreen screen && EConfig.COMMON.setVoidWorldAsDefault.get()) {
|
||||
|
|
@ -159,6 +152,36 @@ public class ClientHandler {
|
|||
event.register(OAK_BARREL_COMPOSTING);
|
||||
}
|
||||
|
||||
private static void onModelBake(ModelEvent.ModifyBakingResult event) {
|
||||
var model = new InfestedLeavesBakedModel();
|
||||
for (var state : EBlocks.INFESTED_LEAVES.get().getStateDefinition().getPossibleStates()) {
|
||||
var location = BlockModelShaper.stateToModelLocation(state);
|
||||
event.getModels().put(location, model);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addBlockColorHandler(RegisterColorHandlersEvent.Block event) {
|
||||
var blockColors = event.getBlockColors();
|
||||
event.register((state, level, pos, tintIndex) -> {
|
||||
int innerColor;
|
||||
if (level != null && pos != null && level.getBlockEntity(pos) instanceof InfestedLeavesBlockEntity infestedLeavesBlockEntity) {
|
||||
var mimicState = infestedLeavesBlockEntity.getMimic();
|
||||
try {
|
||||
innerColor = blockColors.getColor(mimicState, level, pos, tintIndex);
|
||||
} catch (Exception e) {
|
||||
// The block may be unhappy that the BlockState in the world (infested leaves) does not match
|
||||
// the mimic state that was provided in the argument. In such cases, use the default foliage
|
||||
// color resolver.
|
||||
innerColor = level.getBlockTint(pos, BiomeColors.FOLIAGE_COLOR_RESOLVER);
|
||||
}
|
||||
} else {
|
||||
innerColor = FoliageColor.getDefaultColor();
|
||||
}
|
||||
int gray = (30 * FastColor.ARGB32.red(innerColor) + 59 * FastColor.ARGB32.green(innerColor) + 11 * FastColor.ARGB32.blue(innerColor)) / 100;
|
||||
return FastColor.ARGB32.color(255, gray, gray, gray);
|
||||
}, EBlocks.INFESTED_LEAVES.get());
|
||||
}
|
||||
|
||||
private static void onRecipesUpdated(RecipesUpdatedEvent event) {
|
||||
if (!Minecraft.getInstance().isSingleplayer()) {
|
||||
RecipeUtil.reload(event.getRecipeManager());
|
||||
|
|
|
|||
|
|
@ -337,6 +337,10 @@ public class RenderUtil {
|
|||
builder.addVertex(pose, edgeMin, minY, edgeMax).setColor(r, g, b, 255).setUv(uMax, vMax).setUv1(0, 10).setUv2(lightU, lightV).setNormal(normal.x, normal.y, normal.z);
|
||||
}
|
||||
|
||||
public static float mix(float a, float b, float progress) {
|
||||
return Math.fma(b - a, progress, a);
|
||||
}
|
||||
|
||||
public interface IrisAccess {
|
||||
boolean areShadersEnabled();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package thedarkcolour.exdeorum.client.model;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.neoforged.neoforge.client.ChunkRenderTypeSet;
|
||||
import net.neoforged.neoforge.client.model.IDynamicBakedModel;
|
||||
import net.neoforged.neoforge.client.model.data.ModelData;
|
||||
import net.neoforged.neoforge.common.util.TriState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InfestedLeavesBakedModel implements IDynamicBakedModel {
|
||||
private final BlockModelShaper modelShaper;
|
||||
private final BlockState fallbackState;
|
||||
|
||||
public InfestedLeavesBakedModel() {
|
||||
this.modelShaper = Minecraft.getInstance().getModelManager().getBlockModelShaper();
|
||||
this.fallbackState = Blocks.OAK_LEAVES.defaultBlockState();
|
||||
}
|
||||
|
||||
private BlockState getMimicState(ModelData modelData) {
|
||||
var mimicState = modelData.get(InfestedLeavesBlockEntity.MIMIC_PROPERTY);
|
||||
if (mimicState == null) {
|
||||
return this.fallbackState;
|
||||
}
|
||||
return mimicState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState blockState, @Nullable Direction direction, RandomSource randomSource, ModelData modelData, @Nullable RenderType renderType) {
|
||||
var mimicState = getMimicState(modelData);
|
||||
var model = modelShaper.getBlockModel(mimicState);
|
||||
|
||||
return model.getQuads(mimicState, direction, randomSource, modelData, renderType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, ModelData data) {
|
||||
var mimicState = getMimicState(data);
|
||||
var model = modelShaper.getBlockModel(mimicState);
|
||||
|
||||
return model.getRenderTypes(mimicState, rand, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TriState useAmbientOcclusion(BlockState state, ModelData data, RenderType renderType) {
|
||||
var mimicState = getMimicState(data);
|
||||
var model = modelShaper.getBlockModel(mimicState);
|
||||
return model.useAmbientOcclusion(mimicState, data, renderType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBlockLight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomRenderer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon(ModelData data) {
|
||||
return modelShaper.getBlockModel(getMimicState(data)).getParticleIcon(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon() {
|
||||
return modelShaper.getParticleIcon(this.fallbackState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrides getOverrides() {
|
||||
return ItemOverrides.EMPTY;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,19 +19,27 @@
|
|||
package thedarkcolour.exdeorum.client.ter;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.neoforged.neoforge.client.RenderTypeHelper;
|
||||
import net.neoforged.neoforge.client.model.data.ModelData;
|
||||
import net.neoforged.neoforge.client.model.pipeline.VertexConsumerWrapper;
|
||||
import thedarkcolour.exdeorum.block.InfestedLeavesBlock;
|
||||
import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity;
|
||||
import thedarkcolour.exdeorum.client.RenderUtil;
|
||||
import thedarkcolour.exdeorum.config.EConfig;
|
||||
|
||||
public class InfestedLeavesRenderer implements BlockEntityRenderer<InfestedLeavesBlockEntity> {
|
||||
|
||||
@Override
|
||||
public void render(InfestedLeavesBlockEntity te, float partialTicks, PoseStack stack, MultiBufferSource buffer, int light, int unused) {
|
||||
if (EConfig.CLIENT.useFastInfestedLeaves.get() || RenderUtil.IRIS_ACCESS.areShadersEnabled()) return;
|
||||
// We render a static model when the animation is finished
|
||||
if (te.getBlockState().getValue(InfestedLeavesBlock.FULLY_INFESTED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mc = Minecraft.getInstance();
|
||||
var state = te.getMimic();
|
||||
|
|
@ -46,10 +54,32 @@ public class InfestedLeavesRenderer implements BlockEntityRenderer<InfestedLeave
|
|||
}
|
||||
|
||||
// Get infested percentage
|
||||
int progress = Math.min(te.getProgress(), 16000);
|
||||
float progress = Math.min(te.getProgress(), 16000) / 16000f;
|
||||
// Render
|
||||
var model = mc.getBlockRenderer().getBlockModel(state);
|
||||
var pos = te.getBlockPos();
|
||||
mc.getBlockRenderer().getModelRenderer().tesselateBlock(level, model, state, pos, stack, buffer.getBuffer(RenderUtil.TINTED_CUTOUT_MIPPED), false, level.random, state.getSeed(pos), progress, ModelData.EMPTY, null);
|
||||
|
||||
for (var renderType : model.getRenderTypes(state, level.random, ModelData.EMPTY)) {
|
||||
// Dynamically blend the provided vertex colors towards grayscale
|
||||
var vertexConsumer = new VertexConsumerWrapper(buffer.getBuffer(RenderTypeHelper.getMovingBlockRenderType(renderType))) {
|
||||
@Override
|
||||
public VertexConsumer setColor(int r, int g, int b, int a) {
|
||||
float rF = (r / 255f), gF = (g / 255f), bF = (b / 255f);
|
||||
|
||||
float avg = rF * 0.3f + gF * 0.59f + bF * 0.11f;
|
||||
|
||||
return super.setColor(
|
||||
Math.round(RenderUtil.mix(rF, avg, progress) * 255),
|
||||
Math.round(RenderUtil.mix(gF, avg, progress) * 255),
|
||||
Math.round(RenderUtil.mix(bF, avg, progress) * 255),
|
||||
a
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
mc.getBlockRenderer().getModelRenderer().tesselateBlock(level, model, state, pos, stack, vertexConsumer,
|
||||
true, level.random, state.getSeed(pos), OverlayTexture.NO_OVERLAY, ModelData.EMPTY,
|
||||
renderType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,15 +40,11 @@ public class EConfig {
|
|||
public static final Server SERVER;
|
||||
|
||||
public static class Client {
|
||||
public final BooleanValue useFastInfestedLeaves;
|
||||
public final BooleanValue rainbowCompostDuringJune;
|
||||
|
||||
public Client(ModConfigSpec.Builder builder) {
|
||||
builder.comment("Client configuration for Ex Deorum").push("client");
|
||||
|
||||
this.useFastInfestedLeaves = builder
|
||||
.comment("Whether to use a simplified renderer for infested leaves (reduces FPS lag with lots of infested trees)")
|
||||
.define("use_fast_infested_leaves", false);
|
||||
this.rainbowCompostDuringJune = builder
|
||||
.comment("Whether compost in barrels appears as rainbow colored during the month of June")
|
||||
.define("rainbow_compost_during_june", true);
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
#version 150
|
||||
|
||||
#moj_import <fog.glsl>
|
||||
|
||||
uniform sampler2D Sampler0;
|
||||
|
||||
uniform vec4 ColorModulator;
|
||||
uniform float FogStart;
|
||||
uniform float FogEnd;
|
||||
uniform vec4 FogColor;
|
||||
|
||||
in float vertexDistance;
|
||||
in vec4 vertexColor;
|
||||
in vec2 texCoord0;
|
||||
in float progress;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 oldColor = texture(Sampler0, texCoord0) * vertexColor * ColorModulator;
|
||||
if (oldColor.a < 0.5) {
|
||||
discard;
|
||||
}
|
||||
float avg = oldColor.r * 0.3 + oldColor.g * 0.59 + oldColor.b * 0.11;
|
||||
|
||||
vec4 color;
|
||||
if (progress == 1.0f) {
|
||||
color = vec4(avg, avg, avg, progress);
|
||||
} else {
|
||||
color = vec4(
|
||||
mix(oldColor.r, avg, progress),
|
||||
mix(oldColor.g, avg, progress),
|
||||
mix(oldColor.b, avg, progress),
|
||||
oldColor.a
|
||||
);
|
||||
}
|
||||
fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"blend": {
|
||||
"func": "add",
|
||||
"srcrgb": "srcalpha",
|
||||
"dstrgb": "1-srcalpha"
|
||||
},
|
||||
"vertex": "exdeorum:rendertype_tinted_cutout_mipped",
|
||||
"fragment": "exdeorum:rendertype_tinted_cutout_mipped",
|
||||
"attributes": [
|
||||
"Position",
|
||||
"Color",
|
||||
"UV0",
|
||||
"UV1",
|
||||
"UV2",
|
||||
"Normal"
|
||||
],
|
||||
"samplers": [
|
||||
{ "name": "Sampler0" },
|
||||
{ "name": "Sampler2" }
|
||||
],
|
||||
"uniforms": [
|
||||
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ChunkOffset", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] },
|
||||
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
|
||||
{ "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
|
||||
{ "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },
|
||||
{ "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] },
|
||||
{ "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] }
|
||||
]
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#version 150
|
||||
|
||||
#moj_import <light.glsl>
|
||||
#moj_import <fog.glsl>
|
||||
|
||||
in vec3 Position;
|
||||
in vec4 Color;
|
||||
in vec2 UV0;
|
||||
in ivec2 UV1;
|
||||
in ivec2 UV2;
|
||||
in vec3 Normal;
|
||||
|
||||
uniform sampler2D Sampler2;
|
||||
|
||||
uniform mat4 ModelViewMat;
|
||||
uniform mat4 ProjMat;
|
||||
uniform vec3 ChunkOffset;
|
||||
uniform int FogShape;
|
||||
|
||||
out float vertexDistance;
|
||||
out vec4 vertexColor;
|
||||
out vec2 texCoord0;
|
||||
out float progress;
|
||||
|
||||
void main() {
|
||||
vec3 pos = Position + ChunkOffset;
|
||||
gl_Position = ProjMat * ModelViewMat * vec4(pos, 1.0);
|
||||
|
||||
vertexDistance = fog_distance(pos, FogShape);
|
||||
vertexColor = Color * minecraft_sample_lightmap(Sampler2, UV2);
|
||||
texCoord0 = UV0;
|
||||
|
||||
progress = UV1.x / 16000.0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user