Replace offset shifting animations with palette cycling#57
Replace offset shifting animations with palette cycling#57morganchristiansson wants to merge 4 commits into
Conversation
| // array for all palettes | ||
| std::vector<bobPAL> global::palArray(MAXBOBPAL); | ||
| // palette animation data per tileset (indexed by 8-bit tileset bmpArray index) | ||
| std::vector<std::vector<PaletteAnimData>> global::tilesetAnimData; |
There was a problem hiding this comment.
A bit hard to understand the comment as it only describes 1 vector, not which one nor the other one. maybe add a using PaletteAnimation = std::vector<PaletteAnimData> and name the variable vector<PaletteAnimation> tilesetAnimations or similar
Especially the above
auto& animData = global::tilesetAnimData[tilesetIdx8];
...
for(auto& anim : animData)
is confusingly named if anim is of type PaletteAnimData but animData is not.
You could also turn it around and renamed PaletteAnimData to PaletteAnimation and the vector of that is AnimData to match this code, but not sure if this is clearer.
Anyway such variable vs type naming mismatches are a clear indication of (mis)namings that need to be improved.
2ca6406 to
f3a57cc
Compare
|
Ok I cleaned this up a lot. I learned libsiedler2 is external project pre-dating RttR and supports writing. |
| // array for all palettes | ||
| std::vector<bobPAL> global::palArray(MAXBOBPAL); | ||
| // Palette animation entries keyed by the bmpArray slot of the 8-bit tileset surface. | ||
| std::map<Uint16, std::map<int, PaletteAnimation>> global::paletteAnimations; |
There was a problem hiding this comment.
Why is this a map of maps? What is the key of the 2nd?
There was a problem hiding this comment.
Outer key is tileset id, inner key is palAnimIdx from GDL lua.
Could also use vector[MapType] as outer, but tileset id was more accessible.
There was a problem hiding this comment.
Outer key is tileset id, inner key is palAnimIdx from GDL lua. Could also use vector[MapType] as outer, but tileset id was more accessible.
But why is this used/stored? The inner map is effectively used as an expensive vector
There was a problem hiding this comment.
Right there's only 3 tilesets, could use separate fields too, and each tileset has only 2 animations, water and lava.
Tho could be possibly change that in the GDL lua.
Can also add a field to the struct in bmpArrar but then every surface/texture gets it. Not sure what's optimal.
There was a problem hiding this comment.
You apply the animation to the whole tileset, don't you? So for each animation step you apply all animations for that tilesets. You don't differentiate between water an lava, do you?
So why isn't this a mapping of tileset to list/vector of animations? I might be missing/forgetting something so in that case please remind me exactly where you'd use the 2nd key
There was a problem hiding this comment.
Good point, not sure and I will check. I think there are separate palAninIdx in same biome lua file tho. Maybe I'm wrong.
There was a problem hiding this comment.
Yep you're right. They are per biome not per sprite/texture.
std::array<std::vector<PaletteAnimation>, 3> paletteAnimations
Also this will be reworked a bit in gl2-terrain also to support it with OpenGL.
s25client precomputes and cycles textures, seems reasonable given 32bit non-paletted textures.
There was a problem hiding this comment.
s25edit (current): runtime palette cycling
- CRNG animations stored as PaletteAnimation structs
- Each frame: rotates the entire 8-bit tileset palette in-place, then blits 8-bit → 32-bit surface
- Simple, but requires SDL palette surfaces and can't work with OpenGL textures directly
s25client/RTTR: pre-compute all animation frames at load time
- Each terrain type (water, lava) has its own palAnimIdx in the Lua config referencing a specific CRNG entry
- ExtractAnimatedTexture extracts the terrain's texture region into a paletted buffer, then generates N pre-rotated copies (one per animation frame) by calling anim.apply() which shifts the palette by one step each iteration
- Each frame becomes its own glArchivItem_Bitmap_Raw with a baked palette — no runtime palette work needed
- At render time, just cycles texture index: terrainTextures[curIdx].textures[frame]
- Naturally OpenGL-compatible since textures hold final RGBA pixel data
The RTTR approach is basically "bake the animation into separate textures at load time" instead of "rotate the palette at runtime." For s25edit's future OpenGL port, that's the direction to look at — though s25edit would still apply all animations globally to the tileset rather than per-terrain, the pre-computation strategy would remove the need for runtime palette surface manipulation.
|
|
||
| // chunk-identifier (4 Bytes) | ||
| // search for the "CMAP" and skip other chunk-types | ||
| const Uint16 tilesetSlot = static_cast<Uint16>(bmpArray - global::bmpArray.data()); |
There was a problem hiding this comment.
Move before the comment or somewhere else so it makes sense, remove chunkIdx if not required
| Point16& left, Point16& right, Point16& upper2, Point16& left2, Point16& right2) | ||
| { | ||
| const auto animOffset = Point16(-texture_move, texture_move); | ||
| // Offset-shifting animation replaced by palette cycling animation (UpdatePaletteAnimations) |
| } | ||
|
|
||
| /// Map a MapType to the 8-bit or 32-bit tileset bmpArray slot | ||
| static Uint16 tilesetIdxForMapType(MapType mapType, bool want32bit) |
| // Palette animation parsed from a CRNG chunk in an LBM file | ||
| struct PaletteAnimation | ||
| { | ||
| bool isActive = false; |
There was a problem hiding this comment.
Does it need default values at all? I'd expect that if there is an animation it will have all values set before
| static void rotatePaletteRange(SDL_Palette* pal, uint8_t firstClr, int colorCount, int deltaOffset, bool moveUp) | ||
| { | ||
| std::array<SDL_Color, 256> rotated; | ||
| memcpy(rotated.data(), pal->colors, sizeof(SDL_Color) * 256); |
There was a problem hiding this comment.
Avoid sizeof
| memcpy(rotated.data(), pal->colors, sizeof(SDL_Color) * 256); | |
| std::copy_n(pal->colors, rotated.size(), rotated.begin()); |
Actually: You don't use that at all: You set all colors manually and could simply do rotated[i] = pal->colors[firstClr + srcIdx];
Or is this missing something?
|
|
||
| // 32-bit tileset surface for blitting after palette updates | ||
| const Uint16 tilesetIdx32 = tilesetIdxForMapType(mapType, true); | ||
| auto* surf32 = global::bmpArray[tilesetIdx32].surface.get(); |
There was a problem hiding this comment.
If you assume this is non_NULL use a reference
| auto* surf32 = global::bmpArray[tilesetIdx32].surface.get(); | |
| auto& surf32 = *global::bmpArray[tilesetIdx32].surface; |
| uint8_t lastClr = 0; | ||
| int currentOffset = 0; | ||
| int lastAppliedOffset = 0; | ||
| uint32_t lastUpdateTime = 0; |
There was a problem hiding this comment.
Do you need a fixed size here?
| uint32_t lastUpdateTime = 0; | |
| unsigned lastUpdateTime = 0; |
| const Uint16 tilesetIdx32 = tilesetIdxForMapType(mapType, true); | ||
| auto* surf32 = global::bmpArray[tilesetIdx32].surface.get(); | ||
|
|
||
| const Uint32 now = SDL_GetTicks(); |
There was a problem hiding this comment.
Be consistent in use of types for this and lastUpdateTime. Why not just use unsigned?
| if(!anim.isActive || anim.rate == 0) | ||
| continue; | ||
|
|
||
| const int colorCount = anim.lastClr - anim.firstClr + 1; | ||
| if(colorCount <= 1) | ||
| continue; |
There was a problem hiding this comment.
Wouldn't it make sense to not add "inactive" animations at all?
And verify they are valid before adding: (lastClr>firstClr && rate>0)
|
|
I forgot to push? Will push tomorrow |
f3a57cc to
923a1bc
Compare
923a1bc to
a1e596d
Compare
|
Also removed the special handling of ice floe water animations which is not needed anymore. |
|
Also kind of regretting not using libsiedler2 to read files, but the CFile addition is kinda clean. Maybe best to keep CFile or maybe best to throw it out for libsiedler2. |
84555ea to
190336a
Compare
Using pos values from GameDataLoader broke the offset shifting animations that started crolling in unrelated textures.
Existing animations also jump as the offset resets in the animation cycle.
Do what s25client does - animate water and lava by cycling palettes.