Skip to content

Fix F3DEX3 binary export of triangles and light counts - #739

Merged
Dragorn421 merged 1 commit into
Fast-64:mainfrom
Taldariner:fix-f3dex3-binary-tris
Sep 22, 2026
Merged

Dragorn421 merged 1 commit into
Fast-64:mainfrom
Taldariner:fix-f3dex3-binary-tris

Conversation

@Taldariner

Copy link
Copy Markdown
Contributor

Problem

Exporting a display list as Binary or Insertable Binary with F3DEX3 selected fails before anything is written:

ValueError: invalid literal for int() with base 10: 'NUMLIGHTS_1NUMLIGHTS_1NUMLIGHTS_1...'

and, once that is out of the way:

PluginError: SP2Triangles not available in Fast3D.

Both reproduce without a scene:

blender -b --python-expr "import addon_utils; addon_utils.enable('fast64'); from fast64.fast64_internal.f3d.f3d_gbi import F3D; print(repr(F3D('F3DEX3').NUML('NUMLIGHTS_1')))"
blender -b --python-expr "import addon_utils; addon_utils.enable('fast64'); from fast64.fast64_internal.f3d.f3d_gbi import F3D, SP2Triangles; SP2Triangles(0,1,2,0,2,1,0,0).to_binary(F3D('F3DEX3'), [])"

The first prints the macro name repeated 16 times instead of a number, the second raises.

Cause

Two unrelated slips, both specific to F3DEX3.

F3D.NUML returns n * 0x10 for F3DEX3 before n has been looked up in self.numLights, so it multiplies the macro name string instead of the light count.

Three binary encoders test F3DEX_GBI or F3DLP_GBI, i.e. "is this F3DEX1", where what is meant is "is this any F3DEX". F3DEX3 deliberately leaves F3DEX_GBI false — the comment in F3D.__init__ spells it out: F3DEX2 is F3DEX1 and F3DEX3 is F3DEX2, but F3DEX3 is not F3DEX1 — so it fell through to the original F3D encodings:

  • _gsSP1Triangle_w1f scaled vertex indices by 10 instead of 2, and wrote the flag as its own byte. From F3DEX2 on, that byte lands on the command byte: a triangle with flag 2 turned G_TRI1 (0x05) into 0x07.
  • SP2Triangles raised, which is why no F3DEX3 binary export ever completed.
  • SPCullDisplayList used the F3D vertex scaling.

Fix

NUML looks the count up first. The three encoders test not f3d.F3D_OLD_GBI, which is true for every F3DEX generation and false for original F3D, so F3D keeps its own encodings.

Verification

Blender 4.2.16 LTS.

  • Encoded SP1Triangle and SP2Triangles over 34 index/flag combinations for F3D, F3DEX/LX, F3DLX.Rej, F3DEX2/LX2 and F3DEX3, then decoded the words back. Every case now round-trips its indices, its flag and its command byte. Of 62 encodings, the 14 that change are all F3DEX3; F3D, F3DEX1 and F3DEX2 come out byte identical.
  • Insertable binary export of a test mesh: F3DEX3 went from raising to producing a display list whose 8 two-triangle commands all index inside the 56 entry vertex buffer, with correctly scaled indices. F3D and F3DEX2 exports are byte identical before and after.
  • C export of a 22 mesh corpus for F3D, F3DEX2 and F3DEX3 is unchanged.

I can check the encoding and the structure of the output, not that a console draws it, so this is "F3DEX3 binary export now writes what the GBI specifies" rather than "F3DEX3 binary export is fully supported".

…ounts

Exporting a display list as binary or insertable binary with F3DEX3 selected fails
before writing anything:

    ValueError: invalid literal for int() with base 10: 'NUMLIGHTS_1NUMLIGHTS_1...'

and, past that, `PluginError: SP2Triangles not available in Fast3D.`

Two separate causes, both specific to F3DEX3:

`F3D.NUML` multiplied the macro name by 0x10 instead of the light count it looks up,
because the F3DEX3 branch runs before the lookup.

Three binary encoders test `F3DEX_GBI or F3DLP_GBI`, that is "is this F3DEX1", where
what they mean is "is this any F3DEX". F3DEX3 deliberately leaves `F3DEX_GBI` false
(see the comment in `F3D.__init__`: F3DEX3 is F3DEX2, but F3DEX3 is not F3DEX1), so
it fell through to the original F3D encodings:

* `_gsSP1Triangle_w1f` scaled vertex indices by 10 instead of 2 and wrote flag as a
  separate byte. In F3DEX2 and later that byte lands on the command byte, so a
  triangle with flag 2 turned G_TRI1 (0x05) into 0x07.
* `SP2Triangles` raised outright, which is why no F3DEX3 binary export completed.
* `SPCullDisplayList` used the F3D vertex scaling.

Verification, Blender 4.2.16 LTS:

* encoding SP1Triangle and SP2Triangles over 34 index/flag combinations for F3D,
  F3DEX/LX, F3DLX.Rej, F3DEX2/LX2 and F3DEX3, then decoding the words back: every
  case now round-trips its indices, its flag and its command byte. Of 62 encodings,
  the 14 that change are all F3DEX3; F3D, F3DEX1 and F3DEX2 are byte identical.
* insertable binary export of a test mesh: F3DEX3 went from raising to producing a
  display list whose 8 two-triangle commands index inside the 56 vertex buffer.
  F3D and F3DEX2 exports are byte identical before and after.
* C export of a 22 mesh corpus for F3D, F3DEX2 and F3DEX3 is unchanged.
@Dragorn421 Dragorn421 added the merge soon Will be merged in a few days at most if nothing else comes up label Sep 20, 2026
@Dragorn421
Dragorn421 merged commit ba1249f into Fast-64:main Sep 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge soon Will be merged in a few days at most if nothing else comes up

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants