Animation State Bleeding with GeckoLib Entities in FeatureRenderer
Hey peeps,
my mod renders animated shoulder hamsters using GeckoLib. The shoulder hamsters work just fine in vanilla. The implementation uses a
FeatureRenderer
on the player model, which in turn renders several client-side "dummy" GeoEntity
instances—one for each shoulder/head slot. Each dummy entity has its own AnimationController
with a unique name based on its instance ID.
The Issue: With Iris enabled, all shoulder pet dummies appear to share the same animation state, (but I think it's purely visual; watch the end of the video for a note on this) moving in perfect sync. Furthermore, their animations are hijacked by any in-world entity of the same type that is also being rendered. This animation bleeding does not occur without Iris.
My guess is that Iris's batched entity rendering is causing GeckoLib to reuse animation data across these dummy instances, despite their unique controller names.
Video: Here is a screen recording demonstrating the issue (vanilla vs. Iris comparison)
Does anyone know what could be causing this?
I'm happy to provide a minimal reproducible example .zip if needed. Thanks in advance!
These attached files provide a complete picture of the rendering and state management pipeline for the shoulder pets.
1. HamsterShoulderFeatureRenderer.java
: The core class that manages the dummy entities and initiates their rendering. This is the most important file.
2. ClientShoulderHamsterData.java
: Shows how the animation state for each shoulder pet is managed and ticked independently on the client thread.
3. ShoulderHamsterState.java
: The state machine class that determines which animation should be playing for a single shoulder pet.
4. HamsterEntity.java
: Specifically the registerControllers
method, so they can see how the GeckoLib animation controller is set up.
5. ShoulderHamsterRenderer.java
: The specialized GeoEntityRenderer
used for the dummy entities.Adorable Hamster Pets
YouTube
iris shaders visual glitch
Adorable Hamster Pets - 70+ Variants, Shoulder Launching, Diamond Sniffing, Cheek Pouch Inventories, Unique Personalities, 30+ Animations, and much more! https://youtu.be/2KuEjC1ZkwE
Download here:
https://modrinth.com/mod/adorable-hamster-pets
https://www.curseforge.com/minecraft/mc-mods/adorable-hamster-pets
Join "The Cheek Pouch" Discord h...
Solution:Jump to solution
SOLVED!
Ok so I knew the key clue was that the animations appeared correct on the pause screen. This confirmed that my underlying animation data and state machines were working perfectly, so the problem had to be purely a render-time issue.
The problem was specifically within GeckoLib's
AnimatableManager
. My guess is that when Iris's batch rendering processes multiple GeoEntity
instances in the same frame, the manager for the first entity would get its lastUpdateTime
set. When the renderer immediately moved to the next entity in the batch, the time delta since the last update was near-zero, causing GeckoLib to (correctly, from its perspective) skip the animation tick for that entity. This resulted in it being rendered with the stale bone data from the previously rendered entity....15 Replies
@Moderators anyone have any ideas what could be going wrong here?
Please don't ping moderators for help unless it's help moderating the server. @Support Team is who you want.
But otherwise I'll have a look
Actually I have no idea if this is BER (Batched Entity Rendering), I'll ping @IMS and he'll respond at some point :)
Ahh thank you— my bad. I actually just logged back on to see what the roles were when I saw your comment. I appreciate anyone who wants to get eyes on this!
I’m willing to go to pretty much whatever lengths I need to go to to fix this aside from removing Geckolib, if I can be done from my end! Hamsters gotta have shaders, know what I’m sayin? Lol
hi
I… have no clue what could possibly be wrong here
BER is just fundamentally broken on 1.21, and is being removed with 1.21.9
the only thing I can think of is making the state a map tied to the Entity? (Can’t read all the code right now, so sorry if that’s already the case)
I’m guessing you read the part where I said everything works fine until I install Iris?
yeah
ber meaning batched entity rendering, to be clear
Iris's feature
Any other @Support Team have any ideas? I realllly want the hamsters to be shader-compatible
Can't help, I don't have enough knowledge in graphics programming.
:yes:
Ahh ok. Are there any other support team who maybe aren't online yet? Just wondering if I should try and reach out to.... like the fabric discord or something idk
Only a small few of the support teams are devs. Guess you have to wait for them to provide input.
ok, so I guess the person I'm looking for is @Head Developer?
Hmm yes that's exactly the approach I'm currently using. On the client, each
PlayerEntity
has a custom data object (ClientShoulderHamsterData
). This object holds a Map<ShoulderLocation, ShoulderHamsterState>
, where each ShoulderHamsterState
is an independent state machine that gets ticked every frame.
The FeatureRenderer
then applies the unique state from that map to the corresponding dummy HamsterEntity
for each shoulder slot right before rendering it.
So, the state is indeed managed independently per-instance on my end. The issue seems to be that during Iris's batched rendering (maybe?), something ends up using the animation data from just one of those instances for all of them, despite each having a unique controller name.
But here's the weirdest and most intriguing part: if I pause the game, while it's paused I can see in the blurred background that they immediately flick back to whatever animation state they are actually in, rather than the shared identical animations. In other words, as long as the game is paused, it seems to be showing a snapshot of everything working just fine. It's super weird.
Check out the this part of the video to see what I'm talking about.Solution
SOLVED!
Ok so I knew the key clue was that the animations appeared correct on the pause screen. This confirmed that my underlying animation data and state machines were working perfectly, so the problem had to be purely a render-time issue.
The problem was specifically within GeckoLib's
AnimatableManager
. My guess is that when Iris's batch rendering processes multiple GeoEntity
instances in the same frame, the manager for the first entity would get its lastUpdateTime
set. When the renderer immediately moved to the next entity in the batch, the time delta since the last update was near-zero, causing GeckoLib to (correctly, from its perspective) skip the animation tick for that entity. This resulted in it being rendered with the stale bone data from the previously rendered entity.
The fix was to manually force GeckoLib's AnimatableManager
to re-evaluate its state for each dummy entity right before its render call. By setting its lastUpdateTime
to 0
, I trick the manager into thinking a very long time has passed, guaranteeing it runs a full animation update and ignores any polluted state from other entities in the batch.Here's the snippet that fixed it, placed inside my
FeatureRenderer
's render logic for each shoulder pet:
Thanks again for the help!Congrats on finding a solution.
:haha_yes:
This one was a doozy