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

[1.21.3] RegisterRenderStateModifiersEvent for appending custom data to render state objects #1650

Open
wants to merge 51 commits into
base: 1.21.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
fab1b31
Draft for event
dhyces Oct 31, 2024
3139c92
Example 1
dhyces Nov 1, 2024
3c5e760
Example 2
dhyces Nov 1, 2024
99600ad
Cherry-pick changes from example 2 branch
dhyces Oct 31, 2024
f98e132
Apply Tslat's feedback
dhyces Nov 11, 2024
a8ef1cf
Rename to `RegisterRenderStateModifiersEvent`
dhyces Nov 11, 2024
b41cdf4
Spotless
dhyces Nov 11, 2024
9691504
Use appropriate class from the entry
dhyces Nov 11, 2024
b519839
Reset render data before computing modifiers
dhyces Nov 11, 2024
8643b7d
Update EntityRenderState.java.patch
dhyces Nov 12, 2024
48db639
Remove lazy map init
dhyces Nov 14, 2024
84dacf3
Merge branch '1.21.x' into feat/render-state-event
dhyces Nov 14, 2024
ea86bb1
Merge branch '1.21.x' into feat/render-state-event
dhyces Nov 19, 2024
c79f020
Update with proposed changes and add jdocs
dhyces Nov 19, 2024
d6f6d5e
Spotless
dhyces Nov 19, 2024
162cc71
Remove rather unnecessary interface indirection
dhyces Nov 19, 2024
2ff63e8
Missed patch generation
dhyces Nov 19, 2024
bc9fec4
Fix entity render states
dhyces Nov 19, 2024
432b640
Fail instead of throw
dhyces Nov 19, 2024
d15a61d
Add another fail condition
dhyces Nov 19, 2024
0548610
Add map render state modifiers
dhyces Nov 19, 2024
ec1d9fb
Spotless
dhyces Nov 19, 2024
07abe9f
Inline
dhyces Nov 19, 2024
9a70d17
Move some classes to specific package
dhyces Nov 19, 2024
fb09e53
Split out MapDecorationRenderStateModifier
dhyces Nov 19, 2024
346fc0b
Add override annotations
dhyces Nov 19, 2024
ed942c8
Move update methods into RenderStateExtensions
dhyces Nov 19, 2024
3fbf679
Move event into subpackage and reduce method vis
dhyces Nov 19, 2024
3194e3a
Apply formatting
dhyces Nov 19, 2024
8ad9a50
Generate patches
dhyces Nov 19, 2024
c1c76b8
Merge branch '1.21.x' into feat/render-state-event
dhyces Nov 19, 2024
075db02
Docs changes
dhyces Nov 20, 2024
4c44bf2
Simplify RenderStateExtensions
dhyces Nov 20, 2024
cdf43f7
Fix flipped method call
dhyces Nov 20, 2024
169c0d3
Update docs
dhyces Nov 20, 2024
67d86e6
Add living entity register method
dhyces Nov 21, 2024
1e24034
Some more jdoc changes
dhyces Nov 21, 2024
6e0cca3
Update to using TypeTokens
dhyces Nov 21, 2024
e057759
Apply formatting
dhyces Nov 21, 2024
532ced9
Adjust test scaling
dhyces Nov 21, 2024
1332b90
Remove redundant cast
dhyces Nov 21, 2024
b429629
Work on runtime type safety checks
dhyces Nov 21, 2024
1f34a18
Adjust type safety (big thanks to @lukebemish)
dhyces Nov 22, 2024
dc0bba0
Example usage docs
dhyces Nov 22, 2024
7f2e7fc
Convenience method
dhyces Nov 22, 2024
e00f760
Update test
dhyces Nov 23, 2024
54a5d6a
Merge branch '1.21.x' into feat/render-state-event
dhyces Nov 23, 2024
623bb3f
Apply feedback
dhyces Nov 24, 2024
d400b27
Merge branch 'feat/render-state-event' of https://github.com/dhyces/N…
dhyces Nov 24, 2024
fa02d48
Change which event group is used
dhyces Nov 24, 2024
dce2dc5
Merge branch '1.21.x' into feat/render-state-event
dhyces Nov 24, 2024
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
Expand Up @@ -9,6 +9,14 @@
this.renderNameTag(p_364816_, p_364816_.nameTag, p_114488_, p_114489_, p_114490_);
}
}
@@ -245,6 +_,7 @@
public final S createRenderState(T p_361382_, float p_360885_) {
S s = this.reusedState;
this.extractRenderState(p_361382_, s, p_360885_);
+ net.neoforged.neoforge.client.ClientHooks.onUpdateRenderState(this, p_361382_, s);
return s;
}

@@ -270,7 +_,12 @@
}

Expand All @@ -23,11 +31,12 @@
if (flag) {
p_361028_.nameTag = this.getNameTag(p_362104_);
p_361028_.nameTagAttachment = p_362104_.getAttachments().getNullable(EntityAttachment.NAME_TAG, 0, p_362104_.getYRot(p_362204_));
@@ -302,5 +_,7 @@
@@ -302,5 +_,8 @@
}

p_361028_.displayFireAnimation = p_362104_.displayFireAnimation();
+
+ p_361028_.partialTick = p_362204_;
+ net.neoforged.neoforge.attachment.AttachmentInternals.copyEntityAttachments(p_362104_, p_362104_, false);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
--- a/net/minecraft/client/renderer/entity/state/EntityRenderState.java
+++ b/net/minecraft/client/renderer/entity/state/EntityRenderState.java
@@ -27,6 +_,7 @@
@@ -7,7 +_,7 @@
import net.neoforged.api.distmarker.OnlyIn;

@OnlyIn(Dist.CLIENT)
-public class EntityRenderState {
+public class EntityRenderState extends net.neoforged.neoforge.attachment.AttachmentHolder implements net.neoforged.neoforge.client.extensions.IRenderStateExtension {
public double x;
public double y;
public double z;
@@ -27,6 +_,27 @@
public Vec3 nameTagAttachment;
@Nullable
public EntityRenderState.LeashState leashState;
+ public float partialTick;
+ private java.util.Map<net.minecraft.util.context.ContextKey<?>, Object> extensions = java.util.Map.of(); // NeoForge: allow generic extension of render states
dhyces marked this conversation as resolved.
Show resolved Hide resolved
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ @Override
+ public <T> T getRenderData(net.minecraft.util.context.ContextKey<T> key) {
+ return (T) extensions.getOrDefault(key, null);
+ }
+
+ @Override
+ public <T> void setRenderData(net.minecraft.util.context.ContextKey<T> key, @Nullable T data) {
+ if (extensions == java.util.Map.<net.minecraft.util.context.ContextKey<?>, Object>of()) {
+ extensions = new it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap<>();
dhyces marked this conversation as resolved.
Show resolved Hide resolved
+ }
+ if (data == null) {
+ extensions.remove(key);
+ } else {
+ extensions.put(key, data);
+ }
+ }

@OnlyIn(Dist.CLIENT)
public static class LeashState {
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public static void copyEntityAttachments(Entity from, Entity to, boolean isDeath
copyAttachments(from.registryAccess(), from, to, isDeath ? type -> type.copyOnDeath : type -> true);
}

public static <H extends AttachmentHolder> void copyEntityAttachments(Entity from, H to, boolean isDeath) {
copyAttachments(from.registryAccess(), from, to, isDeath ? type -> type.copyOnDeath : type -> true);
}

@SubscribeEvent
public static void onPlayerClone(PlayerEvent.Clone event) {
event.getEntity().copyAttachmentsFrom(event.getOriginal(), event.isWasDeath());
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/net/neoforged/neoforge/client/ClientHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.chunk.RenderChunkRegion;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.entity.state.HumanoidRenderState;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
Expand Down Expand Up @@ -135,6 +137,7 @@
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.asm.enumextension.ExtensionInfo;
import net.neoforged.neoforge.client.entity.animation.json.AnimationTypeManager;
import net.neoforged.neoforge.client.entity.state.EntityRenderStateModifier;
import net.neoforged.neoforge.client.event.AddSectionGeometryEvent;
import net.neoforged.neoforge.client.event.CalculateDetachedCameraDistanceEvent;
import net.neoforged.neoforge.client.event.CalculatePlayerTurnEvent;
Expand All @@ -156,6 +159,7 @@
import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent;
import net.neoforged.neoforge.client.event.RegisterMaterialAtlasesEvent;
import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent;
import net.neoforged.neoforge.client.event.RegisterRenderStateModifiersEvent;
import net.neoforged.neoforge.client.event.RegisterShadersEvent;
import net.neoforged.neoforge.client.event.RegisterSpriteSourceTypesEvent;
import net.neoforged.neoforge.client.event.RenderArmEvent;
Expand Down Expand Up @@ -272,6 +276,15 @@ public static boolean onDrawHighlight(LevelRenderer context, Camera camera, Bloc
return NeoForge.EVENT_BUS.post(new RenderHighlightEvent.Block(context, camera, target, deltaTracker, poseStack, bufferSource, forTranslucentBlocks)).isCanceled();
}

public static <E extends Entity, S extends EntityRenderState> void onUpdateRenderState(EntityRenderer<E, S> renderer, E entity, S renderState) {
var modifiers = RenderStateExtensions.getCachedEntityModifiers(renderer);
if (!modifiers.isEmpty()) {
for (EntityRenderStateModifier<E, S> modifier : modifiers) {
modifier.accept(entity, renderState);
}
}
}

public static void dispatchRenderStage(RenderLevelStageEvent.Stage stage, LevelRenderer levelRenderer, @Nullable PoseStack poseStack, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, int renderTick, Camera camera, Frustum frustum) {
var mc = Minecraft.getInstance();
var profiler = Profiler.get();
Expand Down Expand Up @@ -990,6 +1003,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou
ModLoader.postEvent(new RegisterClientReloadListenersEvent(resourceManager));
ModLoader.postEvent(new EntityRenderersEvent.RegisterLayerDefinitions());
ModLoader.postEvent(new EntityRenderersEvent.RegisterRenderers());
ModLoader.postEvent(new RegisterRenderStateModifiersEvent());
ClientTooltipComponentManager.init();
EntitySpectatorShaderManager.init();
ClientHooks.onRegisterKeyMappings(mc.options);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client;

import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import net.minecraft.Util;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.world.entity.Entity;
import net.neoforged.neoforge.client.entity.state.EntityRenderStateModifier;
import org.jetbrains.annotations.ApiStatus;

public final class RenderStateExtensions {
private RenderStateExtensions() {}

private static final Map<Class<? extends EntityRenderer<?, ?>>, Collection<EntityRenderStateModifier<?, ?>>> ENTITY_EXTENSIONS = new Reference2ObjectArrayMap<>();

private static final Map<Class<? extends EntityRenderer<?, ?>>, Collection<EntityRenderStateModifier<?, ?>>> ENTITY_CACHE = Util.make(new Reference2ObjectOpenHashMap<>(), map -> map.defaultReturnValue(List.of()));

@SuppressWarnings("unchecked")
static <E extends Entity, S extends EntityRenderState> Collection<EntityRenderStateModifier<E, S>> getCachedEntityModifiers(EntityRenderer<E, S> renderer) {
return (Collection<EntityRenderStateModifier<E, S>>) (Object) ENTITY_CACHE.computeIfAbsent((Class<? extends EntityRenderer<E, S>>) renderer.getClass(), aClass -> {
var list = new ObjectArrayList<EntityRenderStateModifier<?, ?>>();
for (var entry : ENTITY_EXTENSIONS.entrySet()) {
if (aClass.isInstance(entry.getClass())) {
dhyces marked this conversation as resolved.
Show resolved Hide resolved
list.addAll(entry.getValue());
}
}
if (list.isEmpty()) {
return null;
}
return list;
});
}

@ApiStatus.Internal
public static <E extends Entity, S extends EntityRenderState> void registerExtender(Class<? extends EntityRenderer<E, S>> baseRenderer, EntityRenderStateModifier<E, S> modifier) {
ENTITY_EXTENSIONS.computeIfAbsent(baseRenderer, aClass -> new ObjectArrayList<>()).add(modifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client.entity.state;

import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.world.entity.Entity;

public interface EntityRenderStateModifier<E extends Entity, S extends EntityRenderState> {
void accept(E entity, S renderState);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

@FieldsAreNonnullByDefault
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package net.neoforged.neoforge.client.entity.state;

import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.FieldsAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client.event;

import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.world.entity.Entity;
import net.neoforged.bus.api.Event;
import net.neoforged.fml.event.IModBusEvent;
import net.neoforged.neoforge.client.RenderStateExtensions;
import net.neoforged.neoforge.client.entity.state.EntityRenderStateModifier;
import org.jetbrains.annotations.ApiStatus;

public class RegisterRenderStateModifiersEvent extends Event implements IModBusEvent {
@ApiStatus.Internal
public RegisterRenderStateModifiersEvent() {}

public <E extends Entity, S extends EntityRenderState> void registerEntityModifier(Class<? extends EntityRenderer<E, S>> baseRenderer, EntityRenderStateModifier<E, S> modifier) {
RenderStateExtensions.registerExtender(baseRenderer, modifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client.extensions;

import net.minecraft.util.context.ContextKey;
import org.jetbrains.annotations.Nullable;

public interface IRenderStateExtension {
XFactHD marked this conversation as resolved.
Show resolved Hide resolved
@Nullable
<T> T getRenderData(ContextKey<T> key);

<T> void setRenderData(ContextKey<T> key, @Nullable T data);

default <T> T getRenderDataOrThrow(ContextKey<T> key) {
T data = getRenderData(key);
if (data == null) {
throw new IllegalStateException("No value associated for key " + key);
}
return data;
}

default <T> T getRenderDataOrDefault(ContextKey<T> key, T defaultVal) {
T data = getRenderData(key);
if (data == null) {
return defaultVal;
}
return data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,28 @@
package net.neoforged.neoforge.debug.client;

import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.math.Axis;
import java.util.Map;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.Items;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.context.ContextKey;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.neoforge.client.event.AddSectionGeometryEvent;
import net.neoforged.neoforge.client.event.ClientChatEvent;
import net.neoforged.neoforge.client.event.ClientPlayerChangeGameTypeEvent;
import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent;
import net.neoforged.neoforge.client.event.RegisterRenderStateModifiersEvent;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.neoforged.neoforge.client.event.RenderPlayerEvent;
import net.neoforged.neoforge.client.model.data.ModelData;
Expand Down Expand Up @@ -126,4 +131,31 @@ static void renderPlayerEvent(final DynamicTest test) {
test.requestConfirmation(Minecraft.getInstance().player, Component.literal("Is an iron block rendered above you in third-person?"));
});
}

@TestHolder(description = { "" }, enabledByDefault = true)
static void updateRenderState(final DynamicTest test) {
var key = new ContextKey<Integer>(ResourceLocation.fromNamespaceAndPath(test.createModId(), "test"));
var testAttachment = test.registrationHelper().attachments().registerSimpleAttachment("test", () -> 3);
test.whenEnabled(listeners -> {
listeners.mod().addListener((RegisterRenderStateModifiersEvent event) -> {
event.registerEntityModifier(PlayerRenderer.class, (entity, renderState) -> {
renderState.setRenderData(key, 5);
});
});
listeners.forge().addListener((RenderPlayerEvent.Post event) -> {
int numRender = event.getRenderState().getData(testAttachment);
int xRotation = event.getRenderState().getRenderDataOrDefault(key, -1);
var poseStack = event.getPoseStack();
poseStack.pushPose();
for (int i = 0; i < numRender; i++) {
poseStack.translate(0, 1, 0);
poseStack.pushPose();
poseStack.mulPose(Axis.XP.rotation(xRotation));
Minecraft.getInstance().getBlockRenderer().renderSingleBlock(Blocks.CALCITE.defaultBlockState(), poseStack, event.getMultiBufferSource(), event.getPackedLight(), OverlayTexture.NO_OVERLAY, ModelData.EMPTY, RenderType.solid());
poseStack.popPose();
}
poseStack.popPose();
});
});
}
}
Loading