Skip to content

Playground: load earcut and procedural textures, and provide name to test scripts - #1823

Merged
bkaradzic-microsoft merged 3 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:playground-earcut
Aug 12, 2026
Merged

Playground: load earcut and procedural textures, and provide name to test scripts#1823
bkaradzic-microsoft merged 3 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:playground-earcut

Conversation

@bkaradzic-microsoft

@bkaradzic-microsoft bkaradzic-microsoft commented Aug 7, 2026

Copy link
Copy Markdown
Member

Three test failures on the native Playground turned out to have the same root cause: the native harness provides a smaller browser environment than the web Playground, so scripts that work on babylonjs.com die here on a missing global. None of it is renderer-specific, so this fixes the same scenes on both the bgfx and the WebGPU/Dawn paths.

earcut is not defined

PolygonMeshBuilder resolves earcut as a global at triangulation time rather than importing it, so any scene that builds a polygon throws. The web Playground loads earcut as a separate script; we never did.

Pinned to ^2.2.4 deliberately, for two reasons — 3.x does ship dist/earcut.min.js, so "no UMD build" is not one of them:

  • The shape of the global changed. 2.x assigns the function itself (.earcut = e()); 3.x assigns a namespace object (.earcut = {}). PolygonMeshBuilder calls earcut(...), so 3.x throws at triangulation time.
  • It matches the environment these tests replicate. cdn.babylonjs.com/earcut.min.js, which playground/index.html loads, is itself 2.x (6,519 bytes, global-is-the-function).

Thanks to @bghgary for catching the original, incorrect rationale.

BABYLON.WoodProceduralTexture is not a constructor

The procedural texture classes live in their own babylonjs-procedural-textures package, which was never in the bundle. The existing config even documents this — one test is disabled with the reason "BABYLON.NormalMapProceduralTexture is not included in the native script bundle".

Wired into BABYLON_SCRIPTS, PlaygroundScripts.cpp, the Android asset copy, and getNightly.js so it stays in step with the other Babylon packages. Pinned to 9.15.0 to match every other babylonjs-* entry in the lock file — on ^9.15.0 npm resolves it to 9.20.0 and then nests a second full copy of babylonjs under it.

name is not defined

Some scripts reference a bare name without declaring it. In a browser that silently resolves to window.name (""), so the bug is invisible on the web but throws here.

Fixed by declaring var name = "" in the function that encloses the eval. Because those are direct evals, the evaluated script sees that scope and resolves name exactly as it would on the web — without leaking a real global, and without rewriting the script text (so line numbers in stack traces and any "use strict" prologue are preserved).

Defining an actual global name is not an option, and I tried: it breaks the Babylon UMD bundles, which probe for name while being evaluated. babylonjs.loaders.js then throws Error: Invalid argument at load time and every test fails.

Depends on

BabylonJS/Babylon.js#18799 must land first. The nightly's "Update scripts from snapshot" step refills every babylon* file in the Native artifact from the CDN snapshot, but the downlevel that follows names only babylon.max.js — so a newly added bundle is refilled and then run un-downleveled. #18799 passes the directory instead, which is correct both before and after this PR.

Validation

Full 680-test sweep on the Dawn/WebGPU backend, before vs. after:

before after
PASS 622 625
FAIL 56 53

Zero regressions. Polygon flips to PASS outright; Procedural textures and Show all procedural textures go from hard exceptions to rendering (they still miss the pixel threshold, tracked separately). The other two deltas (Iridescence NME, Sprite maps) are known order-dependent flakes, not caused by this change.

…o test scripts

Three unrelated-looking test failures all had the same root cause: the native
Playground provides a smaller browser environment than the web Playground, so
scripts that work on the web die here on a missing global.

- `earcut`: PolygonMeshBuilder looks `earcut` up as a global at triangulation
  time rather than importing it, so any polygon scene fails with
  `ReferenceError: earcut is not defined`. Pinned to ^2.2.4 because that is the
  last line shipping a UMD build (`dist/earcut.min.js`); 3.x is ESM-only and
  cannot be loaded by the plain-script loader.

- `babylonjs-procedural-textures`: the procedural texture classes
  (WoodProceduralTexture, BrickProceduralTexture, ...) live in their own package
  which was never loaded, so those scenes failed with
  `BABYLON.WoodProceduralTexture is not a constructor`.

- `name`: some scripts reference `name` without declaring it. In a browser that
  silently resolves to window.name (""), so the bug is invisible there but
  throws `ReferenceError: name is not defined` here. Declared in the scope that
  encloses the eval so a direct eval resolves it exactly as the web does.
  Deliberately *not* defined as a real global: doing that breaks the Babylon UMD
  bundles, which probe for `name` during evaluation and throw at load time.

None of this is renderer-specific; it fixes the same scenes on both the bgfx and
the WebGPU/Dawn paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
Copilot AI lite review requested due to automatic review settings August 7, 2026 15:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the native Playground harness to more closely match the web Playground’s “ambient browser globals” by bundling additional Babylon-related scripts (earcut + procedural textures) and by providing a name binding visible to directly-evaluated test scripts, reducing harness-specific test failures across backends.

Changes:

  • Add earcut.min.js and babylonjs.proceduralTextures.js to the native Playground script bundle and bootstrap load order.
  • Ensure Android asset packaging and the nightly download script include the new Babylon procedural textures artifact.
  • Add a scoped var name = "" binding around direct eval() execution paths in validation_native.js to match browser behavior.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Apps/scripts/getNightly.js Downloads the procedural textures library artifacts alongside other Babylon nightly files.
Apps/Playground/Shared/PlaygroundScripts.cpp Loads earcut.min.js before polygon scenes run, and loads procedural textures after BABYLON is initialized.
Apps/Playground/Scripts/validation_native.js Introduces a local name binding in eval scopes to prevent ReferenceError: name is not defined for browser-authored scripts.
Apps/Playground/CMakeLists.txt Adds earcut + procedural textures to the packaged script set for native Playground builds.
Apps/Playground/Android/app/build.gradle Copies earcut + procedural textures into Android assets for the Playground app.
Apps/package.json Adds babylonjs-procedural-textures and earcut dependencies for bundling/packaging.
Apps/package-lock.json Locks the new dependencies and their resolved versions.
Files not reviewed (1)
  • Apps/package-lock.json: Generated file

Comment thread Apps/package-lock.json
Comment thread Apps/package.json Outdated
Comment thread Apps/package.json Outdated
… bundle

Addresses review feedback on the earcut/procedural-textures change.

package.json pinned babylonjs-procedural-textures with a caret while the PR
called for an exact pin; on ^9.15.0 npm resolves 9.20.0 and nests a second
full copy of babylonjs underneath it. Pin it exactly, in both package.json
and the lock file's root dependency range, so regenerating the lock file
cannot drift.

getNightly replaces seven babylon*.js bundles but only babylon.max.js was
down-leveled to ES5 afterwards, so the other six ran un-transpiled on
Chakra -- and any bundle added to BABYLON_SCRIPTS later would silently
inherit the same gap. Derive the down-level list from the download table
itself and run it from getNightly.js, so the two can no longer disagree.
Source maps are excluded by the .js anchor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
@bkaradzic-microsoft

Copy link
Copy Markdown
Member Author

Thanks — all three review comments were valid; pushed in e421e9a.

Exact pin for babylonjs-procedural-textures. Correct catch: the PR description said "pinned to 9.15.0" but package.json carried ^9.15.0. The lock file happened to resolve 9.15.0, so the nesting problem was latent rather than visible — regenerating it would have reintroduced 9.20.0 and the second nested babylonjs copy. Now exact in both package.json and the lock file's root dependency range.

Down-leveling more than babylon.max.js. Also correct, and the gap was wider than the comment suggested: getNightly replaces seven babylon*.js bundles and only babylon.max.js was being down-leveled, so gui, loaders, materials, serializers, addons — and now proceduralTextures — were running un-transpiled on Chakra after a nightly refresh.

Rather than lengthen the hardcoded argument list (which is what let the two drift apart in the first place), the down-level list is now derived from the download table inside getNightly.js and invoked from there, so adding a download can't silently skip the step again. Source maps are excluded by the .js anchor, and downlevelNativeScripts.mjs filters on basename as a second guard. npm run downlevel:native-scripts stays available for manual use.

CI note: Ubuntu_Clang_Hermes SIGSEGV'd on the first run, ~82 tests in. It was a flake — the re-run completed the same 304 tests as master with ran=283 passed=283 failed=0.

@bghgary bghgary left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

Blocked on a matching Babylon.js change; four inline.

Comment thread Apps/package.json
Comment thread Apps/scripts/getNightly.js
Comment thread Apps/Playground/Shared/PlaygroundScripts.cpp Outdated
Comment thread Apps/Playground/Shared/PlaygroundScripts.cpp Outdated
Review feedback from bghgary on BabylonJS#1823. The earcut comment keeps only the two
facts a reader needs (the global lookup and the failure without it), and the
procedural textures comment is dropped: the seven separate-package loads above
it carry no comment either, and the two that do state something the code
cannot show.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
@bkaradzic-microsoft

Copy link
Copy Markdown
Member Author

Thanks -- all four addressed.

  • Comment trims (be0da80): applied both suggestions verbatim.
  • earcut rationale: you were right, 3.x does ship dist/earcut.min.js. The real reasons are the global changing from the function to a namespace, and cdn.babylonjs.com/earcut.min.js being 2.x. Description corrected.
  • Babylon.js side: Native nightly: downlevel every bundle, not just babylon.max.js Babylon.js#18799 is up, doing what you suggested (pass the directory, not a filename). Marked as a dependency in the description.

One measured correction on that last one: babylonjs.proceduralTextures.js would not actually have broken the nightly -- it is the most conservative of the seven bundles (no optional chaining, no private fields, no generators), while loaders, serializers and addons already run un-downleveled with all three. Details and the full inventory are in the thread. The gap is real, just older and broader than this PR, so #18799 fixes the mechanism rather than naming the new file.

Re-requesting review.

sebavan pushed a commit to BabylonJS/Babylon.js that referenced this pull request Aug 12, 2026
The "Update scripts from snapshot" step refills **every** `babylon*`
file in the two Babylon Native artifact directories from the CDN
snapshot:

```bat
set destDir=..\BabylonNativeNightlyPlayground\RelWithDebInfo\Scripts
for /f %%i in ('dir /b %destDir%\babylon*') do (
  unzip -p snapshot.zip "**%%i" > %destDir%\%%i
)
```

...but the downlevel that follows names a single file:

```bat
npm run downlevel:native-scripts -- ..\BabylonNativeNightlyPlayground\RelWithDebInfo\Scripts\babylon.max.js
```

So the refill is a glob and the downlevel is an allowlist of one. Any
bundle added to the Native script set later gets refilled from the
snapshot and then executed **un-downleveled**, which on Chakra surfaces
as a parse error at load with nothing pointing at the cause.

This passes the directory instead. `scripts/downlevelNativeScripts.mjs`
already walks a directory and selects files itself with
`/^babylon.*\.js$/i` — the same set the refill loop uses — so no change
to the script is needed.

### Please note: this is not a pure no-op

Six bundles are currently refilled from the snapshot but never
downleveled, and this makes them transpiled too. Syntax actually present
in each bundle on the CDN today:

| bundle | downleveled today | `?.` | `#priv` | `**` | `async` |
`function*` |
|---|---|---|---|---|---|---|
| `babylon.max.js` | **yes** | 19 | 2 | 74 | 1 | 631 |
| `babylonjs.loaders.js` | no | 2 | 0 | 0 | 0 | 148 |
| `babylonjs.serializers.js` | no | 2 | 0 | 0 | 0 | 62 |
| `babylonjs.addons.js` | no | 1 | 0 | 6 | 0 | 17 |
| `babylonjs.materials.js` | no | 0 | 0 | 3 | 0 | 14 |
| `babylon.gui.js` | no | 0 | 0 | 0 | 0 | 18 |
| `babylonjs.proceduralTextures.js` | n/a (not yet shipped) | 0 | 0 | 0
| 0 | 0 |

All seven are emitted at an ES2015 target, so transpiling them is the
intended direction rather than a workaround — but it is a behavior
change, and it costs some CI time. Flagging it explicitly rather than
describing this as a no-op.

### Verification

Ran the existing script against a directory laid out like the artifact:

```
babylon.max.js                    -> downleveled  (var / function / Math.pow)
babylonjs.proceduralTextures.js   -> downleveled  (var / function)
babylon.max.js.map                -> untouched    (.js anchor excludes maps)
earcut.min.js                     -> untouched    (not babylon*)
ammo.js                           -> untouched    (not babylon*)
```

`ci-monorepo.yml` still parses as YAML.

### Context

Prompted by BabylonJS/BabylonNative#1823, which adds
`babylonjs.proceduralTextures.js` to the Playground script set. Per
review there, this needs to land first: naming the new file on this side
would fail until it exists in the Native artifact, whereas globbing the
directory is correct both before and after.

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
@bkaradzic-microsoft

Copy link
Copy Markdown
Member Author

The blocker is cleared: BabylonJS/Babylon.js#18799 is merged, so the nightly now downlevels every babylon*.js in the Native artifact directories rather than only babylon.max.js.

@bghgary bghgary left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

The ordering blocker is cleared.

@bkaradzic-microsoft
bkaradzic-microsoft merged commit ca4bfb8 into BabylonJS:master Aug 12, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants