OBJFileLoader: Fix AssetContainer handling and add opt-in texture loading waits - #18895
noname0310 wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
It introduces incorrect _blockEntityCollection restoration in new/modified code paths, which can break scene collection blocking state and cause entities to register into the wrong collections.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the OBJ/MTL loading pipeline to improve AssetContainer ownership/collection correctness (meshes/geometries/materials/textures) and adds an opt-in waitForTextures loading mode so callers can await referenced texture loads before completing an OBJ load.
Changes:
- Include created
Geometryinstances inOBJFileLoader.importMeshAsyncresults and propagate them intoAssetContainer.geometries. - Add
OBJLoadingOptions.waitForTextures(defaultfalse) and implement optional awaiting of referenced texture loads, including support for delayed textures. - Improve material/texture handling (skip unused MTL materials, reuse textures for line material cloning, and preserve container ownership).
File summaries
| File | Description |
|---|---|
| packages/dev/loaders/test/unit/OBJ/assetContainer.test.ts | Adds unit tests covering container ownership, geometry returns, material/texture reuse, and optional texture-waiting behavior. |
| packages/dev/loaders/src/OBJ/solidParser.ts | Adjusts geometry/container ownership behavior and blocks scene collection during container loads. |
| packages/dev/loaders/src/OBJ/objLoadingOptions.ts | Adds the waitForTextures?: boolean option to OBJ loading options. |
| packages/dev/loaders/src/OBJ/objFileLoader.pure.ts | Returns geometries from importMeshAsync, pushes them into AssetContainer, and optionally awaits texture load promises from MTL parsing. |
| packages/dev/loaders/src/OBJ/mtlFileLoader.ts | Extends parseMTL to optionally track/await texture loading, filter loaded materials, and honor per-load invertTextureY. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (materialNames && !materialNames.has(value)) { | ||
| material = null; | ||
| continue; | ||
| } |
| scene._blockEntityCollection = !!assetContainer; | ||
| newMaterial = new StandardMaterial(Geometry.RandomId(), scene); | ||
| newMaterial._parentContainer = assetContainer; | ||
| scene._blockEntityCollection = false; |
There was a problem hiding this comment.
I'm tired of seeing these pointless AI reviews...
There was a problem hiding this comment.
this is actually valid. In SolidParser lines 924–927, _blockEntityCollection is unconditionally reset to false. This is only safe if its previous value was false and material construction cannot throw - neither is guaranteed. The similar glTF implementation means glTF has the same pre-existing weakness; it does not justify introducing it in new OBJ code. The new line-material path at objFileLoader.pure.ts lines 397–401 also restores on success but lacks try/finally.
RaananW
left a comment
There was a problem hiding this comment.
🤖 This review comment was created by an AI agent.
Requesting changes for one new correctness blocker:
OBJFileLoader._parseSolidAsync(objFileLoader.pure.ts:356-363): withwaitForTextures: trueand the defaultmaterialLoadingFailsSilently: true,Promise.allrejects as soon as any texture fails; the catch immediately resolves the outer MTL promise, so the OBJ load can complete while other referenced textures are still pending. This violates the option’s wait contract. In silent mode, catch each texture rejection (or usePromise.allSettled) and still await every texture; add a multi-texture regression test where one fails before another settles.
Existing review threads: Copilot’s solidParser.ts restoration concern remains valid and unresolved, so I did not duplicate it inline. Its mtlFileLoader.ts comment describes behavior already present on the base commit and is not counted as PR-introduced.
CI: repository build/test/lint/typecheck checks have not run; only GitGuardian passed. This review requests changes for the code issue above, not unfinished CI.
|
The OBJ does the same as glTF, so I’d leave this for a separate PR. The waitForTextures issue has been fixed, with regression tests |
|
Commented about the copilot comment. The older Copilot comment on |
RaananW
left a comment
There was a problem hiding this comment.
🤖 This review comment was created by an AI agent.
Re-review verdict: 🔴 Request changes
The follow-up commit fixes the previously reported waitForTextures contract: Promise.allSettled now waits for every referenced texture before propagating or silently handling failures, and the new two-texture tests cover both completion and failure of the remaining request.
One previously raised correctness blocker remains on the current head: _blockEntityCollection is still not restored with try/finally around the newly container-aware material constructors in SolidParser.parse (solidParser.ts:924-927) and OBJFileLoader._parseSolidAsync (objFileLoader.pure.ts:397-401). A constructor failure leaves the scene-wide flag altered, while a pre-existing blocked state can be clobbered. The analogous MTL path predates this PR and is not counted as a new blocker. Please preserve the prior value and restore it in finally for these new paths.
CI:
CI safety: ✅ No credential-exposure risk found in the changed source/tests.
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). |
|
Reviewer - this PR has made changes to the build configuration file. This build will release a new package on npm If that was unintentional please make sure to revert those changes or close this PR. |
|
Snapshot stored with reference name: Test environment: To test a playground add it to the URL, for example: https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/18895/merge/index.html#WGZLGJ#4600 Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves): https://playground.babylonjs.com/?snapshot=refs/pull/18895/merge To test the snapshot in the playground with a playground ID add it after the snapshot query string: https://playground.babylonjs.com/?snapshot=refs/pull/18895/merge#BCU1XR#0 If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools. |
🟢 Memory Leak Test Results4 passed, 0 leaked out of 4 scenarios 🟢 All memory leak tests passed — no leaks detected. Passed Scenarios (4)
|
|
Visualization tests for WebGPU |
⚡ Performance Test Results🟢 All performance tests passed — no regressions detected. |
|
WebGL2 visualization test reporter: |
Remove the four previous-value snapshots introduced by this PR and reset _blockEntityCollection to false, matching the existing glTF and OBJ loader pattern. Keep the existing finally blocks and material texture cleanup.
|
OBJ's for instance: Babylon.js/packages/dev/loaders/src/glTF/2.0/glTFLoader.pure.ts Lines 2415 to 2419 in deab6a2 If anything, the code elsewhere that saves and restores the previous state appears to be the result of a loss of context. |
deltakosh
left a comment
There was a problem hiding this comment.
Hi! Thanks for the detailed loader work. I found two correctness issues in the new texture-waiting path:\n\n1. A second waitForTextures load can hang forever when it reuses an already-failed cached texture. The new wrapper subscribes after the cached texture's error notification has fired, so its deferred promise never settles. Please retry or replay the cached failure state and cover a second load of the same failed URL.\n\n2. With waitForTextures: true and materialLoadingFailsSilently: false, a texture failure rejects before the created meshes, geometries, materials, and textures are copied into the AssetContainer. Because scene collection was blocked and the catch only clears _assetContainer, those resources are orphaned and cannot be disposed by the caller. Please track or clean up all resources on this rejection path and add a reference-count regression test.\n\nThe earlier _blockEntityCollection restoration concern is not part of this review. The changelog label and full required CI are also still missing. Thanks for taking another pass at these edge cases!
RaananW
left a comment
There was a problem hiding this comment.
🤖 This review comment was created by an AI agent.
Re-review verdict: 🔴 Request changes
The prior _blockEntityCollection blocker is still open on this head. The latest commit removes the previous-value snapshots and resets the scene-wide flag to false. That can still clobber a caller's existing blocked state, and the new point-cloud and line-material constructor paths still do not restore the flag if construction throws:
SolidParser.parse(solidParser.ts:924-927)OBJFileLoader._parseSolidAsync(objFileLoader.pure.ts:397-400)
Please preserve the previous value and restore it in finally for the new paths. The two texture-waiting issues raised in the current team review are also blocking and are not repeated here.
CI:
CI safety: ✅ No credential-exposure risk found in the changed source and tests.
|
@deltakosh I've fixed the cleanup when loading throws. The missing onError callback when reusing a cached InternalTexture was identified as a bug and addressed in #11217 in 2021, but it appears to still persist. Would it be okay if I opened a separate PR to fix it? |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
🟢 Memory Leak Test Results4 passed, 0 leaked out of 4 scenarios 🟢 All memory leak tests passed — no leaks detected. Passed Scenarios (4)
|
|
WebGL2 visualization test reporter: |
⚡ Performance Test Results🟢 All performance tests passed — no regressions detected. |
RaananW
left a comment
There was a problem hiding this comment.
🤖 This review comment was created by an AI agent.
Thanks for the cleanup update. Resources are now tracked as they are created, and a failed container is disposed. This fixes the earlier orphaned-resource concern.
I still need these changes:
-
The earlier
_blockEntityCollectionblocker is still open in the new point and line material paths. They replace the caller's prior value withfalse. A constructor error can also leave the value changed. Please save the old value and restore it infinally: solidParser.ts:924-928 and objFileLoader.pure.ts:373-398. -
waitForTextures: truecan still wait forever on a second load of an already failed cached texture._GetTextureonly receives future callbacks, after the cached error was already sent. Please retry the failed texture or replay its error, and add a second-load regression test: mtlFileLoader.ts:267-276. -
Required CI is not green.
Monorepo CI (Memory leak tests (packages suite))andMonorepo CI (Visualization tests - WebGL 2)are failing. Performance and WebGPU checks are still pending.
CI safety: I found no credential exposure risk in the changed source and tests.
|
Visualization tests for WebGPU |
|
any update here? |
Summary
This PR fixes resource collection and ownership in the OBJ loader and adds an option to wait for referenced textures before completing a load.
Changes
importMeshAsyncand include them inAssetContainer.geometries._parentContainer.invertTextureYoption.Optional texture loading waits
Adds
OBJLoadingOptions.waitForTexturesdefaulting tofalseto preserve the existing non-waiting behavior.When enabled:
materialLoadingFailsSilently.Validation
Note: The tests were AI-generated, but I have not reviewed their code quality.