From 7243b07057d7ead119dc71c977c766e42930ca5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 13 Jul 2026 19:55:07 +0200 Subject: [PATCH] Fix TilemapGPULayer animated tiles after the first rendering wrong createAnimationDataTexture stored the raw animation ordinal in _animationDataIndexMap, but each animation header occupies 2 texels in the animation data texture (duration and frame offset) and the shader reads the header at texel `index`. Only ordinal 0 landed on a valid header; every animated tile after the first read a bogus header and resolved to a wrong tile. Store the texel offset (`ordinal * 2`), matching how the frame offset is already stored. The map value is consumed only by TilemapGPULayer.generateLayerDataTexture (which looks it up directly) and by SubmitterTilemapGPULayer (which only reads `.size`), so doubling is safe. Fixes #7331. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/tilemaps/Tileset.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tilemaps/Tileset.js b/src/tilemaps/Tileset.js index bc75214198..a2e34265f2 100644 --- a/src/tilemaps/Tileset.js +++ b/src/tilemaps/Tileset.js @@ -614,7 +614,13 @@ var Tileset = new Class({ var animationDuration = tileDatum.animationDuration; // This index maps to an animation, not a single tile. - indexToAnimMap.set(i, animations.length); + // The value is a texel offset into the animation data texture, + // where each animation header occupies 2 texels (duration and + // frame offset), so it must be `2 * ordinal` - the same way the + // frame offset above is stored in texel units. The shader reads + // the header at texel `index`; storing the raw ordinal made + // every animated tile after the first read a wrong header. + indexToAnimMap.set(i, animations.length * 2); // This animation points to a run of frames. animations.push([ animationDuration, animFrames.length ]);