threedim: new 3D asset, scene-graph and object processes (from #2109 stack) - #2122
Open
jcelerier wants to merge 12 commits into
Open
threedim: new 3D asset, scene-graph and object processes (from #2109 stack)#2122jcelerier wants to merge 12 commits into
jcelerier wants to merge 12 commits into
Conversation
jcelerier
force-pushed
the
plane/interop
branch
2 times, most recently
from
July 17, 2026 14:10
07a445e to
bc681f8
Compare
jcelerier
added a commit
that referenced
this pull request
Jul 17, 2026
FIX-GLTF (GltfParser.cpp): validate every accessor's byte range against its bufferView/buffer BEFORE extract_primitive dereferences it — a crafted glTF with an out-of-range accessor offset/count read past the buffer (heap OOB). Adds accessor_within_bounds / range_within_view / buffer_source_byte_size guards + an all-accessors sweep in validate(). Test: test_regression_splat_reload (Splat ports stable across save/reload). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
jcelerier
added a commit
that referenced
this pull request
Jul 17, 2026
FIX-GLTF (GltfParser.cpp): validate every accessor's byte range against its bufferView/buffer BEFORE extract_primitive dereferences it — a crafted glTF with an out-of-range accessor offset/count read past the buffer (heap OOB). Adds accessor_within_bounds / range_within_view / buffer_source_byte_size guards + an all-accessors sweep in validate(). Test: test_regression_splat_reload (Splat ports stable across save/reload). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
jcelerier
added a commit
that referenced
this pull request
Jul 17, 2026
FIX-GLTF (GltfParser.cpp): validate every accessor's byte range against its bufferView/buffer BEFORE extract_primitive dereferences it — a crafted glTF with an out-of-range accessor offset/count read past the buffer (heap OOB). Adds accessor_within_bounds / range_within_view / buffer_source_byte_size guards + an all-accessors sweep in validate(). Test: test_regression_splat_reload (Splat ports stable across save/reload). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
jcelerier
added a commit
that referenced
this pull request
Jul 17, 2026
test_unit_image_loader (22 cases / 881 assertions): score::gfx
decodeImageFrom{Path,Memory} (gfx TextureLoader) + Threedim LDR ImageLoader +
ArrayToTexture, across PNG8/16, JPEG, BMP, TIFF8/16, WebP, GIF, TGA, PPM/PGM,
SVG, ICO — exact dimensions, exact/toleranced pixels, RGBA byte order, straight
alpha, top-left origin, 16-bit precision, and graceful rejection of truncated /
garbage / empty / wrong-MIME inputs (ASAN-clean).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
jcelerier
force-pushed
the
planf/objects
branch
2 times, most recently
from
July 18, 2026 13:37
7c5c25d to
f79c9cc
Compare
jcelerier
force-pushed
the
planf/objects
branch
2 times, most recently
from
July 18, 2026 20:38
3fe10b1 to
5f1b3e3
Compare
jcelerier
force-pushed
the
planf/objects
branch
2 times, most recently
from
July 19, 2026 21:59
582ebb1 to
df39bc1
Compare
jcelerier
force-pushed
the
plane/interop
branch
2 times, most recently
from
July 20, 2026 23:09
76333c0 to
ca08bb3
Compare
jcelerier
force-pushed
the
planf/objects
branch
2 times, most recently
from
July 21, 2026 06:13
d81056a to
d8d137a
Compare
jcelerier
force-pushed
the
plane/interop
branch
from
August 11, 2026 23:48
cfe9c11 to
b68d334
Compare
jcelerier
force-pushed
the
planf/objects
branch
from
August 11, 2026 23:48
30d7f59 to
3049c13
Compare
jcelerier
force-pushed
the
plane/interop
branch
2 times, most recently
from
August 15, 2026 13:17
6b18a07 to
0a07aa0
Compare
jcelerier
force-pushed
the
planf/objects
branch
4 times, most recently
from
August 15, 2026 22:20
29bae02 to
ddb1944
Compare
jcelerier
force-pushed
the
plane/interop
branch
2 times, most recently
from
August 16, 2026 03:43
4065f27 to
6b30420
Compare
jcelerier
force-pushed
the
planf/objects
branch
from
August 16, 2026 03:43
ddb1944 to
5aaaa91
Compare
jcelerier
force-pushed
the
plane/interop
branch
from
August 16, 2026 13:56
6b30420 to
a110d4a
Compare
jcelerier
force-pushed
the
planf/objects
branch
from
August 16, 2026 13:56
5aaaa91 to
2d4ba38
Compare
jcelerier
force-pushed
the
plane/interop
branch
from
August 16, 2026 15:29
a110d4a to
38fc230
Compare
jcelerier
force-pushed
the
planf/objects
branch
from
August 16, 2026 15:29
2d4ba38 to
f9d4745
Compare
jcelerier
force-pushed
the
plane/interop
branch
from
August 16, 2026 16:33
38fc230 to
6ef9a7f
Compare
(cherry picked from commit a012d1cbdc702366d16093fda112fb3bba4a78dd)
fastgltf's configure step downloads the simdjson single-header when no simdjson::simdjson target or system package exists; network-isolated builds (Flatpak, Nix) fail there. Vendor the pinned single-header copy (v3.12.3, what fastgltf would download) so the build is self-contained. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
Adds the new 3D processes that build on the scene pipeline: geometry and asset loaders (glTF/FBX via fastgltf/ufbx, image and buffer helpers), cameras, lights and transforms, the scene-graph family (preprocessor, filters, switch/selector/group, duplicator, from-meshes, inspector), buffer/texture inject and extract, PBR materials and instancing, animation with humanoid retargeting and inverse kinematics, and text-to-mesh/texture. These are cross-coupled (they share loader/scene headers), so they land as one coherent unit. The existing ModelDisplay, RenderPipeline and Splat nodes keep their current scene-aware versions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
Brings the new 3D objects up to their final state, folding in the bug fixes made after they were first added: Instancer GPU use-after-free and instance-count bounds, OBJ and glTF index bounds-checks, GPU-resource release on partial-init failure, Camera stale transform-slot, extraction buffer sizing and SRB layout, cubemap face sizing and UBO slots, the spherical and triplanar projection shader fixes, animation speed and skinned-mesh deformation, and the review-round corrections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
ObjectGallery --list [filter] : print every registered process (name,
uuid), optionally filtered by name.
ObjectGallery --filter <substr> : open a window, instantiate each matching
[--seconds N] object, route its texture output to the
window and play, so all the texture-
producing objects can be watched at once.
Built on the MinimalGUIApplication + window-device path the gfx testers use,
so it drives the real engine. A handy way to try the whole object catalogue.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
GltfParser.cpp: validate every accessor's byte range against its bufferView/buffer BEFORE extract_primitive dereferences it — a crafted glTF with an out-of-range accessor offset/count read past the buffer (heap OOB). Adds accessor_within_bounds / range_within_view / buffer_source_byte_size guards + an all-accessors sweep in validate(). Test: test_regression_splat_reload (Splat ports stable across save/reload). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
test_unit_image_loader (22 cases / 881 assertions): score::gfx
decodeImageFrom{Path,Memory} (gfx TextureLoader) + Threedim LDR ImageLoader +
ArrayToTexture, across PNG8/16, JPEG, BMP, TIFF8/16, WebP, GIF, TGA, PPM/PGM,
SVG, ICO — exact dimensions, exact/toleranced pixels, RGBA byte order, straight
alpha, top-left origin, 16-bit precision, and graceful rejection of truncated /
garbage / empty / wrong-MIME inputs (ASAN-clean).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
Deduplicates the raw QImage handling and picks up decodeImageFromMemory's RGBA8888 canonicalization and footer-less TGA fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
On glibc < 2.40 there is no _Float16, so the R16F/RGBA16F case compiled to nothing and the texture was uploaded with uninitialized bytes. Convert with a software IEEE 754 binary16 (round-to-nearest-even) fallback instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
12 Catch2 suites pinning the fixes made while building out the 3D processes: animation speed and skinned-mesh deformation, camera state release, cubemap face clamping, compute-extraction SRB layout, extraction index and buffer lifetime, HDR extension handling, instancer clamping and stale buffers, OBJ parse crashes and shader string generation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
Fulldome was one hardcoded mapping. A dome is not one projection: which r = f(theta) a venue wants depends on what the content is for, and the four classical fisheye laws are not interchangeable. equidistant r = theta / (fov/2) uniform angular resolution equisolid r = sin(theta/2) / sin(fov/4) equal-area, what a fisheye lens does stereographic r = tan(theta/2) / tan(fov/4) conformal, the "little planet" look orthographic r = sin(theta) / sin(fov/2) parallel-projection sphere Each denominator is f evaluated at the half-FOV, so theta = fov/2 lands on r = 1 and the image circle meets the frame wherever the user put the FOV. The two half-angle families reduce to fov/4 for that reason and not by accident -- it is the same normalisation, not a different one. Separate shader snippets rather than a uniform the shader branches on: the projection is fixed for a whole frame, so branching would cost every vertex what selecting a program costs once. Equidistant is unchanged from the previous behaviour bit-for-bit -- its half_fov_rad is the old proj_ratio spelled differently -- so existing domes keep the picture they had. Orthographic is only meaningful to 180 degrees: sin(theta) folds past the pole, so beyond that the rear hemisphere mirrors onto the front. Left to the user like every other framing decision here, and said so in the comment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
The shaders were reachable only by writing an integer to the Camera inlet from OSC or an automation, which is not a way anyone discovers a projection. All four are baked for every draw regardless, so they cost nothing extra to list. Indices follow the projections[] order in the renderer. 1 stays equidistant so documents written when it was the only dome mode keep the picture they had. "1-pass" stopped telling the user anything once the other three arrived -- they are all single-pass -- so the entry now names the projection instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The first cluster of new 3D objects from
split/threedim, on top of the interop stack (#2121). These processes are cross-coupled (they share loader/scene headers, so intermediate subsets don't build) and therefore land as one coherent unit — the same way the scene rework did.Stacked on #2121; retargets to master as the stack merges.
What it adds
fastgltfandufbxsubmodules), unifiedAssetLoader, image/buffer/texture helpers,ObjLoader→GeometryLoaderrename.Camera,CameraArray,CameraSwitch,Light,Transform3D.PBRMesh,MaterialOverride,ConfigurePrimitive,Instancer, shadow-cascade setup.AnimationPlayer, humanoid retargeting, inverse kinematics.The existing
ModelDisplay,RenderPipelineandSplatnodes keep their current scene-aware versions (the oldSplat/node is intentionally preserved). The point-cloudPrimitiveCloudfamily lands in a follow-up PR (it needs the spz/zstd submodules).Validation
ctest: 17/17.🤖 Generated with Claude Code