Commit fa4d341
Frames an animation can call by name
An image asset in explicit mode cuts its sheet into cells that each carry a
RegionName, and an animation on it can list those names -- block1 block2 block3
block4 -- instead of listing 0 1 2 3. The point is that a named list survives the
sheet being re-cut or re-ordered, which a numbered one does not.
The engine has played these correctly for as long as they have existed.
ImageFrameProviderCore branches on the mode in all four places that matter, and
the runtime path from validate through play and update to the frame area is
mode-aware throughout. Only AUTHORING was broken, and by one character:
AnimationAsset_ScriptBinding.h formatted a StringTableEntry -- a const char* --
through "%d", so getNamedAnimationFrames returned a row of pointer addresses.
Nothing that asked an animation for its named frames could recover them, so the
Asset Manager refused such an asset twice over: AssetInspector sent it to the
stock inspector, and AssetAnimationStage::canEdit denied it a palette and a
timeline. Both refusals carried comments explaining that the API did not round
trip, and both were right.
NAMED CELLS MODE IS NO LONGER A FLAG ANYBODY SETS. The field, its setter, its
write function and the member are gone; getNamedCellsMode() is a live read of
mImageAsset->getExplicitMode(). The image already held the only honest answer,
and a stored copy could disagree with it the moment that image was re-cut -- and
did worse than that, since a person could set the flag true on an image with no
names at all and get an animation with no frames and no explanation. Nothing has
to keep the two in step now because there is only one of them. The refresh
cascade already reaches here: setExplicitMode ends in refreshAsset,
AssetManager::updateAssetDependencies has the edge from the animation's Image
field, and the drain loop is index-based over a growing vector, so the dependent
animation is dispatched in the same drain that dispatched the image.
The whole editor stays in INDEX space, and that is what kept this small. The
palette shows cell N, the timeline holds cell N, a drag carries cell N, the
range dialog builds "28 29 30"; only loading and committing know that names
exist. Every gesture, the caret arithmetic, the hold detection and the undo
transaction are unchanged.
The one thing index space cannot carry is a name whose cell has been deleted. It
resolves to no index, and EVERY such name resolves to the same -1, so a list
round-tripped through indices would come back from a single edit with two broken
frames merged into one and the other silently committed away. So the timeline
keeps a parallel mSlotNames, always exactly the size of mSlots: mSlots stays the
drawing truth and mSlotNames the authoring truth. setFrames takes indices and
derives names, setNamedFrames takes names and derives indices, and both always
fill both -- which is why appendFrame, insertFrameAtPoint and the range dialog
needed no changes at all. A missing frame draws as an outlined empty cell in the
theme's error color with the name it could not find under it, in the same ink,
and says so again in its tooltip and in the inspector's warning line. It is kept
rather than dropped because dropping it is a deletion the user never asked for
and could not have seen happen.
Cells label themselves in both grids now, so the payoff is visible where the
work is done. That is one virtual on the shared base, so the palette and the
timeline cannot label the same cell differently -- which would make dragging
between them a guess. Long names clip with an ellipsis and the full text is in
the tooltip; the clip measures and draws the same string rather than pairing
getStrNWidth with dglDrawTextN, whose counts are bytes and UTF16 units
respectively and disagree the moment a name is not ASCII.
EVERY EXPLICIT CELL NOW HAS A NAME, which is the invariant the rest rests on. A
cell stored without one is named Frame<N>, seeded at its own index and walked
past anything already taken -- not hypothetical, since deleting a cell from the
middle renumbers every one after it. It happens in calculateExplicitMode, the
one funnel every path ends in, because the TAML read pushes straight into
mExplicitFrames and never goes near addExplicitCell. What stood there before was
a "repair" in four places that could not have worked: it read dSscanf FROM the
empty name INTO a U32 passed by value where a pointer was required, and never
assigned a name to anything. Its guard never fired either -- it compared a
console or TAML buffer against StringTable->EmptyString by POINTER, and neither
is ever interned. The image editor's Add Cell button now asks the engine for the
name instead of building "Frame" @ index itself with no uniqueness check, which
is how adding a cell after deleting one from the middle produced a duplicate
that the rename box beside it would have refused.
Switching an image between explicit and cell mode converts the animations on it,
so the switch is a decision rather than a commitment. Both lists are kept in
memory and only the one in use is written, and the conversion runs from
onAssetRefresh, setImage and initializeAsset -- NOT from validateFrames, which
is where it obviously belongs and where it would have destroyed data. That is
called from inside both frame setters, so setAnimationFrames("") -- what the
editor sends when the timeline is emptied, and what copyFieldsFrom sends on
every single copy -- would have seen an empty active list beside a full one and
put the frames the user had just cleared straight back. validateFrames stays a
pure derivation that touches neither specified list. initializeAsset is new
here: settling this after the whole file is read is what makes the result
independent of TAML field order, which mattered as soon as anything depended on
Image and a frame list together.
Gating the write on the mode is what closes the round trip. Both lists used to
be written whenever they had content, and the named one is applied last and used
to force named mode on -- so an animation given numbered frames after ever
having had named ones came back from its own file named. That in turn made an
older landmine reachable: onTamlCustomWrite gated the Cells node on explicit
mode, so saving an image with the mode off deleted every cell in the file and
with them the only thing that could ever resolve those names again. The cells
are authored data that outlive the mode -- copyAssetStateTo says so in as many
words -- so they are written whenever there are any. Which means the file has to
state the mode out loud, because the read infers explicit mode from the presence
of a Cells node and must keep doing so for every file written before this. A
file that states it is believed; only a file that says nothing is inferred from.
Engine defects fixed on the way, all reachable before any of this:
- getExplicitCellOffset returned NULL from a Vector2-returning function when not
in explicit mode, which selects Vector2(const char*), which calls setString on
a null pointer and dereferences it. The image editor's swap-cells path reaches
it.
- all four getExplicitCell accessors indexed with Vector<T>::at, which takes a
U32 and only asserts -- so it is unchecked in release and at(-1) was a read at
four billion. A failed name lookup is exactly what -1 means around here.
- getExplicitCellName and getExplicitCellIndex refused to answer while explicit
mode was off, which is precisely when a name has to be translated back into an
index. The guards are off those two; the four mutators keep theirs.
- getCellByName's empty-name guard was the same pointer comparison as above, so
an empty name matched the first frame of any image whose cells are unnamed.
- ImageFrameProviderCore::mUsingNamedFrame and mNamedImageFrame were never
initialized by the constructor and never cleared by clearAssets, and
validRender reads the first on the first frame of every static sprite -- an
indeterminate true then dereferences an equally indeterminate name.
- getNamedAnimationFrames sized its return buffer at a fixed 4096 that suits a
list of integers. A region name has no length limit and dSprintf truncates in
silence, so a long animation would have lost its tail and said nothing. Both
it and getMissingFrames measure first.
- mValidatedNameFrames was missing its VECTOR_SET_ASSOCIATION, and the dead
mAnimationIntegration field is gone.
Two new bindings exist to stop script having to branch. getFrameCount answers in
whichever space the animation uses; getAnimationFrameCount refuses in named mode
and returns -1, which read as "fewer than one" to Keep Frame Rate and as "-1
frames" on the inspector's info line. getMissingFrames returns the names no cell
answers to, which is cheaper and more honest than N console calls per refresh.
animationFrameConversionTests and imageAssetCellNameTests cover the arithmetic
through the statics, because building a real explicit cell needs a bitmap and a
unit test has no GL context to load one into: the round trip, a hold surviving
it, everything that fails to resolve in either direction, and the naming search
including the collision walk and the case fold. AnimationAssetCarriesNamedFrames
lost its mode assertion with the field and gained a sibling asserting that BOTH
lists survive a copy, which is what makes the mode switch reversible.
animationFrameValidation carries the engine half -- most valuably that
getNamedAnimationFrames returns four names and not four numbers -- plus the mode
switch, the auto-naming, and the file keeping its cells with the mode off.
assetAnimationTimeline drives the editor half through to reading the saved file
back. The shot harness gains the two pictures that only a picture settles:
whether a name reads at 48 pixels, and whether a missing frame is findable.
The toybox 1234 image and 1234Animation are the demo pair, and the only named
assets in the tree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Xh3XULt9PtdCtsaYHGnYE1 parent 9e4ae28 commit fa4d341
29 files changed
Lines changed: 2063 additions & 195 deletions
File tree
- cmake
- editor
- AssetAdmin
- Animation
- ImageEditor
- Inspector
- EditorCore/Themes/BaseTheme
- engine/source
- 2d
- assets
- core
- gui/editor
- testing/tests
- tests
- shots
- smoke
- toybox/ToyAssets/1/assets
- animations
- images
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
343 | 343 | | |
344 | 344 | | |
345 | 345 | | |
| 346 | + | |
346 | 347 | | |
347 | 348 | | |
348 | 349 | | |
| |||
355 | 356 | | |
356 | 357 | | |
357 | 358 | | |
| 359 | + | |
358 | 360 | | |
359 | 361 | | |
360 | 362 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
86 | 101 | | |
87 | | - | |
88 | | - | |
| 102 | + | |
89 | 103 | | |
90 | | - | |
| 104 | + | |
91 | 105 | | |
92 | 106 | | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
97 | 115 | | |
98 | 116 | | |
99 | 117 | | |
| |||
124 | 142 | | |
125 | 143 | | |
126 | 144 | | |
127 | | - | |
| 145 | + | |
128 | 146 | | |
129 | 147 | | |
130 | 148 | | |
| |||
464 | 482 | | |
465 | 483 | | |
466 | 484 | | |
467 | | - | |
| 485 | + | |
468 | 486 | | |
469 | 487 | | |
470 | 488 | | |
471 | 489 | | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
472 | 496 | | |
473 | 497 | | |
474 | 498 | | |
| 499 | + | |
475 | 500 | | |
476 | 501 | | |
477 | 502 | | |
| |||
558 | 583 | | |
559 | 584 | | |
560 | 585 | | |
561 | | - | |
| 586 | + | |
562 | 587 | | |
563 | | - | |
| 588 | + | |
564 | 589 | | |
565 | 590 | | |
566 | 591 | | |
567 | 592 | | |
| 593 | + | |
| 594 | + | |
568 | 595 | | |
569 | 596 | | |
570 | 597 | | |
571 | | - | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
572 | 604 | | |
573 | 605 | | |
574 | 606 | | |
| |||
586 | 618 | | |
587 | 619 | | |
588 | 620 | | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
589 | 626 | | |
590 | | - | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
591 | 635 | | |
592 | 636 | | |
593 | 637 | | |
| |||
657 | 701 | | |
658 | 702 | | |
659 | 703 | | |
660 | | - | |
| 704 | + | |
661 | 705 | | |
662 | 706 | | |
663 | 707 | | |
| |||
Lines changed: 29 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
92 | 97 | | |
93 | 98 | | |
94 | 99 | | |
95 | 100 | | |
96 | 101 | | |
97 | 102 | | |
98 | 103 | | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
99 | 111 | | |
100 | 112 | | |
101 | 113 | | |
| |||
119 | 131 | | |
120 | 132 | | |
121 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
122 | 138 | | |
123 | 139 | | |
124 | 140 | | |
125 | | - | |
| 141 | + | |
126 | 142 | | |
127 | 143 | | |
128 | 144 | | |
| |||
137 | 153 | | |
138 | 154 | | |
139 | 155 | | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
140 | 162 | | |
141 | 163 | | |
142 | | - | |
143 | | - | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
144 | 170 | | |
145 | 171 | | |
146 | 172 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
737 | 737 | | |
738 | 738 | | |
739 | 739 | | |
740 | | - | |
| 740 | + | |
741 | 741 | | |
742 | | - | |
743 | | - | |
744 | | - | |
745 | | - | |
746 | | - | |
747 | | - | |
748 | | - | |
749 | | - | |
750 | | - | |
751 | | - | |
752 | | - | |
753 | | - | |
754 | | - | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
755 | 748 | | |
756 | 749 | | |
757 | 750 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
144 | 152 | | |
145 | 153 | | |
146 | 154 | | |
147 | | - | |
148 | 155 | | |
149 | 156 | | |
150 | 157 | | |
151 | 158 | | |
152 | 159 | | |
153 | 160 | | |
154 | 161 | | |
155 | | - | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
156 | 167 | | |
157 | 168 | | |
158 | 169 | | |
| |||
Lines changed: 34 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
| |||
206 | 209 | | |
207 | 210 | | |
208 | 211 | | |
209 | | - | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
210 | 216 | | |
211 | 217 | | |
212 | 218 | | |
| |||
253 | 259 | | |
254 | 260 | | |
255 | 261 | | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
256 | 281 | | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
| 282 | + | |
| 283 | + | |
261 | 284 | | |
262 | 285 | | |
263 | 286 | | |
264 | 287 | | |
265 | 288 | | |
266 | 289 | | |
267 | | - | |
| 290 | + | |
268 | 291 | | |
269 | 292 | | |
270 | 293 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1931 | 1931 | | |
1932 | 1932 | | |
1933 | 1933 | | |
| 1934 | + | |
| 1935 | + | |
| 1936 | + | |
| 1937 | + | |
| 1938 | + | |
| 1939 | + | |
| 1940 | + | |
| 1941 | + | |
1934 | 1942 | | |
1935 | 1943 | | |
1936 | 1944 | | |
| |||
0 commit comments