| … | … | @@ -10,8 +10,12 @@ Two systems in one repo, and it matters which one you are in: |
| 10 | 10 | identically in-game. Lives in `blender_addon/geometry_deserializer.py` and |
| 11 | 11 | `test/`. **This is the active work** and has its own section below. |
| 12 | 12 | |
| 13 | | **Verified 2026-09-04** against v0.58.0, Blender 5.2.0, Godot 4.7.beta custom. |
| 14 | | Everything dated here was measured on that day; anything older is marked. |
| 13 | **Verified 2026-09-05** against v0.80.0, Blender 5.2.0 (`fbe6228777e7`), Godot |
| 14 | 4.7.beta custom `3bb2c0245`. Every number below was produced by RUNNING the |
| 15 | thing on that day — the audits, all 54 test files, the terrain end-to-end — and |
| 16 | anything older is marked. The previous revision of this file quoted a socket |
| 17 | floor it had not measured, and that sentence became the argument for a wrong |
| 18 | claim to the user (hyper-games #63 Part 3). Re-run, do not quote. |
| 15 | 19 | |
| 16 | 20 | ## MANDATORY: Version Bump on EVERY Change |
| 17 | 21 | |
| … | … | @@ -21,8 +25,9 @@ Everything dated here was measured on that day; anything older is marked. |
| 21 | 25 | 3. Rebuild C++ AFTER the bump — **all three targets**, see Build |
| 22 | 26 | 4. .so is **symlinked** to hyper-tps (no copy needed) |
| 23 | 27 | |
| 24 | | **Current version: 0.61.0** (branch: `sdf-volume-bridge`, 26 commits unpushed |
| 25 | | to `bare` as of 2026-09-04) |
| 28 | **Current version: 0.80.0** (branch: `sdf-volume-bridge`, HEAD `d0efc30`, |
| 29 | **49 commits unpushed** to `bare` as of 2026-09-05; proc-nodes carries 38 of its |
| 30 | own on `heightmap-erosion-toggle`, HEAD `be03703`) |
| 26 | 31 | |
| 27 | 32 | ## Where things are |
| 28 | 33 | |
| … | … | @@ -243,6 +248,37 @@ point domain 3 verts / 0 tris, face domain 6 verts / 4 tris. |
| 243 | 248 | Consequence: `Position`, `Normal` and `NamedAttribute` no longer take a Geometry |
| 244 | 249 | input, matching Blender. |
| 245 | 250 | |
| 251 | ## What v0.62.0 -> v0.80.0 closed (hyper-games #62, #63) |
| 252 | |
| 253 | Measured 2026-09-05, not quoted: |
| 254 | |
| 255 | - **The ledger is 5 of 101 mapped types**, down from 52. All five are mesh |
| 256 | primitives (`audit_missing_sockets.py` prints them). ⚠ #63 says "all of them |
| 257 | UV Map outputs" — the audit says otherwise: `MeshCylinder` is `1 < 5`, missing |
| 258 | **Top, Side, Bottom** as well as UV Map. The other four are UV Map alone. |
| 259 | - **UNRESOLVED is 0.** The five remaining unmatched proc sockets are GODOT-ONLY |
| 260 | and deliberate: kept AFTER Blender's last socket so they cannot displace a |
| 261 | real one, and unreachable by any Blender index. |
| 262 | - **A multi-input socket is a KIND, not a workaround.** `SocketDef.multi` |
| 263 | (`proc-nodes/src/graph/proc_node.h`). Join Geometry's "Geometry A"/"Geometry B" |
| 264 | pair is gone. It took TWO fixes — the kind, and the serializer, which emitted |
| 265 | one connection per input socket and followed `links[0]`, so the extra links |
| 266 | were lost before proc-nodes ever saw them. |
| 267 | - **Item lists cross.** Capture Attribute has one, the repeat and simulation |
| 268 | zones one each on their OUTPUT node, Foreach has three (`input_items`, |
| 269 | `main_items`, `generation_items`). Two vocabularies, NOT interchangeable: an |
| 270 | item reports `data_type` as a CD_PROP code (FLOAT_VECTOR 48) while |
| 271 | `capture_items.new()` takes a socket-type STRING ('VECTOR'). |
| 272 | - **The edge and corner domains are real**, and `face_offsets` is load-bearing: |
| 273 | `ProcMesh::compute_edges()`, `has_ngons()`. Only `MeshGrid` emits quads so far. |
| 274 | - **A per-face field has a producer**: Extrude Mesh writes Top/Side on |
| 275 | `AttrDomain::Face`. A `Selection` input now exists on **24** node types |
| 276 | (measured from the socket dump), where the old note here listed six that |
| 277 | lacked one. |
| 278 | - **Ramp and curve DATA cross both ways**, per element: Colour Ramp 298/298 |
| 279 | (worst 4.718e-16), Float Curve 121/121 (4.885e-15), RGB Curves 17/17 |
| 280 | (4.441e-15), plus 26/32/5 on the three crossing suites. |
| 281 | |
| 246 | 282 | ## Traps recorded here so they are not rediscovered |
| 247 | 283 | |
| 248 | 284 | - **`SocketValue::to_float()` takes `vec.x`** from a non-field scalar, so a |
| … | … | @@ -257,7 +293,24 @@ input, matching Blender. |
| 257 | 293 | ate hyper-tps' 30-line Jolt experiment block twice. `hyper-tps/scripts/dev-editor.sh` |
| 258 | 294 | now restores it when the editor changed no setting. |
| 259 | 295 | - **A build guard that compares mtimes is unsound** — scons keys on content, so |
| 260 | | it cries wolf on a clean tree. |
| 296 | it cries wolf on a clean tree. Same shape: `git checkout --` on an UNTRACKED |
| 297 | file restores nothing and `git diff --quiet` is vacuous on one, so a mutation |
| 298 | sweep guarded that way reports "unchanged" for every mutation and skips every |
| 299 | build. Guard on relink time as well as file change. |
| 300 | - **On a Blender socket, a `default_value` is a UI affordance until proven |
| 301 | otherwise.** Implicit-field sockets IGNORE it and Blender substitutes the |
| 302 | implicit field: texture `Vector`, Scale Elements' `Center`, Random Value's |
| 303 | `ID`, Extrude Mesh's `Offset` (unconnected it is the NORMAL — setting the |
| 304 | default to (1,0,0) changes nothing, measured), Store Named Attribute's and |
| 305 | foreach's `Selection`. A harness that sweeps such a socket through its default |
| 306 | feeds Blender a number it never reads, and the NODE then looks wrong. Ask |
| 307 | `is_input_connected`. This trap has now cost time in #62 and three more times |
| 308 | in #63. |
| 309 | - **`__extend__` is not a data socket.** It is Blender's affordance for growing |
| 310 | an item list and carries nothing. Counting it broke the socket audit AND the |
| 311 | serialized socket indices — the Foreach OUTPUT node has one in the MIDDLE, |
| 312 | between `main_items` and `generation_items`, so every index after it was one |
| 313 | too high. |
| 261 | 314 | - `graphs/terrain.tres` in hyper-tps has been **broken since 2026-03-27**: node 3 |
| 262 | 315 | is `null` while `connections` still references it, so it evaluates to no mesh. |
| 263 | 316 | It took SlopeGradient's Slope into socket 2 — the same mis-wiring as the test |
| … | … | @@ -265,8 +318,11 @@ input, matching Blender. |
| 265 | 318 | |
| 266 | 319 | ## Post-mortems |
| 267 | 320 | |
| 268 | | `hyper-games` #60 (and its comment) is the full account, in the #41/#44/#45/#53/ |
| 269 | | #55–#59 series. Read it before resuming parity work. |
| 321 | The series is `hyper-games` #41/#44/#45/#53/#55–#63, newest first. **#63 and |
| 322 | #62 are the two to read before resuming parity work**; #60 is the older full |
| 323 | account. #63's own NEXT list is the queue: the GLSL debt (untouched), the five |
| 324 | UV Map outputs, quads for the other six primitives, Merge By Distance Connected |
| 325 | + Edge Paths to Curves, and growing the map past 101/360. |
| 270 | 326 | |
| 271 | 327 | --- |
| 272 | 328 | |
| … | … | @@ -641,7 +697,21 @@ reach fingertips (Z already aligns with chain). Works for all leaf bones (breast |
| 641 | 697 | now a SYMLINK to the source, so there is nothing to copy — but the restart is |
| 642 | 698 | still required. |
| 643 | 699 | - **The C++ test suites do not build** — source only, no SConstruct targets |
| 644 | | - No known numerical gap in the terrain path as of v0.61.0 |
| 700 | - **`test/test_blender_godot_bridge.py` is RED and flaky** — the material-graph |
| 701 | integration suite, the SCENE-SYNC half, not parity. Measured over three runs |
| 702 | 2026-09-05: **6/3, 6/3, 8/1**. The failures are Godot-side aborts (`signal 11` |
| 703 | in one run, `rc=-6` in another), so WHICH tests fail moves between runs; only |
| 704 | `output_defaults applied` failed all three, with `errs=[]` — the headless |
| 705 | subprocess printed neither `OK` nor `ERR:`. Not a timeout: the project starts |
| 706 | headless in **1.4 s** against the suite's 30 s budget. ⚠ It hardcodes |
| 707 | `GODOT_BIN = "godot4"`, which on PATH is **stock 4.6.stable**, while every |
| 708 | other cross-engine test uses the 4.7.beta custom build; forcing 4.7 with a |
| 709 | PATH shim changed which tests failed, not the count. Undiagnosed. |
| 710 | - **`test/test_ptex_converter.py` SKIPS** — it imports `pytest`, which Blender's |
| 711 | bundled Python does not have. It is not passing; it is not running. |
| 712 | - No known numerical gap in the terrain path as of v0.80.0: measured |
| 713 | 2026-09-05, correlation **1.000000**, RMS **1.967e-07**, median residual |
| 714 | **3.553e-15** over 576 vertices, 0 vertices over 1% of the height span. |
| 645 | 715 | |
| 646 | 716 | ## Testing |
| 647 | 717 | |
| … | … | @@ -664,9 +734,24 @@ $BL --background --factory-startup --python test/test_rotation_matrix_values.py |
| 664 | 734 | $BL --background --factory-startup --python test/test_phacelle_parity.py # 7/7, 9.537e-07 |
| 665 | 735 | $BL --background --factory-startup --python test/test_erosion_filter_parity.py # 6/6, 2.980e-08 |
| 666 | 736 | $BL --background --factory-startup --python test/test_rotation_matrix_parity.py # 17/17 |
| 737 | |
| 738 | # the whole thing — every file runs under Blender, ~20 min |
| 739 | for f in test/test_*.py; do $BL --background --factory-startup --python $f; done |
| 667 | 740 | ``` |
| 668 | 741 | |
| 669 | | Counts and deltas above were **measured 2026-09-04**, all green. |
| 742 | **Measured 2026-09-05 by running all 54 `test_*.py`:** |
| 743 | |
| 744 | | | | |
| 745 | |---|---| |
| 746 | | files that print a `N/N passed` line | **46 — 1138 assertions, 0 failed** | |
| 747 | | biggest suites | color_ramp 298, float_curve 121, math_ops 71, random_value 62, map_range 49, material_serializer 41, node_primitives 40 | |
| 748 | | files with their own dialect | 8 — see below | |
| 749 | |
| 750 | The 8: `erosion_noise_parity` **PASS** (identity orientation, corr 1.000000); |
| 751 | `importer_coverage` *all 101 types build*; `python_shm` 11; `tile_baker` 26; |
| 752 | `cross_language` all passed; `bench_tile_baker` is a benchmark and asserts |
| 753 | nothing; `ptex_converter` **SKIPS** (no pytest); `blender_godot_bridge` |
| 754 | **FAILS** — both under Known Issues. |
| 670 | 755 | |
| 671 | 756 | The C++ suites (`test_shm_protocol.cpp`, `test_uds.cpp`, `test_page_table.cpp`, |
| 672 | 757 | `test_tile_cache.cpp`, `test_tile_requests.cpp`, `test_validation.cpp`, |
| … | … | @@ -687,20 +772,38 @@ $GODOT --headless --path ../proc-nodes/demo --script res://socket_dump.gd -- /tm |
| 687 | 772 | PROC_SOCKETS=/tmp/ps.json $BL --background --factory-startup --python test/audit_socket_names.py |
| 688 | 773 | ``` |
| 689 | 774 | |
| 690 | | Measured 2026-09-04, Blender 5.2.0: |
| 775 | There is a third audit, the reverse of the second — which of BLENDER'S sockets |
| 776 | have nowhere to land on our node: |
| 777 | |
| 778 | ```bash |
| 779 | PROC_SOCKETS=/tmp/ps.json $BL --background --factory-startup --python test/audit_missing_sockets.py |
| 780 | ``` |
| 781 | |
| 782 | Measured 2026-09-05, Blender 5.2.0, proc-nodes `be03703`: |
| 691 | 783 | |
| 692 | 784 | | | | |
| 693 | 785 | |---|---| |
| 694 | 786 | | registered `bpy.types.Node` classes | 571 | |
| 695 | 787 | | **instantiable in a GeometryNodeTree** | **360** — the honest universe | |
| 696 | | | `GEOMETRY_NODE_MAP` entries | 98 (27.2%), 0 dead | |
| 697 | | | missing | 262 — other 79, curve 39, point/volume 35, mesh 35, input 27, string 13, field/utility 10, instance 7, attribute 6, texture 6, matrix/rotation 5 | |
| 698 | | | sockets by NAME | 174 (81.7%) | |
| 699 | | | sockets by INDEX (fallback) | 33 (15.5%) | |
| 700 | | | **UNRESOLVED** (dropped at runtime) | **6** — `CaptureAttribute:Value`, `CurveToMesh:Resolution`, `DistributePointsOnFaces:Count`, `JoinGeometry:Geometry B`, `RepeatZoneInput:Geometry`, `SimulationZoneInput:Geometry` | |
| 701 | | |
| 702 | | The last two are `CUSTOM` multi-sockets and may not be representable, so the |
| 703 | | realistic floor is 2, not 0. |
| 788 | | offered in the add menu (>=) | 336 | |
| 789 | | `GEOMETRY_NODE_MAP` entries | **101** (28.1%), 0 dead | |
| 790 | | missing | 259 — other 77, curve 39, point/volume 35, mesh 35, input 26, string 13, field/utility 10, instance 7, attribute 6, texture 6, matrix/rotation 5 | |
| 791 | | sockets by NAME | **279 (90.3%)** | |
| 792 | | sockets by INDEX (fallback, type agrees) | 25 (8.1%) | |
| 793 | | GODOT-ONLY (past Blender's last socket) | 5 (1.6%) — `CurveToMesh:Resolution,Closed`, `DistributePointsOnFaces:Count`, `RepeatZoneInput:Geometry`, `SimulationZoneInput:Geometry` | |
| 794 | | **UNRESOLVED** (dropped at runtime) | **0** | |
| 795 | | missing-socket ledger | **5 of 101 types** — `MeshCube`, `MeshGrid`, `MeshIcoSphere`, `MeshUVSphere` each missing `UV Map`; `MeshCylinder` `1 < 5`, also missing `Top`, `Side`, `Bottom` | |
| 796 | |
| 797 | ⛔ **The old text here said the two zone `CUSTOM` sockets "may not be |
| 798 | representable, so the realistic floor is 2, not 0".** That was never measured |
| 799 | and it is wrong twice over. It became the argument for telling the user that |
| 800 | multi-inputs and item lists could not be represented (#63 Part 3) — geometry |
| 801 | nodes is the rosetta stone; everything is representable, as a nested group or at |
| 802 | the bridge. And the instrument was wrong three ways: it counted `__extend__` as |
| 803 | a data socket, its scratch tree had NO GROUP INTERFACE so Group Input/Output |
| 804 | matched against nothing, and it reported deliberate Godot-only sockets as |
| 805 | "dropped at runtime" when nothing addresses them. **Fix the measurement first; |
| 806 | the number only means something afterwards.** |
| 704 | 807 | |
| 705 | 808 | **Do not filter Blender's sockets on `enabled`.** Blender hides sockets by MODE |
| 706 | 809 | — Noise Texture's `W` only in 4D, `Offset`/`Gain` only for multifractal types — |
| … | … | @@ -727,3 +830,12 @@ number the tool got wrong. |
| 727 | 830 | exactly, **GradientNoise parity 1.000000** |
| 728 | 831 | - 2026-09-04 (unversioned): coverage/socket audits, rotation-matrix parity |
| 729 | 832 | across 18 nodes, and the erosion re-port down to `erosion_filter` |
| 833 | - v0.62.0-v0.68.0 (#62): the bridged mountain crosses exactly (2582018 verts, |
| 834 | same bounds); nine node families ported from Blender's own source at 61a36e08 |
| 835 | (hash, Voronoi, Noise Texture, Gradient Texture, Map Range, Math + Vector |
| 836 | Math, Mix, Wave + Vector Rotate, mesh primitives); the graph FRAME marked at |
| 837 | the bridge instead of converting values; ledger 52 -> 17 |
| 838 | - v0.68.0-v0.80.0 (#63): ramp and curve DATA both directions; the edge domain; |
| 839 | quads; multi-input as a socket kind; item lists and zone pairing; the |
| 840 | attribute family typed for all nine `data_type`s; ledger 17 -> 5, UNRESOLVED |
| 841 | 6 -> 0, socket-by-name 81.7% -> 90.3% |