Skip to content

About

Convert any .fla file to Godot Engine scene

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

🎬 Flash to Godot — Export Pipeline

A suite of optimized JSFL scripts for Adobe Animate / Flash Professional that natively exports animations, symbols, and vector shapes directly to Godot Engine 4 (.tscn files).


🚀 Features

  • Native Vector Extraction (Polygon2D): Automatically converts Flash shapes and gradients into native Godot Polygon2D nodes. Supports complex geometry with hole management (via bridges), T-junction repair, and utilizes adaptive Casteljau subdivision for perfectly smooth Bezier curves.
  • Animation Baking: Converts Flash interpolations (Tweens) and keyframes directly into native tracks for Godot's AnimationPlayer (handles position, rotation, scale, visible, and color). Also manages timeline slicing via Flash labels.
  • Masks & Shaders: Faithfully reproduces Flash clipping masks (clip_children), ColorTransform effects (color offsets), and blend modes (add, multiply) through the automatic generation of dedicated .gdshader files.
  • Group Safety: Non-destructive process that temporarily converts static groups into symbols to preserve their absolute transformation matrices.

📁 Script Project Structure

For the pipeline to work correctly, you must keep the following folder structure:

📂 flash-to-godot/
│
├── 📄 main.jsfl                  # Main script (UI entry point)
│
└── 📂 modules/
    ├── 📄 inspector.jsfl      # FLA metadata analyzer and extractor
    └── 📄 godotBuilder.jsfl   # .tscn scenes, shaders, and animations compiler

⚙️ Configuration & Installation

Prerequisites

  • Adobe Animate (or Flash Pro with JSFL API support).
  • Godot Engine 4.x (generated files target Godot 4's standard scene format 3).

Pipeline Usage

  1. Open your animation or .fla document in Adobe Animate.
  2. Go to the top menu: Commands > Run Command...
  3. Select the main.jsfl file at the root of this folder.
  4. A file explorer window will open: select the root folder of your Godot project (the folder containing the project.godot file).
  5. The script automatically calculates relative paths, creates the necessary folder tree, and processes the elements. You can monitor the real-time progress in the Output panel of Adobe Animate.

📂 Exported File Structure in Godot

Once the pipeline has finished, the following files will be generated inside your Godot project:

📂 YourGodotProject/
│
├── 📄 main.tscn                  # Reconstructed main scene (active Flash scene)
│
├── 📂 shaders/                   # CanvasItem shaders to reproduce Flash effects
│   ├── 🧪 flash_color_normal.gdshader   # ColorTransform support (Offsets)
│   ├── 🧪 flash_color_add.gdshader      # "Add" / "Screen" blend mode
│   └── 🧪 flash_color_mul.gdshader      # "Multiply" blend mode
│
└── 📂 symbols/                   # Godot sub-scenes (.tscn) generated for MovieClips
    ├── 📄 MyNestedSymbol.tscn
    └── ...

(Files named debug_data.json and debug_log.txt will also be generated at the root to assist in troubleshooting).


⚠️ Notes

  • Scripts (AS3/JS): Code is ignored; only visual properties and animations are exported.
  • Massive Vector Files: Very complex shapes or those with an enormous amount of details may temporarily freeze Animate's UI during the export.
  • Guide Layers: Guide Layers are respected and exported as hidden nodes (visible = false).

🧪 Tests Outside Adobe Animate

The tools/ folder contains Node.js scripts for testing the pipeline (inspector.jsfl + godotBuilder.jsfl) without opening Adobe Animate, starting from a debug_data.json already extracted from a real .fla. Useful before any change in modules/ to catch a regression with a plain diff instead of reopening Godot on every attempt. See tools/README.md for usage.


📜 Version History

main.jsfl — Invalid JSON escaping in jsflStringify

  • Bug: debug_data.json (generated on every export for debugging) was not strictly valid JSON per the spec (RFC 8259). The homemade jsflStringify function escaped \, ", \n and \r, but no other control character (0x00-0x1F). The actionScript field sometimes embeds AS3 code pasted as-is, which can contain real tabs or other control characters — a standard JSON parser (JSON.parse, Python json.load...) rejects the whole file at the first unescaped character it hits, even if the file "looks" fine to the eye.
  • Fixed: new jsonEscapeString function (escapes all 0x00-0x1F characters, including \t, as \uXXXX except for the short forms \n/\r/\t), used at both call sites that duplicated the old partial escaping.
  • To repair a debug_data.json already generated by the old version: node tools/repair_debug_json.js path/to/debug_data.json.

godotBuilder.jsfl

v4.23 - Runtime pre-triangulation for multi-keyframe shape tweens

  • Problem: a genuine Flash shape tween (organic vertex-level reshaping across many keyframes, e.g. kopakibini's KopaSea_2.tscn water animation -- up to 80 keyframes, ~1200 vertices each) exports as an animated Polygon2D "polygon" track. Godot re-triangulates that raw polygon on every single value change, every time the animation plays -- real, avoidable CPU cost for a shape whose keyframes are all known in advance.
  • Fixed: any Polygon2D whose fill will be track-animated across more than one keyframe (!isStaticLayer || elem._isTweenShape, the same split already used everywhere else for static-vs-animated) now also gets TweenShapeMeshConverter.gd attached. Nothing about the export itself changes -- the same ":polygon"/":uv"/":polygons"/":texture"/":color"/":visible" tracks are emitted exactly as before. At the target Godot project's own runtime, the FIRST time each scene loads, that script reads its own keyframe values straight off its AnimationPlayer, triangulates each DISTINCT shape ONCE via Godot's own Geometry2D.triangulate_polygon() (never a hand-rolled triangulator), replaces itself with a TweenShapeMesh (a plain MeshInstance2D) holding one pre-built ArrayMesh per keyframe, and adds a ":mesh_index" int track driving the swap. The original source tracks are left in place but disabled (Animation.track_set_enabled), not renamed or removed, so every instance of the same PackedScene -- Godot resource-caches each Animation sub-resource, so all instances share the same one -- can always independently re-derive the identical shapes/mesh order, with no dependency on instance load ordering.
  • An earlier version of this fix hand-ported earcut (mapbox, v2.2.4) into godotBuilder.jsfl to pre-triangulate at EXPORT time instead, storing the result as frame_positions/frame_uvs/frame_indices arrays directly in the .tscn. Reverted in favor of the runtime approach above: no algorithm to port/verify blind (Geometry2D.triangulate_polygon() is Godot's own, already-tested code), a much smaller exporter diff (one script = property line vs. new node types and per-keyframe array serialization), and a lighter .tscn (kopakibini: ~25-26MB with the export-time arrays vs. ~19.6MB, matching the pre-feature baseline, with the runtime script). Trade-off: triangulation now happens once per scene LOAD instead of once per export -- negligible for a background element instanced once, worth remembering if a shape tween is ever instanced many times on screen at once.
  • Verified via tools/pipeline_snapshot.js against bacteria_debutants/kopakibini/N/n_character: the only diff versus the pre-feature baseline is the new scripts/TweenShapeMesh.gd + scripts/TweenShapeMeshConverter.gd files and one added script = ExtResource(...) property per qualifying shape -- every existing track/geometry byte-identical. The runtime conversion itself (triangulation correctness, multi-instance idempotency, animation-track surgery) could not be verified this way and needs confirming in the actual Godot editor.

v4.22 - Two more _bridgeHoles gaps found via the same harness

  • Candidate pool too small: maxCand (nearest outer/hole vertex pairs considered) was 50 -- on dense multi-hole shapes, ALL 50 nearest candidates could fail the full check, leaving that hole uncut even with v4.21's retry-every-candidate fix. Raised to 200; verified against a real project's whole library that 200 clears every case that was purely a "candidate pool too small" problem (confirmed separately: for one remaining case, even literally every possible pair, well beyond 200, still fails -- see below).
  • The outer contour can itself already be self-intersecting, independent of any hole (a dense/complex Flash shape) -- every candidate bridge built on top of it necessarily also self-intersects, so every hole on it silently ends up uncut, and _cleanupPolygonVertices's own repair pass never sees this (it only runs on _bridgeHoles' output, and skips repair above 500 vertices regardless). Fixed by repairing the outer contour FIRST, before it's used as a bridge base.
  • Verified on N's whole library (848 hole-shapes) with the same majority-vote-of-several-guaranteed-inside-points harness (see v4.21): self-intersecting bridged results 12 -> 0, hole-not-excluded cases 26 -> 16. bacteria_debutants/kopakibini: zero diff both times (neither project has a shape that needed either fix) -- confirms both changes are pure gap-filling, no risk to already-working geometry.
  • The remaining 16 cases are a genuinely different, harder problem: for at least one of them, literally every possible outer/hole vertex pair (not just the closest N) still fails the full check -- no single straight-line bridge segment exists without crossing something. Fixing that needs a smarter algorithm (e.g. a multi-segment/visibility-graph bridge path), a bigger undertaking than a threshold or retry-loop fix and harder to verify without a Godot render; left as a known limitation rather than guessed at blind.

v4.21 - _bridgeHoles gives up on a hole too easily

  • Bug (pre-existing): when merging a hole into its outer contour, the code picked the SINGLE closest outer/hole vertex pair that passed a segment-crossing check (_bridgeBlocked), then ran one FINAL, more complete self-intersection check on the full result (covering the two eps-offset points _bridgeBlocked never sees). If that final check failed, the code gave up on the hole ENTIRELY -- no retry with the next-closest candidate -- silently leaving the hole completely uncut (rendered as if it were solid fill).
  • Found via a proper verification harness: for each hole, sample several guaranteed-inside points (a naive vertex-centroid can land OUTSIDE a concave hole; a single scanline sample can land inside the bridge's own tiny, intentional connector channel near the seam and read as a false "leak" -- multiple samples + majority vote avoid both traps) and check they stay excluded from the final bridged polygon.
  • Fixed: try candidates in proximity order until one produces a bridge that ACTUALLY passes the full self-intersection check, instead of stopping at the first one that merely passes the cheaper segment-crossing pre-check.
  • Verified on N's whole library (848 hole-shapes): the harness found the hole not excluded in 68 -> 26 cases; the fix is real, not just cosmetic (bacteria_debutants: zero diff -- no shape there hit the bug; kopakibini: 3 files changed -- decor.tscn, sol.tscn, tuboz_1.tscn -- same node count, larger polygon data in each, zero self-intersections in the new output: exactly the signature of a previously-silently-uncut hole now correctly merged in).

v4.20 - Fixed a threshold mismatch between self-intersection detection and repair

  • Bug (pre-existing): _hasSelfIntersection (decides whether a contour needs repair, both before AND -- since v4.19's added post-repair verification -- after) uses a STRICT, zero-tolerance crossing test (_segCross: d1*d2<0 && d3*d4<0). _removeSelfIntersections (the repair itself, "un-twist by reversing" a crossed loop) used a much more lenient ua/ub window (0.01-0.99), excluding any crossing within 1% of either segment's endpoint from being fixable. A near-endpoint crossing fell in the gap between the two: flagged as a real problem, never eligible for repair -- Godot's earcut then fails to triangulate the contour (an invisible/malformed Polygon2D, reported as "the color doesn't display").
  • Fixed: narrowed _removeSelfIntersections' window to 0.0001-0.9999, matching _hasSelfIntersection's strictness (true endpoint coincidences, where the "crossing" is just the shared vertex, are still excluded -- fixing those would be meaningless).
  • Verified: zero diff on bacteria_debutants/kopakibini. On N's 1054 variant scenes, a segment-intersection scan across all 17,792 generated Polygon2D found 49 self-intersecting polygons (43 files) before the fix, 35 (30 files) after -- including the exact reported case (HAT/40.tscn's shape_layer_Item_0), now clean. The remaining ~30 files need further investigation (likely a different failure mode -- possibly multiple simultaneous crossings the bounded retry from v4.19 still can't fully untangle).

v4.19 - Fixed a dangling-track bug in _demoteLine2DToNode2D

  • Bug (pre-existing, not variant-scenes-specific): when a pooled shape occurrence goes from stroke-only (its wrapper created directly as a Line2D) to also having a fill, _demoteLine2DToNode2D turns the wrapper into a Node2D and rewrites its existing :points/:width/:default_color animation track paths to target a Line_0 CHILD -- but never actually created that child node, relying on _processElementNode's own stroke-processing loop (later in the SAME keyframe) to create it. If that same keyframe has ZERO strokes of its own (gained a fill and lost its stroke in the same transition), the loop never runs, Line_0 never gets created, and the rewritten tracks dangle: Godot logs "couldn't resolve track" and silently drops them.
  • A real Flash animation timeline almost never hits this (a stroke-only shape gaining a fill usually keeps a stroke too, since keyframes evolve gradually); buildVariantScenes (v4.18) hits it constantly, since jumping between 4 unrelated orientation designs makes "fill in A, stroke-only in B" a completely normal transition.
  • Fixed: _demoteLine2DToNode2D now creates the real Line_0 child up front, carrying over whatever static Line2D properties the wrapper already had, before any track rewriting happens.
  • Verified: zero diff on bacteria_debutants/kopakibini (real animation never actually depended on the old, lazy creation). On N's 1054 variant scenes: a NodePath-vs-declared-node validator found 87 files / 1046 dangling track references before the fix, 0 after.

v4.18 - Variant scenes: pooled orientations instead of parallel subtrees

  • Reworked buildVariantScenes (v4.16): orientations (FRONT/BACK/LEFT/RIGHT/...) are no longer built as 4 separate, fully-duplicated subtrees toggled by visible -- _buildOrientationSynthSymbol instead combines them as 4 consecutive KEYFRAMES of one synthetic symbol (by layer NAME, so orientations with a different layer count/order still combine correctly), fed unmodified through _buildSceneTree. This reuses, exactly as-is, the SAME wrapper-pooling that already handles real Flash keyframe-to-keyframe animation: a shape occupying the same "slot" across orientations becomes ONE shared node whose polygon/uv/texture/visible are driven by AnimationPlayer value tracks, instead of 4 separate copies of the node. buildSceneForSymbol's existing "has frame labels" path then splits the result into a RESET + one named animation per orientation, unchanged -- gameplay still calls AnimationPlayer.play("FRONT") exactly as before.
  • Real project (N, 1054 variant scenes): 68,390 -> 36,384 nodes (-47%). A single scene sample (HAT/30.tscn, 4 orientations): 192 -> 54 nodes (-72%).
  • _sliceSymbolAtFrame (the old "one orientation = one fully separate subtree" builder) and the AnimationPlayer visibility-toggle track builder it needed are gone, replaced by _findCoveringKeyframe + _buildOrientationSynthSymbol.
  • No VisibleOnScreenEnabler2D either (same reasoning as v4.15's skipEnablerFor): a variant scene is always attached as equipment to a character, never placed standalone in a room, so the character's own subtree already handles on/off-screen culling -- an enabler here would just be a redundant node repeated on every single generated frame.

v4.17 - Cross-tree same-color merging (_mergeSameColorAcrossTree)

  • Generalizes _mergeSameColorSiblings (v4.9, same-parent + consecutive only) to the WHOLE tree: two same-texture _mergeSafe Polygon2D no longer need to be direct siblings, nor immediately consecutive under their parent, to merge. Runs LAST in _buildSceneTree (after every other tree-reshaping step), walking the tree in Godot's actual paint order (a preorder traversal, since this pipeline never writes z_index) and merging a run of same-texture candidates only if NOTHING else painted between them visually overlaps their combined bounding box — reordering two shapes nothing else touches can't change what ends up on top of what. A node whose bounds can't be computed (Sprite2D, PackedScene, ColorRect...) is treated as an opaque, always-blocking obstacle, and a merge never crosses a CanvasGroup boundary (that changes compositing semantics for its whole subtree) — conservative by construction: a missed opportunity is acceptable, a wrongly-reordered visual isn't.
  • Verified on both reference projects: only 1 file changed in each (bacteria_debutants: puits.tscn, kopakibini: tuboz_1.tscn), both a single, correct, vertex/UV-preserving merge of a same-parent-but-non-consecutive pair the old pass couldn't reach. On the N project's variant scenes (v4.16, dense overlapping decorative content): 243 fewer nodes across 1054 files (~0.45%) — much less than a naive "unique textures vs. total shapes" count would suggest, because most same-color shapes in that kind of art are legitimately separated by other, differently-colored, visually-overlapping shapes, which the safety check correctly refuses to merge across.

v4.16 - Equipment variant scenes (buildVariantScenes)

  • New, independent entry point for FLA libraries that pack multiple DESIGNS of the same piece of equipment as separate FRAMES of a handful of parallel clips (one clip per view), e.g. HAT/HAT_FRONT, HAT/HAT_BACK, HAT/HAT_LEFT, HAT/HAT_RIGHT (or SHOES/SHOES_PROFILE, SHOES/SHOES_SIT for folders using a different view set): frame N of each clip is the SAME design seen from a different angle. _findVariantGroups detects every <folder>/<prefix>_<ORIENTATION> group in the library automatically (orientation ∈ _VARIANT_ORIENTATIONS = FRONT/BACK/LEFT/RIGHT/PROFILE/SIT; no folder name to configure, and a group only needs 2+ of them present), and for every frame N common to all of a group's parts, generates one combined scene variants/<prefix>/<N>.tscn (Flash's 1-based frame numbering): one child group per orientation, plus an AnimationPlayer with one visible-toggling animation per orientation so gameplay can pick the view with AnimationPlayer.play("FRONT")/("BACK")/etc.
  • Reuses the exact same rendering pipeline as any other symbol (shapes, gradients, shaders, masks) via _buildSceneTree, which buildSceneForSymbol was split into so a symbol's node tree could be built without immediately writing it to its own standalone .tscn — needed here to embed several symbols' single-frame content as children of one hand-assembled combined scene. _computeShaderNeedsAndReferences, _computeGodotProjectRoot, _computeBoundsIndex and _buildSymbolMap were similarly extracted out of buildGodotScenes so both entry points share the same logic. Verified behavior-preserving (byte-for-byte zero diff on real projects) at every extraction step.
  • _collectSymbolDependencies: a variant part's frames can themselves instance OTHER library symbols (e.g. a decoration nested inside one hat design) — those become PackedScene ext_resources pointing at symbols/<path>.tscn, same as a normal export. Since buildVariantScenes never builds the parts themselves as standalone scenes, it walks every part's full timeline first to find every symbol transitively referenced this way, and builds a normal standalone scene for each of them via buildSceneForSymbol — otherwise Godot fails with "Cannot open file ... .tscn" on any variant scene that happens to nest one.
  • _isInlineableStaticSymbol/_inlineStaticInstances: a variant frame is always static (one frame, no animation happening within it), so any instanced symbol that is ITSELF static (single frame, no frame script, no mask/masked layer) gets its content drawn DIRECTLY into the frame instead of staying a separate PackedScene sub-scene — same matrix/colorTransform/blendMode composition formula as _preprocessTweens' auto-tween expansion, applied recursively (a decoration can itself instance another static decoration) with a cycle guard. Genuinely animated/scripted/masked nested symbols are left as real instances. Real project: dropped 3482 → 27 dependency symbols needing their own scene, and 1054 → 159 → 6 variant scenes still containing any sub-scene at all (only the ones nesting real animated content, e.g. a blinking garland or a swinging tail).
  • Runs from Node.js without Adobe Animate, straight from an already-extracted debug_data.json: node tools/build_variant_scenes.js debug_data.json <output_dir> (see tools/README.md).

v4.15 - A single VisibleOnScreenEnabler2D per subtree

  • A symbol that is NEVER placed directly by the main scene, and is ALWAYS instanced as a child of at least one other symbol, no longer gets its own VisibleOnScreenEnabler2D: the ancestor that instances it already carries one, which disables its ENTIRE subtree via process_mode (so also this symbol's AnimationPlayer) when it leaves the screen — an enabler here would be redundant. Computed once for the whole project via the referencedBy graph (symbol -> which symbols instance it), already used for shader-need propagation.
  • Safety: a symbol keeps its enabler as soon as it's placed directly by the main scene AT LEAST ONCE, even if it's also nested elsewhere (dual usage = not safe enough to remove it). Accepted limitation: if ALL of a symbol's parents turn out to be non-animated themselves (single-frame, so without their own enabler), that symbol loses its individual culling for nothing — a marginal case in practice.

v4.13 - Fixed the VisibleOnScreenEnabler2D rect

  • BUG (present even before the session that fixed it): the enabler's detection rect ignored the actually-computed bounding box size (minX/maxX/minY/maxY) and used a FIXED 20x20px box around its center instead. On a normal-sized animated symbol, this tiny rect exits and re-enters the detection field on every slight movement, which toggles the node enabled/disabled in a loop — thrashing measured in profiling as repeated spikes in "Process" time (far worse than having no enabler at all).
  • Fixed: the rect now covers the real extent (frame-0 bbox × localScaleFactor) with a generous margin (50% of the bbox size, 100px minimum) to absorb the typical movement of an animation (position/rotation/scale) that this bbox, computed only from frame 0, doesn't capture.
  • Simply removing the node outright (-1 node/animated scene) was considered instead of the fix; measured under real conditions, this makes the spikes disappear but leaves every AnimationPlayer running continuously even off-screen (lower average FPS in a room with several instances) — hence the final choice to fix the rect rather than remove the mechanism.

v4.12 - REVERTED - Promoting the scene root to a single shape

  • Attempt: _promoteRootSingleShapeChild made a symbol scene's root adopt the type and properties of its single Polygon2D/Line2D child (same principle as v4.7's "node = 1st element", applied to the root itself) to save one node.
  • Bug confirmed in production: when that shape had its own position/rotation/scale (an almost systematic case — a Flash symbol's vertices are almost always defined far from its origin point), that offset ended up on the scene's ROOT. But any scene instanced elsewhere (e.g. torche_3.tscn instancing torchemanche.tscn) declares its own position on the instance node, which ENTIRELY REPLACES the instanced root's own position (Godot doesn't add them together). The shape's internal offset thus simply vanished, shifting the element on screen.
  • Unlike v4.9 (_mergeSameColorSiblings), which bakes the merged nodes' transform directly into the vertices (so it's never exposed to an external override), v4.12 left the transform as a node property on the root — the one position exposed to an override by whatever instances it. A correct fix exists (bake the shape's transform into its polygon/uv before promotion, as v4.9 already does) but wasn't implemented; the function was removed entirely in the meantime.

v4.11 - Pre-tinting statically-colored shapes (color transform)

  • _bakeStaticShaderTints: for any shape using Flash's "Advanced Color Effect" shader (color_mult/color_offset_255) whose tint NEVER changes across the whole timeline (checked via anim.tracks, which ALWAYS contains these sub-properties since optimizeTracks never optimizes them away automatically), applies the shader's exact formula (clamp(tex*mult + offset/255, 0, 1), taken from flash_color_normal.gdshader) directly to the source Gradient's colors, and gives the node an already-tinted texture instead of the shader. The node then no longer needs a material, and becomes eligible for cross-sibling merging (_markBakeableShapes/v4.9).
  • Limited to the "normal" blend mode (flash_color_normal.gdshader): "add"/"multiply" depend on the framebuffer's content at draw time (real-time GPU blending), impossible to reproduce by pre-tinting a texture — those shapes keep their shader and stay out of merging.
  • Runs BEFORE _setupMaterials/_markBakeableShapes: once the material is removed, the node is treated like any other static Polygon2D by the rest of the pipeline (v4.9-v4.10).

v4.10 - Flattening empty single-child Node2D wrappers

  • _flattenSingleChildGroups removes purely organizational Node2D nodes (folder/layer/mask) that have exactly ONE child, no transform of their own (identity position/rotation/scale/skew), and whose ENTIRE SUBTREE is guaranteed non-animated (no AnimationPlayer track references it or any descendant). The single child moves up directly to the grandparent with no transform recalculation needed (the removed wrapper being guaranteed identity). Chains of nested wrappers (folder > layer > shape) are collapsed in a single pass.
  • Deliberately restricted to the identity-transform case only (no general rotation/scale/skew composition): the Animation sub_resources (RESET/labels) are already serialized as text with hardcoded NodePath("...") BEFORE this pass runs, so any removal had to guarantee that no existing NodePath references it — hence the restriction to 100%-static subtrees rather than a general transform merge (riskier without being able to test against a real Godot render).
  • Runs BEFORE _markBakeableShapes/_mergeSameColorSiblings: by moving shapes up one level, it can surface new cross-sibling merge opportunities (v4.9).

v4.9 - Cross-element merging of same-color static Polygon2D

  • _mergeSameColorSiblings merges, between SIBLING and CONSECUTIVE nodes in render order, the static Polygon2D (marked by _markBakeableShapes: no AnimationPlayer track targets them, nor any of their ancestors) that share the exact same texture (i.e. the same color/gradient, gradCache already deduplicating identical colors to the same SubResource). Complementary to the already-existing intra-shape merge (polyGroups/sig, v4.6), which only merged the contours of a SINGLE Flash element; this one merges across different Flash elements (e.g. two instances of the same leaf on the same layer), reducing node count as early as the .tscn (editor included).
  • Each merged node brings its own transform (position/rotation/scale/skew): the "polygon" vertices are brought back into the parent's local space before merging, the UV is never touched (already correct by construction, independent of the node's transform).
  • Safety: only merges nodes already marked as static by _markBakeableShapes (guaranteed not targeted by any AnimationPlayer track) and without their own material.

v4.7 - Reducing node count (Polygon2D/Line2D wrappers)

  • Two spots where the code created a needless Node2D wrapper even though a naming mechanism ("node itself = 1st element, Poly_0/Line_0.. = following ones") already existed but was unreachable:
    1. Stroke-only shape (0 fill, ≥1 stroke): created a Node2D + Line_0 instead of the Line2D directly as the wrapper. Fixed (with symmetric demotion back to Node2D if a later keyframe adds a 2nd stroke or a fill to the same pooled occurrence).
    2. Multi-fill shape (≥2 polygon groups, e.g. gradient + solid): a Polygon2D -> Node2D demotion was forced unconditionally as soon as polyGroups.length > 1, deadening the code that would have kept the node as Polygon2D (group 0) with Poly_0, Poly_1... as children (offset -1). Demotion removed; the cleanup loops for excess slots (excess Poly_X, anim._maxPoly) fixed to use the same offset -1. _findMaskSprite (masks) extended to recognize the wrapper itself as a visual node, not just its children.
  • Gain: -1 node per stroke-only shape, -1 node per multi-fill shape.

v4.6 - Polygon2D with holes (bridge + safety net + T-junction repair)

  • When a polyData entry (in elem.polygons) has a holes field, the builder merges outer + holes into a single PackedVector2Array via _bridgeHoles. Result: A SINGLE Polygon2D node instead of N stacked nodes (one per Flash contour). Works with both static and animated content.
  • v4.2: robust bridging algorithm for dense multi-hole cases.
  • v4.3: safety net via _hasSelfIntersection (spatial grid) as a guard rail.
  • v4.4: per-hole validation with rollback.
  • v4.6: T-junction repair (_repairTJunctions). inspector.jsfl subdivides Bézier curves independently for each contour (fill). Two adjacent fills share the same curve as their boundary, but the intermediate subdivision points differ by ~0.2 Flash units, creating visible triangular gaps between adjacent Polygon2D. The repair detects a polygon's vertices close to another polygon's edge (distance < 0.5u) and inserts them into that edge → the two polys end up sharing the exact same vertices along their common boundary. Replaces the old perpendicular-inflation mechanism (v4.5), which plugged gaps via overlap but distorted small polys.
  • Outer/hole detection happens upstream in inspector.jsfl v4 (post-processing of s.polygons). See _groupPolygonsWithHoles.
  • Automatically disabled for shape tweens (topology changes between frames -> would break PackedVector2Array interpolation).

About

Convert any .fla file to Godot Engine scene

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages