@auraindustry/aurajs 0.0.6 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -7
- package/benchmarks/perf-thresholds.json +54 -0
- package/package.json +4 -7
- package/src/asset-pack.mjs +5 -1
- package/src/authored-project.mjs +1449 -0
- package/src/authored-runtime.mjs +2016 -0
- package/src/authoring/avatar-animation-graph.mjs +648 -0
- package/src/bin-integrity.mjs +272 -0
- package/src/build-contract/assets.mjs +130 -0
- package/src/build-contract/capabilities.mjs +116 -0
- package/src/build-contract/constants.mjs +6 -0
- package/src/build-contract/helpers.mjs +44 -0
- package/src/build-contract/web-templates.mjs +5993 -0
- package/src/build-contract.mjs +27 -2910
- package/src/bundler.mjs +188 -55
- package/src/cli.mjs +4825 -1512
- package/src/commands/project-authoring.mjs +434 -0
- package/src/config.mjs +27 -0
- package/src/conformance/cases/app-and-ui-runtime-cases.mjs +3309 -0
- package/src/conformance/cases/core-runtime-cases.mjs +1431 -0
- package/src/conformance/cases/index.mjs +11 -0
- package/src/conformance/cases/scene3d-and-media-cases.mjs +2094 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +1776 -0
- package/src/conformance/shared.mjs +27 -0
- package/src/conformance-runner.mjs +25 -13
- package/src/conformance.mjs +619 -4020
- package/src/cutscene.mjs +362 -5
- package/src/dev-cli-action.mjs +249 -0
- package/src/dev-cli-inspect.mjs +92 -0
- package/src/dev-cli-state.mjs +80 -0
- package/src/external-asset-cache.mjs +587 -0
- package/src/external-asset-policy.mjs +217 -0
- package/src/external-package-surface.mjs +206 -0
- package/src/game-action-runtime.mjs +869 -0
- package/src/game-state-runtime.mjs +206 -6
- package/src/headless-action.mjs +186 -0
- package/src/headless-test/runtime-animation.mjs +1173 -0
- package/src/headless-test/runtime-coordinator.mjs +1514 -0
- package/src/headless-test/runtime-primitives.mjs +320 -0
- package/src/headless-test/runtime-world.mjs +2253 -0
- package/src/headless-test.mjs +392 -4298
- package/src/host-binary.mjs +342 -14
- package/src/icon-discovery.mjs +64 -0
- package/src/make-catalog.mjs +109 -0
- package/src/make.mjs +197 -0
- package/src/package-integrity.mjs +586 -0
- package/src/perf-benchmark.mjs +353 -0
- package/src/postinstall.mjs +5 -5
- package/src/prefabs/index.mjs +34 -0
- package/src/prefabs/scene-serialization.mjs +184 -0
- package/src/project-importer.mjs +620 -0
- package/src/project-registry.mjs +24 -0
- package/src/publish-command.mjs +195 -0
- package/src/publish-env-example.mjs +83 -0
- package/src/publish-validation.mjs +708 -0
- package/src/retro/assets/compile.mjs +232 -0
- package/src/retro/backend-gba/authoring.mjs +1029 -0
- package/src/retro/backend-gba/rom.mjs +363 -0
- package/src/retro/backend-gbc/rom.mjs +85 -0
- package/src/retro/build.mjs +278 -0
- package/src/retro/cli/commands.mjs +292 -0
- package/src/retro/cli/templates.mjs +84 -0
- package/src/retro/diagnostics/catalog.mjs +110 -0
- package/src/retro/diagnostics/emit.mjs +72 -0
- package/src/retro/emulator/case-overlay.mjs +64 -0
- package/src/retro/emulator/discovery.mjs +158 -0
- package/src/retro/emulator/macos-case-overlay.swift +220 -0
- package/src/retro/emulator/profiles.mjs +146 -0
- package/src/retro/emulator/runner.mjs +289 -0
- package/src/retro/frontend/load-project.mjs +98 -0
- package/src/retro/index.mjs +30 -0
- package/src/retro/ir/build-ir.mjs +108 -0
- package/src/retro/runtime-gba/contract.mjs +151 -0
- package/src/retro/runtime-gbc/contract.mjs +117 -0
- package/src/retro/shared/span.mjs +26 -0
- package/src/retro/shared/targets.mjs +64 -0
- package/src/retro/validator/check-project.mjs +114 -0
- package/src/runtime-hotspot-audit.mjs +707 -0
- package/src/scaffold/config.mjs +1000 -0
- package/src/scaffold/fs.mjs +56 -0
- package/src/scaffold/layout.mjs +318 -0
- package/src/scaffold/project-docs.mjs +438 -0
- package/src/scaffold.mjs +93 -596
- package/src/scene-composition/index.mjs +326 -0
- package/src/scene-composition/runtime.mjs +751 -0
- package/src/self-hosted-assets.mjs +604 -0
- package/src/session-client.mjs +750 -0
- package/src/session-native-launcher.mjs +74 -0
- package/src/session-protocol.mjs +75 -0
- package/src/session-runtime.mjs +321 -0
- package/src/session-server.mjs +360 -0
- package/src/shader-kits/index.mjs +773 -0
- package/src/starter-content-registry.mjs +292 -0
- package/src/state-artifacts.mjs +662 -24
- package/src/state-dev-reload.mjs +99 -2
- package/src/terminal-ui.mjs +245 -0
- package/src/web-conformance.mjs +219 -0
- package/templates/create/2d/config/gameplay/shooter.config.js +26 -0
- package/templates/create/2d/content/gameplay/waves.json +26 -0
- package/templates/create/2d/content/registries/.gitkeep +1 -0
- package/templates/create/2d/docs/design/.gitkeep +1 -0
- package/templates/create/2d/docs/design/loop.md +5 -0
- package/templates/create/2d/prefabs/enemies.prefab.js +90 -0
- package/templates/create/2d/prefabs/enemy-basic.prefab.js +18 -0
- package/templates/create/2d/prefabs/player.prefab.js +36 -0
- package/templates/create/2d/prefabs/projectiles.prefab.js +35 -0
- package/templates/create/2d/scenes/boot.scene.js +12 -0
- package/templates/create/2d/scenes/gameplay.scene.js +230 -0
- package/templates/create/2d/scenes/menu.scene.js +9 -0
- package/templates/create/2d/src/main.js +6 -185
- package/templates/create/2d/src/runtime/app.js +49 -0
- package/templates/create/2d/src/runtime/capabilities.js +35 -0
- package/templates/create/2d/ui/hud.screen.js +40 -0
- package/templates/create/2d/ui/pause.screen.js +149 -0
- package/templates/create/2d/ui/settings.screen.js +347 -0
- package/templates/create/2d/ui/title.screen.js +13 -0
- package/templates/create/2d-adventure/aura.config.json +28 -0
- package/templates/create/2d-adventure/config/gameplay/adventure.config.js +14 -0
- package/templates/create/2d-adventure/content/gameplay/world.js +46 -0
- package/templates/create/2d-adventure/content/registries/.gitkeep +1 -0
- package/templates/create/2d-adventure/docs/design/loop.md +5 -0
- package/templates/create/2d-adventure/prefabs/player.prefab.js +54 -0
- package/templates/create/2d-adventure/prefabs/relic.prefab.js +38 -0
- package/templates/create/2d-adventure/prefabs/world.prefab.js +125 -0
- package/templates/create/2d-adventure/scenes/gameplay.scene.js +256 -0
- package/templates/create/2d-adventure/src/runtime/capabilities.js +34 -0
- package/templates/create/2d-adventure/ui/hud.screen.js +60 -0
- package/templates/create/2d-survivor/config/gameplay/survivor.config.js +33 -0
- package/templates/create/2d-survivor/content/gameplay/spawn-zones.json +29 -0
- package/templates/create/2d-survivor/content/registries/.gitkeep +1 -0
- package/templates/create/2d-survivor/docs/design/.gitkeep +1 -0
- package/templates/create/2d-survivor/docs/design/loop.md +5 -0
- package/templates/create/2d-survivor/prefabs/enemies.prefab.js +178 -0
- package/templates/create/2d-survivor/prefabs/enemy-swarm.prefab.js +18 -0
- package/templates/create/2d-survivor/prefabs/player.prefab.js +42 -0
- package/templates/create/2d-survivor/prefabs/projectiles.prefab.js +56 -0
- package/templates/create/2d-survivor/scenes/boot.scene.js +12 -0
- package/templates/create/2d-survivor/scenes/gameplay.scene.js +314 -0
- package/templates/create/2d-survivor/scenes/menu.scene.js +9 -0
- package/templates/create/2d-survivor/src/main.js +5 -332
- package/templates/create/2d-survivor/src/runtime/app.js +49 -0
- package/templates/create/2d-survivor/src/runtime/capabilities.js +35 -0
- package/templates/create/2d-survivor/ui/hud.screen.js +45 -0
- package/templates/create/2d-survivor/ui/title.screen.js +13 -0
- package/templates/create/3d/assets/models/starter-avatar.gltf +184 -0
- package/templates/create/3d/config/gameplay/.gitkeep +1 -0
- package/templates/create/3d/content/gameplay/checkpoints.json +33 -0
- package/templates/create/3d/content/gameplay/course.js +40 -0
- package/templates/create/3d/content/registries/.gitkeep +1 -0
- package/templates/create/3d/docs/design/.gitkeep +1 -0
- package/templates/create/3d/docs/design/loop.md +5 -0
- package/templates/create/3d/prefabs/checkpoint.prefab.js +15 -0
- package/templates/create/3d/prefabs/player.prefab.js +204 -0
- package/templates/create/3d/prefabs/world.prefab.js +112 -0
- package/templates/create/3d/scenes/boot.scene.js +12 -0
- package/templates/create/3d/scenes/checkpoint.scene.js +9 -0
- package/templates/create/3d/scenes/gameplay.scene.js +292 -0
- package/templates/create/3d/src/main.js +6 -295
- package/templates/create/3d/src/runtime/app.js +49 -0
- package/templates/create/3d/src/runtime/capabilities.js +53 -0
- package/templates/create/3d/src/runtime/materials.js +34 -0
- package/templates/create/3d/src/runtime/state.js +39 -0
- package/templates/create/3d/ui/hud.screen.js +75 -0
- package/templates/create/3d/ui/pause.screen.js +166 -0
- package/templates/create/3d/ui/settings.screen.js +387 -0
- package/templates/create/3d-adventure/assets/models/starter-avatar.gltf +184 -0
- package/templates/create/3d-adventure/aura.config.json +28 -0
- package/templates/create/3d-adventure/config/gameplay/adventure.config.js +9 -0
- package/templates/create/3d-adventure/content/gameplay/course.js +62 -0
- package/templates/create/3d-adventure/content/registries/.gitkeep +1 -0
- package/templates/create/3d-adventure/docs/design/loop.md +5 -0
- package/templates/create/3d-adventure/prefabs/player.prefab.js +168 -0
- package/templates/create/3d-adventure/prefabs/relic.prefab.js +35 -0
- package/templates/create/3d-adventure/prefabs/world.prefab.js +119 -0
- package/templates/create/3d-adventure/scenes/gameplay.scene.js +358 -0
- package/templates/create/3d-adventure/src/runtime/capabilities.js +56 -0
- package/templates/create/3d-adventure/src/runtime/materials.js +39 -0
- package/templates/create/3d-adventure/src/runtime/state.js +31 -0
- package/templates/create/3d-adventure/ui/hud.screen.js +70 -0
- package/templates/create/3d-adventure/ui/pause.screen.js +437 -0
- package/templates/create/3d-collectathon/assets/models/starter-avatar.gltf +184 -0
- package/templates/create/3d-collectathon/config/gameplay/.gitkeep +1 -0
- package/templates/create/3d-collectathon/content/gameplay/collectibles.json +26 -0
- package/templates/create/3d-collectathon/content/gameplay/course.js +46 -0
- package/templates/create/3d-collectathon/content/registries/.gitkeep +1 -0
- package/templates/create/3d-collectathon/docs/design/.gitkeep +1 -0
- package/templates/create/3d-collectathon/docs/design/loop.md +5 -0
- package/templates/create/3d-collectathon/prefabs/collectible.prefab.js +15 -0
- package/templates/create/3d-collectathon/prefabs/player.prefab.js +207 -0
- package/templates/create/3d-collectathon/prefabs/world.prefab.js +112 -0
- package/templates/create/3d-collectathon/scenes/boot.scene.js +12 -0
- package/templates/create/3d-collectathon/scenes/checkpoint.scene.js +9 -0
- package/templates/create/3d-collectathon/scenes/gameplay.scene.js +200 -0
- package/templates/create/3d-collectathon/src/main.js +5 -355
- package/templates/create/3d-collectathon/src/runtime/app.js +49 -0
- package/templates/create/3d-collectathon/src/runtime/capabilities.js +53 -0
- package/templates/create/3d-collectathon/src/runtime/materials.js +34 -0
- package/templates/create/3d-collectathon/src/runtime/state.js +27 -0
- package/templates/create/3d-collectathon/ui/hud.screen.js +66 -0
- package/templates/create/3d-collectathon/ui/pause.screen.js +13 -0
- package/templates/create/blank/config/gameplay/.gitkeep +1 -0
- package/templates/create/blank/content/gameplay/.gitkeep +1 -0
- package/templates/create/blank/content/registries/.gitkeep +1 -0
- package/templates/create/blank/docs/design/.gitkeep +1 -0
- package/templates/create/blank/docs/design/loop.md +5 -0
- package/templates/create/blank/prefabs/.gitkeep +1 -0
- package/templates/create/blank/scenes/.gitkeep +1 -0
- package/templates/create/blank/src/runtime/.gitkeep +1 -0
- package/templates/create/blank/ui/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/assets/audio/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/assets/fonts/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/assets/sprites/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/assets/starter/README.md +11 -0
- package/templates/create/deckbuilder-2d/assets/ui/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/aura.config.json +28 -0
- package/templates/create/deckbuilder-2d/config/gameplay/deckbuilder.config.js +26 -0
- package/templates/create/deckbuilder-2d/content/cards/guard.card.js +19 -0
- package/templates/create/deckbuilder-2d/content/cards/spark.card.js +20 -0
- package/templates/create/deckbuilder-2d/content/cards/starter.deck.js +69 -0
- package/templates/create/deckbuilder-2d/content/cards/strike.card.js +19 -0
- package/templates/create/deckbuilder-2d/content/cards/survey.card.js +20 -0
- package/templates/create/deckbuilder-2d/content/encounters/training-battle.encounter.js +14 -0
- package/templates/create/deckbuilder-2d/content/encounters/training-battle.js +65 -0
- package/templates/create/deckbuilder-2d/content/enemies/training-automaton.enemy.js +48 -0
- package/templates/create/deckbuilder-2d/content/gameplay/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/content/registries/cards.registry.js +26 -0
- package/templates/create/deckbuilder-2d/content/registries/encounters.registry.js +20 -0
- package/templates/create/deckbuilder-2d/content/registries/enemies.registry.js +20 -0
- package/templates/create/deckbuilder-2d/content/registries/relics.registry.js +20 -0
- package/templates/create/deckbuilder-2d/content/relics/ember-charm.relic.js +18 -0
- package/templates/create/deckbuilder-2d/docs/design/loop.md +12 -0
- package/templates/create/deckbuilder-2d/prefabs/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/scenes/boot.scene.js +84 -0
- package/templates/create/deckbuilder-2d/scenes/gameplay.scene.js +641 -0
- package/templates/create/deckbuilder-2d/src/components/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/src/main.js +17 -0
- package/templates/create/deckbuilder-2d/src/runtime/capabilities.js +22 -0
- package/templates/create/deckbuilder-2d/src/shared/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/src/systems/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/tests/smoke/.gitkeep +1 -0
- package/templates/create/deckbuilder-2d/ui/hud.screen.js +80 -0
- package/templates/create/deckbuilder-2d/ui/pause.screen.js +146 -0
- package/templates/create/deckbuilder-2d/ui/settings.screen.js +342 -0
- package/templates/create/local-multiplayer/aura.config.json +40 -0
- package/templates/create/local-multiplayer/config/gameplay/local-multiplayer.config.js +26 -0
- package/templates/create/local-multiplayer/content/gameplay/room-layout.js +13 -0
- package/templates/create/local-multiplayer/content/registries/.gitkeep +1 -0
- package/templates/create/local-multiplayer/docs/design/loop.md +14 -0
- package/templates/create/local-multiplayer/prefabs/player.prefab.js +99 -0
- package/templates/create/local-multiplayer/scenes/boot.scene.js +12 -0
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +443 -0
- package/templates/create/local-multiplayer/src/main.js +17 -0
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +28 -0
- package/templates/create/local-multiplayer/ui/hud.screen.js +60 -0
- package/templates/create/shared/src/runtime/project-inspector.js +105 -0
- package/templates/create/shared/src/runtime/scene-flow.js +290 -0
- package/templates/create/shared/src/runtime/screen-shell.js +222 -0
- package/templates/create/shared/src/runtime/ui-forms.js +209 -0
- package/templates/create/shared/src/runtime/ui-settings.js +237 -0
- package/templates/create/shared/src/runtime/ui-theme.js +352 -0
- package/templates/create/shared/src/starter-utils/adventure-objectives.js +102 -0
- package/templates/create/shared/src/starter-utils/animation-2d.js +337 -0
- package/templates/create/shared/src/starter-utils/avatar-3d.js +404 -0
- package/templates/create/shared/src/starter-utils/combat-feedback-2d.js +320 -0
- package/templates/create/shared/src/starter-utils/core.js +39 -3
- package/templates/create/shared/src/starter-utils/index.js +8 -2
- package/templates/create/shared/src/starter-utils/platformer-3d.js +34 -3
- package/templates/create/shared/src/starter-utils/triggers.js +662 -0
- package/templates/create/shared/src/starter-utils/tween-2d.js +615 -0
- package/templates/create/video-cutscene/assets/video/.gitkeep +0 -0
- package/templates/create/video-cutscene/aura.config.json +28 -0
- package/templates/create/video-cutscene/config/gameplay/.gitkeep +0 -0
- package/templates/create/video-cutscene/content/gameplay/.gitkeep +0 -0
- package/templates/create/video-cutscene/content/registries/.gitkeep +0 -0
- package/templates/create/video-cutscene/docs/design/loop.md +22 -0
- package/templates/create/video-cutscene/prefabs/.gitkeep +0 -0
- package/templates/create/video-cutscene/scenes/boot.scene.js +11 -0
- package/templates/create/video-cutscene/scenes/cutscene.scene.js +113 -0
- package/templates/create/video-cutscene/scenes/gameplay.scene.js +50 -0
- package/templates/create/video-cutscene/src/main.js +17 -0
- package/templates/create/video-cutscene/src/runtime/app.js +52 -0
- package/templates/create/video-cutscene/src/runtime/capabilities.js +35 -0
- package/templates/create/video-cutscene/src/runtime/state.js +13 -0
- package/templates/create/video-cutscene/ui/.gitkeep +0 -0
- package/templates/create-bin/play.js +1187 -0
- package/templates/make/README.md +46 -0
- package/templates/make/catalog.json +51 -0
- package/templates/make/component/files/{{MAKE_NAME}}.component.js +20 -0
- package/templates/make/component/manifest.json +9 -0
- package/templates/make/data/files/{{MAKE_NAME}}.json +14 -0
- package/templates/make/data/manifest.json +9 -0
- package/templates/make/material/files/{{MAKE_NAME}}.material.json +17 -0
- package/templates/make/material/manifest.json +9 -0
- package/templates/make/prefab/files/{{MAKE_NAME}}.prefab.js +20 -0
- package/templates/make/prefab/manifest.json +9 -0
- package/templates/make/scene/files/{{MAKE_NAME}}.scene.js +31 -0
- package/templates/make/scene/manifest.json +9 -0
- package/templates/make/shader/files/{{MAKE_NAME}}.shader.js +23 -0
- package/templates/make/shader/manifest.json +9 -0
- package/templates/make/system/files/{{MAKE_NAME}}.system.js +15 -0
- package/templates/make/system/manifest.json +9 -0
- package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.js +16 -0
- package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.json +23 -0
- package/templates/make/ui-screen/manifest.json +10 -0
- package/templates/make-starters/deckbuilder-2d/card/files/{{MAKE_NAME}}.card.js +22 -0
- package/templates/make-starters/deckbuilder-2d/card/manifest.json +9 -0
- package/templates/make-starters/deckbuilder-2d/catalog.json +34 -0
- package/templates/make-starters/deckbuilder-2d/encounter/files/{{MAKE_NAME}}.encounter.js +18 -0
- package/templates/make-starters/deckbuilder-2d/encounter/manifest.json +9 -0
- package/templates/make-starters/deckbuilder-2d/enemy/files/{{MAKE_NAME}}.enemy.js +28 -0
- package/templates/make-starters/deckbuilder-2d/enemy/manifest.json +9 -0
- package/templates/make-starters/deckbuilder-2d/relic/files/{{MAKE_NAME}}.relic.js +23 -0
- package/templates/make-starters/deckbuilder-2d/relic/manifest.json +9 -0
- package/templates/retro/platformer/README.md +10 -0
- package/templates/retro/platformer/assets/retro/assets.json +91 -0
- package/templates/retro/platformer/aura.config.json +7 -0
- package/templates/retro/platformer/package.json +5 -0
- package/templates/retro/platformer/src/main.js +40 -0
- package/templates/retro/puzzle-grid/README.md +10 -0
- package/templates/retro/puzzle-grid/assets/retro/assets.json +90 -0
- package/templates/retro/puzzle-grid/aura.config.json +7 -0
- package/templates/retro/puzzle-grid/package.json +5 -0
- package/templates/retro/puzzle-grid/src/main.js +29 -0
- package/templates/retro/tactics-grid/README.md +10 -0
- package/templates/retro/tactics-grid/assets/retro/assets.json +90 -0
- package/templates/retro/tactics-grid/aura.config.json +7 -0
- package/templates/retro/tactics-grid/package.json +5 -0
- package/templates/retro/tactics-grid/src/main.js +35 -0
- package/templates/retro/topdown-adventure/README.md +10 -0
- package/templates/retro/topdown-adventure/assets/retro/assets.json +95 -0
- package/templates/retro/topdown-adventure/aura.config.json +7 -0
- package/templates/retro/topdown-adventure/package.json +5 -0
- package/templates/retro/topdown-adventure/src/main.js +29 -0
- package/templates/skills/aurajs/SKILL.md +61 -5
|
@@ -0,0 +1,2094 @@
|
|
|
1
|
+
export const SCENE3D_AND_MEDIA_CONFORMANCE_CASES = [
|
|
2
|
+
{
|
|
3
|
+
id: 'scene3d-transform-hierarchy',
|
|
4
|
+
modes: ['shim', 'native'],
|
|
5
|
+
namespaces: ['scene3d'],
|
|
6
|
+
functions: [
|
|
7
|
+
'aura.scene3d.createNode',
|
|
8
|
+
'aura.scene3d.removeNode',
|
|
9
|
+
'aura.scene3d.setParent',
|
|
10
|
+
'aura.scene3d.getParent',
|
|
11
|
+
'aura.scene3d.setLocalTransform',
|
|
12
|
+
'aura.scene3d.getLocalTransform',
|
|
13
|
+
'aura.scene3d.getWorldTransform',
|
|
14
|
+
'aura.scene3d.traverse',
|
|
15
|
+
],
|
|
16
|
+
nativeChecks: [
|
|
17
|
+
{
|
|
18
|
+
id: 'scene3d.parent-child.world-transform.propagates',
|
|
19
|
+
expression: "(() => { const root = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const child = aura.scene3d.createNode({ position: { x: 2, y: 0, z: 0 } }); const linked = aura.scene3d.setParent(child, root); const world = aura.scene3d.getWorldTransform(child); return linked === true && !!world && Number(world.position.x.toFixed(6)) === 3; })()",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'scene3d.reparent.detach.semantics.stable',
|
|
23
|
+
expression: "(() => { const parentA = aura.scene3d.createNode({ position: { x: 10, y: 0, z: 0 } }); const parentB = aura.scene3d.createNode({ position: { x: 20, y: 0, z: 0 } }); const child = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const linkA = aura.scene3d.setParent(child, parentA); const worldA = aura.scene3d.getWorldTransform(child); const localA = aura.scene3d.getLocalTransform(child); const linkB = aura.scene3d.setParent(child, parentB); const worldB = aura.scene3d.getWorldTransform(child); const localB = aura.scene3d.getLocalTransform(child); const detached = aura.scene3d.setParent(child, null); const worldDetached = aura.scene3d.getWorldTransform(child); const localDetached = aura.scene3d.getLocalTransform(child); return linkA === true && linkB === true && detached === true && Number(worldA.position.x.toFixed(6)) === 11 && Number(worldB.position.x.toFixed(6)) === 21 && Number(worldDetached.position.x.toFixed(6)) === 1 && Number(localA.position.x.toFixed(6)) === 1 && Number(localB.position.x.toFixed(6)) === 1 && Number(localDetached.position.x.toFixed(6)) === 1 && aura.scene3d.getParent(child) === null; })()",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'scene3d.traverse.deterministic-order',
|
|
27
|
+
expression: "(() => { const root = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const childZ = aura.scene3d.createNode({ position: { x: 3, y: 0, z: 0 } }); const childA = aura.scene3d.createNode({ position: { x: 2, y: 0, z: 0 } }); const leaf = aura.scene3d.createNode({ position: { x: 4, y: 0, z: 0 } }); aura.scene3d.setParent(childZ, root); aura.scene3d.setParent(childA, root); aura.scene3d.setParent(leaf, childA); const sample = () => { const trace = []; aura.scene3d.traverse(root, (id, world) => trace.push(`${id}:${Number(world.position.x.toFixed(3))}`)); return trace.join('|'); }; const first = sample(); const second = sample(); return first === second && first === `${root}:1|${childZ}:4|${childA}:3|${leaf}:7`; })()",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'scene3d.local-update.ordering.deterministic',
|
|
31
|
+
expression: "(() => { const sample = () => { const root = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const child = aura.scene3d.createNode({ position: { x: 2, y: 0, z: 0 } }); const link = aura.scene3d.setParent(child, root); const before = aura.scene3d.getWorldTransform(child); const updated = aura.scene3d.setLocalTransform(root, { position: { x: 5, y: 0, z: 0 } }); const after = aura.scene3d.getWorldTransform(child); const trace = []; aura.scene3d.traverse(root, (_id, world) => trace.push(Number(world.position.x.toFixed(3)))); return JSON.stringify({ link, updated, before: before ? Number(before.position.x.toFixed(3)) : null, after: after ? Number(after.position.x.toFixed(3)) : null, trace }); }; const first = sample(); const second = sample(); const expected = JSON.stringify({ link: true, updated: true, before: 3, after: 7, trace: [5, 7] }); return first === second && first === expected; })()",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'scene3d.invalid-input.safe-false',
|
|
35
|
+
expression: "(() => { const root = aura.scene3d.createNode(); const child = aura.scene3d.createNode(); return aura.scene3d.setParent(root, root) === false && aura.scene3d.setParent(root, 999999) === false && aura.scene3d.setLocalTransform(root, null) === false && aura.scene3d.getWorldTransform(999999) === null && aura.scene3d.traverse(999999, () => {}) === false && aura.scene3d.setParent(child, root) === true && aura.scene3d.setParent(root, child) === false; })()",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
source: `
|
|
39
|
+
aura.setup = function () {
|
|
40
|
+
const root = aura.scene3d.createNode({ position: { x: 1, y: 2, z: 3 } });
|
|
41
|
+
const child = aura.scene3d.createNode({ position: { x: 0.5, y: 0, z: 0 } });
|
|
42
|
+
aura.scene3d.setParent(child, root);
|
|
43
|
+
};
|
|
44
|
+
`,
|
|
45
|
+
frames: 2,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'threejs-scene3d-visibility-layer-culling-runtime',
|
|
49
|
+
modes: ['native'],
|
|
50
|
+
namespaces: ['scene3d', 'draw3d', 'mesh', 'material', 'debug'],
|
|
51
|
+
functions: [
|
|
52
|
+
'aura.scene3d.bindRenderNode',
|
|
53
|
+
'aura.scene3d.unbindRenderNode',
|
|
54
|
+
'aura.scene3d.setNodeVisibility',
|
|
55
|
+
'aura.scene3d.setNodeLayer',
|
|
56
|
+
'aura.scene3d.setNodeCulling',
|
|
57
|
+
'aura.scene3d.setCameraLayerMask',
|
|
58
|
+
'aura.scene3d.setCullBounds',
|
|
59
|
+
'aura.scene3d.clearCullBounds',
|
|
60
|
+
'aura.scene3d.submitRenderBindings',
|
|
61
|
+
'aura.scene3d.getRenderSubmissionState',
|
|
62
|
+
'aura.debug.inspectorStats',
|
|
63
|
+
],
|
|
64
|
+
nativeChecks: [
|
|
65
|
+
{
|
|
66
|
+
id: 'scene3d.render-state.surface.methods',
|
|
67
|
+
expression: "(() => ['bindRenderNode','unbindRenderNode','setNodeVisibility','setNodeLayer','setNodeCulling','setCameraLayerMask','setCullBounds','clearCullBounds','submitRenderBindings','getRenderSubmissionState'].every((name) => typeof aura.scene3d?.[name] === 'function'))()",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'scene3d.render-state.submission.deterministic',
|
|
71
|
+
expression: "(() => { const runSample = () => { const mesh = aura.mesh.createBox(1, 1, 1); const material = aura.material.create({ color: { r: 0.8, g: 0.7, b: 0.6, a: 1 }, metallic: 0.1, roughness: 0.9 }); const inside = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const layerFiltered = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const hidden = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const boundsFiltered = aura.scene3d.createNode({ position: { x: 4, y: 0, z: -2 } }); const bindInside = aura.scene3d.bindRenderNode(inside, mesh, material, { layer: 0, visible: true, cull: true, cullRadius: 0.5 }); const bindLayer = aura.scene3d.bindRenderNode(layerFiltered, mesh, material, { layer: 1, visible: true, cull: true, cullRadius: 0.5 }); const bindHidden = aura.scene3d.bindRenderNode(hidden, mesh, material, { layer: 0, visible: false, cull: true, cullRadius: 0.5 }); const bindBounds = aura.scene3d.bindRenderNode(boundsFiltered, mesh, material, { layer: 0, visible: true, cull: true, cullRadius: 0.5 }); const setMask = aura.scene3d.setCameraLayerMask(1); const setBounds = aura.scene3d.setCullBounds({ min: { x: -1, y: -1, z: -3 }, max: { x: 1, y: 1, z: -1 } }); aura.debug.enableInspector(true); const before = aura.debug.inspectorStats(); const submit = aura.scene3d.submitRenderBindings(); const after = aura.debug.inspectorStats(); aura.debug.enableInspector(false); const beforeSubmission = before?.scene3dRuntime?.submission || {}; const afterSubmission = after?.scene3dRuntime?.submission || {}; const render = aura.scene3d.getRenderSubmissionState(); const delta = Number(afterSubmission.drawMeshPending || 0) - Number(beforeSubmission.drawMeshPending || 0); aura.scene3d.removeNode(boundsFiltered); aura.scene3d.removeNode(hidden); aura.scene3d.removeNode(layerFiltered); aura.scene3d.removeNode(inside); aura.scene3d.clearCullBounds(); aura.material.unload(material); aura.mesh.unload(mesh); return { bindInside: bindInside?.ok === true, bindLayer: bindLayer?.ok === true, bindHidden: bindHidden?.ok === true, bindBounds: bindBounds?.ok === true, setMask: setMask?.ok === true, setBounds: setBounds?.ok === true, submitOk: submit?.ok === true, submitted: Number(submit?.submitted || 0), culledHidden: Number(submit?.culledHidden || 0), culledLayer: Number(submit?.culledLayer || 0), culledBounds: Number(submit?.culledBounds || 0), skippedMissingNode: Number(submit?.skippedMissingNode || 0), delta, renderSubmitted: Number(render?.submitted || 0), renderFingerprint: Number(render?.orderFingerprint || 0), inspectorSubmitted: Number(afterSubmission.scene3dSubmitted || 0), inspectorCulledHidden: Number(afterSubmission.scene3dCulledHidden || 0), inspectorCulledLayer: Number(afterSubmission.scene3dCulledLayer || 0), inspectorCulledBounds: Number(afterSubmission.scene3dCulledBounds || 0), inspectorReasonCode: afterSubmission.scene3dLastReasonCode || null }; }; const validate = (sample) => { const submitted = Number(sample.submitted || 0); const culledHidden = Number(sample.culledHidden || 0); const culledLayer = Number(sample.culledLayer || 0); const culledBounds = Number(sample.culledBounds || 0); const skippedMissingNode = Number(sample.skippedMissingNode || 0); return sample.bindInside && sample.bindLayer && sample.bindHidden && sample.bindBounds && sample.setMask && sample.setBounds && sample.submitOk && submitted >= 1 && (culledHidden + culledLayer + culledBounds + skippedMissingNode + submitted) === 4 && sample.delta === submitted && sample.renderSubmitted === submitted && Number.isFinite(sample.renderFingerprint) && sample.renderFingerprint > 0 && sample.inspectorSubmitted === submitted && sample.inspectorCulledHidden === culledHidden && sample.inspectorCulledLayer === culledLayer && sample.inspectorCulledBounds === culledBounds && (sample.inspectorReasonCode === 'scene3d_render_submitted' || sample.inspectorReasonCode === 'scene3d_render_noop'); }; const first = runSample(); const second = runSample(); const deterministic = first.submitted === second.submitted && first.culledHidden === second.culledHidden && first.culledLayer === second.culledLayer && first.culledBounds === second.culledBounds && first.skippedMissingNode === second.skippedMissingNode && first.delta === second.delta; return validate(first) && validate(second) && deterministic; })()",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'scene3d.render-state.reason-codes',
|
|
75
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const mesh = aura.mesh.createBox(1, 1, 1); const material = aura.material.create({ color: { r: 1, g: 1, b: 1, a: 1 } }); const node = aura.scene3d.createNode(); const invalidNode = aura.scene3d.bindRenderNode(0, mesh, material); const invalidMesh = aura.scene3d.bindRenderNode(node, 0, material); const invalidMaterial = aura.scene3d.bindRenderNode(node, mesh, 0); const invalidOptions = aura.scene3d.bindRenderNode(node, mesh, material, 'bad'); const bound = aura.scene3d.bindRenderNode(node, mesh, material); const invalidVisibility = aura.scene3d.setNodeVisibility(node, 'yes'); const invalidLayer = aura.scene3d.setNodeLayer(node, 42); const invalidCull = aura.scene3d.setNodeCulling(node, 'yes'); const invalidCullRadius = aura.scene3d.setNodeCulling(node, true, { cullRadius: -1 }); const invalidMask = aura.scene3d.setCameraLayerMask(-1); const invalidBounds = aura.scene3d.setCullBounds({ min: { x: 1, y: 0, z: 0 }, max: { x: 0, y: 1, z: 1 } }); const missingBinding = aura.scene3d.setNodeLayer(999999, 0); const unbound = aura.scene3d.unbindRenderNode(node); const missingAfterUnbind = aura.scene3d.unbindRenderNode(node); aura.scene3d.removeNode(node); aura.material.unload(material); aura.mesh.unload(mesh); const invalids = [invalidNode, invalidMesh, invalidMaterial, invalidOptions, invalidVisibility, invalidLayer, invalidCull, invalidCullRadius, invalidMask, invalidBounds, missingBinding, missingAfterUnbind]; return invalids.every((entry) => typeof reasonOf(entry) === 'string' && reasonOf(entry).length > 0) && bound?.ok === true && unbound?.ok === true; })()",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
source: `
|
|
79
|
+
aura.setup = function () {};
|
|
80
|
+
`,
|
|
81
|
+
nativeFrames: 2,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'terrain-runtime-native',
|
|
85
|
+
modes: ['native'],
|
|
86
|
+
namespaces: ['terrain', 'camera3d', 'draw3d', 'debug'],
|
|
87
|
+
functions: [
|
|
88
|
+
'aura.terrain.create',
|
|
89
|
+
'aura.terrain.setSplatMap',
|
|
90
|
+
'aura.terrain.setLayerTexture',
|
|
91
|
+
'aura.terrain.setHeightmap',
|
|
92
|
+
'aura.terrain.addVegetation',
|
|
93
|
+
'aura.terrain.getInfo',
|
|
94
|
+
'aura.terrain.getVegetationInfo',
|
|
95
|
+
'aura.terrain.getSubmissionState',
|
|
96
|
+
'aura.terrain.sampleHeight',
|
|
97
|
+
'aura.camera3d.perspective',
|
|
98
|
+
'aura.camera3d.setPosition',
|
|
99
|
+
'aura.camera3d.lookAt',
|
|
100
|
+
'aura.draw3d.clear3d',
|
|
101
|
+
'aura.debug.inspectorStats',
|
|
102
|
+
],
|
|
103
|
+
nativeChecks: [
|
|
104
|
+
{
|
|
105
|
+
id: 'terrain.runtime.surface.methods',
|
|
106
|
+
expression: "(() => ['create','setSplatMap','setLayerTexture','setHeightmap','addVegetation','getInfo','getVegetationInfo','getSubmissionState','sampleHeight'].every((name) => typeof aura.terrain?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.draw3d?.clear3d === 'function' && typeof aura.debug?.inspectorStats === 'function')()",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'terrain.runtime.setup',
|
|
110
|
+
expression: `(() => {
|
|
111
|
+
try {
|
|
112
|
+
const fixtureRoot = 'src/cli/test-fixtures/terrain';
|
|
113
|
+
const assets = {
|
|
114
|
+
splat: \`\${fixtureRoot}/terrain-splat-rgba.png\`,
|
|
115
|
+
grass: \`\${fixtureRoot}/terrain-layer-grass.png\`,
|
|
116
|
+
rock: \`\${fixtureRoot}/terrain-layer-rock.png\`,
|
|
117
|
+
earth: \`\${fixtureRoot}/terrain-layer-earth.png\`,
|
|
118
|
+
vegetation: \`\${fixtureRoot}/terrain-vegetation-blade.png\`,
|
|
119
|
+
};
|
|
120
|
+
const terrainId = aura.terrain.create({
|
|
121
|
+
width: 16,
|
|
122
|
+
depth: 16,
|
|
123
|
+
segmentsX: 6,
|
|
124
|
+
segmentsZ: 6,
|
|
125
|
+
maxHeight: 5,
|
|
126
|
+
});
|
|
127
|
+
const rowSize = 7;
|
|
128
|
+
const heightmap = Array.from({ length: rowSize * rowSize }, (_, index) => {
|
|
129
|
+
const x = index % rowSize;
|
|
130
|
+
const z = Math.floor(index / rowSize);
|
|
131
|
+
const ridge = x === 3 || z === 3 ? 0.72 : 0.18;
|
|
132
|
+
const detail = ((x + z) % 2 === 0 ? 0.08 : 0.02);
|
|
133
|
+
return Math.min(1, ridge + detail);
|
|
134
|
+
});
|
|
135
|
+
aura.terrain.setSplatMap(terrainId, assets.splat);
|
|
136
|
+
aura.terrain.setLayerTexture(terrainId, 0, assets.grass, 4);
|
|
137
|
+
aura.terrain.setLayerTexture(terrainId, 1, assets.rock, 5);
|
|
138
|
+
aura.terrain.setLayerTexture(terrainId, 2, assets.earth, 6);
|
|
139
|
+
aura.terrain.setHeightmap(terrainId, heightmap);
|
|
140
|
+
const centerHeight = aura.terrain.sampleHeight(terrainId, 0, 0);
|
|
141
|
+
const edgeHeight = aura.terrain.sampleHeight(terrainId, 7.5, 7.5);
|
|
142
|
+
const vegetationId = aura.terrain.addVegetation(terrainId, {
|
|
143
|
+
texture: assets.vegetation,
|
|
144
|
+
density: 0.45,
|
|
145
|
+
minHeight: 0.7,
|
|
146
|
+
maxHeight: 1.35,
|
|
147
|
+
splatChannel: 0,
|
|
148
|
+
alphaCutoff: 0.1,
|
|
149
|
+
windDirection: [1, 0.25],
|
|
150
|
+
windStrength: 0.45,
|
|
151
|
+
windFrequency: 1.8,
|
|
152
|
+
});
|
|
153
|
+
let badLayer = null;
|
|
154
|
+
try {
|
|
155
|
+
aura.terrain.setLayerTexture(terrainId, 8, assets.grass, 4);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
badLayer = String(error && error.message || error);
|
|
158
|
+
}
|
|
159
|
+
let badMissingTerrain = null;
|
|
160
|
+
try {
|
|
161
|
+
aura.terrain.setSplatMap(999999, assets.splat);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
badMissingTerrain = String(error && error.message || error);
|
|
164
|
+
}
|
|
165
|
+
let badVegetation = null;
|
|
166
|
+
try {
|
|
167
|
+
aura.terrain.addVegetation(terrainId, { density: 1 });
|
|
168
|
+
} catch (error) {
|
|
169
|
+
badVegetation = String(error && error.message || error);
|
|
170
|
+
}
|
|
171
|
+
const info = aura.terrain.getInfo(terrainId);
|
|
172
|
+
const vegetation = aura.terrain.getVegetationInfo(vegetationId);
|
|
173
|
+
aura.camera3d.perspective(55, 0.1, 80);
|
|
174
|
+
aura.camera3d.setPosition(0, 8.2, 13.6);
|
|
175
|
+
aura.camera3d.lookAt(0, 1.4, 0);
|
|
176
|
+
const baseDraw = aura.draw;
|
|
177
|
+
globalThis.__terrainRuntimeProbe = {
|
|
178
|
+
assets,
|
|
179
|
+
terrainId,
|
|
180
|
+
vegetationId,
|
|
181
|
+
info,
|
|
182
|
+
vegetation,
|
|
183
|
+
centerHeight,
|
|
184
|
+
edgeHeight,
|
|
185
|
+
badLayer,
|
|
186
|
+
badMissingTerrain,
|
|
187
|
+
badVegetation,
|
|
188
|
+
frames: [],
|
|
189
|
+
};
|
|
190
|
+
aura.draw = function () {
|
|
191
|
+
const probe = globalThis.__terrainRuntimeProbe || {};
|
|
192
|
+
const frames = Array.isArray(probe.frames) ? probe.frames : [];
|
|
193
|
+
const frameIndex = frames.length;
|
|
194
|
+
const orbitX = Number((Math.sin(frameIndex * 0.42) * 2.2).toFixed(3));
|
|
195
|
+
const orbitZ = Number((13.6 - frameIndex * 0.28).toFixed(3));
|
|
196
|
+
aura.camera3d.setPosition(orbitX, 8.2, orbitZ);
|
|
197
|
+
aura.camera3d.lookAt(0, 1.4, 0);
|
|
198
|
+
aura.draw3d.clear3d({ r: 0.17, g: 0.2, b: 0.24, a: 1 });
|
|
199
|
+
frames.push({ frameIndex, orbitX, orbitZ });
|
|
200
|
+
if (typeof baseDraw === 'function') baseDraw();
|
|
201
|
+
};
|
|
202
|
+
return Number.isInteger(terrainId)
|
|
203
|
+
&& terrainId > 0
|
|
204
|
+
&& Number.isInteger(vegetationId)
|
|
205
|
+
&& vegetationId > 0
|
|
206
|
+
&& info?.ok === true
|
|
207
|
+
&& info?.hasSplatMap === true
|
|
208
|
+
&& info?.layers?.[0]?.texturePath === assets.grass
|
|
209
|
+
&& info?.layers?.[1]?.texturePath === assets.rock
|
|
210
|
+
&& info?.layers?.[2]?.texturePath === assets.earth
|
|
211
|
+
&& vegetation?.ok === true
|
|
212
|
+
&& vegetation?.texturePath === assets.vegetation
|
|
213
|
+
&& Number(vegetation?.totalInstances || 0) > 0
|
|
214
|
+
&& Number(centerHeight || 0) > Number(edgeHeight || 0)
|
|
215
|
+
&& badLayer?.includes('[reason:invalid_layer]')
|
|
216
|
+
&& badMissingTerrain?.includes('[reason:missing_terrain]')
|
|
217
|
+
&& badVegetation?.includes('[reason:invalid_vegetation_texture]');
|
|
218
|
+
} catch (_) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
})()`,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
nativePostChecks: [
|
|
225
|
+
{
|
|
226
|
+
id: 'terrain.runtime.evidence',
|
|
227
|
+
expression: `(() => {
|
|
228
|
+
try {
|
|
229
|
+
const probe = globalThis.__terrainRuntimeProbe || {};
|
|
230
|
+
const frames = Array.isArray(probe.frames) ? probe.frames : [];
|
|
231
|
+
aura.debug.enableInspector(true);
|
|
232
|
+
const stats = aura.debug.inspectorStats() || {};
|
|
233
|
+
aura.debug.enableInspector(false);
|
|
234
|
+
const terrainState = aura.terrain.getSubmissionState() || {};
|
|
235
|
+
const info = aura.terrain.getInfo(probe.terrainId) || {};
|
|
236
|
+
const vegetation = aura.terrain.getVegetationInfo(probe.vegetationId) || {};
|
|
237
|
+
const terrainRuntime = stats.terrainRuntime || {};
|
|
238
|
+
const scene3d = stats.scene3dRuntime || {};
|
|
239
|
+
const submission = scene3d.submission || {};
|
|
240
|
+
const cameraTrace = new Set(frames.map((frame) => \`\${frame.orbitX}:\${frame.orbitZ}\`));
|
|
241
|
+
return Number.isInteger(probe.terrainId)
|
|
242
|
+
&& probe.terrainId > 0
|
|
243
|
+
&& Number.isInteger(probe.vegetationId)
|
|
244
|
+
&& probe.vegetationId > 0
|
|
245
|
+
&& frames.length === 5
|
|
246
|
+
&& cameraTrace.size >= 3
|
|
247
|
+
&& info?.ok === true
|
|
248
|
+
&& info?.hasSplatMap === true
|
|
249
|
+
&& vegetation?.ok === true
|
|
250
|
+
&& Number(terrainState.terrainCount || 0) === 1
|
|
251
|
+
&& Number(terrainState.visibleTerrainCount || 0) === 1
|
|
252
|
+
&& Number(terrainState.terrainVertexCount || 0) > 0
|
|
253
|
+
&& Number(terrainState.terrainIndexCount || 0) > 0
|
|
254
|
+
&& Number(terrainState.vegetationLayerCount || 0) >= 1
|
|
255
|
+
&& Number(terrainState.vegetationInstanceCount || 0) > 0
|
|
256
|
+
&& Number(terrainState.lastRenderedTerrainBatchCount || 0) >= 1
|
|
257
|
+
&& Number(terrainState.lastRenderedTerrainIndexCount || 0) > 0
|
|
258
|
+
&& Number(terrainState.lastRenderedVegetationInstanceCount || 0) > 0
|
|
259
|
+
&& String(terrainState.lastRenderReasonCode || '') === 'terrain_render_submitted'
|
|
260
|
+
&& Number(terrainRuntime.lastRenderedTerrainBatchCount || 0) === Number(terrainState.lastRenderedTerrainBatchCount || 0)
|
|
261
|
+
&& Number(terrainRuntime.lastRenderedVegetationBatchCount || 0) === Number(terrainState.lastRenderedVegetationBatchCount || 0)
|
|
262
|
+
&& Number(submission.terrainLastRenderedTerrainBatchCount || 0) === Number(terrainState.lastRenderedTerrainBatchCount || 0)
|
|
263
|
+
&& Number(submission.terrainLastRenderedTerrainIndexCount || 0) === Number(terrainState.lastRenderedTerrainIndexCount || 0)
|
|
264
|
+
&& Number(submission.terrainLastRenderedVegetationBatchCount || 0) === Number(terrainState.lastRenderedVegetationBatchCount || 0)
|
|
265
|
+
&& Number(submission.terrainLastRenderedVegetationInstanceCount || 0) === Number(terrainState.lastRenderedVegetationInstanceCount || 0)
|
|
266
|
+
&& String(submission.terrainLastRenderReasonCode || '') === 'terrain_render_submitted'
|
|
267
|
+
&& Number(probe.centerHeight || 0) > Number(probe.edgeHeight || 0);
|
|
268
|
+
} catch (_) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
})()`,
|
|
272
|
+
debugExpression: `(() => {
|
|
273
|
+
try {
|
|
274
|
+
const probe = globalThis.__terrainRuntimeProbe || {};
|
|
275
|
+
aura.debug.enableInspector(true);
|
|
276
|
+
const stats = aura.debug.inspectorStats() || {};
|
|
277
|
+
aura.debug.enableInspector(false);
|
|
278
|
+
return {
|
|
279
|
+
probe,
|
|
280
|
+
terrainState: aura.terrain.getSubmissionState() || null,
|
|
281
|
+
info: aura.terrain.getInfo(probe.terrainId) || null,
|
|
282
|
+
vegetation: aura.terrain.getVegetationInfo(probe.vegetationId) || null,
|
|
283
|
+
scene3dRuntime: stats.scene3dRuntime || null,
|
|
284
|
+
terrainRuntime: stats.terrainRuntime || null,
|
|
285
|
+
};
|
|
286
|
+
} catch (error) {
|
|
287
|
+
return { debugError: String(error && error.message ? error.message : error) };
|
|
288
|
+
}
|
|
289
|
+
})()`,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
source: `
|
|
293
|
+
globalThis.__terrainRuntimeProbe = { frames: [] };
|
|
294
|
+
`,
|
|
295
|
+
nativeFrames: 5,
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: 'threejs-terrain-canonical-runtime',
|
|
299
|
+
modes: ['native'],
|
|
300
|
+
namespaces: ['terrain', 'draw3d', 'camera3d', 'debug'],
|
|
301
|
+
functions: [
|
|
302
|
+
'aura.terrain.create',
|
|
303
|
+
'aura.terrain.destroy',
|
|
304
|
+
'aura.terrain.setSplatMap',
|
|
305
|
+
'aura.terrain.setLayerTexture',
|
|
306
|
+
'aura.terrain.setHeightmap',
|
|
307
|
+
'aura.terrain.sampleHeight',
|
|
308
|
+
'aura.terrain.getInfo',
|
|
309
|
+
'aura.terrain.getVegetationInfo',
|
|
310
|
+
'aura.terrain.getSubmissionState',
|
|
311
|
+
'aura.terrain.addVegetation',
|
|
312
|
+
'aura.terrain.removeVegetation',
|
|
313
|
+
'aura.terrain.setVisible',
|
|
314
|
+
'aura.draw3d.clear3d',
|
|
315
|
+
'aura.camera3d.perspective',
|
|
316
|
+
'aura.camera3d.setPosition',
|
|
317
|
+
'aura.camera3d.lookAt',
|
|
318
|
+
'aura.debug.inspectorStats',
|
|
319
|
+
],
|
|
320
|
+
nativeChecks: [
|
|
321
|
+
{
|
|
322
|
+
id: 'terrain.surface.methods',
|
|
323
|
+
expression: "(() => ['create','destroy','setSplatMap','setLayerTexture','setHeightmap','sampleHeight','getInfo','getVegetationInfo','getSubmissionState','addVegetation','removeVegetation','setVisible'].every((name) => typeof aura.terrain?.[name] === 'function') && typeof aura.draw3d?.clear3d === 'function' && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: 'terrain.reason-codes',
|
|
327
|
+
expression: "(() => { const capture = (invoke) => { try { invoke(); return ''; } catch (error) { return String(error && error.message ? error.message : error); } }; const terrainId = aura.terrain.create({ width: 4, depth: 4, segmentsX: 2, segmentsZ: 2, maxHeight: 1.25 }); const missingInfo = aura.terrain.getInfo(999999); const missingVegetation = aura.terrain.getVegetationInfo(999999); const badLayer = capture(() => aura.terrain.setLayerTexture(terrainId, 9, 'bad.png')); const badSplat = capture(() => aura.terrain.setSplatMap(terrainId, ' ')); const badVegetation = capture(() => aura.terrain.addVegetation(terrainId, { density: 1 })); aura.terrain.destroy(terrainId); return missingInfo?.reasonCode === 'missing_terrain' && missingVegetation?.reasonCode === 'missing_vegetation' && badLayer.includes('[reason:invalid_layer]') && badSplat.includes('[reason:invalid_splat_texture_path]') && badVegetation.includes('[reason:invalid_vegetation_texture]'); })()",
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
id: 'terrain.runtime.setup',
|
|
331
|
+
expression: `(() => { try { const splatPath = 'src/cli/test-fixtures/terrain/terrain-splat.png'; const grassPath = 'src/cli/test-fixtures/terrain/terrain-grass.png'; const rockPath = 'src/cli/test-fixtures/terrain/terrain-rock.png'; const sandPath = 'src/cli/test-fixtures/terrain/terrain-sand.png'; const soilPath = 'src/cli/test-fixtures/terrain/terrain-soil.png'; const bladePath = 'src/cli/test-fixtures/terrain/terrain-grass-blade.png'; const texturePaths = [grassPath, rockPath, sandPath, soilPath]; const terrainId = aura.terrain.create({ width: 6, depth: 6, segmentsX: 4, segmentsZ: 4, maxHeight: 2.4 }); const heightmap = [0.05, 0.12, 0.22, 0.12, 0.05, 0.12, 0.35, 0.68, 0.42, 0.14, 0.18, 0.74, 1.35, 0.86, 0.2, 0.1, 0.42, 0.82, 0.56, 0.12, 0.02, 0.08, 0.18, 0.08, 0.02]; aura.terrain.setSplatMap(terrainId, splatPath); texturePaths.forEach((texturePath, index) => { aura.terrain.setLayerTexture(terrainId, index, texturePath, index === 2 ? 6 : 4); }); aura.terrain.setHeightmap(terrainId, heightmap); const vegetationId = aura.terrain.addVegetation(terrainId, { texture: bladePath, density: 2.4, minHeight: 0.05, maxHeight: 2.4, windStrength: 0.3, windFrequency: 1.1, alphaCutoff: 0.35, maxDistance: 32, lodLevels: 1, splatChannel: 0, windDirection: [1, 0.15] }); const info = aura.terrain.getInfo(terrainId); const vegetation = aura.terrain.getVegetationInfo(vegetationId); const sampleHeight = Number(aura.terrain.sampleHeight(terrainId, 0, 0) || 0); const edgeHeight = Number(aura.terrain.sampleHeight(terrainId, -2.5, -2.5) || 0); aura.camera3d.perspective(58, 0.1, 64); aura.camera3d.setPosition(4.6, 3.4, 5.8); aura.camera3d.lookAt(0, 0.7, 0); const baseDraw = aura.draw; globalThis.__terrainRuntimeProbe = { terrainId, vegetationId, texturePaths, splatPath, bladePath, sampleHeight, edgeHeight, frames: [] }; aura.draw = function () { const probe = globalThis.__terrainRuntimeProbe || {}; const frameIndex = Array.isArray(probe.frames) ? probe.frames.length : 0; aura.camera3d.setPosition(4.6 - (frameIndex * 0.35), 3.4, 5.8 - (frameIndex * 0.2)); aura.camera3d.lookAt(0, 0.7, 0); aura.draw3d.clear3d({ r: 0.67, g: 0.78, b: 0.92, a: 1 }); aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const terrainState = stats.terrainRuntime || aura.terrain.getSubmissionState() || {}; probe.frames.push({ frameIndex, visibleTerrain: Number(terrainState.lastRenderVisibleTerrainCount || 0), terrainBatches: Number(terrainState.lastRenderedTerrainBatchCount || 0), terrainIndices: Number(terrainState.lastRenderedTerrainIndexCount || 0), vegetationBatches: Number(terrainState.lastRenderedVegetationBatchCount || 0), vegetationInstances: Number(terrainState.lastRenderedVegetationInstanceCount || 0), renderReason: terrainState.lastRenderReasonCode || null }); baseDraw(); }; return Number.isInteger(terrainId) && terrainId > 0 && Number.isInteger(vegetationId) && vegetationId > 0 && info?.reasonCode === 'terrain_info_ok' && info?.hasSplatMap === true && info?.layers?.[0]?.texturePath === grassPath && info?.layers?.[1]?.texturePath === rockPath && vegetation?.reasonCode === 'terrain_vegetation_info_ok' && vegetation?.texturePath === bladePath && Number(vegetation?.totalInstances || 0) > 0 && sampleHeight > edgeHeight && sampleHeight > 0.5; } catch (_) { return false; } })()`,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
nativePostChecks: [
|
|
335
|
+
{
|
|
336
|
+
id: 'terrain.runtime.evidence',
|
|
337
|
+
expression: `(() => { try { const probe = globalThis.__terrainRuntimeProbe || {}; const frames = Array.isArray(probe.frames) ? probe.frames : []; const terrainState = aura.terrain.getSubmissionState() || {}; const info = aura.terrain.getInfo(probe.terrainId); const vegetation = aura.terrain.getVegetationInfo(probe.vegetationId); aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const terrainRuntime = stats.terrainRuntime || {}; const submission = stats?.scene3dRuntime?.submission || {}; const renderedTerrainBatchSet = new Set(frames.map((frame) => Number(frame.terrainBatches || 0)).filter((value) => value > 0)); return Number.isInteger(probe.terrainId) && probe.terrainId > 0 && Number.isInteger(probe.vegetationId) && probe.vegetationId > 0 && frames.length === 4 && frames.every((frame, index) => frame.frameIndex === index) && renderedTerrainBatchSet.size === 1 && [...renderedTerrainBatchSet][0] >= 1 && frames.slice(1).every((frame) => frame.visibleTerrain >= 1 && frame.terrainIndices > 0 && frame.vegetationInstances > 0 && frame.renderReason === 'terrain_render_submitted') && Number(terrainState.pendingCommandCount || 0) === 0 && Number(terrainState.lastRenderVisibleTerrainCount || 0) >= 1 && Number(terrainState.lastRenderedTerrainBatchCount || 0) >= 1 && Number(terrainState.lastRenderedTerrainIndexCount || 0) > 0 && Number(terrainState.lastRenderedVegetationInstanceCount || 0) > 0 && terrainState.lastRenderReasonCode === 'terrain_render_submitted' && info?.hasSplatMap === true && vegetation?.reasonCode === 'terrain_vegetation_info_ok' && Number(probe.sampleHeight || 0) > Number(probe.edgeHeight || 0) && Number(terrainRuntime.lastRenderedTerrainBatchCount || 0) >= 1 && Number(terrainRuntime.lastRenderedVegetationInstanceCount || 0) > 0 && terrainRuntime.lastRenderReasonCode === 'terrain_render_submitted' && Number(submission.terrainQueueDepth || 0) === 0 && Number(submission.terrainLastRenderedTerrainBatchCount || 0) >= 1 && Number(submission.terrainLastRenderedVegetationInstanceCount || 0) > 0 && submission.terrainLastRenderReasonCode === 'terrain_render_submitted'; } catch (_) { return false; } })()`,
|
|
338
|
+
debugExpression: `(() => { try { const probe = globalThis.__terrainRuntimeProbe || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { probe, terrainState: aura.terrain.getSubmissionState() || null, terrainInfo: aura.terrain.getInfo(probe.terrainId) || null, vegetationInfo: aura.terrain.getVegetationInfo(probe.vegetationId) || null, scene3dRuntime: stats.scene3dRuntime || null, terrainRuntime: stats.terrainRuntime || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()`,
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
source: `
|
|
342
|
+
globalThis.__terrainRuntimeProbe = { frames: [] };
|
|
343
|
+
`,
|
|
344
|
+
nativeFrames: 4,
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
id: 'terrain-biome-world-authoring-runtime',
|
|
348
|
+
modes: ['native'],
|
|
349
|
+
namespaces: ['terrain', 'draw3d', 'camera3d', 'debug'],
|
|
350
|
+
functions: [
|
|
351
|
+
'aura.terrain.createWorld',
|
|
352
|
+
'aura.terrain.registerBiome',
|
|
353
|
+
'aura.terrain.addRegion',
|
|
354
|
+
'aura.terrain.applyBiome',
|
|
355
|
+
'aura.terrain.streamWorld',
|
|
356
|
+
'aura.terrain.getWorldInfo',
|
|
357
|
+
'aura.terrain.getRegionInfo',
|
|
358
|
+
'aura.terrain.sampleWorldHeight',
|
|
359
|
+
'aura.terrain.getSubmissionState',
|
|
360
|
+
'aura.draw3d.clear3d',
|
|
361
|
+
'aura.camera3d.perspective',
|
|
362
|
+
'aura.camera3d.setPosition',
|
|
363
|
+
'aura.camera3d.lookAt',
|
|
364
|
+
'aura.debug.inspectorStats',
|
|
365
|
+
],
|
|
366
|
+
nativeChecks: [
|
|
367
|
+
{
|
|
368
|
+
id: 'terrain.world-authoring.surface.methods',
|
|
369
|
+
expression: "(() => ['createWorld','registerBiome','addRegion','applyBiome','streamWorld','getWorldInfo','getRegionInfo','sampleWorldHeight','getSubmissionState'].every((name) => typeof aura.terrain?.[name] === 'function') && typeof aura.draw3d?.clear3d === 'function' && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: 'terrain.world-authoring.runtime.setup',
|
|
373
|
+
expression: `(() => { try { const state = globalThis.__terrainWorldAuthoringRuntime || {}; const fixtureRoot = 'src/cli/test-fixtures/terrain'; const assets = { splat: \`\${fixtureRoot}/terrain-splat.png\`, grass: \`\${fixtureRoot}/terrain-grass.png\`, rock: \`\${fixtureRoot}/terrain-rock.png\`, sand: \`\${fixtureRoot}/terrain-sand.png\`, soil: \`\${fixtureRoot}/terrain-soil.png\`, blade: \`\${fixtureRoot}/terrain-grass-blade.png\` }; const ridge = (crest, bias) => Array.from({ length: 25 }, (_, index) => { const x = index % 5; const z = Math.floor(index / 5); const major = x === 2 || z === 2 ? crest : bias; const detail = ((x + z) % 2 === 0 ? 0.05 : 0.02); return Math.min(1, major + detail); }); const meadow = aura.terrain.registerBiome('meadow', { splatMap: assets.splat, layers: [ { texture: assets.grass, uvScale: 4 }, { texture: assets.rock, uvScale: 6 }, { texture: assets.soil, uvScale: 5 } ], vegetation: [ { texture: assets.blade, density: 0.55, minHeight: 0.45, maxHeight: 1.15, maxDistance: 30, lodLevels: 1, windDirection: [1, 0.15], windStrength: 0.35, windFrequency: 1.1 } ] }); const dry = aura.terrain.registerBiome('dry', { splatMap: assets.splat, layers: [ { texture: assets.sand, uvScale: 5 }, { texture: assets.rock, uvScale: 7 }, { texture: assets.soil, uvScale: 4 } ], vegetation: [ { texture: assets.blade, density: 0.18, minHeight: 0.3, maxHeight: 0.7, maxDistance: 24, lodLevels: 1, windDirection: [0.75, 0.05], windStrength: 0.2, windFrequency: 0.8 } ] }); const worldId = aura.terrain.createWorld({ loadRadius: 10, unloadRadius: 14 }); const regionA = aura.terrain.addRegion(worldId, { key: 'meadow-a', origin: { x: 0, y: 0, z: 0 }, terrain: { width: 8, depth: 8, segmentsX: 4, segmentsZ: 4, maxHeight: 2.6 }, heightmap: ridge(0.82, 0.08), biome: 'meadow' }); const regionB = aura.terrain.addRegion(worldId, { key: 'dry-b', origin: { x: 10, y: 0, z: 0 }, terrain: { width: 8, depth: 8, segmentsX: 4, segmentsZ: 4, maxHeight: 1.9 }, heightmap: ridge(0.56, 0.03), biome: 'dry' }); const regionC = aura.terrain.addRegion(worldId, { key: 'meadow-c', origin: { x: 20, y: 0, z: 0 }, terrain: { width: 8, depth: 8, segmentsX: 4, segmentsZ: 4, maxHeight: 2.4 }, heightmap: ridge(0.74, 0.07), biome: 'meadow' }); const initialStream = aura.terrain.streamWorld(worldId, { x: 0, y: 0, z: 0 }); const worldInfo = aura.terrain.getWorldInfo(worldId); const regionInfoA = aura.terrain.getRegionInfo(regionA.regionId); const regionInfoB = aura.terrain.getRegionInfo(regionB.regionId); const regionInfoC = aura.terrain.getRegionInfo(regionC.regionId); const sampleA = aura.terrain.sampleWorldHeight(worldId, 0, 0); const sampleB = aura.terrain.sampleWorldHeight(worldId, 10, 0); const sampleC = aura.terrain.sampleWorldHeight(worldId, 20, 0); const outside = aura.terrain.sampleWorldHeight(worldId, 64, 64); const missingWorld = aura.terrain.getWorldInfo(999999); const missingRegion = aura.terrain.getRegionInfo(999999); state.assets = assets; state.worldId = worldId; state.regionIds = [regionA.regionId, regionB.regionId, regionC.regionId]; state.focus = { x: 0, y: 0, z: 0 }; state.streamHistory = []; state.samples = { a: sampleA, b: sampleB, c: sampleC, outside }; state.regionInfoAtSetup = { a: regionInfoA, b: regionInfoB, c: regionInfoC }; state.initialStream = initialStream; state.missingWorld = missingWorld; state.missingRegion = missingRegion; state.frameCount = 0; aura.camera3d.perspective(56, 0.1, 80); const updateCamera = () => { const orbit = 12; const x = state.focus.x + (Math.sin(state.frameCount * 0.08) * orbit); const z = 12 + (Math.cos(state.frameCount * 0.08) * 4); aura.camera3d.setPosition(x, 6.2, z); aura.camera3d.lookAt(state.focus.x + 2, 0.9, 0); }; const baseUpdate = aura.update; const baseDraw = aura.draw; aura.update = function (dt) { state.frameCount += 1; if (state.frameCount === 36) { state.focus = { x: 20, y: 0, z: 0 }; } const stream = aura.terrain.streamWorld(worldId, state.focus); const info = aura.terrain.getWorldInfo(worldId); state.streamHistory.push({ frame: state.frameCount, focusX: state.focus.x, changed: Number(stream?.changedRegionCount || 0), visible: Number(stream?.visibleRegionCount || 0), infoVisible: Number(info?.visibleRegionCount || 0), lastChanged: Number(info?.lastChangedRegionCount || 0) }); updateCamera(); if (typeof baseUpdate === 'function') baseUpdate(dt); }; aura.draw = function () { aura.draw3d.clear3d({ r: 0.62, g: 0.75, b: 0.9, a: 1 }); if (typeof baseDraw === 'function') baseDraw(); }; updateCamera(); return meadow?.ok === true && dry?.ok === true && Number.isInteger(worldId) && worldId > 0 && regionA?.ok === true && regionB?.ok === true && regionC?.ok === true && initialStream?.ok === true && worldInfo?.ok === true && worldInfo?.regionCount === 3 && worldInfo?.visibleRegionCount === 2 && regionInfoA?.biome === 'meadow' && regionInfoB?.biome === 'dry' && regionInfoC?.biome === 'meadow' && Array.isArray(regionInfoA?.vegetationIds) && regionInfoA.vegetationIds.length >= 1 && regionInfoC?.visible === false && Number(sampleA || 0) > 0.6 && Number(sampleB || 0) > 0.2 && Number(sampleC || 0) > 0.5 && outside === null && missingWorld?.reasonCode === 'missing_world' && missingRegion?.reasonCode === 'missing_region'; } catch (_) { return false; } })()`,
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
nativePostChecks: [
|
|
377
|
+
{
|
|
378
|
+
id: 'terrain.world-authoring.runtime.evidence',
|
|
379
|
+
expression: `(() => { try { const state = globalThis.__terrainWorldAuthoringRuntime || {}; const worldInfo = aura.terrain.getWorldInfo(state.worldId || 0); const regions = Array.isArray(state.regionIds) ? state.regionIds.map((regionId) => aura.terrain.getRegionInfo(regionId)) : []; const terrainState = aura.terrain.getSubmissionState() || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const terrainRuntime = stats.terrainRuntime || {}; const submission = stats?.scene3dRuntime?.submission || {}; const frames = Array.isArray(state.streamHistory) ? state.streamHistory : []; const early = frames.filter((frame) => frame.frame <= 10); const late = frames.filter((frame) => frame.frame >= 36); return Number.isInteger(state.worldId) && state.worldId > 0 && frames.length === 72 && early.every((frame) => frame.visible === 2 && frame.focusX === 0) && late.some((frame) => frame.changed === 2) && late.every((frame) => frame.visible === 2 && frame.focusX === 20) && worldInfo?.ok === true && worldInfo?.visibleRegionCount === 2 && worldInfo?.lastChangedRegionCount === 0 && Array.isArray(regions) && regions.length === 3 && regions[0]?.visible === false && regions[1]?.visible === true && regions[2]?.visible === true && regions[0]?.biome === 'meadow' && regions[1]?.biome === 'dry' && regions[2]?.biome === 'meadow' && Array.isArray(regions[2]?.vegetationIds) && regions[2].vegetationIds.length >= 1 && Number(terrainState.worldCount || 0) === 1 && Number(terrainState.regionCount || 0) === 3 && Number(terrainState.visibleRegionCount || 0) === 2 && Number(terrainState.biomeCount || 0) === 2 && Number(terrainState.terrainCount || 0) === 3 && Number(terrainState.vegetationLayerCount || 0) >= 3 && Number(terrainState.lastRenderVisibleTerrainCount || 0) >= 2 && Number(terrainState.lastRenderedTerrainBatchCount || 0) >= 2 && Number(terrainState.lastRenderedVegetationInstanceCount || 0) > 0 && terrainState.lastRenderReasonCode === 'terrain_render_submitted' && Number(terrainRuntime.worldCount || terrainState.worldCount || 0) === 1 && Number(submission.terrainQueueDepth || 0) === 0 && Number(submission.terrainLastRenderedTerrainBatchCount || 0) >= 2 && Number(submission.terrainLastRenderedVegetationInstanceCount || 0) > 0 && submission.terrainLastRenderReasonCode === 'terrain_render_submitted' && Number(state.samples?.a || 0) > 0.6 && Number(state.samples?.b || 0) > 0.2 && Number(state.samples?.c || 0) > 0.5 && state.samples?.outside === null && state.missingWorld?.reasonCode === 'missing_world' && state.missingRegion?.reasonCode === 'missing_region'; } catch (_) { return false; } })()`,
|
|
380
|
+
debugExpression: `(() => { try { const state = globalThis.__terrainWorldAuthoringRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state, worldInfo: aura.terrain.getWorldInfo(state.worldId || 0) || null, regions: Array.isArray(state.regionIds) ? state.regionIds.map((regionId) => aura.terrain.getRegionInfo(regionId) || null) : [], terrainState: aura.terrain.getSubmissionState() || null, scene3dRuntime: stats.scene3dRuntime || null, terrainRuntime: stats.terrainRuntime || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()`,
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
source: `
|
|
384
|
+
globalThis.__terrainWorldAuthoringRuntime = {
|
|
385
|
+
worldId: 0,
|
|
386
|
+
regionIds: [],
|
|
387
|
+
focus: { x: 0, y: 0, z: 0 },
|
|
388
|
+
streamHistory: [],
|
|
389
|
+
samples: {},
|
|
390
|
+
regionInfoAtSetup: {},
|
|
391
|
+
initialStream: null,
|
|
392
|
+
missingWorld: null,
|
|
393
|
+
missingRegion: null,
|
|
394
|
+
frameCount: 0,
|
|
395
|
+
};
|
|
396
|
+
`,
|
|
397
|
+
nativeFrames: 72,
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
id: 'threejs-scene3d-gltf-scene-import-runtime',
|
|
401
|
+
modes: ['native'],
|
|
402
|
+
namespaces: ['scene3d', 'mesh', 'material', 'debug'],
|
|
403
|
+
functions: [
|
|
404
|
+
'aura.scene3d.loadGltfScene',
|
|
405
|
+
'aura.scene3d.unloadGltfScene',
|
|
406
|
+
'aura.scene3d.getImportedScene',
|
|
407
|
+
'aura.scene3d.getImportedSceneMetadata',
|
|
408
|
+
'aura.scene3d.getImportedCameras',
|
|
409
|
+
'aura.scene3d.getImportedLights',
|
|
410
|
+
'aura.scene3d.getImportedAnimations',
|
|
411
|
+
'aura.scene3d.playImportedAnimation',
|
|
412
|
+
'aura.scene3d.submitRenderBindings',
|
|
413
|
+
'aura.scene3d.getRenderSubmissionState',
|
|
414
|
+
'aura.scene3d.removeNode',
|
|
415
|
+
'aura.scene3d.setCameraLayerMask',
|
|
416
|
+
'aura.debug.inspectorStats',
|
|
417
|
+
'aura.material.unload',
|
|
418
|
+
'aura.mesh.unload',
|
|
419
|
+
],
|
|
420
|
+
nativeChecks: [
|
|
421
|
+
{
|
|
422
|
+
id: 'scene3d.gltf-import.surface.methods',
|
|
423
|
+
expression: "(() => ['loadGltfScene','unloadGltfScene','getImportedScene','getImportedSceneMetadata','getImportedCameras','getImportedLights','getImportedAnimations','playImportedAnimation','submitRenderBindings','getRenderSubmissionState'].every((name) => typeof aura.scene3d?.[name] === 'function'))()",
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: 'scene3d.gltf-import.reason-codes',
|
|
427
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const expectReason = (invoke, allowMissingAsset = false) => { try { const entry = invoke(); const reason = reasonOf(entry); return typeof reason === 'string' && reason.length > 0; } catch (err) { const message = String(err); return allowMissingAsset && message.includes('Asset not found:'); } }; return expectReason(() => aura.scene3d.loadGltfScene(' '), true) && expectReason(() => aura.scene3d.loadGltfScene('ok.gltf', 'bad'), true) && expectReason(() => aura.scene3d.getImportedScene(999999)) && expectReason(() => aura.scene3d.getImportedScene('bad')) && expectReason(() => aura.scene3d.loadGltfScene('__missing__/scene.gltf'), true); })()",
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: 'scene3d.gltf-import.metadata-camera-light-animation-native',
|
|
431
|
+
expression: `(() => {
|
|
432
|
+
const scenePaths = [
|
|
433
|
+
'../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
434
|
+
'../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
435
|
+
'../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
436
|
+
'../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
437
|
+
'../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
438
|
+
'../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
439
|
+
'../../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
440
|
+
'src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
441
|
+
];
|
|
442
|
+
const round = (value) => Number(Number(value || 0).toFixed(6));
|
|
443
|
+
const toSortedNumericArray = (values) => (Array.isArray(values)
|
|
444
|
+
? values.map((value) => Number(value)).filter((value) => Number.isInteger(value) && value >= 0).sort((a, b) => a - b)
|
|
445
|
+
: []);
|
|
446
|
+
const toRoundedArray = (values) => (Array.isArray(values)
|
|
447
|
+
? values.map((value) => round(value))
|
|
448
|
+
: []);
|
|
449
|
+
const loadScene = () => {
|
|
450
|
+
for (const scenePath of scenePaths) {
|
|
451
|
+
let imported = null;
|
|
452
|
+
try {
|
|
453
|
+
imported = aura.scene3d.loadGltfScene(scenePath);
|
|
454
|
+
} catch (_) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (imported?.ok === true && Number.isInteger(imported.importId) && imported.importId > 0) {
|
|
458
|
+
return { scenePath, importId: imported.importId };
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return null;
|
|
462
|
+
};
|
|
463
|
+
const sample = () => {
|
|
464
|
+
const loaded = loadScene();
|
|
465
|
+
if (!loaded) return null;
|
|
466
|
+
const metadata = aura.scene3d.getImportedSceneMetadata(loaded.importId);
|
|
467
|
+
const cameras = aura.scene3d.getImportedCameras(loaded.importId);
|
|
468
|
+
const lights = aura.scene3d.getImportedLights(loaded.importId);
|
|
469
|
+
const animations = aura.scene3d.getImportedAnimations(loaded.importId);
|
|
470
|
+
const playback = aura.scene3d.playImportedAnimation(loaded.importId, 'TranslateMesh', { loop: false, speed: 1.25 });
|
|
471
|
+
const mesh = Array.isArray(metadata?.meshes) ? metadata.meshes[0] : null;
|
|
472
|
+
const material = Array.isArray(metadata?.materials) ? metadata.materials[0] : null;
|
|
473
|
+
const texture = Array.isArray(metadata?.textures) ? metadata.textures[0] : null;
|
|
474
|
+
if (typeof aura.scene3d.unloadGltfScene === 'function') {
|
|
475
|
+
try { aura.scene3d.unloadGltfScene(loaded.importId); } catch (_) {}
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
loadedPath: loaded.scenePath,
|
|
479
|
+
defaultSceneIndex: Number(metadata?.defaultSceneIndex ?? -1),
|
|
480
|
+
sceneName: typeof metadata?.scenes?.[0]?.name === 'string' ? metadata.scenes[0].name : null,
|
|
481
|
+
rootNodes: toSortedNumericArray(metadata?.scenes?.[0]?.rootNodeIndices),
|
|
482
|
+
meshName: typeof mesh?.name === 'string' ? mesh.name : null,
|
|
483
|
+
meshSourceIndex: Number(mesh?.sourceMeshIndex ?? -1),
|
|
484
|
+
meshPrimitiveCount: Number(mesh?.primitiveCount ?? 0),
|
|
485
|
+
meshHasMorphTargets: mesh?.hasMorphTargets === true,
|
|
486
|
+
meshWeights: toRoundedArray(mesh?.weights),
|
|
487
|
+
meshMorphTargetCounts: toSortedNumericArray(mesh?.primitiveMorphTargetCounts),
|
|
488
|
+
meshMaterialIndices: toSortedNumericArray(mesh?.primitiveMaterialIndices),
|
|
489
|
+
materialName: typeof material?.name === 'string' ? material.name : null,
|
|
490
|
+
materialSourceIndex: Number(material?.sourceMaterialIndex ?? -1),
|
|
491
|
+
materialBaseColor: {
|
|
492
|
+
r: round(material?.baseColor?.r),
|
|
493
|
+
g: round(material?.baseColor?.g),
|
|
494
|
+
b: round(material?.baseColor?.b),
|
|
495
|
+
a: round(material?.baseColor?.a),
|
|
496
|
+
},
|
|
497
|
+
materialEmissive: {
|
|
498
|
+
r: round(material?.emissive?.r),
|
|
499
|
+
g: round(material?.emissive?.g),
|
|
500
|
+
b: round(material?.emissive?.b),
|
|
501
|
+
},
|
|
502
|
+
materialMetallic: round(material?.metallic),
|
|
503
|
+
materialRoughness: round(material?.roughness),
|
|
504
|
+
materialAlphaMode: typeof material?.alphaMode === 'string' ? material.alphaMode : null,
|
|
505
|
+
materialAlphaCutoff: round(material?.alphaCutoff),
|
|
506
|
+
materialDoubleSided: material?.doubleSided === true,
|
|
507
|
+
baseColorTextureIndex: Number(material?.baseColorTextureIndex ?? -1),
|
|
508
|
+
metallicRoughnessTextureIndex: Number(material?.metallicRoughnessTextureIndex ?? -1),
|
|
509
|
+
normalTextureIndex: Number(material?.normalTextureIndex ?? -1),
|
|
510
|
+
normalTextureScale: round(material?.normalTextureScale),
|
|
511
|
+
occlusionTextureIndex: Number(material?.occlusionTextureIndex ?? -1),
|
|
512
|
+
occlusionTextureStrength: round(material?.occlusionTextureStrength),
|
|
513
|
+
emissiveTextureIndex: Number(material?.emissiveTextureIndex ?? -1),
|
|
514
|
+
textureName: typeof texture?.name === 'string' ? texture.name : null,
|
|
515
|
+
textureSourceIndex: Number(texture?.sourceTextureIndex ?? -1),
|
|
516
|
+
textureImageIndex: Number(texture?.sourceImageIndex ?? -1),
|
|
517
|
+
textureImageName: typeof texture?.imageName === 'string' ? texture.imageName : null,
|
|
518
|
+
textureImageUri: typeof texture?.imageUri === 'string' ? texture.imageUri : null,
|
|
519
|
+
textureSamplerIndex: Number(texture?.samplerIndex ?? -1),
|
|
520
|
+
textureMinFilter: Number(texture?.minFilter ?? -1),
|
|
521
|
+
textureMagFilter: Number(texture?.magFilter ?? -1),
|
|
522
|
+
textureWrapS: Number(texture?.wrapS ?? 0),
|
|
523
|
+
textureWrapT: Number(texture?.wrapT ?? 0),
|
|
524
|
+
cameraName: typeof cameras?.[0]?.name === 'string' ? cameras[0].name : null,
|
|
525
|
+
cameraType: typeof cameras?.[0]?.type === 'string' ? cameras[0].type : null,
|
|
526
|
+
cameraNode: Number(cameras?.[0]?.nodeIndex ?? -1),
|
|
527
|
+
cameraNear: round(cameras?.[0]?.near),
|
|
528
|
+
cameraFar: round(cameras?.[0]?.far),
|
|
529
|
+
lightName: typeof lights?.[0]?.name === 'string' ? lights[0].name : null,
|
|
530
|
+
lightType: typeof lights?.[0]?.type === 'string' ? lights[0].type : null,
|
|
531
|
+
lightNode: Number(lights?.[0]?.nodeIndex ?? -1),
|
|
532
|
+
lightIntensity: round(lights?.[0]?.intensity),
|
|
533
|
+
lightColor: { r: round(lights?.[0]?.color?.r), g: round(lights?.[0]?.color?.g), b: round(lights?.[0]?.color?.b) },
|
|
534
|
+
animationName: typeof animations?.[0]?.name === 'string' ? animations[0].name : null,
|
|
535
|
+
animationDuration: round(animations?.[0]?.duration),
|
|
536
|
+
animationChannelCount: Array.isArray(animations?.[0]?.channels) ? animations[0].channels.length : 0,
|
|
537
|
+
animationTargetNode: Number(animations?.[0]?.channels?.[0]?.targetNode ?? -1),
|
|
538
|
+
animationPath: typeof animations?.[0]?.channels?.[0]?.path === 'string' ? animations[0].channels[0].path : null,
|
|
539
|
+
animationKeyframeCount: Number(animations?.[0]?.channels?.[0]?.keyframeCount ?? 0),
|
|
540
|
+
playbackName: typeof playback?.name === 'string' ? playback.name : null,
|
|
541
|
+
playbackDuration: round(playback?.duration),
|
|
542
|
+
playbackLoop: playback?.loop === false,
|
|
543
|
+
playbackSpeed: round(playback?.speed),
|
|
544
|
+
playbackChannelCount: Number(playback?.channelCount ?? 0),
|
|
545
|
+
};
|
|
546
|
+
};
|
|
547
|
+
const first = sample();
|
|
548
|
+
const second = sample();
|
|
549
|
+
return !!first
|
|
550
|
+
&& !!second
|
|
551
|
+
&& JSON.stringify(first) === JSON.stringify(second)
|
|
552
|
+
&& first.defaultSceneIndex === 0
|
|
553
|
+
&& first.sceneName === 'Gameplay'
|
|
554
|
+
&& JSON.stringify(first.rootNodes) === '[0]'
|
|
555
|
+
&& first.meshName === 'MorphHull'
|
|
556
|
+
&& first.meshSourceIndex === 0
|
|
557
|
+
&& first.meshPrimitiveCount === 1
|
|
558
|
+
&& first.meshHasMorphTargets === true
|
|
559
|
+
&& JSON.stringify(first.meshWeights) === '[0.25,0.75]'
|
|
560
|
+
&& JSON.stringify(first.meshMorphTargetCounts) === '[2]'
|
|
561
|
+
&& JSON.stringify(first.meshMaterialIndices) === '[0]'
|
|
562
|
+
&& first.materialName === 'Hull'
|
|
563
|
+
&& first.materialSourceIndex === 0
|
|
564
|
+
&& JSON.stringify(first.materialBaseColor) === '{"r":0.7,"g":0.75,"b":0.9,"a":0.85}'
|
|
565
|
+
&& JSON.stringify(first.materialEmissive) === '{"r":0.1,"g":0.2,"b":0.3}'
|
|
566
|
+
&& first.materialMetallic === 0.2
|
|
567
|
+
&& first.materialRoughness === 0.65
|
|
568
|
+
&& first.materialAlphaMode === 'BLEND'
|
|
569
|
+
&& first.materialAlphaCutoff === 0.4
|
|
570
|
+
&& first.materialDoubleSided === true
|
|
571
|
+
&& first.baseColorTextureIndex === 0
|
|
572
|
+
&& first.metallicRoughnessTextureIndex === 0
|
|
573
|
+
&& first.normalTextureIndex === 0
|
|
574
|
+
&& first.normalTextureScale === 0.5
|
|
575
|
+
&& first.occlusionTextureIndex === 0
|
|
576
|
+
&& first.occlusionTextureStrength === 0.8
|
|
577
|
+
&& first.emissiveTextureIndex === 0
|
|
578
|
+
&& first.textureName === 'HullTex'
|
|
579
|
+
&& first.textureSourceIndex === 0
|
|
580
|
+
&& first.textureImageIndex === 0
|
|
581
|
+
&& first.textureImageName === 'HullImage'
|
|
582
|
+
&& first.textureImageUri === 'textures/hull-base.png'
|
|
583
|
+
&& first.textureSamplerIndex === 0
|
|
584
|
+
&& first.textureMinFilter === 9987
|
|
585
|
+
&& first.textureMagFilter === 9729
|
|
586
|
+
&& first.textureWrapS === 33071
|
|
587
|
+
&& first.textureWrapT === 33648
|
|
588
|
+
&& first.cameraName === 'MainCam'
|
|
589
|
+
&& first.cameraType === 'perspective'
|
|
590
|
+
&& first.cameraNode === 1
|
|
591
|
+
&& first.cameraNear === 0.1
|
|
592
|
+
&& first.cameraFar === 250
|
|
593
|
+
&& first.lightName === 'Sun'
|
|
594
|
+
&& first.lightType === 'directional'
|
|
595
|
+
&& first.lightNode === 2
|
|
596
|
+
&& first.lightIntensity === 3
|
|
597
|
+
&& JSON.stringify(first.lightColor) === '{"r":1,"g":0.95,"b":0.9}'
|
|
598
|
+
&& first.animationName === 'TranslateMesh'
|
|
599
|
+
&& first.animationDuration === 2
|
|
600
|
+
&& first.animationChannelCount === 1
|
|
601
|
+
&& first.animationTargetNode === 3
|
|
602
|
+
&& first.animationPath === 'translation'
|
|
603
|
+
&& first.animationKeyframeCount === 3
|
|
604
|
+
&& first.playbackName === 'TranslateMesh'
|
|
605
|
+
&& first.playbackDuration === 2
|
|
606
|
+
&& first.playbackLoop === true
|
|
607
|
+
&& first.playbackSpeed === 1.25
|
|
608
|
+
&& first.playbackChannelCount === 1;
|
|
609
|
+
})()`,
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
id: 'scene3d.gltf-import.material-texture-fidelity-native',
|
|
613
|
+
expression: `(() => {
|
|
614
|
+
const scenePaths = [
|
|
615
|
+
'../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
616
|
+
'../../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
617
|
+
'../../../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
618
|
+
'../../../../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
619
|
+
'../../../../../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
620
|
+
'../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
621
|
+
'../../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
622
|
+
'src/cli/test-fixtures/scene3d/import-scene-material-fidelity.gltf',
|
|
623
|
+
];
|
|
624
|
+
const round = (value) => Number(Number(value || 0).toFixed(6));
|
|
625
|
+
const vec2 = (value) => value ? { x: round(value.x), y: round(value.y) } : null;
|
|
626
|
+
const vec3 = (value) => value ? { r: round(value.r), g: round(value.g), b: round(value.b) } : null;
|
|
627
|
+
const loadScene = () => {
|
|
628
|
+
for (const scenePath of scenePaths) {
|
|
629
|
+
let imported = null;
|
|
630
|
+
try {
|
|
631
|
+
imported = aura.scene3d.loadGltfScene(scenePath);
|
|
632
|
+
} catch (_) {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
if (imported?.ok === true && Number.isInteger(imported.importId) && imported.importId > 0) {
|
|
636
|
+
return { scenePath, importId: imported.importId };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return null;
|
|
640
|
+
};
|
|
641
|
+
const unload = (importId) => {
|
|
642
|
+
if (!Number.isInteger(importId) || importId <= 0) return;
|
|
643
|
+
try { aura.scene3d.unloadGltfScene(importId); } catch (_) {}
|
|
644
|
+
};
|
|
645
|
+
const sample = () => {
|
|
646
|
+
const loaded = loadScene();
|
|
647
|
+
if (!loaded) return null;
|
|
648
|
+
const metadata = aura.scene3d.getImportedSceneMetadata(loaded.importId);
|
|
649
|
+
const material = Array.isArray(metadata?.materials) ? metadata.materials[0] : null;
|
|
650
|
+
const texture = Array.isArray(metadata?.textures) ? metadata.textures[0] : null;
|
|
651
|
+
unload(loaded.importId);
|
|
652
|
+
return {
|
|
653
|
+
loadedPath: loaded.scenePath,
|
|
654
|
+
sceneName: typeof metadata?.scenes?.[0]?.name === 'string' ? metadata.scenes[0].name : null,
|
|
655
|
+
extensionsUsed: Array.isArray(metadata?.extensionsUsed) ? metadata.extensionsUsed.slice() : [],
|
|
656
|
+
extensionsRequired: Array.isArray(metadata?.extensionsRequired) ? metadata.extensionsRequired.slice() : [],
|
|
657
|
+
unsupportedExtensionsUsed: Array.isArray(metadata?.unsupportedExtensionsUsed) ? metadata.unsupportedExtensionsUsed.slice() : [],
|
|
658
|
+
unsupportedExtensionsRequired: Array.isArray(metadata?.unsupportedExtensionsRequired) ? metadata.unsupportedExtensionsRequired.slice() : [],
|
|
659
|
+
materialName: typeof material?.name === 'string' ? material.name : null,
|
|
660
|
+
baseColorTextureIndex: Number(material?.baseColorTextureIndex ?? -1),
|
|
661
|
+
baseColorTexCoord: Number(material?.baseColorTexture?.texCoord ?? -1),
|
|
662
|
+
baseColorTransform: {
|
|
663
|
+
offset: vec2(material?.baseColorTexture?.transform?.offset),
|
|
664
|
+
rotation: round(material?.baseColorTexture?.transform?.rotation),
|
|
665
|
+
scale: vec2(material?.baseColorTexture?.transform?.scale),
|
|
666
|
+
texCoord: Number(material?.baseColorTexture?.transform?.texCoord ?? -1),
|
|
667
|
+
},
|
|
668
|
+
metallicRoughnessTexCoord: Number(material?.metallicRoughnessTexture?.texCoord ?? -1),
|
|
669
|
+
normalTextureTexCoord: Number(material?.normalTexture?.texture?.texCoord ?? -1),
|
|
670
|
+
normalTextureScale: round(material?.normalTexture?.scale),
|
|
671
|
+
normalTransform: {
|
|
672
|
+
offset: vec2(material?.normalTexture?.texture?.transform?.offset),
|
|
673
|
+
rotation: round(material?.normalTexture?.texture?.transform?.rotation),
|
|
674
|
+
scale: vec2(material?.normalTexture?.texture?.transform?.scale),
|
|
675
|
+
texCoord: material?.normalTexture?.texture?.transform?.texCoord ?? null,
|
|
676
|
+
},
|
|
677
|
+
occlusionTextureTexCoord: Number(material?.occlusionTexture?.texture?.texCoord ?? -1),
|
|
678
|
+
occlusionTextureStrength: round(material?.occlusionTexture?.strength),
|
|
679
|
+
occlusionTransformRotation: round(material?.occlusionTexture?.texture?.transform?.rotation),
|
|
680
|
+
emissiveTextureTexCoord: Number(material?.emissiveTexture?.texCoord ?? -1),
|
|
681
|
+
extensions: {
|
|
682
|
+
unlit: material?.extensions?.unlit === true,
|
|
683
|
+
emissiveStrength: round(material?.extensions?.emissiveStrength),
|
|
684
|
+
clearcoatFactor: round(material?.extensions?.clearcoat?.factor),
|
|
685
|
+
clearcoatTextureTexCoord: Number(material?.extensions?.clearcoat?.texture?.texCoord ?? -1),
|
|
686
|
+
clearcoatRoughnessFactor: round(material?.extensions?.clearcoat?.roughnessFactor),
|
|
687
|
+
clearcoatRoughnessTextureTexCoord: Number(material?.extensions?.clearcoat?.roughnessTexture?.texCoord ?? -1),
|
|
688
|
+
clearcoatNormalScale: round(material?.extensions?.clearcoat?.normalTexture?.scale),
|
|
689
|
+
clearcoatNormalTexCoord: Number(material?.extensions?.clearcoat?.normalTexture?.texture?.texCoord ?? -1),
|
|
690
|
+
sheenColorFactor: vec3(material?.extensions?.sheen?.colorFactor),
|
|
691
|
+
sheenColorTextureTexCoord: Number(material?.extensions?.sheen?.colorTexture?.texCoord ?? -1),
|
|
692
|
+
sheenRoughnessFactor: round(material?.extensions?.sheen?.roughnessFactor),
|
|
693
|
+
sheenRoughnessTextureTexCoord: Number(material?.extensions?.sheen?.roughnessTexture?.texCoord ?? -1),
|
|
694
|
+
transmissionFactor: round(material?.extensions?.transmission?.factor),
|
|
695
|
+
transmissionTextureTexCoord: Number(material?.extensions?.transmission?.texture?.texCoord ?? -1),
|
|
696
|
+
ior: round(material?.extensions?.ior),
|
|
697
|
+
},
|
|
698
|
+
textureName: typeof texture?.name === 'string' ? texture.name : null,
|
|
699
|
+
textureImageUri: typeof texture?.imageUri === 'string' ? texture.imageUri : null,
|
|
700
|
+
textureWrapS: Number(texture?.wrapS ?? 0),
|
|
701
|
+
textureWrapT: Number(texture?.wrapT ?? 0),
|
|
702
|
+
};
|
|
703
|
+
};
|
|
704
|
+
const first = sample();
|
|
705
|
+
const second = sample();
|
|
706
|
+
return !!first
|
|
707
|
+
&& !!second
|
|
708
|
+
&& JSON.stringify(first) === JSON.stringify(second)
|
|
709
|
+
&& first.sceneName === 'MaterialFidelity'
|
|
710
|
+
&& JSON.stringify(first.extensionsUsed) === '["KHR_materials_clearcoat","KHR_materials_emissive_strength","KHR_materials_ior","KHR_materials_sheen","KHR_materials_specular","KHR_materials_transmission","KHR_materials_unlit","KHR_texture_basisu","KHR_texture_transform"]'
|
|
711
|
+
&& JSON.stringify(first.extensionsRequired) === '["KHR_materials_ior","KHR_texture_transform"]'
|
|
712
|
+
&& JSON.stringify(first.unsupportedExtensionsUsed) === '["KHR_texture_basisu"]'
|
|
713
|
+
&& JSON.stringify(first.unsupportedExtensionsRequired) === '[]'
|
|
714
|
+
&& first.materialName === 'FidelityHull'
|
|
715
|
+
&& first.baseColorTextureIndex === 0
|
|
716
|
+
&& first.baseColorTexCoord === 1
|
|
717
|
+
&& JSON.stringify(first.baseColorTransform) === '{"offset":{"x":0.25,"y":0.5},"rotation":0.75,"scale":{"x":1.5,"y":0.5},"texCoord":0}'
|
|
718
|
+
&& first.metallicRoughnessTexCoord === 2
|
|
719
|
+
&& first.normalTextureTexCoord === 3
|
|
720
|
+
&& first.normalTextureScale === 0.5
|
|
721
|
+
&& JSON.stringify(first.normalTransform) === '{"offset":{"x":0.1,"y":0.2},"rotation":0,"scale":{"x":0.8,"y":0.9},"texCoord":null}'
|
|
722
|
+
&& first.occlusionTextureTexCoord === 4
|
|
723
|
+
&& first.occlusionTextureStrength === 0.8
|
|
724
|
+
&& first.occlusionTransformRotation === 0.2
|
|
725
|
+
&& first.emissiveTextureTexCoord === 5
|
|
726
|
+
&& first.extensions.unlit === true
|
|
727
|
+
&& first.extensions.emissiveStrength === 2.5
|
|
728
|
+
&& first.extensions.clearcoatFactor === 0.6
|
|
729
|
+
&& first.extensions.clearcoatTextureTexCoord === 6
|
|
730
|
+
&& first.extensions.clearcoatRoughnessFactor === 0.35
|
|
731
|
+
&& first.extensions.clearcoatRoughnessTextureTexCoord === 7
|
|
732
|
+
&& first.extensions.clearcoatNormalScale === 0.25
|
|
733
|
+
&& first.extensions.clearcoatNormalTexCoord === 8
|
|
734
|
+
&& JSON.stringify(first.extensions.sheenColorFactor) === '{"r":0.55,"g":0.3,"b":0.1}'
|
|
735
|
+
&& first.extensions.sheenColorTextureTexCoord === 10
|
|
736
|
+
&& first.extensions.sheenRoughnessFactor === 0.45
|
|
737
|
+
&& first.extensions.sheenRoughnessTextureTexCoord === 11
|
|
738
|
+
&& first.extensions.transmissionFactor === 0.45
|
|
739
|
+
&& first.extensions.transmissionTextureTexCoord === 9
|
|
740
|
+
&& first.extensions.ior === 1.7
|
|
741
|
+
&& first.textureName === 'FidelityTex'
|
|
742
|
+
&& first.textureImageUri === 'textures/fidelity-base.png'
|
|
743
|
+
&& first.textureWrapS === 33071
|
|
744
|
+
&& first.textureWrapT === 33648;
|
|
745
|
+
})()`,
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
id: 'scene3d.gltf-import.imported-animation-playback-runtime',
|
|
749
|
+
expression: `(() => {
|
|
750
|
+
const scenePaths = [
|
|
751
|
+
'../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
752
|
+
'../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
753
|
+
'../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
754
|
+
'../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
755
|
+
'../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
756
|
+
'../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
757
|
+
'../../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
758
|
+
'src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
759
|
+
];
|
|
760
|
+
const importOptions = { bindRenderNodes: true, layer: 2, visible: true, cull: false };
|
|
761
|
+
const metadataOnlyOptions = { bindRenderNodes: false };
|
|
762
|
+
const round = (value) => Number(Number(value || 0).toFixed(6));
|
|
763
|
+
const loadScene = (options) => {
|
|
764
|
+
for (const scenePath of scenePaths) {
|
|
765
|
+
let imported = null;
|
|
766
|
+
try {
|
|
767
|
+
imported = aura.scene3d.loadGltfScene(scenePath, options);
|
|
768
|
+
} catch (_) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (imported?.ok === true && Number.isInteger(imported.importId) && imported.importId > 0) {
|
|
772
|
+
return { scenePath, importId: imported.importId };
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return null;
|
|
776
|
+
};
|
|
777
|
+
const unload = (importId) => {
|
|
778
|
+
if (!Number.isInteger(importId) || importId <= 0) return;
|
|
779
|
+
if (typeof aura.scene3d.unloadGltfScene !== 'function') return;
|
|
780
|
+
try { aura.scene3d.unloadGltfScene(importId); } catch (_) {}
|
|
781
|
+
};
|
|
782
|
+
const sample = () => {
|
|
783
|
+
const loaded = loadScene(importOptions);
|
|
784
|
+
if (!loaded) return null;
|
|
785
|
+
const metadataOnly = loadScene(metadataOnlyOptions);
|
|
786
|
+
const invalidImport = aura.scene3d.playImportedAnimation(0, 'TranslateMesh');
|
|
787
|
+
const invalidName = aura.scene3d.playImportedAnimation(loaded.importId, ' ');
|
|
788
|
+
const missingAnimation = aura.scene3d.playImportedAnimation(loaded.importId, 'MissingClip');
|
|
789
|
+
const metadataOnlyPlayback = metadataOnly
|
|
790
|
+
? aura.scene3d.playImportedAnimation(metadataOnly.importId, 'TranslateMesh')
|
|
791
|
+
: { reason: null };
|
|
792
|
+
const playback = aura.scene3d.playImportedAnimation(loaded.importId, 'TranslateMesh', {
|
|
793
|
+
loop: false,
|
|
794
|
+
speed: 1.25,
|
|
795
|
+
});
|
|
796
|
+
const stateStart = playback?.ok === true ? aura.scene3d.getClipState(playback.clipId) : null;
|
|
797
|
+
const stepA = playback?.ok === true ? aura.scene3d.updateClips(0.5) : null;
|
|
798
|
+
const stateA = playback?.ok === true ? aura.scene3d.getClipState(playback.clipId) : null;
|
|
799
|
+
const stepB = playback?.ok === true ? aura.scene3d.updateClips(1.2) : null;
|
|
800
|
+
const stateB = playback?.ok === true ? aura.scene3d.getClipState(playback.clipId) : null;
|
|
801
|
+
aura.debug.enableInspector(true);
|
|
802
|
+
const stats = aura.debug.inspectorStats() || {};
|
|
803
|
+
aura.debug.enableInspector(false);
|
|
804
|
+
unload(metadataOnly?.importId);
|
|
805
|
+
unload(loaded.importId);
|
|
806
|
+
const animation = stats?.scene3dRuntime?.animation || {};
|
|
807
|
+
return {
|
|
808
|
+
invalidImportReason: typeof invalidImport?.reason === 'string' ? invalidImport.reason : null,
|
|
809
|
+
invalidNameReason: typeof invalidName?.reason === 'string' ? invalidName.reason : null,
|
|
810
|
+
missingAnimationReason: typeof missingAnimation?.reason === 'string' ? missingAnimation.reason : null,
|
|
811
|
+
metadataOnlyReason: typeof metadataOnlyPlayback?.reason === 'string'
|
|
812
|
+
? metadataOnlyPlayback.reason
|
|
813
|
+
: null,
|
|
814
|
+
playbackOk: playback?.ok === true,
|
|
815
|
+
playbackName: typeof playback?.name === 'string' ? playback.name : null,
|
|
816
|
+
playbackLoop: playback?.loop === false,
|
|
817
|
+
playbackSpeed: round(playback?.speed),
|
|
818
|
+
playbackChannelCount: Number(playback?.channelCount || 0),
|
|
819
|
+
playbackTargetNode: Number(playback?.targetNode ?? -1),
|
|
820
|
+
playbackTargetPath: typeof playback?.targetPath === 'string' ? playback.targetPath : null,
|
|
821
|
+
clipIdPositive: Number.isInteger(playback?.clipId) && playback.clipId > 0,
|
|
822
|
+
nodeIdPositive: Number.isInteger(playback?.nodeId) && playback.nodeId > 0,
|
|
823
|
+
stateStartTime: round(stateStart?.time),
|
|
824
|
+
stateStartPlaying: stateStart?.playing === true,
|
|
825
|
+
stepAOk: stepA?.ok === true,
|
|
826
|
+
stateATime: round(stateA?.time),
|
|
827
|
+
stateAProgress: round(stateA?.progress),
|
|
828
|
+
stateAPlaying: stateA?.playing === true,
|
|
829
|
+
stepBOk: stepB?.ok === true,
|
|
830
|
+
stateBTime: round(stateB?.time),
|
|
831
|
+
stateBProgress: round(stateB?.progress),
|
|
832
|
+
stateBCompleted: stateB?.completed === true,
|
|
833
|
+
stateBPlaying: stateB?.playing === false,
|
|
834
|
+
stateBLoops: Number(stateB?.loops || 0),
|
|
835
|
+
anim: {
|
|
836
|
+
clipCount: Number(animation.clipCount || 0),
|
|
837
|
+
playingClipCount: Number(animation.playingClipCount || 0),
|
|
838
|
+
activeWeightSum: round(animation.activeWeightSum),
|
|
839
|
+
completeEventsDispatched: Number(animation.completeEventsDispatched || 0) > 0 ? 1 : 0,
|
|
840
|
+
blendEventsDispatched: Number(animation.blendEventsDispatched || 0) > 0 ? 1 : 0,
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
};
|
|
844
|
+
const first = sample();
|
|
845
|
+
const second = sample();
|
|
846
|
+
return !!first
|
|
847
|
+
&& !!second
|
|
848
|
+
&& JSON.stringify(first) === JSON.stringify(second)
|
|
849
|
+
&& first.invalidImportReason === 'invalid_import_id'
|
|
850
|
+
&& first.invalidNameReason === 'invalid_animation_name'
|
|
851
|
+
&& first.missingAnimationReason === 'missing_imported_animation'
|
|
852
|
+
&& first.metadataOnlyReason === 'scene_import_metadata_only'
|
|
853
|
+
&& first.playbackOk === true
|
|
854
|
+
&& first.playbackName === 'TranslateMesh'
|
|
855
|
+
&& first.playbackLoop === true
|
|
856
|
+
&& first.playbackSpeed === 1.25
|
|
857
|
+
&& first.playbackChannelCount === 1
|
|
858
|
+
&& first.playbackTargetNode === 3
|
|
859
|
+
&& first.playbackTargetPath === 'translation'
|
|
860
|
+
&& first.clipIdPositive === true
|
|
861
|
+
&& first.nodeIdPositive === true
|
|
862
|
+
&& first.stateStartTime === 0
|
|
863
|
+
&& first.stateStartPlaying === true
|
|
864
|
+
&& first.stepAOk === true
|
|
865
|
+
&& first.stateATime === 0.625
|
|
866
|
+
&& first.stateAProgress === 0.3125
|
|
867
|
+
&& first.stateAPlaying === true
|
|
868
|
+
&& first.stepBOk === true
|
|
869
|
+
&& first.stateBTime === 2
|
|
870
|
+
&& first.stateBProgress === 1
|
|
871
|
+
&& first.stateBCompleted === true
|
|
872
|
+
&& first.stateBPlaying === true
|
|
873
|
+
&& first.stateBLoops === 0
|
|
874
|
+
&& first.anim.clipCount === 1
|
|
875
|
+
&& first.anim.playingClipCount === 0
|
|
876
|
+
&& first.anim.activeWeightSum === 1
|
|
877
|
+
&& first.anim.completeEventsDispatched === 1
|
|
878
|
+
&& first.anim.blendEventsDispatched === 0;
|
|
879
|
+
})()`,
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
id: 'scene3d.gltf-import.imported-morph-runtime-setup',
|
|
883
|
+
expression: `(() => {
|
|
884
|
+
const scenePaths = [
|
|
885
|
+
'../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
886
|
+
'../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
887
|
+
'../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
888
|
+
'../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
889
|
+
'../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
890
|
+
'../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
891
|
+
'../../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
892
|
+
'src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
893
|
+
];
|
|
894
|
+
const importOptions = { bindRenderNodes: true, layer: 2, visible: true, cull: false };
|
|
895
|
+
const round = (value) => Number(Number(value || 0).toFixed(6));
|
|
896
|
+
const roundArray = (values) => (Array.isArray(values) ? values.map((value) => round(value)) : []);
|
|
897
|
+
const baseState = globalThis.__importedMorphRuntime || {};
|
|
898
|
+
const loadScene = () => {
|
|
899
|
+
for (const scenePath of scenePaths) {
|
|
900
|
+
let imported = null;
|
|
901
|
+
try {
|
|
902
|
+
imported = aura.scene3d.loadGltfScene(scenePath, importOptions);
|
|
903
|
+
} catch (_) {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (imported?.ok === true && Number.isInteger(imported.importId) && imported.importId > 0) {
|
|
907
|
+
return { scenePath, importId: imported.importId };
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return null;
|
|
911
|
+
};
|
|
912
|
+
const loaded = loadScene();
|
|
913
|
+
if (!loaded) return false;
|
|
914
|
+
const queried = aura.scene3d.getImportedScene(loaded.importId);
|
|
915
|
+
if (!queried || queried.ok !== true) return false;
|
|
916
|
+
const morphBinding = (Array.isArray(queried.primitiveBindings) ? queried.primitiveBindings : [])
|
|
917
|
+
.find((entry) => entry?.hasMorphTargets === true) || null;
|
|
918
|
+
const runtimeBinding = queried.materials?.[0]?.runtimeBinding || null;
|
|
919
|
+
const runtimeTexture = queried.textures?.[0] || null;
|
|
920
|
+
aura.scene3d.setCameraLayerMask(1 << 2);
|
|
921
|
+
const baseDraw = typeof aura.draw === 'function' ? aura.draw.bind(aura) : function () {};
|
|
922
|
+
aura.draw = function () {
|
|
923
|
+
aura.scene3d.submitRenderBindings();
|
|
924
|
+
baseDraw();
|
|
925
|
+
};
|
|
926
|
+
globalThis.__importedMorphRuntime = {
|
|
927
|
+
...baseState,
|
|
928
|
+
importId: loaded.importId,
|
|
929
|
+
scenePath: loaded.scenePath,
|
|
930
|
+
};
|
|
931
|
+
return queried.runtimeReasonCode === 'scene_import_runtime_bound'
|
|
932
|
+
&& Number(queried.importedMorphPrimitiveCount || 0) === 1
|
|
933
|
+
&& Number(queried.boundMorphPrimitiveCount || 0) === 1
|
|
934
|
+
&& Number(queried.morphTargetCount || 0) === 2
|
|
935
|
+
&& Number(queried.nonZeroMorphWeightPrimitiveCount || 0) === 1
|
|
936
|
+
&& morphBinding?.primitiveIndex === 0
|
|
937
|
+
&& Number(morphBinding?.morphTargetCount || 0) === 2
|
|
938
|
+
&& morphBinding?.hasNonZeroMorphWeights === true
|
|
939
|
+
&& JSON.stringify(roundArray(morphBinding?.weights || [])) === '[0.25,0.75]'
|
|
940
|
+
&& runtimeBinding?.alphaMode === 'blend'
|
|
941
|
+
&& round(runtimeBinding?.alphaCutoff) === 0.4
|
|
942
|
+
&& runtimeBinding?.doubleSided === true
|
|
943
|
+
&& runtimeBinding?.textureFilter === 'linear'
|
|
944
|
+
&& JSON.stringify(Array.isArray(runtimeBinding?.omissions) ? runtimeBinding.omissions.slice().sort() : []) === '[\"normal_texture_scale_unapplied\",\"texture_sampler_modes_quantized\"]'
|
|
945
|
+
&& Number(runtimeTexture?.sourceTextureIndex || 0) === 0
|
|
946
|
+
&& runtimeTexture?.textureFilter === 'linear'
|
|
947
|
+
&& runtimeTexture?.runtimeBindable === true
|
|
948
|
+
&& runtimeTexture?.reasonCode === 'scene_texture_runtime_bindable'
|
|
949
|
+
&& String(runtimeTexture?.resolvedPath || '').endsWith('textures/hull-base.png');
|
|
950
|
+
})()`,
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
id: 'scene3d.gltf-import.deterministic-hierarchy-material-bindings',
|
|
954
|
+
expression: `(() => {
|
|
955
|
+
const scenePaths = [
|
|
956
|
+
'../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
957
|
+
'../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
958
|
+
'../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
959
|
+
'../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
960
|
+
'../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
961
|
+
'../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
962
|
+
'../../../../../../../../../src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
963
|
+
'src/cli/test-fixtures/scene3d/import-scene-rich.gltf',
|
|
964
|
+
];
|
|
965
|
+
const importOptions = { bindRenderNodes: true, layer: 2, visible: true, cull: false };
|
|
966
|
+
const toSortedNumericArray = (values) => (Array.isArray(values)
|
|
967
|
+
? values.map((value) => Number(value)).filter((value) => Number.isInteger(value) && value >= 0).sort((a, b) => a - b)
|
|
968
|
+
: []);
|
|
969
|
+
const normalizeColor = (color) => ({
|
|
970
|
+
r: Number((Number(color?.r ?? 0)).toFixed(6)),
|
|
971
|
+
g: Number((Number(color?.g ?? 0)).toFixed(6)),
|
|
972
|
+
b: Number((Number(color?.b ?? 0)).toFixed(6)),
|
|
973
|
+
a: Number((Number(color?.a ?? 0)).toFixed(6)),
|
|
974
|
+
});
|
|
975
|
+
const loadScene = () => {
|
|
976
|
+
for (const scenePath of scenePaths) {
|
|
977
|
+
let imported = null;
|
|
978
|
+
try {
|
|
979
|
+
imported = aura.scene3d.loadGltfScene(scenePath, importOptions);
|
|
980
|
+
} catch (_) {
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if (imported?.ok === true && Number.isInteger(imported.importId) && imported.importId > 0) {
|
|
984
|
+
return { scenePath, importId: imported.importId };
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return null;
|
|
988
|
+
};
|
|
989
|
+
const sample = () => {
|
|
990
|
+
const loaded = loadScene();
|
|
991
|
+
if (!loaded) return null;
|
|
992
|
+
const queried = aura.scene3d.getImportedScene(loaded.importId);
|
|
993
|
+
if (!queried || queried.ok !== true) {
|
|
994
|
+
if (typeof aura.scene3d.unloadGltfScene === 'function') {
|
|
995
|
+
try { aura.scene3d.unloadGltfScene(loaded.importId); } catch (_) {}
|
|
996
|
+
}
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
const nodeHandles = (Array.isArray(queried.nodeHandles) ? queried.nodeHandles : []).map((entry) => ({
|
|
1000
|
+
sourceNodeIndex: Number(entry?.sourceNodeIndex || 0),
|
|
1001
|
+
parentSourceNodeIndex: Number.isInteger(entry?.parentSourceNodeIndex) ? Number(entry.parentSourceNodeIndex) : null,
|
|
1002
|
+
nodeId: Number(entry?.nodeId || 0),
|
|
1003
|
+
parentNodeId: Number.isInteger(entry?.parentNodeId) ? Number(entry.parentNodeId) : null,
|
|
1004
|
+
})).sort((a, b) => (a.sourceNodeIndex - b.sourceNodeIndex) || (a.nodeId - b.nodeId));
|
|
1005
|
+
const primitiveBindings = (Array.isArray(queried.primitiveBindings) ? queried.primitiveBindings : []).map((entry) => ({
|
|
1006
|
+
primitiveIndex: Number(entry?.primitiveIndex || 0),
|
|
1007
|
+
nodeId: Number(entry?.nodeId || 0),
|
|
1008
|
+
meshHandle: Number(entry?.meshHandle || 0),
|
|
1009
|
+
materialHandle: Number(entry?.materialHandle || 0),
|
|
1010
|
+
sourceMeshIndex: Number(entry?.sourceMeshIndex || 0),
|
|
1011
|
+
sourcePrimitiveIndex: Number(entry?.sourcePrimitiveIndex || 0),
|
|
1012
|
+
materialIndex: Number.isInteger(entry?.materialIndex) ? Number(entry.materialIndex) : null,
|
|
1013
|
+
})).sort((a, b) => (a.primitiveIndex - b.primitiveIndex) || (a.nodeId - b.nodeId));
|
|
1014
|
+
const materials = (Array.isArray(queried.materials) ? queried.materials : []).map((entry) => ({
|
|
1015
|
+
sourceMaterialIndex: Number(entry?.sourceMaterialIndex || 0),
|
|
1016
|
+
materialHandle: Number(entry?.materialHandle || 0),
|
|
1017
|
+
name: typeof entry?.name === 'string' ? entry.name : null,
|
|
1018
|
+
baseColor: normalizeColor(entry?.baseColor),
|
|
1019
|
+
metallic: Number((Number(entry?.metallic ?? 0)).toFixed(6)),
|
|
1020
|
+
roughness: Number((Number(entry?.roughness ?? 0)).toFixed(6)),
|
|
1021
|
+
alphaMode: typeof entry?.alphaMode === 'string' ? entry.alphaMode : null,
|
|
1022
|
+
alphaCutoff: Number((Number(entry?.alphaCutoff ?? 0)).toFixed(6)),
|
|
1023
|
+
doubleSided: entry?.doubleSided === true,
|
|
1024
|
+
textureFilter: typeof entry?.textureFilter === 'string' ? entry.textureFilter : null,
|
|
1025
|
+
runtimeBinding: entry?.runtimeBinding ? {
|
|
1026
|
+
alphaMode: typeof entry.runtimeBinding.alphaMode === 'string' ? entry.runtimeBinding.alphaMode : null,
|
|
1027
|
+
alphaCutoff: Number((Number(entry.runtimeBinding.alphaCutoff ?? 0)).toFixed(6)),
|
|
1028
|
+
doubleSided: entry.runtimeBinding.doubleSided === true,
|
|
1029
|
+
textureFilter: typeof entry.runtimeBinding.textureFilter === 'string' ? entry.runtimeBinding.textureFilter : null,
|
|
1030
|
+
} : null,
|
|
1031
|
+
})).sort((a, b) => a.sourceMaterialIndex - b.sourceMaterialIndex);
|
|
1032
|
+
const textures = (Array.isArray(queried.textures) ? queried.textures : []).map((entry) => ({
|
|
1033
|
+
sourceTextureIndex: Number(entry?.sourceTextureIndex || 0),
|
|
1034
|
+
imageUri: typeof entry?.imageUri === 'string' ? entry.imageUri : null,
|
|
1035
|
+
textureFilter: typeof entry?.textureFilter === 'string' ? entry.textureFilter : null,
|
|
1036
|
+
runtimeBindable: entry?.runtimeBindable === true,
|
|
1037
|
+
reasonCode: typeof entry?.reasonCode === 'string' ? entry.reasonCode : null,
|
|
1038
|
+
resolvedAsset: String(entry?.resolvedPath || '').endsWith('textures/hull-base.png'),
|
|
1039
|
+
})).sort((a, b) => a.sourceTextureIndex - b.sourceTextureIndex);
|
|
1040
|
+
const minNodeId = nodeHandles.reduce((min, entry) => (entry.nodeId > 0 && entry.nodeId < min ? entry.nodeId : min), Number.POSITIVE_INFINITY);
|
|
1041
|
+
const minMeshHandle = primitiveBindings.reduce((min, entry) => (entry.meshHandle > 0 && entry.meshHandle < min ? entry.meshHandle : min), Number.POSITIVE_INFINITY);
|
|
1042
|
+
const minMaterialHandle = materials.reduce((min, entry) => (entry.materialHandle > 0 && entry.materialHandle < min ? entry.materialHandle : min), Number.POSITIVE_INFINITY);
|
|
1043
|
+
const nodeBase = Number.isFinite(minNodeId) ? minNodeId : 0;
|
|
1044
|
+
const meshBase = Number.isFinite(minMeshHandle) ? minMeshHandle : 0;
|
|
1045
|
+
const materialBase = Number.isFinite(minMaterialHandle) ? minMaterialHandle : 0;
|
|
1046
|
+
const normalizedNodeHandles = nodeHandles.map((entry) => ({
|
|
1047
|
+
sourceNodeIndex: entry.sourceNodeIndex,
|
|
1048
|
+
parentSourceNodeIndex: entry.parentSourceNodeIndex,
|
|
1049
|
+
nodeOffset: entry.nodeId > 0 ? entry.nodeId - nodeBase : 0,
|
|
1050
|
+
parentNodeOffset: entry.parentNodeId != null && entry.parentNodeId > 0 ? entry.parentNodeId - nodeBase : null,
|
|
1051
|
+
}));
|
|
1052
|
+
const normalizedPrimitiveBindings = primitiveBindings.map((entry) => ({
|
|
1053
|
+
primitiveIndex: entry.primitiveIndex,
|
|
1054
|
+
nodeOffset: entry.nodeId > 0 ? entry.nodeId - nodeBase : 0,
|
|
1055
|
+
meshOffset: entry.meshHandle > 0 ? entry.meshHandle - meshBase : 0,
|
|
1056
|
+
materialOffset: entry.materialHandle > 0 ? entry.materialHandle - materialBase : 0,
|
|
1057
|
+
sourceMeshIndex: entry.sourceMeshIndex,
|
|
1058
|
+
sourcePrimitiveIndex: entry.sourcePrimitiveIndex,
|
|
1059
|
+
materialIndex: entry.materialIndex,
|
|
1060
|
+
}));
|
|
1061
|
+
const normalizedMaterials = materials.map((entry) => ({
|
|
1062
|
+
sourceMaterialIndex: entry.sourceMaterialIndex,
|
|
1063
|
+
materialOffset: entry.materialHandle > 0 ? entry.materialHandle - materialBase : 0,
|
|
1064
|
+
name: entry.name,
|
|
1065
|
+
baseColor: entry.baseColor,
|
|
1066
|
+
metallic: entry.metallic,
|
|
1067
|
+
roughness: entry.roughness,
|
|
1068
|
+
alphaMode: entry.alphaMode,
|
|
1069
|
+
alphaCutoff: entry.alphaCutoff,
|
|
1070
|
+
doubleSided: entry.doubleSided,
|
|
1071
|
+
textureFilter: entry.textureFilter,
|
|
1072
|
+
runtimeBinding: entry.runtimeBinding,
|
|
1073
|
+
}));
|
|
1074
|
+
aura.scene3d.setCameraLayerMask(1 << 2);
|
|
1075
|
+
aura.debug.enableInspector(true);
|
|
1076
|
+
const submit = aura.scene3d.submitRenderBindings();
|
|
1077
|
+
const render = aura.scene3d.getRenderSubmissionState();
|
|
1078
|
+
const stats = aura.debug.inspectorStats() || {};
|
|
1079
|
+
aura.debug.enableInspector(false);
|
|
1080
|
+
if (typeof aura.scene3d.unloadGltfScene === 'function') {
|
|
1081
|
+
try { aura.scene3d.unloadGltfScene(loaded.importId); } catch (_) {}
|
|
1082
|
+
}
|
|
1083
|
+
const importStats = stats?.scene3dRuntime?.importedScenes || {};
|
|
1084
|
+
const queryOrderFingerprint = Number((queried.orderFingerprint >>> 0) || 0);
|
|
1085
|
+
const querySceneFingerprint = Number((queried.sceneFingerprint >>> 0) || 0);
|
|
1086
|
+
const queryRuntimeFingerprint = Number((queried.runtimeFingerprint >>> 0) || 0);
|
|
1087
|
+
return {
|
|
1088
|
+
loadedPath: loaded.scenePath,
|
|
1089
|
+
runtimeReasonCode: queried.runtimeReasonCode || null,
|
|
1090
|
+
nodeCount: Number(queried.nodeCount || 0),
|
|
1091
|
+
primitiveCount: Number(queried.primitiveCount || 0),
|
|
1092
|
+
materialCount: Number(queried.materialCount || 0),
|
|
1093
|
+
roots: toSortedNumericArray(queried.rootSourceNodeIndices),
|
|
1094
|
+
orderFingerprint: queryOrderFingerprint,
|
|
1095
|
+
sceneFingerprint: querySceneFingerprint,
|
|
1096
|
+
runtimeFingerprint: queryRuntimeFingerprint,
|
|
1097
|
+
normalizedNodeHandles,
|
|
1098
|
+
normalizedPrimitiveBindings,
|
|
1099
|
+
normalizedMaterials,
|
|
1100
|
+
textures,
|
|
1101
|
+
importTelemetry: {
|
|
1102
|
+
count: Number(importStats?.count || 0),
|
|
1103
|
+
nodeCount: Number(importStats?.nodeCount || 0),
|
|
1104
|
+
primitiveBindingCount: Number(importStats?.primitiveBindingCount || 0),
|
|
1105
|
+
lastOrderFingerprint: Number(importStats?.lastOrderFingerprint || 0),
|
|
1106
|
+
lastSceneFingerprint: Number(importStats?.lastSceneFingerprint || 0),
|
|
1107
|
+
lastRuntimeFingerprint: Number(importStats?.lastRuntimeFingerprint || 0),
|
|
1108
|
+
lastRuntimeReasonCode: typeof importStats?.lastRuntimeReasonCode === 'string'
|
|
1109
|
+
? importStats.lastRuntimeReasonCode
|
|
1110
|
+
: null,
|
|
1111
|
+
},
|
|
1112
|
+
submitOk: submit?.ok === true,
|
|
1113
|
+
submitted: Number(submit?.submitted || 0),
|
|
1114
|
+
renderSubmitted: Number(render?.submitted || 0),
|
|
1115
|
+
renderReason: render?.lastReasonCode || null,
|
|
1116
|
+
};
|
|
1117
|
+
};
|
|
1118
|
+
const first = sample();
|
|
1119
|
+
const second = sample();
|
|
1120
|
+
const expectedMaterialSignature = JSON.stringify([
|
|
1121
|
+
{
|
|
1122
|
+
sourceMaterialIndex: 0,
|
|
1123
|
+
materialOffset: 0,
|
|
1124
|
+
name: 'Hull',
|
|
1125
|
+
baseColor: { r: 0.7, g: 0.75, b: 0.9, a: 0.85 },
|
|
1126
|
+
metallic: 0.2,
|
|
1127
|
+
roughness: 0.65,
|
|
1128
|
+
alphaMode: 'blend',
|
|
1129
|
+
alphaCutoff: 0.4,
|
|
1130
|
+
doubleSided: true,
|
|
1131
|
+
textureFilter: 'linear',
|
|
1132
|
+
runtimeBinding: {
|
|
1133
|
+
alphaMode: 'blend',
|
|
1134
|
+
alphaCutoff: 0.4,
|
|
1135
|
+
doubleSided: true,
|
|
1136
|
+
textureFilter: 'linear',
|
|
1137
|
+
},
|
|
1138
|
+
},
|
|
1139
|
+
]);
|
|
1140
|
+
const expectedTextureSignature = JSON.stringify([
|
|
1141
|
+
{
|
|
1142
|
+
sourceTextureIndex: 0,
|
|
1143
|
+
imageUri: 'textures/hull-base.png',
|
|
1144
|
+
textureFilter: 'linear',
|
|
1145
|
+
runtimeBindable: true,
|
|
1146
|
+
reasonCode: 'scene_texture_runtime_bindable',
|
|
1147
|
+
resolvedAsset: true,
|
|
1148
|
+
},
|
|
1149
|
+
]);
|
|
1150
|
+
return !!first
|
|
1151
|
+
&& !!second
|
|
1152
|
+
&& JSON.stringify(first) === JSON.stringify(second)
|
|
1153
|
+
&& first.runtimeReasonCode === 'scene_import_runtime_bound'
|
|
1154
|
+
&& first.nodeCount === 4
|
|
1155
|
+
&& first.primitiveCount === 1
|
|
1156
|
+
&& first.materialCount === 1
|
|
1157
|
+
&& JSON.stringify(first.roots) === '[0]'
|
|
1158
|
+
&& first.normalizedNodeHandles.length === 4
|
|
1159
|
+
&& first.normalizedPrimitiveBindings.length === 1
|
|
1160
|
+
&& first.normalizedMaterials.length === 1
|
|
1161
|
+
&& first.textures.length === 1
|
|
1162
|
+
&& first.orderFingerprint > 0
|
|
1163
|
+
&& first.sceneFingerprint > 0
|
|
1164
|
+
&& first.runtimeFingerprint > 0
|
|
1165
|
+
&& first.submitOk === true
|
|
1166
|
+
&& first.submitted >= 1
|
|
1167
|
+
&& first.renderSubmitted >= 1
|
|
1168
|
+
&& (first.renderReason === 'scene3d_render_submitted' || first.renderReason === 'scene3d_render_noop')
|
|
1169
|
+
&& first.importTelemetry.count >= 1
|
|
1170
|
+
&& first.importTelemetry.nodeCount >= first.nodeCount
|
|
1171
|
+
&& first.importTelemetry.primitiveBindingCount >= first.primitiveCount
|
|
1172
|
+
&& first.importTelemetry.lastOrderFingerprint === first.orderFingerprint
|
|
1173
|
+
&& first.importTelemetry.lastSceneFingerprint === first.sceneFingerprint
|
|
1174
|
+
&& first.importTelemetry.lastRuntimeFingerprint === first.runtimeFingerprint
|
|
1175
|
+
&& first.importTelemetry.lastRuntimeReasonCode === 'scene_import_runtime_bound'
|
|
1176
|
+
&& JSON.stringify(first.normalizedMaterials) === expectedMaterialSignature
|
|
1177
|
+
&& JSON.stringify(first.textures) === expectedTextureSignature;
|
|
1178
|
+
})()`,
|
|
1179
|
+
},
|
|
1180
|
+
],
|
|
1181
|
+
nativePostChecks: [
|
|
1182
|
+
{
|
|
1183
|
+
id: 'scene3d.gltf-import.imported-morph-runtime-telemetry',
|
|
1184
|
+
expression: `(() => {
|
|
1185
|
+
const round = (value) => Number(Number(value || 0).toFixed(6));
|
|
1186
|
+
const roundArray = (values) => (Array.isArray(values) ? values.map((value) => round(value)) : []);
|
|
1187
|
+
const state = globalThis.__importedMorphRuntime || {};
|
|
1188
|
+
const importId = Number(state.importId || 0);
|
|
1189
|
+
if (!Number.isInteger(importId) || importId <= 0) return false;
|
|
1190
|
+
const queried = aura.scene3d.getImportedScene(importId);
|
|
1191
|
+
if (!queried || queried.ok !== true) return false;
|
|
1192
|
+
const morphBinding = (Array.isArray(queried.primitiveBindings) ? queried.primitiveBindings : [])
|
|
1193
|
+
.find((entry) => entry?.hasMorphTargets === true) || null;
|
|
1194
|
+
aura.debug.enableInspector(true);
|
|
1195
|
+
const stats = aura.debug.inspectorStats() || {};
|
|
1196
|
+
aura.debug.enableInspector(false);
|
|
1197
|
+
const importTelemetry = stats?.scene3dRuntime?.importedScenes || {};
|
|
1198
|
+
const resources = stats?.scene3dRuntime?.resources || {};
|
|
1199
|
+
const submission = stats?.scene3dRuntime?.submission || {};
|
|
1200
|
+
const render = aura.scene3d.getRenderSubmissionState() || {};
|
|
1201
|
+
return queried.runtimeReasonCode === 'scene_import_runtime_bound'
|
|
1202
|
+
&& Number(queried.importedMorphPrimitiveCount || 0) === 1
|
|
1203
|
+
&& Number(queried.boundMorphPrimitiveCount || 0) === 1
|
|
1204
|
+
&& Number(queried.morphTargetCount || 0) === 2
|
|
1205
|
+
&& Number(queried.nonZeroMorphWeightPrimitiveCount || 0) === 1
|
|
1206
|
+
&& morphBinding?.primitiveIndex === 0
|
|
1207
|
+
&& Number(morphBinding?.morphTargetCount || 0) === 2
|
|
1208
|
+
&& morphBinding?.hasNonZeroMorphWeights === true
|
|
1209
|
+
&& JSON.stringify(roundArray(morphBinding?.weights || [])) === '[0.25,0.75]'
|
|
1210
|
+
&& Number(importTelemetry.count || 0) >= 1
|
|
1211
|
+
&& Number(importTelemetry.importedMorphPrimitiveCount || 0) >= 1
|
|
1212
|
+
&& Number(importTelemetry.boundMorphPrimitiveCount || 0) >= 1
|
|
1213
|
+
&& Number(importTelemetry.morphTargetCount || 0) >= 2
|
|
1214
|
+
&& Number(importTelemetry.nonZeroMorphWeightPrimitiveCount || 0) >= 1
|
|
1215
|
+
&& importTelemetry.lastRuntimeReasonCode === 'scene_import_runtime_bound'
|
|
1216
|
+
&& Number(resources.morphTrackedMeshCount || 0) >= 1
|
|
1217
|
+
&& Number(resources.morphPendingUploadCount || 0) === 0
|
|
1218
|
+
&& Number(submission.morphCandidateDrawCount || 0) >= 1
|
|
1219
|
+
&& Number(submission.morphPreparedBindGroupCount || 0) >= 1
|
|
1220
|
+
&& Number(submission.morphExecutedBindGroupCount || 0) >= 1
|
|
1221
|
+
&& Number(submission.morphExecutedMeshCount || 0) >= 1
|
|
1222
|
+
&& Number(submission.morphExecutedNonZeroWeightDrawCount || 0) >= 1
|
|
1223
|
+
&& Number(submission.morphExecutedNonZeroWeightMeshCount || 0) >= 1
|
|
1224
|
+
&& submission.morphLastReasonCode === 'live_non_zero_weights'
|
|
1225
|
+
&& Number(render.submitted || 0) >= 1
|
|
1226
|
+
&& (render.lastReasonCode === 'scene3d_render_submitted' || render.lastReasonCode === 'scene3d_render_noop');
|
|
1227
|
+
})()`,
|
|
1228
|
+
},
|
|
1229
|
+
],
|
|
1230
|
+
source: `
|
|
1231
|
+
globalThis.__importedMorphRuntime = { importId: 0, scenePath: null };
|
|
1232
|
+
`,
|
|
1233
|
+
nativeFrames: 4,
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
id: 'threejs-3d-vertical-slice-runtime',
|
|
1237
|
+
modes: ['native'],
|
|
1238
|
+
namespaces: ['draw3d', 'camera3d', 'debug', 'light', 'material', 'mesh', 'scene3d'],
|
|
1239
|
+
functions: [
|
|
1240
|
+
'aura.draw3d.clear3d',
|
|
1241
|
+
'aura.draw3d.drawMesh',
|
|
1242
|
+
'aura.draw3d.setPostFXPass',
|
|
1243
|
+
'aura.draw3d.setPostFXEnabled',
|
|
1244
|
+
'aura.draw3d.removePostFXPass',
|
|
1245
|
+
'aura.draw3d.getPostFXState',
|
|
1246
|
+
'aura.camera3d.perspective',
|
|
1247
|
+
'aura.camera3d.setPosition',
|
|
1248
|
+
'aura.camera3d.setTarget',
|
|
1249
|
+
'aura.camera3d.lookAt',
|
|
1250
|
+
'aura.camera3d.getViewMatrix',
|
|
1251
|
+
'aura.camera3d.getProjectionMatrix',
|
|
1252
|
+
'aura.light.ambient',
|
|
1253
|
+
'aura.light.configureDirectionalShadows',
|
|
1254
|
+
'aura.light.configureShadow',
|
|
1255
|
+
'aura.light.directional',
|
|
1256
|
+
'aura.light.getShadowState',
|
|
1257
|
+
'aura.light.getShadowStats',
|
|
1258
|
+
'aura.light.point',
|
|
1259
|
+
'aura.light.setShadowBudget',
|
|
1260
|
+
'aura.light.spot',
|
|
1261
|
+
'aura.light.update',
|
|
1262
|
+
'aura.light.remove',
|
|
1263
|
+
'aura.debug.enableInspector',
|
|
1264
|
+
'aura.debug.inspectorStats',
|
|
1265
|
+
'aura.material.create',
|
|
1266
|
+
'aura.material.clone',
|
|
1267
|
+
'aura.material.setColor',
|
|
1268
|
+
'aura.material.setMetallicRoughness',
|
|
1269
|
+
'aura.material.unload',
|
|
1270
|
+
'aura.mesh.createBox',
|
|
1271
|
+
'aura.mesh.unload',
|
|
1272
|
+
'aura.scene3d.createNode',
|
|
1273
|
+
'aura.scene3d.setParent',
|
|
1274
|
+
'aura.scene3d.setLocalTransform',
|
|
1275
|
+
'aura.scene3d.getWorldTransform',
|
|
1276
|
+
'aura.scene3d.traverse',
|
|
1277
|
+
'aura.scene3d.removeNode',
|
|
1278
|
+
],
|
|
1279
|
+
nativeChecks: [
|
|
1280
|
+
{
|
|
1281
|
+
id: 'light.shadow-runtime-control.surface.methods',
|
|
1282
|
+
expression: "(() => ['configureDirectionalShadows','configureShadow','getShadowState','getShadowStats'].every((name) => typeof aura.light?.[name] === 'function'))()",
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
id: 'draw3d.postfx.surface.methods',
|
|
1286
|
+
expression: "(() => ['setPostFXPass','setPostFXEnabled','removePostFXPass','getPostFXState'].every((name) => typeof aura.draw3d?.[name] === 'function'))()",
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
id: 'light.shadow-runtime-control.roundtrip.native',
|
|
1290
|
+
expression: "(() => { const approx = (value, expected) => Math.abs(Number(value) - expected) <= 1e-6; aura.debug.enableInspector(true); const sun = aura.light.directional({ x: -0.6, y: -1.0, z: -0.2 }, { r: 1, g: 0.95, b: 0.9, a: 1 }, 1.2); const point = aura.light.point({ x: 2, y: 3, z: 1 }, { r: 0.2, g: 0.4, b: 1, a: 1 }, 1.0, 14); const spot = aura.light.spot({ x: -1, y: 4, z: 2 }, { x: 0, y: -1, z: 0 }, { r: 1, g: 0.8, b: 0.6, a: 1 }, 1.1, 20, 0.8); aura.light.setShadowBudget(4); aura.light.configureDirectionalShadows({ enabled: true, quality: 'high', bias: 1, normalBias: 9, filterMode: 'soft', filterRadius: 99, cascadeCount: 99, tileResolution: 999999, lambda: -1, blendWidth: -4, shadowFar: 5, stabilizeCascades: true }); aura.light.configureShadow(point, { enabled: true, quality: 'low', bias: 1, normalBias: 5, filterMode: 'hard', filterRadius: -2 }); aura.light.configureShadow(spot, { enabled: true, quality: 'medium', bias: 0.015, normalBias: 0.04, filterMode: 'pcf', filterRadius: 1.5 }); const directional = aura.light.getShadowState(); const pointState = aura.light.getShadowState(point); const spotState = aura.light.getShadowState(spot); const stats = aura.light.getShadowStats(); const inspector = aura.debug.inspectorStats(); aura.debug.enableInspector(false); aura.light.remove(spot); aura.light.remove(point); aura.light.remove(sun); return directional.directional.enabled === true && directional.directional.quality === 'high' && approx(directional.directional.bias, 0.05) && approx(directional.directional.normalBias, 0.1) && directional.directional.filterMode === 'pcf' && approx(directional.directional.filterRadius, 4.0) && directional.directional.cascadeCount === 4 && directional.directional.tileResolution === 4096 && directional.directional.lambda === 0 && directional.directional.blendWidth === 0 && directional.directional.shadowFar === 10 && directional.directional.stabilizeCascades === true && pointState.type === 'point' && pointState.enabled === true && pointState.quality === 'low' && approx(pointState.bias, 0.05) && approx(pointState.normalBias, 0.1) && pointState.filterMode === 'hard' && approx(pointState.filterRadius, 0.0) && spotState.type === 'spot' && approx(spotState.normalBias, 0.04) && spotState.filterMode === 'pcf' && approx(spotState.filterRadius, 1.5) && stats.shadowCastingCount === 3 && stats.shadowBudget === 4 && stats.budgetSaturated === false && stats.directionalShadowEnabled === true && approx(stats.directionalNormalBias, 0.1) && stats.directionalFilterMode === 'pcf' && approx(stats.directionalFilterRadius, 4.0) && stats.directionalCascadeCount === 4 && stats.directionalStabilizeCascades === true && stats.shadowedPointLightCount === 1 && stats.shadowedSpotLightCount === 1 && inspector?.scene3dRuntime?.shadow?.shadowCastingCount === 3 && inspector?.scene3dRuntime?.shadow?.directionalCascadeCount === 4 && approx(inspector?.scene3dRuntime?.shadow?.directionalNormalBias, 0.1) && inspector?.scene3dRuntime?.shadow?.directionalFilterMode === 'pcf' && approx(inspector?.scene3dRuntime?.shadow?.directionalFilterRadius, 4.0) && inspector?.scene3dRuntime?.shadow?.directionalStabilizeCascades === true; })()",
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
id: 'threejs.vertical-slice.scene-runtime.deterministic',
|
|
1294
|
+
expression: "(() => { const runSample = () => { const root = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const orbit = aura.scene3d.createNode({ position: { x: 0, y: 1, z: 0 }, rotation: { x: 0, y: 0.5, z: 0 } }); const meshNode = aura.scene3d.createNode({ position: { x: 2, y: 0, z: -1 }, scale: { x: 1.5, y: 1.5, z: 1.5 } }); const parentOrbit = aura.scene3d.setParent(orbit, root); const parentMesh = aura.scene3d.setParent(meshNode, orbit); aura.scene3d.setLocalTransform(root, { position: { x: 1.25, y: 0, z: -0.5 } }); const world = aura.scene3d.getWorldTransform(meshNode); const traversal = []; aura.scene3d.traverse(root, (_id, worldTransform) => traversal.push([ Number(worldTransform.position.x.toFixed(3)), Number(worldTransform.position.y.toFixed(3)), Number(worldTransform.position.z.toFixed(3)) ])); aura.camera3d.perspective(60, 0.1, 200); aura.camera3d.setPosition(4, 3, 6); aura.camera3d.setTarget(world.position.x, world.position.y, world.position.z); aura.camera3d.lookAt(world.position.x, world.position.y, world.position.z); const ambient = aura.light.ambient({ r: 0.15, g: 0.15, b: 0.2, a: 1 }, 0.4); const sun = aura.light.directional({ x: -0.7, y: -1, z: -0.2 }, { r: 1, g: 0.95, b: 0.9, a: 1 }, 1.2); const rim = aura.light.point({ x: 3, y: 2, z: 1 }, { r: 0.25, g: 0.45, b: 1, a: 1 }, 2.0, 20); aura.light.update(rim, { position: { x: 2.5, y: 2, z: -0.5 }, intensity: 1.5, range: 18 }); const mesh = aura.mesh.createBox(1, 1, 1); const base = aura.material.create({ color: { r: 0.4, g: 0.6, b: 0.85, a: 1 }, metallic: 0.15, roughness: 0.65 }); const accent = aura.material.clone(base); aura.material.setMetallicRoughness(accent, 0.85, 0.2); aura.material.setColor(accent, { r: 0.95, g: 0.45, b: 0.3, a: 1 }); aura.draw3d.clear3d({ r: 0.04, g: 0.05, b: 0.08, a: 1 }); aura.draw3d.drawMesh(mesh, base, world); aura.draw3d.drawMesh(mesh, accent, { position: { x: world.position.x + 1.2, y: world.position.y, z: world.position.z }, rotation: world.rotation, scale: world.scale }); const view = aura.camera3d.getViewMatrix(); const projection = aura.camera3d.getProjectionMatrix(); const summary = { parentOrbit, parentMesh, world: [ Number(world.position.x.toFixed(3)), Number(world.position.y.toFixed(3)), Number(world.position.z.toFixed(3)) ], traversal, handlesAreNumeric: [ambient, sun, rim, mesh, base, accent].every((handle) => typeof handle === 'number' && Number.isFinite(handle) && handle >= 0), cloneDistinct: accent !== base, matricesFinite: Array.isArray(view) && view.length === 16 && view.every((value) => Number.isFinite(value)) && Array.isArray(projection) && projection.length === 16 && projection.every((value) => Number.isFinite(value)), viewLead: Number(view[0].toFixed(6)), projectionLead: Number(projection[5].toFixed(6)) }; aura.material.unload(accent); aura.material.unload(base); aura.mesh.unload(mesh); aura.light.remove(sun); aura.light.remove(rim); aura.scene3d.removeNode(meshNode); aura.scene3d.removeNode(orbit); aura.scene3d.removeNode(root); return summary; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.parentOrbit === true && first.parentMesh === true && first.cloneDistinct === true && first.handlesAreNumeric === true && first.matricesFinite === true && first.world[0] === 3.25 && first.world[1] === 1 && first.world[2] === -1.5 && first.traversal.length === 3; })()",
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
id: 'draw3d.postfx.chain-mutation.deterministic',
|
|
1298
|
+
expression: "(() => { const runSample = () => { const baseline = aura.draw3d.getPostFXState(); aura.draw3d.removePostFXPass('bloom'); aura.draw3d.removePostFXPass('colorGrade'); aura.draw3d.removePostFXPass('vignette'); aura.draw3d.removePostFXPass('fxaa'); const setBloom = aura.draw3d.setPostFXPass('bloom', { strength: 1.25, radius: 0.6, threshold: 0.7 }); const setFxaa = aura.draw3d.setPostFXPass('fxaa', { enabled: true, strength: 1.0 }); const setColorGrade = aura.draw3d.setPostFXPass('colorGrade', { strength: 0.8 }); const disableBloom = aura.draw3d.setPostFXEnabled('bloom', false); const enableBloom = aura.draw3d.setPostFXEnabled('bloom', true); const removeFxaa = aura.draw3d.removePostFXPass('fxaa'); const setVignette = aura.draw3d.setPostFXPass('vignette', { strength: 0.4, radius: 0.9 }); const state = aura.draw3d.getPostFXState(); return { status: [setBloom?.reasonCode, setFxaa?.reasonCode, setColorGrade?.reasonCode, disableBloom?.reasonCode, enableBloom?.reasonCode, removeFxaa?.reasonCode, setVignette?.reasonCode].join('|'), order: Array.isArray(state?.passes) ? state.passes.map((pass) => pass.pass).join('|') : '', enabled: state?.enabledPasses, total: state?.totalPasses, fingerprint: state?.orderFingerprint, mutationDelta: Number(state?.mutationCount || 0) - Number(baseline?.mutationCount || 0), lastReasonCode: state?.lastReasonCode, lastOk: state?.lastOk }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.status === 'postfx_ok|postfx_ok|postfx_ok|postfx_ok|postfx_ok|postfx_ok|postfx_ok' && first.order === 'colorGrade|bloom|vignette' && first.total === 3 && first.enabled === 3 && Number.isFinite(first.fingerprint) && first.fingerprint > 0 && first.mutationDelta === 11 && first.lastReasonCode === 'postfx_ok' && first.lastOk === true; })()",
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
id: 'threejs.vertical-slice.camera-hierarchy-churn.deterministic',
|
|
1302
|
+
expression: "(() => { const runSample = () => { const root = aura.scene3d.createNode({ position: { x: 0, y: 0, z: 0 } }); const pivot = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const targetNode = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const linkedPivot = aura.scene3d.setParent(pivot, root); const linkedTarget = aura.scene3d.setParent(targetNode, pivot); const trace = []; for (let step = 0; step < 4; step += 1) { aura.scene3d.setLocalTransform(root, { position: { x: step * 0.25, y: 0, z: -step * 0.1 } }); aura.scene3d.setLocalTransform(pivot, { position: { x: 1 + step * 0.5, y: step * 0.25, z: 0 } }); const world = aura.scene3d.getWorldTransform(targetNode); aura.camera3d.perspective(55 + (step * 5), 0.1, 300); aura.camera3d.setPosition(6 - step, 3 + (step * 0.5), 8 - (step * 0.4)); aura.camera3d.lookAt(world.position.x, world.position.y, world.position.z); const view = aura.camera3d.getViewMatrix(); const projection = aura.camera3d.getProjectionMatrix(); trace.push({ target: [Number(world.position.x.toFixed(3)), Number(world.position.y.toFixed(3)), Number(world.position.z.toFixed(3))], viewLead: Number(view[0].toFixed(6)), projectionLead: Number(projection[5].toFixed(6)) }); } aura.scene3d.removeNode(targetNode); aura.scene3d.removeNode(pivot); aura.scene3d.removeNode(root); return { linkedPivot, linkedTarget, trace }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.linkedPivot === true && first.linkedTarget === true && first.trace.length === 4 && first.trace.every((entry) => Number.isFinite(entry.viewLead) && Number.isFinite(entry.projectionLead)); })()",
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
id: 'threejs.vertical-slice.light-mutation.hierarchy-churn.deterministic',
|
|
1306
|
+
expression: "(() => { const runSample = () => { const root = aura.scene3d.createNode({ position: { x: 0, y: 0, z: 0 } }); const orbit = aura.scene3d.createNode({ position: { x: 1, y: 0, z: 0 } }); const meshNode = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); aura.scene3d.setParent(orbit, root); aura.scene3d.setParent(meshNode, orbit); const ambient = aura.light.ambient({ r: 0.1, g: 0.1, b: 0.15, a: 1 }, 0.35); const sun = aura.light.directional({ x: -0.8, y: -1, z: -0.2 }, { r: 1, g: 0.95, b: 0.9, a: 1 }, 1.1); const rim = aura.light.point({ x: 2, y: 2, z: 0 }, { r: 0.2, g: 0.35, b: 1, a: 1 }, 1.4, 18); const mesh = aura.mesh.createBox(1, 1, 1); const material = aura.material.create({ color: { r: 0.45, g: 0.6, b: 0.8, a: 1 }, metallic: 0.2, roughness: 0.6 }); const positions = []; for (let step = 0; step < 3; step += 1) { aura.scene3d.setLocalTransform(root, { position: { x: step * 0.3, y: 0, z: -step * 0.2 } }); aura.scene3d.setLocalTransform(orbit, { position: { x: 1 + (step * 0.4), y: step * 0.2, z: 0 } }); const world = aura.scene3d.getWorldTransform(meshNode); aura.light.update(sun, { direction: { x: -0.8 + (step * 0.1), y: -1, z: -0.2 } }); aura.light.update(rim, { position: { x: world.position.x + 0.75, y: world.position.y + 1.25, z: world.position.z + 0.5 }, intensity: 1.4 + (step * 0.2), range: 18 - step }); aura.draw3d.drawMesh(mesh, material, world); positions.push([Number(world.position.x.toFixed(3)), Number(world.position.y.toFixed(3)), Number(world.position.z.toFixed(3))]); } aura.light.remove(rim); const rimReplacement = aura.light.point({ x: -1, y: 1, z: -1 }, { r: 0.4, g: 0.5, b: 1, a: 1 }, 1.0, 12); aura.light.update(rimReplacement, { intensity: 1.1, range: 10 }); const summary = { handlesNumeric: [ambient, sun, rim, rimReplacement].every((handle) => typeof handle === 'number' && Number.isFinite(handle) && handle >= 0), rimReplaced: rimReplacement !== rim, rimReplacementAdvanced: rimReplacement > rim, positions }; aura.material.unload(material); aura.mesh.unload(mesh); aura.light.remove(rimReplacement); aura.light.remove(sun); aura.scene3d.removeNode(meshNode); aura.scene3d.removeNode(orbit); aura.scene3d.removeNode(root); return summary; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.rimReplaced === true && first.rimReplacementAdvanced === true && first.positions.length === 3 && first.handlesNumeric === true; })()",
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
id: 'threejs.vertical-slice.material-mutation.hierarchy-churn.deterministic',
|
|
1310
|
+
expression: "(() => { const runSample = () => { const root = aura.scene3d.createNode({ position: { x: 0, y: 0, z: 0 } }); const pivot = aura.scene3d.createNode({ position: { x: 0.5, y: 0.25, z: 0 } }); const meshNode = aura.scene3d.createNode({ position: { x: 1.5, y: 0, z: -1.5 }, scale: { x: 1.2, y: 1.2, z: 1.2 } }); aura.scene3d.setParent(pivot, root); aura.scene3d.setParent(meshNode, pivot); const mesh = aura.mesh.createBox(1, 1, 1); const base = aura.material.create({ color: { r: 0.35, g: 0.55, b: 0.85, a: 1 }, metallic: 0.1, roughness: 0.75 }); const accent = aura.material.clone(base); const trail = aura.material.clone(base); const trace = []; for (let step = 0; step < 4; step += 1) { aura.scene3d.setLocalTransform(pivot, { position: { x: 0.5 + (step * 0.2), y: 0.25 + (step * 0.1), z: -step * 0.15 }, rotation: { x: 0, y: step * 0.25, z: 0 } }); const world = aura.scene3d.getWorldTransform(meshNode); const metallic = Number((0.2 + (step * 0.2)).toFixed(3)); const roughness = Number((0.8 - (step * 0.15)).toFixed(3)); aura.material.setMetallicRoughness(accent, metallic, roughness); aura.material.setColor(trail, { r: 0.3 + (step * 0.1), g: 0.45, b: 0.9 - (step * 0.1), a: 1 }); aura.draw3d.drawMesh(mesh, base, world); aura.draw3d.drawMesh(mesh, accent, { position: { x: world.position.x + 0.6, y: world.position.y, z: world.position.z }, rotation: world.rotation, scale: world.scale }); aura.draw3d.drawMesh(mesh, trail, { position: { x: world.position.x - 0.6, y: world.position.y, z: world.position.z }, rotation: world.rotation, scale: world.scale }); trace.push([Number(world.position.x.toFixed(3)), Number(world.rotation.y.toFixed(3)), Number(world.scale.x.toFixed(3)), metallic, roughness]); } const summary = { handlesNumeric: [base, accent, trail].every((handle) => typeof handle === 'number' && Number.isFinite(handle) && handle > 0), distinctHandles: base !== accent && base !== trail && accent !== trail, cloneOrderPreserved: accent > base && trail > accent, trace }; aura.material.unload(trail); aura.material.unload(accent); aura.material.unload(base); aura.mesh.unload(mesh); aura.scene3d.removeNode(meshNode); aura.scene3d.removeNode(pivot); aura.scene3d.removeNode(root); return summary; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.distinctHandles === true && first.cloneOrderPreserved === true && first.trace.length === 4 && first.handlesNumeric === true; })()",
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
id: 'draw3d.postfx.reason-codes.stable',
|
|
1314
|
+
expression: "(() => { const reasonOf = (entry) => (entry && entry.reasonCode) || null; const collect = () => { aura.draw3d.removePostFXPass('bloom'); aura.draw3d.removePostFXPass('custom:glow'); const invalidPassEmpty = aura.draw3d.setPostFXPass('', {}); const invalidPassType = aura.draw3d.setPostFXPass(42, {}); const configuredSsao = aura.draw3d.setPostFXPass('ssao', { radius: 0.6, intensity: 0.8 }); const removeSsao = aura.draw3d.removePostFXPass('ssao'); const invalidOptions = aura.draw3d.setPostFXPass('bloom', 'bad'); const invalidTargetChain = aura.draw3d.setPostFXPass('bloom', { targetChain: 'bad' }); const invalidIntermediateTargetType = aura.draw3d.setPostFXPass('bloom', { targetChain: { intermediateTargets: 'bad' } }); const invalidIntermediateTargetEntry = aura.draw3d.setPostFXPass('bloom', { targetChain: { intermediateTargets: ['ok', 'bad!'] } }); const invalidComposeMode = aura.draw3d.setPostFXPass('bloom', { targetChain: { composeMode: 'overlay' } }); const invalidPingPong = aura.draw3d.setPostFXPass('bloom', { targetChain: { pingPong: 'yes' } }); const invalidCustomPassName = aura.draw3d.setPostFXPass('custom:bad name', {}); const invalidCustomParams = aura.draw3d.setPostFXPass('custom:glow', { customParams: { intensity: 'high' } }); const unsupportedCustomParams = aura.draw3d.setPostFXPass('bloom', { customParams: { intensity: 1 } }); const configured = aura.draw3d.setPostFXPass('custom:glow', { customParams: { intensity: 0.25 } }); const invalidEnabled = aura.draw3d.setPostFXEnabled('custom:glow', 'yes'); const removeConfigured = aura.draw3d.removePostFXPass('custom:glow'); const missingToggle = aura.draw3d.setPostFXEnabled('custom:glow', true); const missingRemove = aura.draw3d.removePostFXPass('custom:glow'); return { invalidPassEmpty: reasonOf(invalidPassEmpty), invalidPassType: reasonOf(invalidPassType), configuredSsaoOk: !!configuredSsao && configuredSsao.ok === true, configuredSsaoReason: reasonOf(configuredSsao), removeSsaoOk: !!removeSsao && removeSsao.ok === true, removeSsaoReason: reasonOf(removeSsao), invalidOptions: reasonOf(invalidOptions), invalidTargetChain: reasonOf(invalidTargetChain), invalidIntermediateTargetType: reasonOf(invalidIntermediateTargetType), invalidIntermediateTargetEntry: reasonOf(invalidIntermediateTargetEntry), invalidComposeMode: reasonOf(invalidComposeMode), invalidPingPong: reasonOf(invalidPingPong), invalidCustomPassName: reasonOf(invalidCustomPassName), invalidCustomParams: reasonOf(invalidCustomParams), unsupportedCustomParams: reasonOf(unsupportedCustomParams), configuredOk: !!configured && configured.ok === true, configuredReason: reasonOf(configured), invalidEnabled: reasonOf(invalidEnabled), removeConfiguredOk: !!removeConfigured && removeConfigured.ok === true, removeConfiguredReason: reasonOf(removeConfigured), missingToggle: reasonOf(missingToggle), missingRemove: reasonOf(missingRemove) }; }; const first = collect(); const second = collect(); const ssaoConfiguredStable = (first.configuredSsaoOk === true && first.configuredSsaoReason === 'postfx_ok') || (first.configuredSsaoOk === false && first.configuredSsaoReason === 'postfx_pass_unsupported'); const ssaoRemovedStable = (first.removeSsaoOk === true && first.removeSsaoReason === 'postfx_ok') || (first.removeSsaoOk === false && first.removeSsaoReason === 'postfx_pass_unsupported'); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPassEmpty === 'postfx_invalid_pass_name' && first.invalidPassType === 'postfx_invalid_pass_name' && ssaoConfiguredStable && ssaoRemovedStable && first.invalidOptions === 'postfx_invalid_options' && first.invalidTargetChain === 'postfx_invalid_target_chain' && first.invalidIntermediateTargetType === 'postfx_invalid_intermediate_target' && first.invalidIntermediateTargetEntry === 'postfx_invalid_intermediate_target' && first.invalidComposeMode === 'postfx_invalid_compose_mode' && first.invalidPingPong === 'postfx_invalid_ping_pong' && first.invalidCustomPassName === 'postfx_invalid_custom_pass_name' && first.invalidCustomParams === 'postfx_invalid_custom_params' && first.unsupportedCustomParams === 'postfx_custom_param_unsupported' && first.configuredOk === true && first.configuredReason === 'postfx_ok' && first.invalidEnabled === 'postfx_invalid_enabled' && first.removeConfiguredOk === true && first.removeConfiguredReason === 'postfx_ok' && first.missingToggle === 'postfx_pass_missing' && first.missingRemove === 'postfx_pass_missing'; })()",
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
id: 'light.shadow-runtime-control.runtime.probe.setup.native',
|
|
1318
|
+
expression: "(() => { try { const mesh = aura.mesh.createBox(1, 1, 1); const material = aura.material.create({ color: { r: 0.65, g: 0.75, b: 0.9, a: 1 }, metallic: 0.1, roughness: 0.7 }); const sun = aura.light.directional({ x: -0.6, y: -1.0, z: -0.2 }, { r: 1, g: 0.95, b: 0.9, a: 1 }, 1.2); const point = aura.light.point({ x: 2, y: 3, z: 1 }, { r: 0.2, g: 0.4, b: 1, a: 1 }, 1.0, 14); const spot = aura.light.spot({ x: -1, y: 4, z: 2 }, { x: 0, y: -1, z: 0 }, { r: 1, g: 0.8, b: 0.6, a: 1 }, 1.1, 20, 0.8); const shadowConfig = { enabled: true, quality: 'high', bias: 1, normalBias: 9, filterMode: 'soft', filterRadius: 99, cascadeCount: 99, tileResolution: 999999, lambda: -1, blendWidth: -4, shadowFar: 5, stabilizeCascades: true }; const applyShadowConfig = (stabilizeCascades) => aura.light.configureDirectionalShadows({ ...shadowConfig, stabilizeCascades }); const sweep = [ { label: 'raw-base', stabilize: false, cameraX: 4.0 }, { label: 'raw-shift-a', stabilize: false, cameraX: 4.01 }, { label: 'raw-shift-b', stabilize: false, cameraX: 4.02 }, { label: 'stable-base', stabilize: true, cameraX: 4.0 }, { label: 'stable-shift-a', stabilize: true, cameraX: 4.01 }, { label: 'stable-shift-b', stabilize: true, cameraX: 4.02 } ]; aura.light.setShadowBudget(4); applyShadowConfig(true); aura.light.configureShadow(point, { enabled: true, quality: 'low', bias: 0.01, normalBias: 0.08, filterMode: 'hard', filterRadius: -1 }); aura.light.configureShadow(spot, { enabled: true, quality: 'medium', bias: 0.015, normalBias: 0.04, filterMode: 'pcf', filterRadius: 1.5 }); aura.camera3d.perspective(60, 0.1, 100); aura.camera3d.setPosition(4, 3, 6); aura.camera3d.lookAt(0, 0.5, 0); const baseDraw = aura.draw; aura.draw = function () { const probe = globalThis.__shadowRuntimeProbe; if (probe && probe.pendingSample) { aura.debug.enableInspector(true); const inspector = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const shadow = inspector?.scene3dRuntime?.shadow || {}; const stats = aura.light.getShadowStats(); probe.trace.push({ label: probe.pendingSample.label, stabilize: probe.pendingSample.stabilize, cameraX: probe.pendingSample.cameraX, statsSnapError: Number(stats?.directionalTexelSnapErrorMax || 0), inspectorSnapError: Number(shadow?.directionalTexelSnapErrorMax || 0), directionalPassCount: Number(stats?.directionalPassCount || 0), cascadeCount: Number(stats?.directionalCascadeCount || 0) }); } const sample = sweep[Math.min(globalThis.__shadowRuntimeProbe.sampleIndex, sweep.length - 1)]; applyShadowConfig(sample.stabilize); aura.camera3d.perspective(60, 0.1, 100); aura.camera3d.setPosition(sample.cameraX, 3, 6); aura.camera3d.lookAt(0, 0.5, 0); globalThis.__shadowRuntimeProbe.pendingSample = sample; globalThis.__shadowRuntimeProbe.sampleIndex += 1; aura.draw3d.clear3d({ r: 0.03, g: 0.04, b: 0.06, a: 1 }); aura.draw3d.drawMesh(mesh, material, { position: { x: 0, y: 0.5, z: 0 }, scale: { x: 1.5, y: 1.5, z: 1.5 } }); baseDraw(); }; globalThis.__shadowRuntimeProbe = { mesh, material, sun, point, spot, trace: [], pendingSample: null, sampleIndex: 0 }; return Number.isInteger(mesh) && mesh > 0 && Number.isInteger(material) && material > 0 && Number.isInteger(sun) && sun > 0 && Number.isInteger(point) && point > 0 && Number.isInteger(spot) && spot > 0; } catch (_) { return false; } })()",
|
|
1319
|
+
},
|
|
1320
|
+
],
|
|
1321
|
+
nativePostChecks: [
|
|
1322
|
+
{
|
|
1323
|
+
id: 'light.shadow-runtime-control.runtime.probe.evidence.native',
|
|
1324
|
+
expression: "(() => { try { const approx = (value, expected) => Math.abs(Number(value) - expected) <= 1e-6; const state = globalThis.__shadowRuntimeProbe || {}; aura.debug.enableInspector(true); const inspector = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const stats = aura.light.getShadowStats(); const rootState = aura.light.getShadowState(); const sunState = aura.light.getShadowState(state.sun); const pointState = aura.light.getShadowState(state.point); const spotState = aura.light.getShadowState(state.spot); const shadow = inspector?.scene3dRuntime?.shadow || {}; return Number.isInteger(state.sun) && state.sun > 0 && Number.isInteger(state.point) && state.point > 0 && Number.isInteger(state.spot) && state.spot > 0 && stats.shadowCastingCount === 3 && stats.shadowBudget === 4 && stats.directionalShadowEnabled === true && approx(stats.directionalNormalBias, 0.1) && stats.directionalFilterMode === 'pcf' && approx(stats.directionalFilterRadius, 4.0) && stats.directionalCascadeCount === 4 && approx(stats.directionalTexelSnapErrorMax, 0.0) && stats.directionalStabilizeCascades === true && stats.shadowedPointLightCount === 1 && stats.shadowedSpotLightCount === 1 && stats.rendererActive === true && stats.directionalPassCount === 4 && stats.multiLightSlotCount === 2 && stats.multiLightPassCount === 2 && stats.effectiveShadowCastingCount === 3 && stats.effectivePointLightCount === 1 && stats.effectiveSpotLightCount === 1 && stats.shadowDrawCount === 1 && rootState.rendererActive === true && rootState.effectiveShadowCastingCount === 3 && rootState.directional?.enabled === true && approx(rootState.directional?.normalBias, 0.1) && rootState.directional?.filterMode === 'pcf' && approx(rootState.directional?.filterRadius, 4.0) && rootState.directional?.stabilizeCascades === true && rootState.directional?.cascadeCount === 4 && sunState.type === 'directional' && approx(sunState.normalBias, 0.1) && sunState.filterMode === 'pcf' && approx(sunState.filterRadius, 4.0) && sunState.stabilizeCascades === true && sunState.effectiveEnabled === true && sunState.effectiveQuality === 'high' && approx(sunState.effectiveBias, 0.05) && sunState.effectiveSlotCount === 4 && pointState.type === 'point' && pointState.enabled === true && approx(pointState.normalBias, 0.08) && pointState.filterMode === 'hard' && approx(pointState.filterRadius, 0.0) && pointState.effectiveEnabled === true && pointState.effectiveQuality === 'low' && approx(pointState.effectiveBias, 0.01) && pointState.effectiveSlotCount === 1 && spotState.type === 'spot' && spotState.enabled === true && approx(spotState.normalBias, 0.04) && spotState.filterMode === 'pcf' && approx(spotState.filterRadius, 1.5) && spotState.effectiveEnabled === true && spotState.effectiveQuality === 'medium' && approx(spotState.effectiveBias, 0.015) && spotState.effectiveSlotCount === 1 && shadow.rendererActive === true && approx(shadow.directionalNormalBias, 0.1) && shadow.directionalFilterMode === 'pcf' && approx(shadow.directionalFilterRadius, 4.0) && approx(shadow.directionalTexelSnapErrorMax, 0.0) && shadow.directionalStabilizeCascades === true && shadow.directionalPassCount === 4 && shadow.multiLightSlotCount === 2 && shadow.multiLightPassCount === 2 && shadow.shadowDrawCount === 1 && shadow.directionalPassCount === stats.directionalPassCount && shadow.multiLightSlotCount === stats.multiLightSlotCount && shadow.multiLightPassCount === stats.multiLightPassCount && shadow.shadowDrawCount === stats.shadowDrawCount; } catch (_) { return false; } })()",
|
|
1325
|
+
debugExpression: "(() => { try { const state = globalThis.__shadowRuntimeProbe || {}; aura.debug.enableInspector(true); const inspector = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state, stats: aura.light.getShadowStats(), rootState: aura.light.getShadowState(), sunState: aura.light.getShadowState(state.sun), pointState: aura.light.getShadowState(state.point), spotState: aura.light.getShadowState(state.spot), shadow: inspector?.scene3dRuntime?.shadow || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1326
|
+
},
|
|
1327
|
+
{
|
|
1328
|
+
id: 'light.shadow-runtime-control.runtime.probe.camera-sweep.native',
|
|
1329
|
+
expression: "(() => { try { const state = globalThis.__shadowRuntimeProbe || {}; const trace = Array.isArray(state.trace) ? state.trace : []; const raw = trace.filter((entry) => entry && entry.stabilize === false); const stabilized = trace.filter((entry) => entry && entry.stabilize === true); const movedRawCamera = raw.length === 3 && raw[0].cameraX !== raw[1].cameraX && raw[1].cameraX !== raw[2].cameraX; const movedStableCamera = stabilized.length === 3 && stabilized[0].cameraX !== stabilized[1].cameraX && stabilized[1].cameraX !== stabilized[2].cameraX; const rawShowsJitter = raw.some((entry) => Number(entry.statsSnapError) > 1e-3 && Number(entry.inspectorSnapError) > 1e-3); const stabilizedQuantized = stabilized.every((entry) => Number(entry.statsSnapError) <= 1e-4 && Number(entry.inspectorSnapError) <= 1e-4); const passCountsStable = trace.every((entry) => Number(entry.directionalPassCount) === 4 && Number(entry.cascadeCount) === 4); return trace.length === 6 && movedRawCamera && movedStableCamera && rawShowsJitter && stabilizedQuantized && passCountsStable; } catch (_) { return false; } })()",
|
|
1330
|
+
debugExpression: "(() => { try { return { state: globalThis.__shadowRuntimeProbe || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1331
|
+
},
|
|
1332
|
+
],
|
|
1333
|
+
source: `
|
|
1334
|
+
globalThis.__shadowRuntimeProbe = {
|
|
1335
|
+
mesh: 0,
|
|
1336
|
+
material: 0,
|
|
1337
|
+
sun: 0,
|
|
1338
|
+
point: 0,
|
|
1339
|
+
spot: 0,
|
|
1340
|
+
trace: [],
|
|
1341
|
+
pendingSample: null,
|
|
1342
|
+
sampleIndex: 0,
|
|
1343
|
+
};
|
|
1344
|
+
`,
|
|
1345
|
+
nativeFrames: 7,
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
id: 'threejs-controls-raycast-interaction',
|
|
1349
|
+
modes: ['native'],
|
|
1350
|
+
namespaces: ['camera3d', 'scene3d'],
|
|
1351
|
+
functions: [
|
|
1352
|
+
'aura.camera3d.setControlProfile',
|
|
1353
|
+
'aura.camera3d.updateControls',
|
|
1354
|
+
'aura.camera3d.getControlState',
|
|
1355
|
+
'aura.scene3d.queryRaycast',
|
|
1356
|
+
],
|
|
1357
|
+
nativeChecks: [
|
|
1358
|
+
{
|
|
1359
|
+
id: 'threejs.controls.surface.methods',
|
|
1360
|
+
expression: "(() => ['setControlProfile','updateControls','getControlState'].every((name) => typeof aura.camera3d?.[name] === 'function'))()",
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
id: 'threejs.raycast.surface.method',
|
|
1364
|
+
expression: "typeof aura.scene3d?.queryRaycast === 'function'",
|
|
1365
|
+
},
|
|
1366
|
+
{
|
|
1367
|
+
id: 'threejs.controls.raycast.deterministic',
|
|
1368
|
+
expression: "(() => { const root = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -3 }, scale: { x: 1.5, y: 1.5, z: 1.5 } }); const near = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -1.5 }, scale: { x: 0.8, y: 0.8, z: 0.8 } }); const far = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -5 }, scale: { x: 2, y: 2, z: 2 } }); aura.scene3d.setParent(near, root); const profile = aura.camera3d.setControlProfile('orbit', { target: { x: 0, y: 0, z: -3 }, distance: 6, damping: 0.15, minDistance: 2, maxDistance: 12 }); const stepA = aura.camera3d.updateControls(1 / 60, { rotateX: 0.2, rotateY: -0.1, panX: 0.05, panY: 0.02, zoom: -0.15 }); const stepB = aura.camera3d.updateControls(1 / 60, { rotateX: 0.2, rotateY: -0.1, panX: 0.05, panY: 0.02, zoom: -0.15 }); const state = aura.camera3d.getControlState(); const cast = aura.scene3d.queryRaycast({ origin: { x: 0, y: 0, z: 0 }, direction: { x: 0, y: 0, z: -1 }, maxDistance: 32 }); const castAgain = aura.scene3d.queryRaycast({ origin: { x: 0, y: 0, z: 0 }, direction: { x: 0, y: 0, z: -1 }, maxDistance: 32 }); aura.scene3d.removeNode(near); aura.scene3d.removeNode(far); aura.scene3d.removeNode(root); const order = Array.isArray(cast?.hits) ? cast.hits.map((entry) => `${entry.nodeId}:${Number(entry.toi).toFixed(6)}`).join('|') : ''; const orderAgain = Array.isArray(castAgain?.hits) ? castAgain.hits.map((entry) => `${entry.nodeId}:${Number(entry.toi).toFixed(6)}`).join('|') : ''; return profile === undefined && stepA === undefined && stepB === undefined && state?.profile === 'orbit' && state?.active === true && cast?.ok === true && cast?.hit === true && cast?.hitCount >= 2 && cast?.firstHit && Number.isFinite(cast.firstHit.toi) && order.length > 0 && order === orderAgain; })()",
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
id: 'threejs.controls.raycast.reason-codes',
|
|
1372
|
+
expression: "(() => { const invalidProfile = aura.camera3d.setControlProfile('freecam'); const invalidControls = aura.camera3d.updateControls(-1, {}); const invalidRayA = aura.scene3d.queryRaycast(null); const invalidRayB = aura.scene3d.queryRaycast({ origin: { x: 0, y: 0, z: 0 }, direction: { x: 0, y: 0, z: 0 } }); return invalidProfile === undefined && invalidControls === undefined && invalidRayA && invalidRayA.ok === false && invalidRayA.reason === 'invalid_raycast_args' && invalidRayB && invalidRayB.ok === false && invalidRayB.reason === 'invalid_raycast_args'; })()",
|
|
1373
|
+
},
|
|
1374
|
+
],
|
|
1375
|
+
source: `
|
|
1376
|
+
aura.setup = function () {};
|
|
1377
|
+
`,
|
|
1378
|
+
nativeFrames: 2,
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
id: 'threejs-occlusion-skinned-runtime',
|
|
1382
|
+
modes: ['native'],
|
|
1383
|
+
namespaces: ['draw3d', 'camera3d', 'mesh', 'material', 'skinnedMesh', 'debug'],
|
|
1384
|
+
functions: [
|
|
1385
|
+
'aura.draw3d.clear3d',
|
|
1386
|
+
'aura.draw3d.drawMesh',
|
|
1387
|
+
'aura.draw3d.setOcclusionCulling',
|
|
1388
|
+
'aura.camera3d.perspective',
|
|
1389
|
+
'aura.camera3d.setPosition',
|
|
1390
|
+
'aura.camera3d.lookAt',
|
|
1391
|
+
'aura.mesh.createBox',
|
|
1392
|
+
'aura.material.create',
|
|
1393
|
+
'aura.skinnedMesh.create',
|
|
1394
|
+
'aura.skinnedMesh.addClip',
|
|
1395
|
+
'aura.skinnedMesh.playAnimation',
|
|
1396
|
+
'aura.skinnedMesh.getJointPosition',
|
|
1397
|
+
'aura.debug.inspectorStats',
|
|
1398
|
+
],
|
|
1399
|
+
nativeChecks: [
|
|
1400
|
+
{
|
|
1401
|
+
id: 'scene3d.occlusion-skinned.surface.methods',
|
|
1402
|
+
expression: "(() => ['clear3d','drawMesh','setOcclusionCulling'].every((name) => typeof aura.draw3d?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && ['createBox'].every((name) => typeof aura.mesh?.[name] === 'function') && typeof aura.material?.create === 'function' && ['create','addClip','playAnimation','getJointPosition'].every((name) => typeof aura.skinnedMesh?.[name] === 'function'))()",
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
id: 'scene3d.occlusion-skinned.runtime.setup',
|
|
1406
|
+
expression: "(() => { try { const wallMesh = aura.mesh.createBox(20, 20, 0.5); const boxMesh = aura.mesh.createBox(1, 1, 1); const wallMaterial = aura.material.create({ color: { r: 0.75, g: 0.75, b: 0.78, a: 1 }, metallic: 0.05, roughness: 0.95 }); const boxMaterial = aura.material.create({ color: { r: 0.35, g: 0.7, b: 0.95, a: 1 }, metallic: 0.15, roughness: 0.55 }); const skinnedId = aura.skinnedMesh.create(boxMesh, { jointCount: 1 }); const jointMatrix = (tx) => new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, 0, 0, 1]); aura.skinnedMesh.addClip({ name: 'wag', duration: 1, loop: true, channels: [{ jointIndex: 0, keyframes: [{ time: 0, transform: jointMatrix(0) }, { time: 0.5, transform: jointMatrix(1) }, { time: 1, transform: jointMatrix(0) }] }] }); aura.skinnedMesh.playAnimation(skinnedId, 'wag', { speed: 12, loop: true }); aura.draw3d.setOcclusionCulling({ enabled: true, conservative: true }); aura.camera3d.perspective(60, 0.1, 100); aura.camera3d.setPosition(0, 0, 6); aura.camera3d.lookAt(0, 0, 0); const baseDraw = aura.draw; aura.draw = function () { aura.draw3d.clear3d({ r: 0.02, g: 0.02, b: 0.03, a: 1 }); aura.draw3d.drawMesh(wallMesh, wallMaterial, { position: { x: 0, y: 0, z: 3 }, scale: { x: 1, y: 1, z: 1 } }); aura.draw3d.drawMesh(boxMesh, boxMaterial, { position: { x: 0, y: 0, z: -8 }, scale: { x: 0.1, y: 0.1, z: 0.1 } }); baseDraw(); }; globalThis.__occlusionSkinned = { skinnedId }; return Number.isInteger(wallMesh) && wallMesh > 0 && Number.isInteger(boxMesh) && boxMesh > 0 && Number.isInteger(wallMaterial) && wallMaterial > 0 && Number.isInteger(boxMaterial) && boxMaterial > 0 && Number.isInteger(skinnedId) && skinnedId > 0; } catch (_) { return false; } })()",
|
|
1407
|
+
},
|
|
1408
|
+
],
|
|
1409
|
+
nativePostChecks: [
|
|
1410
|
+
{
|
|
1411
|
+
id: 'scene3d.occlusion-skinned.runtime.evidence',
|
|
1412
|
+
expression: "(() => { try { const state = globalThis.__occlusionSkinned || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; const animation = scene3d.animation || {}; const occlusion = scene3d.occlusion || {}; const joint = aura.skinnedMesh.getJointPosition(state.skinnedId, 0); const tested = Number(occlusion.tested || 0); const culled = Number(occlusion.culled || 0); const passed = Number(occlusion.passed || 0); const renderedGroups = Number(submission.skinnedRenderedGroups || 0); const fallbackGroups = Number(submission.skinnedGroupsUsingDefaultSkinningData || 0); const hasLastFrameOutcomes = occlusion.hasLastFrameOutcomes === true; const occlusionCountersConsistent = tested >= 1 && (culled + passed) === tested && culled >= 1 && passed >= 1; return Number.isFinite(joint?.x) && Math.abs(Number(joint.x)) > 0.05 && occlusion.enabled === true && occlusion.conservative === true && Number(occlusion.hizLevelCount || 0) >= 1 && Number(occlusion.viewportWidth || 0) >= 1 && Number(occlusion.viewportHeight || 0) >= 1 && hasLastFrameOutcomes && occlusionCountersConsistent && Number(resources.skinnedGroupCount || 0) >= 1 && Number(resources.skinnedAnimationClipCount || 0) >= 1 && Number(resources.skinnedAnimationPlaybackCount || 0) >= 1 && renderedGroups >= 1 && Number(submission.skinnedRenderedInstances || 0) >= 1 && Number(submission.skinnedUploadedJointMatrices || 0) >= 1 && fallbackGroups === renderedGroups && fallbackGroups >= 1 && Number(animation.skinnedTrackedMeshCount || 0) >= 1 && Number(animation.skinnedLastJointUpdateCount || 0) >= 1 && Number(animation.skinnedTotalJointUpdateCount || 0) >= Number(animation.skinnedLastJointUpdateCount || 0) && Number(animation.skinnedLastAdvancedPlaybackCount || 0) >= 1; } catch (_) { return false; } })()",
|
|
1413
|
+
debugExpression: "(() => { try { aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state: globalThis.__occlusionSkinned || null, scene3dRuntime: stats.scene3dRuntime || null, joint: aura.skinnedMesh.getJointPosition(globalThis.__occlusionSkinned?.skinnedId || 0, 0) || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1414
|
+
},
|
|
1415
|
+
],
|
|
1416
|
+
source: `
|
|
1417
|
+
globalThis.__occlusionSkinned = { skinnedId: 0 };
|
|
1418
|
+
`,
|
|
1419
|
+
nativeFrames: 12,
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
id: 'skinned-authored-input-runtime',
|
|
1423
|
+
modes: ['native'],
|
|
1424
|
+
namespaces: ['mesh', 'skinnedMesh', 'camera3d', 'draw3d', 'debug'],
|
|
1425
|
+
functions: [
|
|
1426
|
+
'aura.mesh.createFromVertices',
|
|
1427
|
+
'aura.skinnedMesh.create',
|
|
1428
|
+
'aura.skinnedMesh.addClip',
|
|
1429
|
+
'aura.skinnedMesh.playAnimation',
|
|
1430
|
+
'aura.skinnedMesh.getJointPosition',
|
|
1431
|
+
'aura.debug.inspectorStats',
|
|
1432
|
+
],
|
|
1433
|
+
nativeChecks: [
|
|
1434
|
+
{
|
|
1435
|
+
id: 'skinned.authored-input.surface.methods',
|
|
1436
|
+
expression: "(() => typeof aura.mesh?.createFromVertices === 'function' && ['create','addClip','playAnimation','getJointPosition'].every((name) => typeof aura.skinnedMesh?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.draw3d?.clear3d === 'function' && typeof aura.debug?.inspectorStats === 'function')()",
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
id: 'skinned.authored-input.runtime.setup',
|
|
1440
|
+
expression: `(() => { try { const positions = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]); const indices = new Uint32Array([0, 1, 2, 0, 2, 3]); const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]); const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]); const colors = new Float32Array([1, 0.22, 0.22, 1, 0.22, 1, 0.36, 1, 0.28, 0.56, 1, 1, 1, 0.9, 0.28, 1]); const jointIndices = new Uint32Array([0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]); const jointWeights = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]); const coloredMesh = aura.mesh.createFromVertices(positions, indices, normals, uvs, colors, { jointIndices, jointWeights }); const legacyMesh = aura.mesh.createFromVertices(positions, indices, normals, uvs, { jointIndices, jointWeights }); const coloredSkinnedId = aura.skinnedMesh.create(coloredMesh, { jointCount: 2 }); const legacySkinnedId = aura.skinnedMesh.create(legacyMesh, { jointCount: 2 }); const jointMatrix = (tx) => new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, 0, 0, 1]); aura.skinnedMesh.addClip({ name: 'authored', duration: 1, loop: true, channels: [{ jointIndex: 1, keyframes: [{ time: 0, transform: jointMatrix(0) }, { time: 0.5, transform: jointMatrix(0.5) }, { time: 1, transform: jointMatrix(0) }] }] }); aura.skinnedMesh.playAnimation(coloredSkinnedId, 'authored', { speed: 12, loop: true }); aura.skinnedMesh.playAnimation(legacySkinnedId, 'authored', { speed: 12, loop: true }); aura.camera3d.perspective(60, 0.1, 100); aura.camera3d.setPosition(0, 0, 4); aura.camera3d.lookAt(0, 0, 0); const baseDraw = aura.draw; aura.draw = function () { aura.draw3d.clear3d({ r: 0.02, g: 0.02, b: 0.03, a: 1 }); baseDraw(); }; globalThis.__authoredSkinningRuntime = { coloredMesh, legacyMesh, coloredSkinnedId, legacySkinnedId, vertexColorEntries: colors.length / 4 }; return [coloredMesh, legacyMesh, coloredSkinnedId, legacySkinnedId].every((handle) => Number.isInteger(handle) && handle > 0) && globalThis.__authoredSkinningRuntime.vertexColorEntries === 4; } catch (_) { return false; } })()`,
|
|
1441
|
+
},
|
|
1442
|
+
],
|
|
1443
|
+
nativePostChecks: [
|
|
1444
|
+
{
|
|
1445
|
+
id: 'skinned.authored-input.runtime.evidence',
|
|
1446
|
+
expression: "(() => { try { const state = globalThis.__authoredSkinningRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; const coloredJoint = aura.skinnedMesh.getJointPosition(state.coloredSkinnedId, 1); const legacyJoint = aura.skinnedMesh.getJointPosition(state.legacySkinnedId, 1); const renderedGroups = Number(submission.skinnedRenderedGroups || 0); const defaultGroups = Number(submission.skinnedGroupsUsingDefaultSkinningData || 0); const suppliedGroups = Number(submission.skinnedGroupsUsingSuppliedSkinningData || 0); return state.vertexColorEntries === 4 && state.coloredMesh !== state.legacyMesh && Number.isFinite(coloredJoint?.x) && Math.abs(Number(coloredJoint.x)) > 0.05 && Number.isFinite(legacyJoint?.x) && Math.abs(Number(legacyJoint.x)) > 0.05 && renderedGroups >= 2 && Number(submission.skinnedRenderedInstances || 0) >= 2 && Number(submission.skinnedUploadedJointMatrices || 0) >= 2 && suppliedGroups >= 2 && defaultGroups === 0 && suppliedGroups === renderedGroups && Number(resources.skinnedGroupCount || 0) >= 2; } catch (_) { return false; } })()",
|
|
1447
|
+
debugExpression: "(() => { try { aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state: globalThis.__authoredSkinningRuntime || null, scene3dRuntime: stats.scene3dRuntime || null, coloredJoint: aura.skinnedMesh.getJointPosition(globalThis.__authoredSkinningRuntime?.coloredSkinnedId || 0, 1) || null, legacyJoint: aura.skinnedMesh.getJointPosition(globalThis.__authoredSkinningRuntime?.legacySkinnedId || 0, 1) || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1448
|
+
},
|
|
1449
|
+
],
|
|
1450
|
+
source: `
|
|
1451
|
+
globalThis.__authoredSkinningRuntime = {
|
|
1452
|
+
coloredMesh: 0,
|
|
1453
|
+
legacyMesh: 0,
|
|
1454
|
+
coloredSkinnedId: 0,
|
|
1455
|
+
legacySkinnedId: 0,
|
|
1456
|
+
vertexColorEntries: 0,
|
|
1457
|
+
};
|
|
1458
|
+
`,
|
|
1459
|
+
nativeFrames: 6,
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
id: 'skinned-crowd-instancing-runtime',
|
|
1463
|
+
modes: ['native'],
|
|
1464
|
+
namespaces: ['mesh', 'skinnedMesh', 'camera3d', 'draw3d', 'debug'],
|
|
1465
|
+
functions: [
|
|
1466
|
+
'aura.mesh.createFromVertices',
|
|
1467
|
+
'aura.skinnedMesh.addClip',
|
|
1468
|
+
'aura.skinnedMesh.createCrowd',
|
|
1469
|
+
'aura.skinnedMesh.setCrowdInstanceTransform',
|
|
1470
|
+
'aura.skinnedMesh.playCrowdAnimation',
|
|
1471
|
+
'aura.skinnedMesh.blendCrowdAnimations',
|
|
1472
|
+
'aura.skinnedMesh.getCrowdJointPosition',
|
|
1473
|
+
'aura.skinnedMesh.getCrowdInfo',
|
|
1474
|
+
'aura.debug.inspectorStats',
|
|
1475
|
+
],
|
|
1476
|
+
nativeChecks: [
|
|
1477
|
+
{
|
|
1478
|
+
id: 'skinned.crowd-instancing.surface.methods',
|
|
1479
|
+
expression: "(() => typeof aura.mesh?.createFromVertices === 'function' && ['addClip','createCrowd','setCrowdInstanceTransform','playCrowdAnimation','blendCrowdAnimations','getCrowdJointPosition','getCrowdInfo'].every((name) => typeof aura.skinnedMesh?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.draw3d?.clear3d === 'function' && typeof aura.debug?.inspectorStats === 'function')()",
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
id: 'skinned.crowd-instancing.runtime.setup',
|
|
1483
|
+
expression: `(() => {
|
|
1484
|
+
try {
|
|
1485
|
+
const state = globalThis.__crowdSkinningRuntime;
|
|
1486
|
+
const leadColumns = 16;
|
|
1487
|
+
const leadRows = 12;
|
|
1488
|
+
const supportColumns = 8;
|
|
1489
|
+
const supportRows = 8;
|
|
1490
|
+
const leadCount = leadColumns * leadRows;
|
|
1491
|
+
const supportCount = supportColumns * supportRows;
|
|
1492
|
+
const positions = new Float32Array([-0.4, -0.6, 0, 0.4, -0.6, 0, 0.4, 0.6, 0, -0.4, 0.6, 0]);
|
|
1493
|
+
const indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
1494
|
+
const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
|
|
1495
|
+
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
1496
|
+
const colors = new Float32Array([0.95, 0.55, 0.35, 1, 0.95, 0.75, 0.3, 1, 0.35, 0.85, 0.95, 1, 0.25, 0.45, 0.95, 1]);
|
|
1497
|
+
const jointIndices = new Uint32Array([0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]);
|
|
1498
|
+
const jointWeights = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]);
|
|
1499
|
+
const crowdMesh = aura.mesh.createFromVertices(positions, indices, normals, uvs, colors, { jointIndices, jointWeights });
|
|
1500
|
+
const jointXMatrix = (tx) => new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, 0, 0, 1]);
|
|
1501
|
+
const jointYMatrix = (ty) => new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, ty, 0, 1]);
|
|
1502
|
+
const instanceMatrix = (tx, ty, tz, scale = 1) => new Float32Array([scale, 0, 0, 0, 0, scale, 0, 0, 0, 0, scale, 0, tx, ty, tz, 1]);
|
|
1503
|
+
aura.skinnedMesh.addClip({
|
|
1504
|
+
name: 'crowd-step',
|
|
1505
|
+
duration: 1,
|
|
1506
|
+
loop: true,
|
|
1507
|
+
channels: [{ jointIndex: 1, keyframes: [{ time: 0, transform: jointXMatrix(0) }, { time: 0.5, transform: jointXMatrix(0.45) }, { time: 1, transform: jointXMatrix(0) }] }],
|
|
1508
|
+
});
|
|
1509
|
+
aura.skinnedMesh.addClip({
|
|
1510
|
+
name: 'crowd-lift',
|
|
1511
|
+
duration: 1,
|
|
1512
|
+
loop: true,
|
|
1513
|
+
channels: [{ jointIndex: 1, keyframes: [{ time: 0, transform: jointYMatrix(0) }, { time: 0.5, transform: jointYMatrix(0.22) }, { time: 1, transform: jointYMatrix(0) }] }],
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
let overLimitError = '';
|
|
1517
|
+
try {
|
|
1518
|
+
aura.skinnedMesh.createCrowd(crowdMesh, { jointCount: 2 }, { instanceCount: 4097 });
|
|
1519
|
+
} catch (error) {
|
|
1520
|
+
overLimitError = String(error && error.message || error);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
const leadCrowdId = aura.skinnedMesh.createCrowd(crowdMesh, { jointCount: 2 }, { instanceCount: leadCount });
|
|
1524
|
+
const supportCrowdId = aura.skinnedMesh.createCrowd(crowdMesh, { jointCount: 2 }, { instanceCount: supportCount });
|
|
1525
|
+
|
|
1526
|
+
let leadIndex = 0;
|
|
1527
|
+
for (let row = 0; row < leadRows; row += 1) {
|
|
1528
|
+
for (let col = 0; col < leadColumns; col += 1) {
|
|
1529
|
+
const x = (col - ((leadColumns - 1) * 0.5)) * 0.78;
|
|
1530
|
+
const z = 1.2 + ((row - ((leadRows - 1) * 0.5)) * 0.72);
|
|
1531
|
+
const y = Math.sin((col * 0.75) + (row * 0.55)) * 0.04;
|
|
1532
|
+
const scale = 0.92 + ((leadIndex % 5) * 0.03);
|
|
1533
|
+
const phase = (leadIndex % leadColumns) / leadColumns;
|
|
1534
|
+
const speed = 5 + ((leadIndex % 4) * 0.3);
|
|
1535
|
+
aura.skinnedMesh.setCrowdInstanceTransform(leadCrowdId, leadIndex, instanceMatrix(x, y, z, scale));
|
|
1536
|
+
aura.skinnedMesh.playCrowdAnimation(leadCrowdId, leadIndex, 'crowd-step', {
|
|
1537
|
+
loop: true,
|
|
1538
|
+
speed,
|
|
1539
|
+
time: phase,
|
|
1540
|
+
});
|
|
1541
|
+
leadIndex += 1;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
let supportIndex = 0;
|
|
1546
|
+
for (let row = 0; row < supportRows; row += 1) {
|
|
1547
|
+
for (let col = 0; col < supportColumns; col += 1) {
|
|
1548
|
+
const x = (col - ((supportColumns - 1) * 0.5)) * 1.0;
|
|
1549
|
+
const z = -6.2 + ((row - ((supportRows - 1) * 0.5)) * 0.84);
|
|
1550
|
+
const y = 0.18 + (Math.cos((col * 0.7) + (row * 0.9)) * 0.03);
|
|
1551
|
+
const scale = 0.8 + ((supportIndex % 4) * 0.035);
|
|
1552
|
+
const phase = (supportIndex % supportColumns) / supportColumns;
|
|
1553
|
+
aura.skinnedMesh.setCrowdInstanceTransform(supportCrowdId, supportIndex, instanceMatrix(x, y, z, scale));
|
|
1554
|
+
if (supportIndex % 2 === 0) {
|
|
1555
|
+
aura.skinnedMesh.blendCrowdAnimations(supportCrowdId, supportIndex, [
|
|
1556
|
+
{ name: 'crowd-step', weight: 0.72, time: phase },
|
|
1557
|
+
{ name: 'crowd-lift', weight: 0.28, time: Number((phase * 0.5).toFixed(3)) },
|
|
1558
|
+
]);
|
|
1559
|
+
} else {
|
|
1560
|
+
aura.skinnedMesh.playCrowdAnimation(supportCrowdId, supportIndex, 'crowd-step', {
|
|
1561
|
+
loop: true,
|
|
1562
|
+
speed: 4.6 + ((supportIndex % 3) * 0.4),
|
|
1563
|
+
time: phase,
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
supportIndex += 1;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
aura.camera3d.perspective(55, 0.1, 100);
|
|
1571
|
+
aura.camera3d.setPosition(0, 10.5, 18.5);
|
|
1572
|
+
aura.camera3d.lookAt(0, 0.45, -1.5);
|
|
1573
|
+
const baseDraw = aura.draw;
|
|
1574
|
+
aura.draw = function () {
|
|
1575
|
+
aura.draw3d.clear3d({ r: 0.018, g: 0.02, b: 0.028, a: 1 });
|
|
1576
|
+
baseDraw();
|
|
1577
|
+
};
|
|
1578
|
+
state.crowdMesh = crowdMesh;
|
|
1579
|
+
state.leadCrowdId = leadCrowdId;
|
|
1580
|
+
state.supportCrowdId = supportCrowdId;
|
|
1581
|
+
state.overLimitError = overLimitError;
|
|
1582
|
+
state.expectedRenderedGroups = 2;
|
|
1583
|
+
state.expectedRenderedInstances = leadCount + supportCount;
|
|
1584
|
+
state.expectedAdvancedPlaybackCount = leadCount + (supportCount / 2);
|
|
1585
|
+
return [crowdMesh, leadCrowdId, supportCrowdId].every((handle) => Number.isInteger(handle) && handle > 0)
|
|
1586
|
+
&& overLimitError.includes('opts.instanceCount must be 1..4096');
|
|
1587
|
+
} catch (_) {
|
|
1588
|
+
return false;
|
|
1589
|
+
}
|
|
1590
|
+
})()`,
|
|
1591
|
+
},
|
|
1592
|
+
],
|
|
1593
|
+
nativePostChecks: [
|
|
1594
|
+
{
|
|
1595
|
+
id: 'skinned.crowd-instancing.runtime.evidence',
|
|
1596
|
+
expression: "(() => { try { const state = globalThis.__crowdSkinningRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const leadInfo = aura.skinnedMesh.getCrowdInfo(state.leadCrowdId || 0); const supportInfo = aura.skinnedMesh.getCrowdInfo(state.supportCrowdId || 0); const leadJoints = [0, 47, 143].map((index) => aura.skinnedMesh.getCrowdJointPosition(state.leadCrowdId || 0, index, 1)); const supportJoints = [2, 4, 34].map((index) => aura.skinnedMesh.getCrowdJointPosition(state.supportCrowdId || 0, index, 1)); const allJoints = [...leadJoints, ...supportJoints]; const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; const animation = scene3d.animation || {}; const jointTrace = allJoints.map((joint) => `${Number(Number(joint?.x || 0).toFixed(3))}:${Number(Number(joint?.y || 0).toFixed(3))}`); const uniqueJointTraceCount = new Set(jointTrace).size; const liftedJointCount = supportJoints.filter((joint) => Math.abs(Number(joint?.y || 0)) > 0.01).length; const renderedGroups = Number(submission.skinnedRenderedGroups || 0); const suppliedGroups = Number(submission.skinnedGroupsUsingSuppliedSkinningData || 0); const defaultGroups = Number(submission.skinnedGroupsUsingDefaultSkinningData || 0); const expectedRenderedInstances = Number(state.expectedRenderedInstances || 0); const expectedAdvancedPlaybackCount = Number(state.expectedAdvancedPlaybackCount || 0); return Number.isInteger(state.crowdMesh) && state.crowdMesh > 0 && leadInfo?.meshHandle === state.crowdMesh && supportInfo?.meshHandle === state.crowdMesh && leadInfo?.jointCount === 2 && supportInfo?.jointCount === 2 && leadInfo?.instanceCount === 192 && supportInfo?.instanceCount === 64 && leadInfo?.animatedInstanceCount === 192 && supportInfo?.animatedInstanceCount === 64 && leadInfo?.maxInstanceCount === 4096 && supportInfo?.maxInstanceCount === 4096 && leadInfo?.maxJointCount === 128 && supportInfo?.maxJointCount === 128 && allJoints.every((joint) => Number.isFinite(joint?.x) && Number.isFinite(joint?.y)) && uniqueJointTraceCount >= 4 && liftedJointCount >= 2 && state.overLimitError?.includes('opts.instanceCount must be 1..4096') && renderedGroups >= Number(state.expectedRenderedGroups || 0) && Number(resources.skinnedGroupCount || 0) >= Number(state.expectedRenderedGroups || 0) && Number(submission.skinnedRenderedInstances || 0) >= expectedRenderedInstances && Number(submission.skinnedUploadedTransforms || 0) >= expectedRenderedInstances && Number(submission.skinnedUploadedJointMatrices || 0) >= expectedRenderedInstances * 2 && suppliedGroups === renderedGroups && defaultGroups === 0 && Number(resources.skinnedAnimationPlaybackCount || 0) >= expectedAdvancedPlaybackCount && Number(animation.skinnedTrackedMeshCount || 0) >= expectedRenderedInstances && Number(animation.skinnedLastJointUpdateCount || 0) >= expectedRenderedInstances * 2 && Number(animation.skinnedLastAdvancedPlaybackCount || 0) >= expectedAdvancedPlaybackCount; } catch (_) { return false; } })()",
|
|
1597
|
+
debugExpression: "(() => { try { aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const state = globalThis.__crowdSkinningRuntime || {}; return { state, scene3dRuntime: stats.scene3dRuntime || null, leadInfo: aura.skinnedMesh.getCrowdInfo(state.leadCrowdId || 0) || null, supportInfo: aura.skinnedMesh.getCrowdInfo(state.supportCrowdId || 0) || null, leadJoints: [0, 47, 143].map((index) => aura.skinnedMesh.getCrowdJointPosition(state.leadCrowdId || 0, index, 1) || null), supportJoints: [2, 4, 34].map((index) => aura.skinnedMesh.getCrowdJointPosition(state.supportCrowdId || 0, index, 1) || null) }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1598
|
+
},
|
|
1599
|
+
],
|
|
1600
|
+
source: `
|
|
1601
|
+
globalThis.__crowdSkinningRuntime = {
|
|
1602
|
+
crowdMesh: 0,
|
|
1603
|
+
leadCrowdId: 0,
|
|
1604
|
+
supportCrowdId: 0,
|
|
1605
|
+
overLimitError: '',
|
|
1606
|
+
expectedRenderedGroups: 0,
|
|
1607
|
+
expectedRenderedInstances: 0,
|
|
1608
|
+
};
|
|
1609
|
+
`,
|
|
1610
|
+
nativeFrames: 10,
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
id: 'navmesh-crowd-locomotion-runtime',
|
|
1614
|
+
modes: ['native'],
|
|
1615
|
+
namespaces: ['navmesh', 'character3d'],
|
|
1616
|
+
functions: [
|
|
1617
|
+
'aura.navmesh.bakeFromMesh',
|
|
1618
|
+
'aura.navmesh.findPath',
|
|
1619
|
+
'aura.navmesh.createCrowd',
|
|
1620
|
+
'aura.navmesh.addAgent',
|
|
1621
|
+
'aura.navmesh.setAgentTarget',
|
|
1622
|
+
'aura.navmesh.bindAgentCharacter',
|
|
1623
|
+
'aura.navmesh.setAgentFollowTarget',
|
|
1624
|
+
'aura.navmesh.clearAgentFollowTarget',
|
|
1625
|
+
'aura.navmesh.stepCrowd',
|
|
1626
|
+
'aura.navmesh.getAgentPosition',
|
|
1627
|
+
'aura.navmesh.getAgentVelocity',
|
|
1628
|
+
'aura.navmesh.getAgentState',
|
|
1629
|
+
'aura.navmesh.getCrowdInfo',
|
|
1630
|
+
'aura.character3d.create',
|
|
1631
|
+
'aura.character3d.getPosition',
|
|
1632
|
+
],
|
|
1633
|
+
nativeChecks: [
|
|
1634
|
+
{
|
|
1635
|
+
id: 'navmesh.crowd-locomotion.surface.methods',
|
|
1636
|
+
expression: "(() => ['bakeFromMesh','findPath','createCrowd','addAgent','setAgentTarget','bindAgentCharacter','setAgentFollowTarget','clearAgentFollowTarget','stepCrowd','getAgentPosition','getAgentVelocity','getAgentState','getCrowdInfo'].every((name) => typeof aura.navmesh?.[name] === 'function') && ['create','getPosition'].every((name) => typeof aura.character3d?.[name] === 'function'))()",
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
id: 'navmesh.crowd-locomotion.runtime.setup',
|
|
1640
|
+
expression: `(() => { try { const state = globalThis.__navmeshCrowdRuntime; const buildGrid = (size, subdivisions) => { const positions = []; const indices = []; const row = subdivisions + 1; const step = size / subdivisions; for (let z = 0; z <= subdivisions; z += 1) { for (let x = 0; x <= subdivisions; x += 1) { positions.push(x * step, 0, z * step); } } for (let z = 0; z < subdivisions; z += 1) { for (let x = 0; x < subdivisions; x += 1) { const i = (z * row) + x; indices.push(i, i + 1, i + row + 1); indices.push(i + 1, i + row + 2, i + row + 1); } } return { positions: new Float32Array(positions), indices: new Uint32Array(indices) }; }; const fingerprintPath = (path) => Array.isArray(path) ? path.map((point) => \`\${Number(Number(point?.x || 0).toFixed(3))}:\${Number(Number(point?.z || 0).toFixed(3))}\`).join('|') : ''; const previewStart = { x: 1.2, y: 0, z: 1.2 }; const previewEnd = { x: 8.2, y: 0, z: 7.8 }; const redirectEnd = { x: 2.4, y: 0, z: 8.4 }; const escortBEnd = { x: 9.6, y: 0, z: 2.4 }; const grid = buildGrid(12, 4); const meshId = aura.navmesh.bakeFromMesh(grid.positions, grid.indices, { cellSize: 1.0, agentRadius: 0.45, agentHeight: 1.8 }); const previewPath = aura.navmesh.findPath(meshId, previewStart.x, previewStart.y, previewStart.z, previewEnd.x, previewEnd.y, previewEnd.z); const missingPath = aura.navmesh.findPath(999999, 0, 0, 0, 1, 0, 1); const crowdId = aura.navmesh.createCrowd(meshId, { maxAgents: 8, neighborDist: 3.5, maxNeighbors: 6, timeHorizon: 2.5, obstacleTimeHorizon: 2.5 }); const leaderCharacter = aura.character3d.create({ x: previewStart.x, y: 0.9, z: previewStart.z, radius: 0.35, height: 1.8 }); const escortACharacter = aura.character3d.create({ x: 0.2, y: 0.9, z: 0.6, radius: 0.35, height: 1.8 }); const escortBCharacter = aura.character3d.create({ x: 2.1, y: 0.9, z: 0.4, radius: 0.35, height: 1.8 }); const leader = aura.navmesh.addAgent(crowdId, { x: previewStart.x, y: 0, z: previewStart.z, radius: 0.45, maxSpeed: 2.3 }); const escortA = aura.navmesh.addAgent(crowdId, { x: 0.2, y: 0, z: 0.6, radius: 0.45, maxSpeed: 2.1 }); const escortB = aura.navmesh.addAgent(crowdId, { x: 2.1, y: 0, z: 0.4, radius: 0.45, maxSpeed: 2.1 }); const bindLeader = aura.navmesh.bindAgentCharacter(crowdId, leader, leaderCharacter, { offset: { x: 0, y: 0.9, z: 0 } }); const bindEscortA = aura.navmesh.bindAgentCharacter(crowdId, escortA, escortACharacter, { offset: { x: 0, y: 0.9, z: 0 } }); const bindEscortB = aura.navmesh.bindAgentCharacter(crowdId, escortB, escortBCharacter, { offset: { x: 0, y: 0.9, z: 0 } }); const invalidFollow = aura.navmesh.setAgentFollowTarget(crowdId, escortA, { agentId: 999999 }, { replanDistance: 0.2 }); const invalidStep = aura.navmesh.stepCrowd(crowdId, 0); const leaderTarget = aura.navmesh.setAgentTarget(crowdId, leader, previewEnd.x, previewEnd.y, previewEnd.z); const escortAFollow = aura.navmesh.setAgentFollowTarget(crowdId, escortA, { agentId: leader }, { offset: { x: -1.1, y: 0, z: -0.9 }, stopDistance: 0.7, replanDistance: 0.25 }); const escortBFollow = aura.navmesh.setAgentFollowTarget(crowdId, escortB, { characterId: leaderCharacter }, { offset: { x: 1.1, y: -0.9, z: -0.9 }, stopDistance: 0.7, replanDistance: 0.25 }); state.meshId = meshId; state.crowdId = crowdId; state.characters = { leader: leaderCharacter, escortA: escortACharacter, escortB: escortBCharacter }; state.agents = { leader, escortA, escortB }; state.starts = { leader: { x: previewStart.x, z: previewStart.z }, escortA: { x: 0.2, z: 0.6 }, escortB: { x: 2.1, z: 0.4 } }; state.invalidFollow = invalidFollow; state.invalidStep = invalidStep; state.bindings = { bindLeader, bindEscortA, bindEscortB, leaderTarget, escortAFollow, escortBFollow }; state.pathQueries = { previewStart, previewEnd, redirectStart: null, redirectEnd, escortBStart: null, escortBEnd }; state.previewPath = Array.isArray(previewPath) ? previewPath : null; state.previewFingerprint = fingerprintPath(previewPath); state.missingPath = missingPath; state.initialLeaderPosition = aura.navmesh.getAgentPosition(crowdId, leader); state.initialLeaderVelocity = aura.navmesh.getAgentVelocity(crowdId, leader); state.redirectPath = null; state.redirectFingerprint = ''; state.clearEscortB = null; state.escortBRetarget = null; state.escortBPath = null; state.escortBFingerprint = ''; state.maxLeaderSpeed = 0; state.maxEscortBSpeed = 0; state.stepCount = 0; state.lastStep = null; state.redirect = null; aura.update = function () { state.stepCount += 1; state.lastStep = aura.navmesh.stepCrowd(crowdId, 1 / 60); const leaderVelocity = aura.navmesh.getAgentVelocity(crowdId, leader) || {}; const escortBVelocity = aura.navmesh.getAgentVelocity(crowdId, escortB) || {}; const leaderSpeed = Math.hypot(Number(leaderVelocity.x || 0), Number(leaderVelocity.z || 0)); const escortBSpeed = Math.hypot(Number(escortBVelocity.x || 0), Number(escortBVelocity.z || 0)); state.maxLeaderSpeed = Math.max(Number(state.maxLeaderSpeed || 0), leaderSpeed); state.maxEscortBSpeed = Math.max(Number(state.maxEscortBSpeed || 0), escortBSpeed); if (state.stepCount === 45) { const leaderPosition = aura.navmesh.getAgentPosition(crowdId, leader) || previewStart; state.pathQueries.redirectStart = leaderPosition; state.redirectPath = aura.navmesh.findPath(meshId, leaderPosition.x, leaderPosition.y, leaderPosition.z, redirectEnd.x, redirectEnd.y, redirectEnd.z); state.redirectFingerprint = fingerprintPath(state.redirectPath); state.redirect = aura.navmesh.setAgentTarget(crowdId, leader, redirectEnd.x, redirectEnd.y, redirectEnd.z); } if (state.stepCount === 60) { const escortBPosition = aura.navmesh.getAgentPosition(crowdId, escortB) || state.starts.escortB; state.pathQueries.escortBStart = escortBPosition; state.clearEscortB = aura.navmesh.clearAgentFollowTarget(crowdId, escortB); state.escortBPath = aura.navmesh.findPath(meshId, escortBPosition.x, escortBPosition.y, escortBPosition.z, escortBEnd.x, escortBEnd.y, escortBEnd.z); state.escortBFingerprint = fingerprintPath(state.escortBPath); state.escortBRetarget = aura.navmesh.setAgentTarget(crowdId, escortB, escortBEnd.x, escortBEnd.y, escortBEnd.z); } }; return Number.isInteger(meshId) && meshId > 0 && Number.isInteger(crowdId) && crowdId > 0 && [leaderCharacter, escortACharacter, escortBCharacter, leader, escortA, escortB].every((handle) => Number.isInteger(handle) && handle > 0) && bindLeader?.ok === true && bindEscortA?.ok === true && bindEscortB?.ok === true && leaderTarget?.ok === true && escortAFollow?.ok === true && escortBFollow?.ok === true && Array.isArray(previewPath) && previewPath.length >= 1 && typeof state.previewFingerprint === 'string' && state.previewFingerprint.length > 0 && missingPath === null && Number.isFinite(state.initialLeaderPosition?.x) && Number.isFinite(state.initialLeaderVelocity?.x); } catch (_) { return false; } })()`,
|
|
1641
|
+
},
|
|
1642
|
+
],
|
|
1643
|
+
nativePostChecks: [
|
|
1644
|
+
{
|
|
1645
|
+
id: 'navmesh.crowd-locomotion.runtime.evidence',
|
|
1646
|
+
expression: "(() => { try { const state = globalThis.__navmeshCrowdRuntime || {}; const fingerprintPath = (path) => Array.isArray(path) ? path.map((point) => `${Number(Number(point?.x || 0).toFixed(3))}:${Number(Number(point?.z || 0).toFixed(3))}`).join('|') : ''; const closeTo = (value, target, tolerance = 0.01) => Math.abs(Number(value || 0) - target) <= tolerance; const info = aura.navmesh.getCrowdInfo(state.crowdId || 0); const leader = aura.navmesh.getAgentState(state.crowdId || 0, state.agents?.leader || 0); const escortA = aura.navmesh.getAgentState(state.crowdId || 0, state.agents?.escortA || 0); const escortB = aura.navmesh.getAgentState(state.crowdId || 0, state.agents?.escortB || 0); const leaderPosition = aura.navmesh.getAgentPosition(state.crowdId || 0, state.agents?.leader || 0); const leaderVelocity = aura.navmesh.getAgentVelocity(state.crowdId || 0, state.agents?.leader || 0); const escortBPosition = aura.navmesh.getAgentPosition(state.crowdId || 0, state.agents?.escortB || 0); const escortBVelocity = aura.navmesh.getAgentVelocity(state.crowdId || 0, state.agents?.escortB || 0); const leaderCharacter = aura.character3d.getPosition(state.characters?.leader || 0); const escortACharacter = aura.character3d.getPosition(state.characters?.escortA || 0); const escortBCharacter = aura.character3d.getPosition(state.characters?.escortB || 0); const moved = (position, start) => Math.hypot(Number(position?.x || 0) - Number(start?.x || 0), Number(position?.z || 0) - Number(start?.z || 0)) > 1.0; const synced = (agent, character) => Math.abs((Number(agent?.y || 0) + 0.9) - Number(character?.y || 0)) < 0.05; const previewCheck = aura.navmesh.findPath(state.meshId || 0, state.pathQueries?.previewStart?.x || 0, state.pathQueries?.previewStart?.y || 0, state.pathQueries?.previewStart?.z || 0, state.pathQueries?.previewEnd?.x || 0, state.pathQueries?.previewEnd?.y || 0, state.pathQueries?.previewEnd?.z || 0); const redirectCheck = aura.navmesh.findPath(state.meshId || 0, state.pathQueries?.redirectStart?.x || 0, state.pathQueries?.redirectStart?.y || 0, state.pathQueries?.redirectStart?.z || 0, state.pathQueries?.redirectEnd?.x || 0, state.pathQueries?.redirectEnd?.y || 0, state.pathQueries?.redirectEnd?.z || 0); const escortBCheck = aura.navmesh.findPath(state.meshId || 0, state.pathQueries?.escortBStart?.x || 0, state.pathQueries?.escortBStart?.y || 0, state.pathQueries?.escortBStart?.z || 0, state.pathQueries?.escortBEnd?.x || 0, state.pathQueries?.escortBEnd?.y || 0, state.pathQueries?.escortBEnd?.z || 0); return state.invalidFollow?.ok === false && state.invalidFollow?.reasonCode === 'invalid_follow_target' && state.invalidStep?.ok === false && state.invalidStep?.reasonCode === 'invalid_dt' && state.bindings?.escortAFollow?.reasonCode === 'navmesh_agent_follow_target_set' && state.bindings?.escortBFollow?.reasonCode === 'navmesh_agent_follow_target_set' && state.redirect?.ok === true && state.clearEscortB?.ok === true && state.clearEscortB?.reasonCode === 'navmesh_agent_follow_target_cleared' && state.escortBRetarget?.ok === true && state.escortBRetarget?.reasonCode === 'navmesh_agent_target_set' && info?.ok === true && info?.agentCount === 3 && info?.followingAgents === 3 && info?.boundCharacterCount === 3 && Number(info?.totalReplans || 0) > 0 && state.lastStep?.ok === true && Number(state.lastStep?.syncedCharacters || 0) >= 3 && leader?.ok === true && leader?.followMode === 'point' && escortA?.ok === true && escortA?.followMode === 'agent' && escortB?.ok === true && escortB?.followMode === 'point' && moved(leader?.position, state.starts?.leader) && moved(escortA?.position, state.starts?.escortA) && moved(escortB?.position, state.starts?.escortB) && synced(leader?.position, leaderCharacter) && synced(escortA?.position, escortACharacter) && synced(escortB?.position, escortBCharacter) && closeTo(escortA?.replanDistance, 0.25, 0.001) && closeTo(escortB?.stopDistance, 0.7, 0.01) && Number(state.maxLeaderSpeed || 0) > 0.1 && Number(state.maxEscortBSpeed || 0) > 0.1 && Number.isFinite(leaderPosition?.x) && Number.isFinite(leaderVelocity?.x) && Number.isFinite(escortBPosition?.x) && Number.isFinite(escortBVelocity?.x) && Array.isArray(state.previewPath) && state.previewPath.length >= 1 && typeof state.previewFingerprint === 'string' && state.previewFingerprint === fingerprintPath(previewCheck) && Array.isArray(state.redirectPath) && state.redirectPath.length >= 1 && typeof state.redirectFingerprint === 'string' && state.redirectFingerprint === fingerprintPath(redirectCheck) && Array.isArray(state.escortBPath) && state.escortBPath.length >= 1 && typeof state.escortBFingerprint === 'string' && state.escortBFingerprint === fingerprintPath(escortBCheck) && state.missingPath === null; } catch (_) { return false; } })()",
|
|
1647
|
+
debugExpression: "(() => { try { const state = globalThis.__navmeshCrowdRuntime || {}; return { state, info: aura.navmesh.getCrowdInfo(state.crowdId || 0), leader: aura.navmesh.getAgentState(state.crowdId || 0, state.agents?.leader || 0), escortA: aura.navmesh.getAgentState(state.crowdId || 0, state.agents?.escortA || 0), escortB: aura.navmesh.getAgentState(state.crowdId || 0, state.agents?.escortB || 0), positions: { leader: aura.navmesh.getAgentPosition(state.crowdId || 0, state.agents?.leader || 0), escortB: aura.navmesh.getAgentPosition(state.crowdId || 0, state.agents?.escortB || 0) }, velocities: { leader: aura.navmesh.getAgentVelocity(state.crowdId || 0, state.agents?.leader || 0), escortB: aura.navmesh.getAgentVelocity(state.crowdId || 0, state.agents?.escortB || 0) }, characters: { leader: aura.character3d.getPosition(state.characters?.leader || 0), escortA: aura.character3d.getPosition(state.characters?.escortA || 0), escortB: aura.character3d.getPosition(state.characters?.escortB || 0) } }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1648
|
+
},
|
|
1649
|
+
],
|
|
1650
|
+
source: `
|
|
1651
|
+
globalThis.__navmeshCrowdRuntime = {
|
|
1652
|
+
meshId: 0,
|
|
1653
|
+
crowdId: 0,
|
|
1654
|
+
agents: {},
|
|
1655
|
+
characters: {},
|
|
1656
|
+
starts: {},
|
|
1657
|
+
pathQueries: {},
|
|
1658
|
+
previewPath: null,
|
|
1659
|
+
previewFingerprint: '',
|
|
1660
|
+
missingPath: null,
|
|
1661
|
+
bindings: {},
|
|
1662
|
+
invalidFollow: null,
|
|
1663
|
+
invalidStep: null,
|
|
1664
|
+
redirectPath: null,
|
|
1665
|
+
redirectFingerprint: '',
|
|
1666
|
+
clearEscortB: null,
|
|
1667
|
+
escortBRetarget: null,
|
|
1668
|
+
escortBPath: null,
|
|
1669
|
+
escortBFingerprint: '',
|
|
1670
|
+
lastStep: null,
|
|
1671
|
+
redirect: null,
|
|
1672
|
+
initialLeaderPosition: null,
|
|
1673
|
+
initialLeaderVelocity: null,
|
|
1674
|
+
maxLeaderSpeed: 0,
|
|
1675
|
+
maxEscortBSpeed: 0,
|
|
1676
|
+
stepCount: 0,
|
|
1677
|
+
};
|
|
1678
|
+
`,
|
|
1679
|
+
nativeFrames: 90,
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
id: 'mesh-procedural-generators-runtime',
|
|
1683
|
+
modes: ['native'],
|
|
1684
|
+
namespaces: ['mesh'],
|
|
1685
|
+
functions: [
|
|
1686
|
+
'aura.mesh.createRing',
|
|
1687
|
+
'aura.mesh.createExtrude',
|
|
1688
|
+
'aura.mesh.createLathe',
|
|
1689
|
+
'aura.mesh.createTorus',
|
|
1690
|
+
'aura.mesh.getData',
|
|
1691
|
+
],
|
|
1692
|
+
nativeChecks: [
|
|
1693
|
+
{
|
|
1694
|
+
id: 'mesh.procedural-generators.surface.methods',
|
|
1695
|
+
expression: "(() => ['createRing','createExtrude','createLathe','createTorus','getData'].every((name) => typeof aura.mesh?.[name] === 'function'))()",
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
id: 'mesh.procedural-generators.runtime.setup',
|
|
1699
|
+
expression: "(() => { try { const square = [{ x: -1, y: -1 }, { x: 1, y: -1 }, { x: 1, y: 1 }, { x: -1, y: 1 }]; const profile = [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 1, y: 2 }]; const ring = aura.mesh.createRing(0.5, 1.0, 8); const extrudeLegacy = aura.mesh.createExtrude(square, 1.0, 1); const extrudeOptions = aura.mesh.createExtrude(square, { depth: 1.0, segments: 1 }); const latheLegacy = aura.mesh.createLathe(profile, 8, 0, Math.PI * 2); const latheOptions = aura.mesh.createLathe(profile, { segments: 8, phiStart: 0, phiLength: Math.PI * 2 }); const torus = aura.mesh.createTorus(1.0, 0.3, 24, 16); const ringData = aura.mesh.getData(ring); const extrudeLegacyData = aura.mesh.getData(extrudeLegacy); const extrudeOptionsData = aura.mesh.getData(extrudeOptions); const latheLegacyData = aura.mesh.getData(latheLegacy); const latheOptionsData = aura.mesh.getData(latheOptions); const torusData = aura.mesh.getData(torus); globalThis.__proceduralGeometryRuntime = { ring, extrudeLegacy, extrudeOptions, latheLegacy, latheOptions, torus, initial: { ring: ringData, extrudeLegacy: extrudeLegacyData, extrudeOptions: extrudeOptionsData, latheLegacy: latheLegacyData, latheOptions: latheOptionsData, torus: torusData } }; return Number.isInteger(ring) && ring > 0 && Number.isInteger(extrudeLegacy) && extrudeLegacy > 0 && Number.isInteger(extrudeOptions) && extrudeOptions > 0 && Number.isInteger(latheLegacy) && latheLegacy > 0 && Number.isInteger(latheOptions) && latheOptions > 0 && Number.isInteger(torus) && torus > 0 && Number(ringData?.vertexCount || 0) === 18 && Number(ringData?.indexCount || 0) === 48 && Number(extrudeLegacyData?.vertexCount || 0) === 24 && Number(extrudeLegacyData?.indexCount || 0) === 36 && Number(extrudeOptionsData?.vertexCount || 0) === 24 && Number(extrudeOptionsData?.indexCount || 0) === 36 && Number(latheLegacyData?.vertexCount || 0) === 27 && Number(latheLegacyData?.indexCount || 0) === 96 && Number(latheOptionsData?.vertexCount || 0) === 27 && Number(latheOptionsData?.indexCount || 0) === 96 && Number(torusData?.vertexCount || 0) === 425 && Number(torusData?.indexCount || 0) === 2304 && Number(ringData?.bounds?.max?.x || 0) >= 0.99 && Number(ringData?.bounds?.min?.x || 0) <= -0.99 && Number(extrudeLegacyData?.bounds?.min?.z || 0) <= -0.49 && Number(extrudeLegacyData?.bounds?.max?.z || 0) >= 0.49 && Number(latheLegacyData?.bounds?.max?.y || 0) >= 1.99 && Number(latheLegacyData?.bounds?.min?.y || 0) <= 0.01 && Number(torusData?.bounds?.max?.x || 0) >= 1.29 && Number(torusData?.bounds?.min?.x || 0) <= -1.29 && Number(torusData?.bounds?.max?.y || 0) >= 0.29 && Number(torusData?.bounds?.min?.y || 0) <= -0.29; } catch (_) { return false; } })()",
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
id: 'mesh.procedural-generators.runtime.invalid-authored-input',
|
|
1703
|
+
expression: "(() => { const matchesError = (factory, message) => { try { factory(); return false; } catch (error) { return String(error).includes(message); } }; return matchesError(() => aura.mesh.createRing(1.0, 1.0, 8), 'outerRadius must be a finite number greater than innerRadius') && matchesError(() => aura.mesh.createExtrude([{ x: 0, y: 0 }, { x: Number.NaN, y: 0 }, { x: 0.5, y: 1 }], 1, 1), 'shape2d[1].x must be a finite number') && matchesError(() => aura.mesh.createExtrude([{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 0.5, y: 1 }], { depth: 1.0 }, 1), 'options cannot be combined with positional depth or segments') && matchesError(() => aura.mesh.createLathe([{ x: -0.5, y: 0 }, { x: 1, y: 1 }], 8, 0, Math.PI * 2), 'lathe profile point 0.x must be >= 0') && matchesError(() => aura.mesh.createLathe([{ x: 0.5, y: 0 }, { x: 1, y: 1 }], { segments: 8 }, 0, Math.PI * 2), 'options cannot be combined with positional segments, phiStart, or phiLength'); })()",
|
|
1704
|
+
},
|
|
1705
|
+
],
|
|
1706
|
+
nativePostChecks: [
|
|
1707
|
+
{
|
|
1708
|
+
id: 'mesh.procedural-generators.runtime.evidence',
|
|
1709
|
+
expression: "(() => { try { const state = globalThis.__proceduralGeometryRuntime || {}; const current = { ring: aura.mesh.getData(state.ring || 0), extrudeLegacy: aura.mesh.getData(state.extrudeLegacy || 0), extrudeOptions: aura.mesh.getData(state.extrudeOptions || 0), latheLegacy: aura.mesh.getData(state.latheLegacy || 0), latheOptions: aura.mesh.getData(state.latheOptions || 0), torus: aura.mesh.getData(state.torus || 0) }; return Number(state.initial?.ring?.vertexCount || 0) === 18 && Number(state.initial?.ring?.indexCount || 0) === 48 && Number(state.initial?.extrudeLegacy?.vertexCount || 0) === 24 && Number(state.initial?.extrudeLegacy?.indexCount || 0) === 36 && Number(state.initial?.extrudeOptions?.vertexCount || 0) === 24 && Number(state.initial?.extrudeOptions?.indexCount || 0) === 36 && Number(state.initial?.latheLegacy?.vertexCount || 0) === 27 && Number(state.initial?.latheLegacy?.indexCount || 0) === 96 && Number(state.initial?.latheOptions?.vertexCount || 0) === 27 && Number(state.initial?.latheOptions?.indexCount || 0) === 96 && Number(state.initial?.torus?.vertexCount || 0) === 425 && Number(state.initial?.torus?.indexCount || 0) === 2304 && Number(current.ring?.vertexCount || 0) === Number(state.initial?.ring?.vertexCount || 0) && Number(current.ring?.indexCount || 0) === Number(state.initial?.ring?.indexCount || 0) && Number(current.extrudeLegacy?.vertexCount || 0) === Number(state.initial?.extrudeLegacy?.vertexCount || 0) && Number(current.extrudeLegacy?.indexCount || 0) === Number(state.initial?.extrudeLegacy?.indexCount || 0) && Number(current.extrudeOptions?.vertexCount || 0) === Number(state.initial?.extrudeOptions?.vertexCount || 0) && Number(current.extrudeOptions?.indexCount || 0) === Number(state.initial?.extrudeOptions?.indexCount || 0) && Number(current.latheLegacy?.vertexCount || 0) === Number(state.initial?.latheLegacy?.vertexCount || 0) && Number(current.latheLegacy?.indexCount || 0) === Number(state.initial?.latheLegacy?.indexCount || 0) && Number(current.latheOptions?.vertexCount || 0) === Number(state.initial?.latheOptions?.vertexCount || 0) && Number(current.latheOptions?.indexCount || 0) === Number(state.initial?.latheOptions?.indexCount || 0) && Number(current.torus?.vertexCount || 0) === Number(state.initial?.torus?.vertexCount || 0) && Number(current.torus?.indexCount || 0) === Number(state.initial?.torus?.indexCount || 0); } catch (_) { return false; } })()",
|
|
1710
|
+
},
|
|
1711
|
+
],
|
|
1712
|
+
source: `
|
|
1713
|
+
globalThis.__proceduralGeometryRuntime = {
|
|
1714
|
+
ring: 0,
|
|
1715
|
+
extrudeLegacy: 0,
|
|
1716
|
+
extrudeOptions: 0,
|
|
1717
|
+
latheLegacy: 0,
|
|
1718
|
+
latheOptions: 0,
|
|
1719
|
+
torus: 0,
|
|
1720
|
+
initial: {},
|
|
1721
|
+
};
|
|
1722
|
+
`,
|
|
1723
|
+
nativeFrames: 1,
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
id: 'custom-shader-material-runtime',
|
|
1727
|
+
modes: ['native'],
|
|
1728
|
+
namespaces: ['material', 'mesh', 'draw3d', 'camera3d', 'light'],
|
|
1729
|
+
functions: [
|
|
1730
|
+
'aura.material.create',
|
|
1731
|
+
'aura.material.createCustom',
|
|
1732
|
+
'aura.material.setUniform',
|
|
1733
|
+
'aura.material.setCustomTexture',
|
|
1734
|
+
'aura.mesh.createBox',
|
|
1735
|
+
'aura.draw3d.clear3d',
|
|
1736
|
+
'aura.draw3d.drawMesh',
|
|
1737
|
+
'aura.camera3d.perspective',
|
|
1738
|
+
'aura.camera3d.setPosition',
|
|
1739
|
+
'aura.camera3d.lookAt',
|
|
1740
|
+
'aura.light.ambient',
|
|
1741
|
+
'aura.light.directional',
|
|
1742
|
+
],
|
|
1743
|
+
nativeChecks: [
|
|
1744
|
+
{
|
|
1745
|
+
id: 'material.custom-shader.surface.methods',
|
|
1746
|
+
expression: "(() => ['create','createCustom','setUniform','setCustomTexture'].every((name) => typeof aura.material?.[name] === 'function') && typeof aura.mesh?.createBox === 'function' && typeof aura.draw3d?.drawMesh === 'function')()",
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
id: 'material.custom-shader.runtime.setup',
|
|
1750
|
+
expression: "(() => { try { const state = globalThis.__customShaderRuntime || {}; const mutationReason = globalThis.__customShaderMutationReason; state.floor = aura.mesh.createBox(5, 0.2, 5); state.cube = aura.mesh.createBox(1.25, 1.25, 1.25); state.plain = aura.material.create({ color: { r: 0.2, g: 0.22, b: 0.27, a: 1 }, roughness: 0.92, metallic: 0.04 }); state.textured = aura.material.createCustom({ vertex: state.vertex, fragment: state.texturedFragment, uniforms: { tint: 'vec4', uvScale: 'vec2', pulse: 'float' }, texture: true }); state.solid = aura.material.createCustom({ vertex: state.vertex, fragment: state.solidFragment, uniforms: { tint: 'vec4', pulse: 'float' } }); state.broken = 999999; state.initial.uniformOk = [mutationReason(aura.material.setUniform(state.textured, 'tint', [0.88, 0.94, 1, 1])), mutationReason(aura.material.setUniform(state.textured, 'uvScale', { x: 1.2, y: 0.85 })), mutationReason(aura.material.setUniform(state.textured, 'pulse', 0.72)), mutationReason(aura.material.setUniform(state.solid, 'tint', [0.72, 0.86, 1, 1])), mutationReason(aura.material.setUniform(state.solid, 'pulse', 0.61))]; state.initial.textureOk = mutationReason(aura.material.setCustomTexture(state.textured, state.texturePath)); aura.camera3d.perspective(56, 0.1, 48); aura.camera3d.setPosition(3.1, 2.2, 4.6); aura.camera3d.lookAt(0, 0.35, 0); aura.light.ambient({ r: 1, g: 1, b: 1, a: 1 }, 0.22); aura.light.directional({ x: -0.42, y: -1, z: -0.25 }, { r: 1, g: 0.97, b: 0.94, a: 1 }, 1.08); const baseDraw = aura.draw; aura.draw = function () { const probe = globalThis.__customShaderRuntime || {}; probe.frames.push(probe.frames.length); aura.draw3d.clear3d({ r: 0.03, g: 0.035, b: 0.045, a: 1 }); aura.draw3d.drawMesh(probe.floor, probe.plain, { position: { x: 0, y: -1.05, z: 0 } }); aura.draw3d.drawMesh(probe.cube, probe.textured, { position: { x: -0.82, y: 0, z: 0 }, rotation: { x: 0.14, y: 0.52, z: 0 } }); aura.draw3d.drawMesh(probe.cube, probe.solid, { position: { x: 0.96, y: 0.08, z: 0 }, rotation: { x: 0.08, y: -0.41, z: 0 } }); if (typeof baseDraw === 'function') baseDraw(); }; return Number.isInteger(state.floor) && state.floor > 0 && Number.isInteger(state.cube) && state.cube > 0 && Number.isInteger(state.plain) && state.plain > 0 && Number.isInteger(state.textured) && state.textured > 0 && Number.isInteger(state.solid) && state.solid > 0 && Number.isInteger(state.broken) && state.broken > 0 && Array.isArray(state.initial.uniformOk) && state.initial.uniformOk.every((reason) => reason === 'ok') && state.initial.textureOk === 'ok'; } catch (_) { return false; } })()",
|
|
1751
|
+
},
|
|
1752
|
+
{
|
|
1753
|
+
id: 'material.custom-shader.runtime.invalid-authored-input',
|
|
1754
|
+
expression: "(() => { const state = globalThis.__customShaderRuntime || {}; const mutationReason = globalThis.__customShaderMutationReason; const matchesError = globalThis.__customShaderMatchError; return matchesError(() => aura.material.createCustom(), 'aura.material.createCustom: options is required') && matchesError(() => aura.material.createCustom({ vertex: state.vertex, fragment: state.solidFragment, uniforms: { texture: 'texture' } }), 'aura.material.createCustom: declare texture access with `texture: true`, not `uniforms.texture = \\'texture\\'`') && mutationReason(aura.material.setUniform(state.textured, '', 1)) === 'invalid_uniform_name' && mutationReason(aura.material.setUniform(state.textured, 'missingUniform', 1)) === 'unknown_custom_uniform' && mutationReason(aura.material.setUniform(state.textured, 'pulse', { x: 1 })) === 'invalid_uniform_value' && mutationReason(aura.material.setUniform(state.plain, 'pulse', 1)) === 'not_custom_shader_material' && mutationReason(aura.material.setUniform('bad-handle', 'pulse', 1)) === 'not_custom_shader_material' && mutationReason(aura.material.setCustomTexture(state.solid, state.texturePath)) === 'custom_texture_not_declared' && mutationReason(aura.material.setCustomTexture(state.textured, 42)) === 'invalid_custom_texture_path'; })()",
|
|
1755
|
+
},
|
|
1756
|
+
],
|
|
1757
|
+
nativePostChecks: [
|
|
1758
|
+
{
|
|
1759
|
+
id: 'material.custom-shader.runtime.evidence',
|
|
1760
|
+
expression: "(() => { try { const state = globalThis.__customShaderRuntime || {}; const mutationReason = globalThis.__customShaderMutationReason; const reasons = [mutationReason(aura.material.setUniform(state.textured, 'tint', [0.76, 0.84, 1, 1])), mutationReason(aura.material.setUniform(state.textured, 'uvScale', { x: 0.95, y: 1.1 })), mutationReason(aura.material.setUniform(state.textured, 'pulse', 0.55)), mutationReason(aura.material.setUniform(state.solid, 'pulse', 0.48)), mutationReason(aura.material.setCustomTexture(state.textured, state.altTexturePath))]; const brokenReason = mutationReason(aura.material.setUniform(state.broken, 'pulse', 0.5)); return Array.isArray(state.frames) && state.frames.length === 2 && state.frames[0] === 0 && state.frames[1] === 1 && reasons.every((reason) => reason === 'ok') && brokenReason === 'missing_material_handle' && state.initial?.textureOk === 'ok'; } catch (_) { return false; } })()",
|
|
1761
|
+
},
|
|
1762
|
+
],
|
|
1763
|
+
source: `
|
|
1764
|
+
globalThis.__customShaderMutationReason = (result) => (
|
|
1765
|
+
result && result.ok === false && typeof result.reasonCode === 'string'
|
|
1766
|
+
? result.reasonCode
|
|
1767
|
+
: 'ok'
|
|
1768
|
+
);
|
|
1769
|
+
globalThis.__customShaderMatchError = (factory, message) => {
|
|
1770
|
+
try {
|
|
1771
|
+
factory();
|
|
1772
|
+
return false;
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
return String(error && error.message ? error.message : error).includes(message);
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
globalThis.__customShaderRuntime = {
|
|
1778
|
+
texturePath: 'src/cli/test-fixtures/terrain/terrain-grass.png',
|
|
1779
|
+
altTexturePath: 'src/cli/test-fixtures/terrain/terrain-rock.png',
|
|
1780
|
+
vertex: [
|
|
1781
|
+
'struct CameraUniform { view : mat4x4<f32>, projection : mat4x4<f32> };',
|
|
1782
|
+
'struct ModelUniform { model : mat4x4<f32> };',
|
|
1783
|
+
'struct ProofUniforms { tint : vec4<f32>, uvScale : vec2<f32>, pulse : f32 };',
|
|
1784
|
+
'struct VertexInput { @location(0) position : vec3<f32>, @location(1) normal : vec3<f32>, @location(2) uv : vec2<f32>, @location(3) color : vec4<f32> };',
|
|
1785
|
+
'struct VertexOutput { @builtin(position) clipPosition : vec4<f32>, @location(0) uv : vec2<f32>, @location(1) normal : vec3<f32>, @location(2) color : vec4<f32> };',
|
|
1786
|
+
'@group(0) @binding(0) var<uniform> camera : CameraUniform;',
|
|
1787
|
+
'@group(1) @binding(0) var<uniform> model_uniform : ModelUniform;',
|
|
1788
|
+
'@group(2) @binding(0) var<uniform> proof : ProofUniforms;',
|
|
1789
|
+
'@vertex',
|
|
1790
|
+
'fn vs_main(input : VertexInput) -> VertexOutput {',
|
|
1791
|
+
' var out : VertexOutput;',
|
|
1792
|
+
' let worldPosition = model_uniform.model * vec4<f32>(input.position, 1.0);',
|
|
1793
|
+
' out.clipPosition = camera.projection * camera.view * worldPosition;',
|
|
1794
|
+
' out.uv = input.uv * proof.uvScale;',
|
|
1795
|
+
' out.normal = normalize((model_uniform.model * vec4<f32>(input.normal, 0.0)).xyz);',
|
|
1796
|
+
' out.color = input.color;',
|
|
1797
|
+
' return out;',
|
|
1798
|
+
'}',
|
|
1799
|
+
].join('\\n'),
|
|
1800
|
+
texturedFragment: [
|
|
1801
|
+
'@group(3) @binding(0) var custom_texture : texture_2d<f32>;',
|
|
1802
|
+
'@group(3) @binding(1) var custom_sampler : sampler;',
|
|
1803
|
+
'@fragment',
|
|
1804
|
+
'fn fs_main(input : VertexOutput) -> @location(0) vec4<f32> {',
|
|
1805
|
+
' let normal = normalize(input.normal);',
|
|
1806
|
+
' let lightDir = normalize(vec3<f32>(0.36, 0.88, 0.31));',
|
|
1807
|
+
' let lambert = max(dot(normal, lightDir), 0.22);',
|
|
1808
|
+
' let texel = textureSample(custom_texture, custom_sampler, fract(input.uv));',
|
|
1809
|
+
' let shaded = texel.rgb * proof.tint.rgb * ((lambert * (0.72 + (proof.pulse * 0.28))) + 0.1);',
|
|
1810
|
+
' return vec4<f32>(shaded, texel.a * proof.tint.a);',
|
|
1811
|
+
'}',
|
|
1812
|
+
].join('\\n'),
|
|
1813
|
+
solidFragment: [
|
|
1814
|
+
'@fragment',
|
|
1815
|
+
'fn fs_main(input : VertexOutput) -> @location(0) vec4<f32> {',
|
|
1816
|
+
' let normal = normalize(input.normal);',
|
|
1817
|
+
' let lightDir = normalize(vec3<f32>(0.36, 0.88, 0.31));',
|
|
1818
|
+
' let lambert = max(dot(normal, lightDir), 0.2);',
|
|
1819
|
+
' let shaded = proof.tint.rgb * ((lambert * (0.74 + (proof.pulse * 0.26))) + 0.12);',
|
|
1820
|
+
' return vec4<f32>(shaded, proof.tint.a);',
|
|
1821
|
+
'}',
|
|
1822
|
+
].join('\\n'),
|
|
1823
|
+
badFragment: 'not wgsl',
|
|
1824
|
+
floor: 0,
|
|
1825
|
+
cube: 0,
|
|
1826
|
+
plain: 0,
|
|
1827
|
+
textured: 0,
|
|
1828
|
+
solid: 0,
|
|
1829
|
+
broken: 0,
|
|
1830
|
+
frames: [],
|
|
1831
|
+
initial: {},
|
|
1832
|
+
};
|
|
1833
|
+
`,
|
|
1834
|
+
nativeFrames: 2,
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
id: 'video-texture-runtime-native',
|
|
1838
|
+
modes: ['native'],
|
|
1839
|
+
namespaces: ['video', 'draw3d', 'camera3d', 'debug'],
|
|
1840
|
+
functions: [
|
|
1841
|
+
'aura.video.load',
|
|
1842
|
+
'aura.video.play',
|
|
1843
|
+
'aura.video.pause',
|
|
1844
|
+
'aura.video.stop',
|
|
1845
|
+
'aura.video.seek',
|
|
1846
|
+
'aura.video.getTexture',
|
|
1847
|
+
'aura.video.getInfo',
|
|
1848
|
+
'aura.video.setLooping',
|
|
1849
|
+
'aura.video.unload',
|
|
1850
|
+
'aura.draw3d.clear3d',
|
|
1851
|
+
'aura.draw3d.billboard',
|
|
1852
|
+
'aura.camera3d.perspective',
|
|
1853
|
+
'aura.camera3d.setPosition',
|
|
1854
|
+
'aura.camera3d.lookAt',
|
|
1855
|
+
'aura.debug.inspectorStats',
|
|
1856
|
+
],
|
|
1857
|
+
nativeChecks: [
|
|
1858
|
+
{
|
|
1859
|
+
id: 'video.surface.methods',
|
|
1860
|
+
expression: "(() => ['load','play','pause','stop','seek','getTexture','getInfo','setLooping','unload'].every((name) => typeof aura.video?.[name] === 'function'))()",
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
id: 'video.texture-runtime.setup',
|
|
1864
|
+
expression: "(() => { try { const candidatePaths = ['../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../../../../../src/cli/test-fixtures/video/video-native.mp4', 'src/cli/test-fixtures/video/video-native.mp4']; let handle = 0; let resolvedPath = null; for (const candidate of candidatePaths) { try { const loaded = aura.video.load(candidate, { type: 'mp4', looping: false }); if (Number.isInteger(loaded) && loaded > 0) { handle = loaded; resolvedPath = candidate; break; } } catch (_) {} } if (!Number.isInteger(handle) || handle <= 0) return false; const idleInfo = aura.video.getInfo(handle); aura.video.play(handle); const playingInfo = aura.video.getInfo(handle); aura.video.pause(handle); const pausedInfo = aura.video.getInfo(handle); aura.video.seek(handle, 0.2); aura.video.setLooping(handle, false); aura.video.play(handle); const textureHandle = aura.video.getTexture(handle); aura.camera3d.perspective(55, 0.1, 50); aura.camera3d.setPosition(0, 0, 4); aura.camera3d.lookAt(0, 0, 0); const baseDraw = aura.draw; aura.draw = function () { aura.draw3d.clear3d({ r: 0.01, g: 0.01, b: 0.015, a: 1 }); aura.draw3d.billboard(textureHandle, { x: 0, y: 0, z: 0, width: 2, height: 2, mode: 'face' }); baseDraw(); }; globalThis.__videoRuntime = { handle, textureHandle, resolvedPath }; return idleInfo?.state === 'idle' && idleInfo?.sourceKind === 'mp4' && playingInfo?.state === 'playing' && pausedInfo?.state === 'paused' && Number.isInteger(textureHandle) && textureHandle > 0 && Number(idleInfo?.frameCount || 0) >= 2 && Number(idleInfo?.width || 0) >= 1 && Number(idleInfo?.height || 0) >= 1; } catch (_) { return false; } })()",
|
|
1865
|
+
},
|
|
1866
|
+
],
|
|
1867
|
+
nativePostChecks: [
|
|
1868
|
+
{
|
|
1869
|
+
id: 'video.texture-runtime.evidence',
|
|
1870
|
+
expression: "(() => { try { const state = globalThis.__videoRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; const video = scene3d.video || {}; const info = aura.video.getInfo(state.handle); return Number.isInteger(state.handle) && state.handle > 0 && Number.isInteger(state.textureHandle) && state.textureHandle > 0 && typeof state.resolvedPath === 'string' && state.resolvedPath.length > 0 && !!info && info.sourceKind === 'mp4' && Number(info.frameCount || 0) >= 2 && Number(info.width || 0) >= 1 && Number(info.height || 0) >= 1 && Number(info.currentFrame || 0) >= 1 && Number(info.currentTime || 0) > 0 && ['playing', 'paused', 'stopped'].includes(info.state) && Number(video.activePlayerCount || 0) >= 1 && Number(video.totalTickUploadCount || 0) >= 1 && Number(video.totalProcessedUploadCount || 0) >= 1 && Number(video.lastUploadedVideoHandle || 0) === Number(state.handle) && Number(video.lastUploadedTextureHandle || 0) === Number(state.textureHandle) && Number(video.lastUploadedWidth || 0) >= 1 && Number(video.lastUploadedHeight || 0) >= 1 && Number(video.textureHandleCount || 0) >= 1 && Number(resources.videoTextureHandleCount || 0) >= 1 && Number(submission.billboardLastCommandCount || 0) >= 1 && Number(submission.billboardLastRenderedCount || 0) >= 1 && Number(submission.billboardLastBatchCount || 0) >= 1 && submission.billboardLastReasonCode === 'billboard_submitted'; } catch (_) { return false; } })()",
|
|
1871
|
+
debugExpression: "(() => { try { aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const state = globalThis.__videoRuntime || {}; return { state, info: aura.video.getInfo(state.handle) || null, scene3dRuntime: stats.scene3dRuntime || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
1872
|
+
},
|
|
1873
|
+
],
|
|
1874
|
+
source: `
|
|
1875
|
+
globalThis.__videoRuntime = { handle: 0, textureHandle: 0, resolvedPath: null };
|
|
1876
|
+
`,
|
|
1877
|
+
nativeFrames: 6,
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
id: 'billboard-actor-effect-runtime-native',
|
|
1881
|
+
modes: ['native'],
|
|
1882
|
+
namespaces: ['video', 'mesh', 'material', 'draw3d', 'camera3d', 'debug'],
|
|
1883
|
+
functions: [
|
|
1884
|
+
'aura.video.load',
|
|
1885
|
+
'aura.video.play',
|
|
1886
|
+
'aura.video.getTexture',
|
|
1887
|
+
'aura.video.getInfo',
|
|
1888
|
+
'aura.mesh.createBox',
|
|
1889
|
+
'aura.material.create',
|
|
1890
|
+
'aura.draw3d.drawMesh',
|
|
1891
|
+
'aura.draw3d.clear3d',
|
|
1892
|
+
'aura.draw3d.billboard',
|
|
1893
|
+
'aura.camera3d.perspective',
|
|
1894
|
+
'aura.camera3d.setPosition',
|
|
1895
|
+
'aura.camera3d.lookAt',
|
|
1896
|
+
'aura.debug.inspectorStats',
|
|
1897
|
+
],
|
|
1898
|
+
nativeChecks: [
|
|
1899
|
+
{
|
|
1900
|
+
id: 'billboard.actor-effect.surface.methods',
|
|
1901
|
+
expression: "(() => ['load','play','getTexture','getInfo'].every((name) => typeof aura.video?.[name] === 'function') && typeof aura.mesh?.createBox === 'function' && typeof aura.material?.create === 'function' && ['drawMesh','clear3d','billboard'].every((name) => typeof aura.draw3d?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
id: 'billboard.actor-effect.runtime.setup',
|
|
1905
|
+
expression: `(() => { try { const resolvedPath = 'src/cli/test-fixtures/video/video-spritesheet.png'; const billboardVideoHandle = aura.video.load(resolvedPath, { type: 'spritesheet', columns: 2, rows: 1, frameCount: 2, fps: 12, looping: true }); aura.video.play(billboardVideoHandle); const billboardTexture = aura.video.getTexture(billboardVideoHandle); const billboardInfo = aura.video.getInfo(billboardVideoHandle); const atlasWidth = Number(billboardInfo?.width || 0); const atlasHeight = Number(billboardInfo?.height || 0); const frameWidth = atlasWidth / 2; const frameHeight = atlasHeight; if (!Number.isInteger(billboardTexture) || billboardTexture <= 0 || !Number.isFinite(atlasWidth) || atlasWidth <= 0 || !Number.isFinite(atlasHeight) || atlasHeight <= 0 || !Number.isFinite(frameWidth) || frameWidth <= 0 || !Number.isFinite(frameHeight) || frameHeight <= 0) return false; const floorMesh = aura.mesh.createBox(8, 0.25, 8); const coverMesh = aura.mesh.createBox(0.8, 2.2, 0.8); const floorMaterial = aura.material.create({ color: { r: 0.2, g: 0.24, b: 0.3, a: 1 }, roughness: 0.95, metallic: 0.02 }); const coverMaterial = aura.material.create({ color: { r: 0.42, g: 0.36, b: 0.32, a: 1 }, roughness: 0.9, metallic: 0.04 }); aura.camera3d.perspective(58, 0.1, 80); aura.camera3d.setPosition(0, 2.2, 5.4); aura.camera3d.lookAt(0, 0.8, 0); const baseDraw = aura.draw; globalThis.__billboardActorEffect = { resolvedPath, billboardTexture, billboardVideoHandle, atlasWidth, atlasHeight, frameWidth, frameHeight, floorMesh, coverMesh, floorMaterial, coverMaterial, frames: [] }; aura.draw = function () { const probe = globalThis.__billboardActorEffect || {}; const frameIndex = Array.isArray(probe.frames) ? probe.frames.length : 0; const cameraX = Number((Math.sin(frameIndex * 0.45) * 1.35).toFixed(3)); const cameraZ = Number((5.4 - (frameIndex * 0.16)).toFixed(3)); aura.camera3d.setPosition(cameraX, 2.2, cameraZ); aura.camera3d.lookAt(0, 0.8, 0); aura.draw3d.clear3d({ r: 0.025, g: 0.03, b: 0.04, a: 1 }); aura.draw3d.drawMesh(probe.floorMesh, probe.floorMaterial, { position: { x: 0, y: -1.1, z: 0 } }); aura.draw3d.drawMesh(probe.coverMesh, probe.coverMaterial, { position: { x: 0.45, y: -0.05, z: 0.75 } }); aura.draw3d.billboard(probe.billboardTexture, { x: -1.2, y: 0.1, z: 0.2, width: 1.1, height: 1.7, mode: 'face', frameX: 0, frameY: 0, frameW: probe.frameWidth, frameH: probe.frameHeight, atlasWidth: probe.atlasWidth, atlasHeight: probe.atlasHeight }); aura.draw3d.billboard(probe.billboardTexture, { x: 0, y: 0.1, z: -0.35, width: 1.1, height: 1.7, mode: 'face', frameX: 0, frameY: 0, frameW: probe.frameWidth, frameH: probe.frameHeight, atlasWidth: probe.atlasWidth, atlasHeight: probe.atlasHeight }); aura.draw3d.billboard(probe.billboardTexture, { x: 1.15, y: 0.1, z: -0.1, width: 1.1, height: 1.7, mode: 'face', frameX: 0, frameY: 0, frameW: probe.frameWidth, frameH: probe.frameHeight, atlasWidth: probe.atlasWidth, atlasHeight: probe.atlasHeight }); aura.draw3d.billboard(probe.billboardTexture, { x: 0.95, y: -0.05, z: 1.2, width: 1.3, height: 1.3, mode: 'axis', color: { r: 1, g: 0.9, b: 0.75, a: 0.85 }, frameX: probe.frameWidth, frameY: 0, frameW: probe.frameWidth, frameH: probe.frameHeight, atlasWidth: probe.atlasWidth, atlasHeight: probe.atlasHeight }); probe.frames.push({ frameIndex, cameraX, cameraZ, commandCount: 4, modes: 'face|axis', atlasFrameTrace: 'actor0|actor0|actor0|effect1' }); baseDraw(); }; return Number.isInteger(floorMesh) && floorMesh > 0 && Number.isInteger(coverMesh) && coverMesh > 0 && Number.isInteger(floorMaterial) && floorMaterial > 0 && Number.isInteger(coverMaterial) && coverMaterial > 0 && Number(billboardInfo?.frameCount || 0) === 2; } catch (_) { return false; } })()`,
|
|
1906
|
+
},
|
|
1907
|
+
],
|
|
1908
|
+
nativePostChecks: [
|
|
1909
|
+
{
|
|
1910
|
+
id: 'billboard.actor-effect.runtime.evidence',
|
|
1911
|
+
expression: `(() => { try { const probe = globalThis.__billboardActorEffect || {}; const frames = Array.isArray(probe.frames) ? probe.frames : []; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; const billboardInfo = aura.video.getInfo(probe.billboardVideoHandle); const cameraTrace = new Set(frames.map((frame) => \`\${frame.cameraX}:\${frame.cameraZ}\`)); return typeof probe.resolvedPath === 'string' && probe.resolvedPath.length > 0 && Number.isInteger(probe.billboardTexture) && probe.billboardTexture > 0 && Number.isInteger(probe.billboardVideoHandle) && probe.billboardVideoHandle > 0 && Number(probe.frameWidth || 0) > 0 && Number(probe.frameHeight || 0) > 0 && Number(probe.atlasWidth || 0) === Number(probe.frameWidth || 0) * 2 && Number(probe.atlasHeight || 0) === Number(probe.frameHeight || 0) && frames.length === 6 && frames.every((frame) => frame.commandCount === 4 && frame.modes === 'face|axis' && frame.atlasFrameTrace === 'actor0|actor0|actor0|effect1') && cameraTrace.size >= 3 && Number(billboardInfo?.frameCount || 0) === 2 && Number(billboardInfo?.currentTime || 0) > 0 && Number(resources.videoTextureHandleCount || 0) >= 1 && Number(submission.billboardCommandsPending || 0) >= 4 && Number(submission.billboardUniqueTextureHandles || 0) === 1 && Number(submission.billboardLastCommandCount || 0) === 4 && Number(submission.billboardLastRenderedCount || 0) === 4 && Number(submission.billboardLastBatchCount || 0) === 1 && String(submission.billboardLastReasonCode || '') === 'billboard_submitted'; } catch (_) { return false; } })()`,
|
|
1912
|
+
debugExpression: `(() => { try { const probe = globalThis.__billboardActorEffect || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { probe, billboardInfo: aura.video.getInfo(probe.billboardVideoHandle) || null, scene3dRuntime: stats.scene3dRuntime || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()`,
|
|
1913
|
+
},
|
|
1914
|
+
],
|
|
1915
|
+
source: `
|
|
1916
|
+
globalThis.__billboardActorEffect = { frames: [] };
|
|
1917
|
+
`,
|
|
1918
|
+
nativeFrames: 6,
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
id: 'worldspace-ui-runtime-native',
|
|
1922
|
+
modes: ['native'],
|
|
1923
|
+
namespaces: ['video', 'mesh', 'material', 'draw2d', 'draw3d', 'camera3d', 'scene3d', 'debug'],
|
|
1924
|
+
functions: [
|
|
1925
|
+
'aura.video.load',
|
|
1926
|
+
'aura.video.play',
|
|
1927
|
+
'aura.video.getTexture',
|
|
1928
|
+
'aura.video.getInfo',
|
|
1929
|
+
'aura.mesh.createBox',
|
|
1930
|
+
'aura.material.create',
|
|
1931
|
+
'aura.draw2d.text',
|
|
1932
|
+
'aura.draw3d.clear3d',
|
|
1933
|
+
'aura.draw3d.drawMesh',
|
|
1934
|
+
'aura.draw3d.billboard',
|
|
1935
|
+
'aura.camera3d.perspective',
|
|
1936
|
+
'aura.camera3d.setPosition',
|
|
1937
|
+
'aura.camera3d.lookAt',
|
|
1938
|
+
'aura.scene3d.projectToViewport',
|
|
1939
|
+
'aura.scene3d.createWorldUiLayer',
|
|
1940
|
+
'aura.scene3d.getWorldUiState',
|
|
1941
|
+
'aura.debug.inspectorStats',
|
|
1942
|
+
],
|
|
1943
|
+
nativeChecks: [
|
|
1944
|
+
{
|
|
1945
|
+
id: 'worldspace-ui.surface.methods',
|
|
1946
|
+
expression: "(() => ['load','play','getTexture','getInfo'].every((name) => typeof aura.video?.[name] === 'function') && typeof aura.mesh?.createBox === 'function' && typeof aura.material?.create === 'function' && typeof aura.draw2d?.text === 'function' && ['clear3d','drawMesh','billboard'].every((name) => typeof aura.draw3d?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && ['projectToViewport','createWorldUiLayer','getWorldUiState'].every((name) => typeof aura.scene3d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
id: 'worldspace-ui.runtime.setup',
|
|
1950
|
+
expression: `(() => { try { const resolvedPath = 'src/cli/test-fixtures/video/video-spritesheet.png'; const iconVideoHandle = aura.video.load(resolvedPath, { type: 'spritesheet', columns: 2, rows: 1, frameCount: 2, fps: 12, looping: true }); aura.video.play(iconVideoHandle); const iconTexture = aura.video.getTexture(iconVideoHandle); const floorMesh = aura.mesh.createBox(10, 0.2, 10); const actorMesh = aura.mesh.createBox(0.8, 1.8, 0.8); const floorMaterial = aura.material.create({ color: { r: 0.14, g: 0.16, b: 0.2, a: 1 }, roughness: 0.96, metallic: 0.02 }); const leaderMaterial = aura.material.create({ color: { r: 0.92, g: 0.46, b: 0.34, a: 1 }, roughness: 0.86, metallic: 0.03 }); const pickupMaterial = aura.material.create({ color: { r: 0.28, g: 0.84, b: 0.9, a: 1 }, roughness: 0.84, metallic: 0.05 }); const beaconMaterial = aura.material.create({ color: { r: 0.54, g: 0.92, b: 0.42, a: 1 }, roughness: 0.88, metallic: 0.04 }); aura.camera3d.perspective(56, 0.1, 80); aura.camera3d.setPosition(0, 3.2, 8.4); aura.camera3d.lookAt(0, 1.1, 0); const ui = aura.scene3d.createWorldUiLayer({ labelFontSize: 13, damageFontSize: 18, padding: 14 }); const state = globalThis.__worldspaceUiRuntime || {}; state.resolvedPath = resolvedPath; state.iconVideoHandle = iconVideoHandle; state.iconTexture = iconTexture; state.floorMesh = floorMesh; state.actorMesh = actorMesh; state.materials = { floorMaterial, leaderMaterial, pickupMaterial, beaconMaterial }; state.targets = { leader: { x: -1.3, y: 0, z: 0.15 }, pickup: { x: 0, y: 0, z: -0.5 }, beacon: { x: 1.5, y: 0, z: 0.55 } }; state.ui = ui; state.frames = []; state.projections = []; state.emitted = 0; state.initialProjection = aura.scene3d.projectToViewport({ x: state.targets.leader.x, y: 1.9, z: state.targets.leader.z }, { clamp: true, padding: 14 }); const callout = ui.upsertCallout('leader', () => state.targets.leader, { text: 'Guide', textureHandle: iconTexture, offset: { x: 0, y: 1.9, z: 0 }, width: 0.74, height: 0.74, screenOffset: { x: 0, y: -10 }, scaleByDistance: true, clampToViewport: true, markerColor: { r: 1, g: 0.95, b: 0.86, a: 0.95 }, labelColor: { r: 1, g: 0.96, b: 0.9, a: 1 } }); const label = ui.upsertLabel('pickup', 'Objective', () => state.targets.pickup, { offset: { x: 0, y: 1.35, z: 0 }, screenOffset: { x: 0, y: -12 }, clampToViewport: true, color: { r: 0.86, g: 0.94, b: 1, a: 1 } }); const marker = ui.upsertMarker('beacon', iconTexture, () => state.targets.beacon, { offset: { x: 0, y: 1.8, z: 0 }, width: 0.68, height: 0.68, mode: 'axis', scaleByDistance: true, markerColor: { r: 0.86, g: 1, b: 0.78, a: 0.92 } }); const baseUpdate = aura.update; const baseDraw = aura.draw; aura.update = function (dt) { const probe = globalThis.__worldspaceUiRuntime || {}; probe.frameCount = Number(probe.frameCount || 0) + 1; const elapsed = probe.frameCount / 60; const cameraX = Number((Math.sin(elapsed * 0.8) * 2.4).toFixed(3)); const cameraZ = Number((8.4 - (Math.cos(elapsed * 0.6) * 1.1)).toFixed(3)); aura.camera3d.setPosition(cameraX, 3.2, cameraZ); aura.camera3d.lookAt(0, 1.1, 0); probe.targets.pickup.x = Number((Math.sin(elapsed * 1.4) * 0.45).toFixed(3)); probe.targets.beacon.z = Number((0.55 + (Math.cos(elapsed * 1.2) * 0.38)).toFixed(3)); if (probe.frameCount === 4 || probe.frameCount === 12) { const emitted = probe.ui.emitDamageNumber('-24', () => probe.targets.leader, { offset: { x: 0, y: 1.55, z: 0 }, lifetime: 1.1, floatHeight: 0.82, color: { r: 1, g: 0.82, b: 0.38, a: 1 } }); if (emitted?.ok === true) probe.emitted = Number(probe.emitted || 0) + 1; } probe.ui.update(dt || (1 / 60)); const projection = aura.scene3d.projectToViewport({ x: probe.targets.pickup.x, y: 1.35, z: probe.targets.pickup.z }, { clamp: true, padding: 14 }); probe.projections.push({ frame: probe.frameCount, visible: projection?.visible === true, screenX: Number(projection?.clampedScreenX || 0), screenY: Number(projection?.clampedScreenY || 0) }); probe.frames.push({ frame: probe.frameCount, cameraX, cameraZ, pickupX: probe.targets.pickup.x, beaconZ: probe.targets.beacon.z }); if (typeof baseUpdate === 'function') baseUpdate(dt); }; aura.draw = function () { const probe = globalThis.__worldspaceUiRuntime || {}; aura.draw3d.clear3d({ r: 0.02, g: 0.022, b: 0.03, a: 1 }); aura.draw3d.drawMesh(probe.floorMesh, probe.materials.floorMaterial, { position: { x: 0, y: -0.1, z: 0 } }); aura.draw3d.drawMesh(probe.actorMesh, probe.materials.leaderMaterial, { position: { x: probe.targets.leader.x, y: 0.9, z: probe.targets.leader.z } }); aura.draw3d.drawMesh(probe.actorMesh, probe.materials.pickupMaterial, { position: { x: probe.targets.pickup.x, y: 0.9, z: probe.targets.pickup.z }, scale: { x: 0.7, y: 0.7, z: 0.7 } }); aura.draw3d.drawMesh(probe.actorMesh, probe.materials.beaconMaterial, { position: { x: probe.targets.beacon.x, y: 0.9, z: probe.targets.beacon.z }, scale: { x: 0.8, y: 1.1, z: 0.8 } }); probe.ui.draw(); if (typeof baseDraw === 'function') baseDraw(); }; return Number.isInteger(iconVideoHandle) && iconVideoHandle > 0 && Number.isInteger(iconTexture) && iconTexture > 0 && Number.isInteger(floorMesh) && floorMesh > 0 && Number.isInteger(actorMesh) && actorMesh > 0 && Number.isInteger(floorMaterial) && floorMaterial > 0 && Number.isInteger(leaderMaterial) && leaderMaterial > 0 && Number.isInteger(pickupMaterial) && pickupMaterial > 0 && Number.isInteger(beaconMaterial) && beaconMaterial > 0 && state.initialProjection?.ok === true && callout?.ok === true && label?.ok === true && marker?.ok === true; } catch (_) { return false; } })()`,
|
|
1951
|
+
},
|
|
1952
|
+
],
|
|
1953
|
+
nativePostChecks: [
|
|
1954
|
+
{
|
|
1955
|
+
id: 'worldspace-ui.runtime.evidence',
|
|
1956
|
+
expression: `(() => { try { const state = globalThis.__worldspaceUiRuntime || {}; const layerState = state.ui?.getState ? state.ui.getState() : null; const summary = aura.scene3d.getWorldUiState(); aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const video = scene3d.video || {}; const info = aura.video.getInfo(state.iconVideoHandle); const frames = Array.isArray(state.frames) ? state.frames : []; const projections = Array.isArray(state.projections) ? state.projections : []; return state.initialProjection?.ok === true && Number.isInteger(state.iconTexture) && state.iconTexture > 0 && typeof state.resolvedPath === 'string' && state.resolvedPath.length > 0 && Number(state.emitted || 0) === 2 && layerState?.ok === true && layerState?.pinnedEntryCount === 3 && Number(layerState?.activeDamageNumberCount || 0) >= 1 && Number(layerState?.lastMarkerDrawCount || 0) >= 2 && Number(layerState?.lastLabelDrawCount || 0) >= 2 && Number(layerState?.lastDamageDrawCount || 0) >= 1 && layerState?.lastReasonCode === 'world_ui_layer_drawn' && Number(layerState?.lastProjectionFingerprint || 0) > 0 && summary?.ok === true && summary?.layerCount === 1 && summary?.pinnedEntryCount === 3 && Number(summary?.activeDamageNumberCount || 0) >= 1 && Number(summary?.lastProjectionFingerprint || 0) > 0 && frames.length === 24 && projections.length === 24 && projections.every((entry) => Number.isFinite(entry.screenX) && Number.isFinite(entry.screenY)) && projections.some((entry) => entry.visible === true) && Number(info?.currentTime || 0) > 0 && Number(video?.activePlayerCount || 0) >= 1 && Number(submission?.billboardLastCommandCount || 0) >= 2 && Number(submission?.billboardLastRenderedCount || 0) >= 2 && Number(submission?.billboardLastBatchCount || 0) >= 1 && submission?.billboardLastReasonCode === 'billboard_submitted'; } catch (_) { return false; } })()`,
|
|
1957
|
+
debugExpression: `(() => { try { const state = globalThis.__worldspaceUiRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state, layerState: state.ui?.getState ? state.ui.getState() : null, summary: aura.scene3d.getWorldUiState(), videoInfo: aura.video.getInfo(state.iconVideoHandle) || null, scene3dRuntime: stats.scene3dRuntime || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()`,
|
|
1958
|
+
},
|
|
1959
|
+
],
|
|
1960
|
+
source: `
|
|
1961
|
+
globalThis.__worldspaceUiRuntime = {
|
|
1962
|
+
frameCount: 0,
|
|
1963
|
+
emitted: 0,
|
|
1964
|
+
frames: [],
|
|
1965
|
+
projections: [],
|
|
1966
|
+
initialProjection: null,
|
|
1967
|
+
};
|
|
1968
|
+
`,
|
|
1969
|
+
nativeFrames: 24,
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
id: 'media-presentation-runtime-native',
|
|
1973
|
+
modes: ['native'],
|
|
1974
|
+
namespaces: ['audio', 'video', 'mesh', 'material', 'draw3d', 'camera3d', 'debug'],
|
|
1975
|
+
functions: [
|
|
1976
|
+
'aura.audio.createSpatialEmitter',
|
|
1977
|
+
'aura.video.createBillboardSurface',
|
|
1978
|
+
'aura.video.createPresentation',
|
|
1979
|
+
'aura.video.getInfo',
|
|
1980
|
+
'aura.mesh.createBox',
|
|
1981
|
+
'aura.material.create',
|
|
1982
|
+
'aura.draw3d.clear3d',
|
|
1983
|
+
'aura.draw3d.drawMesh',
|
|
1984
|
+
'aura.draw3d.billboard',
|
|
1985
|
+
'aura.camera3d.perspective',
|
|
1986
|
+
'aura.camera3d.setPosition',
|
|
1987
|
+
'aura.camera3d.lookAt',
|
|
1988
|
+
'aura.debug.inspectorStats',
|
|
1989
|
+
],
|
|
1990
|
+
nativeChecks: [
|
|
1991
|
+
{
|
|
1992
|
+
id: 'media.presentation.surface.methods',
|
|
1993
|
+
expression: "(() => typeof aura.audio?.createSpatialEmitter === 'function' && ['createBillboardSurface','createPresentation','getInfo'].every((name) => typeof aura.video?.[name] === 'function') && typeof aura.mesh?.createBox === 'function' && typeof aura.material?.create === 'function' && ['clear3d','drawMesh','billboard'].every((name) => typeof aura.draw3d?.[name] === 'function') && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
id: 'media.presentation.runtime.setup',
|
|
1997
|
+
expression: `(() => { try { const videoCandidates = ['../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../../src/cli/test-fixtures/video/video-native.mp4', '../../../../../../../src/cli/test-fixtures/video/video-native.mp4', 'src/cli/test-fixtures/video/video-native.mp4']; const audioCandidates = ['audio/presentation-beacon.wav', 'assets/audio/presentation-beacon.wav']; const resolveVideoPath = () => { for (const candidate of videoCandidates) { try { const surface = aura.video.createBillboardSurface(candidate, { target: { x: 0, y: 0, z: 0 }, width: 1, height: 1, loadOptions: { type: 'mp4', looping: true } }); const played = surface.play(); const state = surface.getState(); surface.unload(); if (played?.ok === true && Number.isInteger(state?.handle) && state.handle > 0 && Number.isInteger(state?.textureHandle) && state.textureHandle > 0 && state?.info?.sourceKind === 'mp4') return candidate; } catch (_) {} } return null; }; const resolveAudioPath = () => { for (const candidate of audioCandidates) { try { const emitter = aura.audio.createSpatialEmitter(candidate, { position: { x: 0, y: 0, z: 0 }, syncListener: false, refDistance: 1.5, maxDistance: 8, rolloff: 1.1 }); const played = emitter.play({ volume: 0.02 }); if (played?.ok === true && Number.isInteger(played.handle) && played.handle > 0) { emitter.stop(); return candidate; } } catch (_) {} } return null; }; const videoPath = resolveVideoPath(); const audioPath = resolveAudioPath(); const state = globalThis.__mediaPresentationRuntime || {}; state.videoPath = videoPath; state.audioPath = audioPath; if (!videoPath || !audioPath) return false; const floorMesh = aura.mesh.createBox(10, 0.2, 10); const screenMesh = aura.mesh.createBox(2.95, 1.8, 0.16); const standMesh = aura.mesh.createBox(0.32, 2.2, 0.32); const beaconMesh = aura.mesh.createBox(0.7, 1.6, 0.7); const floorMaterial = aura.material.create({ color: { r: 0.15, g: 0.17, b: 0.2, a: 1 }, roughness: 0.96, metallic: 0.02 }); const screenMaterial = aura.material.create({ color: { r: 0.12, g: 0.13, b: 0.16, a: 1 }, roughness: 0.82, metallic: 0.12 }); const standMaterial = aura.material.create({ color: { r: 0.42, g: 0.36, b: 0.31, a: 1 }, roughness: 0.88, metallic: 0.04 }); const beaconMaterial = aura.material.create({ color: { r: 0.32, g: 0.86, b: 0.98, a: 1 }, roughness: 0.84, metallic: 0.06 }); aura.camera3d.perspective(56, 0.1, 80); aura.camera3d.setPosition(0, 3.1, 8.1); aura.camera3d.lookAt(0, 1.2, -0.4); state.floorMesh = floorMesh; state.screenMesh = screenMesh; state.standMesh = standMesh; state.beaconMesh = beaconMesh; state.materials = { floorMaterial, screenMaterial, standMaterial, beaconMaterial }; state.targets = { screen: { x: 0, y: 1.4, z: -1.45 }, beacon: { x: 1.55, y: 0.8, z: 0.55 } }; state.frames = []; state.elapsed = 0; state.presentation = aura.video.createPresentation(videoPath, { target: () => state.targets.screen, audioPath, width: 2.62, height: 1.46, mode: 'face', offset: { x: 0, y: 0, z: 0.11 }, loadOptions: { type: 'mp4', looping: true }, audio: { offset: { x: 0, y: 0, z: 0.14 }, refDistance: 2.4, maxDistance: 16, rolloff: 1.12, volume: 0.14, loop: true, seekStrategy: 'restart' } }); state.beaconSurface = aura.video.createBillboardSurface(videoPath, { target: () => state.targets.beacon, width: 0.82, height: 0.82, mode: 'axis', offset: { x: 0, y: 1.25, z: 0 }, loadOptions: { type: 'mp4', looping: true } }); state.beaconEmitter = aura.audio.createSpatialEmitter(audioPath, { target: () => state.targets.beacon, offset: { x: 0, y: 0.85, z: 0 }, volume: 0.09, loop: true, refDistance: 1.8, maxDistance: 12, rolloff: 1.3, occlusion: 0.18 }); state.attachPresentation = state.presentation.attach(() => state.targets.screen, { surface: { offset: { x: 0, y: 0, z: 0.11 }, width: 2.62, height: 1.46 }, audio: { offset: { x: 0, y: 0, z: 0.14 } } }); state.attachSurface = state.beaconSurface.attach(() => state.targets.beacon, { offset: { x: 0, y: 1.25, z: 0 }, width: 0.82, height: 0.82, mode: 'axis' }); state.attachEmitter = state.beaconEmitter.attach(() => state.targets.beacon, { offset: { x: 0, y: 0.85, z: 0 } }); state.presentationPlay = state.presentation.play(); state.surfacePlay = state.beaconSurface.play(); state.emitterPlay = state.beaconEmitter.play({ volume: 0.09, loop: true }); state.presentationSeek = state.presentation.seek(0.04); state.surfaceSeek = state.beaconSurface.seek(0.02); state.occlusionResult = state.beaconEmitter.setOcclusion(0.22); const baseUpdate = aura.update; const baseDraw = aura.draw; aura.update = function (dt) { const probe = globalThis.__mediaPresentationRuntime || {}; const delta = Number(dt || (1 / 60)); probe.elapsed = Number(probe.elapsed || 0) + delta; const frame = Array.isArray(probe.frames) ? (probe.frames.length + 1) : 1; const cameraX = Number((Math.sin(probe.elapsed * 0.75) * 2.1).toFixed(3)); const cameraZ = Number((8.1 - (Math.cos(probe.elapsed * 0.6) * 0.95)).toFixed(3)); aura.camera3d.setPosition(cameraX, 3.1, cameraZ); aura.camera3d.lookAt(0, 1.2, -0.35); probe.targets.beacon.x = Number((1.55 + (Math.sin(probe.elapsed * 1.25) * 0.36)).toFixed(3)); probe.targets.beacon.z = Number((0.55 + (Math.cos(probe.elapsed * 1.1) * 0.42)).toFixed(3)); probe.targets.screen.x = Number((Math.sin(probe.elapsed * 0.35) * 0.18).toFixed(3)); probe.presentation.update(); probe.beaconSurface.update(); probe.beaconEmitter.update(); probe.frames.push({ frame, cameraX, cameraZ, beaconX: probe.targets.beacon.x, beaconZ: probe.targets.beacon.z, screenX: probe.targets.screen.x }); if (typeof baseUpdate === 'function') baseUpdate(dt); }; aura.draw = function () { const probe = globalThis.__mediaPresentationRuntime || {}; aura.draw3d.clear3d({ r: 0.018, g: 0.02, b: 0.028, a: 1 }); aura.draw3d.drawMesh(probe.floorMesh, probe.materials.floorMaterial, { position: { x: 0, y: -0.1, z: 0 } }); aura.draw3d.drawMesh(probe.standMesh, probe.materials.standMaterial, { position: { x: probe.targets.screen.x, y: 0.35, z: probe.targets.screen.z } }); aura.draw3d.drawMesh(probe.screenMesh, probe.materials.screenMaterial, { position: { x: probe.targets.screen.x, y: probe.targets.screen.y, z: probe.targets.screen.z } }); aura.draw3d.drawMesh(probe.beaconMesh, probe.materials.beaconMaterial, { position: { x: probe.targets.beacon.x, y: probe.targets.beacon.y, z: probe.targets.beacon.z } }); probe.presentation.draw(); probe.beaconSurface.draw({ color: { r: 0.92, g: 0.98, b: 1, a: 0.9 } }); if (typeof baseDraw === 'function') baseDraw(); }; return state.attachPresentation?.ok === true && state.attachSurface?.ok === true && state.attachEmitter?.ok === true && state.presentationPlay?.ok === true && state.presentationPlay?.audioResult?.ok === true && state.surfacePlay?.ok === true && state.emitterPlay?.ok === true && state.presentationSeek?.ok === true && state.surfaceSeek?.ok === true && state.occlusionResult?.ok === true; } catch (_) { return false; } })()`,
|
|
1998
|
+
},
|
|
1999
|
+
],
|
|
2000
|
+
nativePostChecks: [
|
|
2001
|
+
{
|
|
2002
|
+
id: 'media.presentation.runtime.evidence',
|
|
2003
|
+
expression: `(() => { try { const state = globalThis.__mediaPresentationRuntime || {}; const frames = Array.isArray(state.frames) ? state.frames : []; const presentationState = state.presentation?.getState ? state.presentation.getState() : null; const beaconSurfaceState = state.beaconSurface?.getState ? state.beaconSurface.getState() : null; const beaconEmitterState = state.beaconEmitter?.getState ? state.beaconEmitter.getState() : null; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; const video = scene3d.video || {}; const presentationInfo = aura.video.getInfo(presentationState?.surface?.handle || 0); const beaconInfo = aura.video.getInfo(beaconSurfaceState?.handle || 0); const cameraTrace = new Set(frames.map((entry) => \`\${entry.cameraX}:\${entry.cameraZ}\`)); const beaconTrace = new Set(frames.map((entry) => \`\${entry.beaconX}:\${entry.beaconZ}\`)); return typeof state.videoPath === 'string' && state.videoPath.includes('video-native.mp4') && typeof state.audioPath === 'string' && state.audioPath.includes('presentation-beacon.wav') && presentationState?.ok === true && Number.isInteger(presentationState?.surface?.handle) && presentationState.surface.handle > 0 && Number.isInteger(presentationState?.surface?.textureHandle) && presentationState.surface.textureHandle > 0 && presentationState?.surface?.info?.sourceKind === 'mp4' && Number.isInteger(presentationState?.emitter?.handle) && presentationState.emitter.handle > 0 && typeof presentationState?.emitter?.playbackState === 'string' && presentationState.emitter.listener != null && beaconSurfaceState?.ok === true && Number.isInteger(beaconSurfaceState?.handle) && beaconSurfaceState.handle > 0 && Number.isInteger(beaconSurfaceState?.textureHandle) && beaconSurfaceState.textureHandle > 0 && beaconSurfaceState?.info?.sourceKind === 'mp4' && beaconEmitterState?.ok === true && Number.isInteger(beaconEmitterState?.handle) && beaconEmitterState.handle > 0 && beaconEmitterState.attached === true && typeof beaconEmitterState?.playbackState === 'string' && Math.abs(Number(beaconEmitterState.occlusion || 0) - 0.22) < 0.001 && frames.length === 18 && cameraTrace.size >= 3 && beaconTrace.size >= 3 && Number(presentationInfo?.currentTime || 0) > 0 && Number(beaconInfo?.currentTime || 0) > 0 && presentationInfo?.sourceKind === 'mp4' && beaconInfo?.sourceKind === 'mp4' && Number(presentationInfo?.textureHandle || 0) === Number(presentationState.surface.textureHandle || 0) && Number(beaconInfo?.textureHandle || 0) === Number(beaconSurfaceState.textureHandle || 0) && Number(video.activePlayerCount || 0) >= 2 && Number(video.totalTickUploadCount || 0) >= 2 && Number(video.totalProcessedUploadCount || 0) >= 2 && Number(video.textureHandleCount || 0) >= 2 && Number(resources.videoTextureHandleCount || 0) >= 2 && Number(submission.billboardLastCommandCount || 0) >= 2 && Number(submission.billboardLastRenderedCount || 0) >= 2 && Number(submission.billboardLastBatchCount || 0) >= 1 && submission.billboardLastReasonCode === 'billboard_submitted'; } catch (_) { return false; } })()`,
|
|
2004
|
+
debugExpression: `(() => { try { const state = globalThis.__mediaPresentationRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state: { videoPath: state.videoPath || null, audioPath: state.audioPath || null, frames: Array.isArray(state.frames) ? state.frames : [] }, presentationState: state.presentation?.getState ? state.presentation.getState() : null, beaconSurfaceState: state.beaconSurface?.getState ? state.beaconSurface.getState() : null, beaconEmitterState: state.beaconEmitter?.getState ? state.beaconEmitter.getState() : null, scene3dRuntime: stats.scene3dRuntime || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()`,
|
|
2005
|
+
},
|
|
2006
|
+
],
|
|
2007
|
+
source: `
|
|
2008
|
+
globalThis.__mediaPresentationRuntime = {
|
|
2009
|
+
elapsed: 0,
|
|
2010
|
+
frames: [],
|
|
2011
|
+
videoPath: null,
|
|
2012
|
+
audioPath: null,
|
|
2013
|
+
};
|
|
2014
|
+
`,
|
|
2015
|
+
nativeFrames: 18,
|
|
2016
|
+
},
|
|
2017
|
+
{
|
|
2018
|
+
id: 'threejs-morph-target-runtime',
|
|
2019
|
+
modes: ['native'],
|
|
2020
|
+
namespaces: ['mesh', 'material', 'draw3d', 'camera3d', 'debug'],
|
|
2021
|
+
functions: [
|
|
2022
|
+
'aura.mesh.createBox',
|
|
2023
|
+
'aura.mesh.setMorphTargets',
|
|
2024
|
+
'aura.mesh.setMorphWeights',
|
|
2025
|
+
'aura.mesh.getData',
|
|
2026
|
+
'aura.material.create',
|
|
2027
|
+
'aura.draw3d.drawMesh',
|
|
2028
|
+
'aura.draw3d.clear3d',
|
|
2029
|
+
'aura.camera3d.perspective',
|
|
2030
|
+
'aura.camera3d.setPosition',
|
|
2031
|
+
'aura.camera3d.lookAt',
|
|
2032
|
+
'aura.debug.inspectorStats',
|
|
2033
|
+
],
|
|
2034
|
+
nativeChecks: [
|
|
2035
|
+
{
|
|
2036
|
+
id: 'mesh.morph-runtime.surface.methods',
|
|
2037
|
+
expression: "(() => ['createBox','setMorphTargets','setMorphWeights','getData'].every((name) => typeof aura.mesh?.[name] === 'function') && typeof aura.material?.create === 'function' && typeof aura.draw3d?.drawMesh === 'function' && typeof aura.draw3d?.clear3d === 'function' && ['perspective','setPosition','lookAt'].every((name) => typeof aura.camera3d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
id: 'mesh.morph-runtime.setup',
|
|
2041
|
+
expression: "(() => { try { const mesh = aura.mesh.createBox(1, 1, 1); const initial = aura.mesh.getData(mesh); if (!initial || Number(initial.vertexCount || 0) <= 0) return false; const material = aura.material.create({ color: { r: 0.76, g: 0.68, b: 0.9, a: 1 }, roughness: 0.72, metallic: 0.08 }); const makeTarget = (offset) => ({ positions: Array.from({ length: initial.vertexCount * 3 }, (_, index) => (index % 3 === 0 ? offset : 0)), normals: Array.from({ length: initial.vertexCount * 3 }, () => 0) }); aura.mesh.setMorphTargets(mesh, [makeTarget(0.18), makeTarget(-0.11), makeTarget(0.07), makeTarget(0.03)]); aura.mesh.setMorphWeights(mesh, [0.4, 0.0, 0.0, 0.0]); const updated = aura.mesh.getData(mesh); aura.camera3d.perspective(60, 0.1, 100); aura.camera3d.setPosition(0, 0, 4); aura.camera3d.lookAt(0, 0, 0); const baseDraw = aura.draw; aura.draw = function () { aura.draw3d.clear3d({ r: 0.025, g: 0.025, b: 0.035, a: 1 }); aura.draw3d.drawMesh(mesh, material, { position: { x: 0, y: 0, z: 0 } }); baseDraw(); }; globalThis.__morphRuntime = { mesh, material, morphTargetCount: Number(updated?.morphTargetCount || 0) }; return Number.isInteger(mesh) && mesh > 0 && Number.isInteger(material) && material > 0 && Number(updated?.morphTargetCount || 0) === 4; } catch (_) { return false; } })()",
|
|
2042
|
+
},
|
|
2043
|
+
],
|
|
2044
|
+
nativePostChecks: [
|
|
2045
|
+
{
|
|
2046
|
+
id: 'mesh.morph-runtime.evidence',
|
|
2047
|
+
expression: "(() => { try { const state = globalThis.__morphRuntime || {}; aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); const scene3d = stats.scene3dRuntime || {}; const submission = scene3d.submission || {}; const resources = scene3d.resources || {}; return Number.isInteger(state.mesh) && state.mesh > 0 && Number.isInteger(state.material) && state.material > 0 && Number(state.morphTargetCount || 0) === 4 && Number(resources.morphTrackedMeshCount || 0) >= 1 && Number(resources.morphPendingUploadCount || 0) === 0 && Number(submission.morphCandidateDrawCount || 0) >= 1 && Number(submission.morphPreparedBindGroupCount || 0) >= 1 && Number(submission.morphExecutedBindGroupCount || 0) >= 1 && Number(submission.morphExecutedMeshCount || 0) >= 1 && Number(submission.morphExecutedNonZeroWeightDrawCount || 0) >= 1 && Number(submission.morphExecutedNonZeroWeightMeshCount || 0) >= 1 && submission.morphLastReasonCode === 'live_non_zero_weights'; } catch (_) { return false; } })()",
|
|
2048
|
+
debugExpression: "(() => { try { aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats() || {}; aura.debug.enableInspector(false); return { state: globalThis.__morphRuntime || null, scene3dRuntime: stats.scene3dRuntime || null, meshData: aura.mesh.getData(globalThis.__morphRuntime?.mesh || 0) || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
|
|
2049
|
+
},
|
|
2050
|
+
],
|
|
2051
|
+
source: `
|
|
2052
|
+
globalThis.__morphRuntime = { mesh: 0, material: 0, morphTargetCount: 0 };
|
|
2053
|
+
`,
|
|
2054
|
+
nativeFrames: 4,
|
|
2055
|
+
},
|
|
2056
|
+
{
|
|
2057
|
+
id: 'threejs-scene3d-clip-skinning-runtime',
|
|
2058
|
+
modes: ['native'],
|
|
2059
|
+
namespaces: ['scene3d', 'debug'],
|
|
2060
|
+
functions: [
|
|
2061
|
+
'aura.scene3d.createClip',
|
|
2062
|
+
'aura.scene3d.playClip',
|
|
2063
|
+
'aura.scene3d.pauseClip',
|
|
2064
|
+
'aura.scene3d.resumeClip',
|
|
2065
|
+
'aura.scene3d.seekClip',
|
|
2066
|
+
'aura.scene3d.setClipWeight',
|
|
2067
|
+
'aura.scene3d.setClipLoop',
|
|
2068
|
+
'aura.scene3d.setClipSkinning',
|
|
2069
|
+
'aura.scene3d.crossfadeClips',
|
|
2070
|
+
'aura.scene3d.updateClips',
|
|
2071
|
+
'aura.scene3d.onClipEvent',
|
|
2072
|
+
'aura.scene3d.getClipState',
|
|
2073
|
+
'aura.debug.inspectorStats',
|
|
2074
|
+
],
|
|
2075
|
+
nativeChecks: [
|
|
2076
|
+
{
|
|
2077
|
+
id: 'scene3d.clip.surface.methods',
|
|
2078
|
+
expression: "(() => ['createClip','playClip','pauseClip','resumeClip','seekClip','setClipWeight','setClipLoop','setClipSkinning','crossfadeClips','updateClips','onClipEvent','getClipState'].every((name) => typeof aura.scene3d?.[name] === 'function'))()",
|
|
2079
|
+
},
|
|
2080
|
+
{
|
|
2081
|
+
id: 'scene3d.clip-skinning.deterministic',
|
|
2082
|
+
expression: "(() => { const runSample = () => { const nodeId = aura.scene3d.createNode(); const clipA = aura.scene3d.createClip({ nodeId, name: 'idle', duration: 1, time: 0, playing: true, loop: false, weight: 1, skinningInfluence: 1, boneCount: 4 }); const clipB = aura.scene3d.createClip({ nodeId, name: 'run', duration: 1, time: 0, playing: false, loop: true, weight: 0, skinningInfluence: 0.5, boneCount: 6 }); if (!clipA?.ok || !clipB?.ok) return null; const clipAId = clipA.clipId; const clipBId = clipB.clipId; const calls = []; const early = aura.scene3d.onClipEvent(clipAId, (evt) => calls.push(`early:${evt.type}:${evt.tag || ''}:${evt.loops || 0}`), 5); const late = aura.scene3d.onClipEvent(clipAId, (evt) => calls.push(`late:${evt.type}:${evt.tag || ''}:${evt.loops || 0}`), 20); const fade = aura.scene3d.crossfadeClips(clipAId, clipBId, { duration: 0.5, toTime: 0.2, eventTag: 'mix' }); const stepA = aura.scene3d.updateClips(0.5); const skin = aura.scene3d.setClipSkinning(clipBId, { influence: 0.75, boneCount: 8 }); const stepB = aura.scene3d.updateClips(0.25); const stateA = aura.scene3d.getClipState(clipAId); const stateB = aura.scene3d.getClipState(clipBId); aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); aura.debug.enableInspector(false); const anim = stats?.scene3dRuntime?.animation || {}; aura.scene3d.removeNode(nodeId); return { fade: fade?.ok === true, stepA: stepA?.ok === true, skin: skin?.ok === true, stepB: stepB?.ok === true, early: early?.ok === true, late: late?.ok === true, stateAPlaying: stateA?.playing === false, stateACompleted: stateA?.completed === true, stateAWeight: stateA ? Number(stateA.weight.toFixed(6)) : null, stateBPlaying: stateB?.playing === true, stateBTime: stateB ? Number(stateB.time.toFixed(6)) : null, stateBWeight: stateB ? Number(stateB.weight.toFixed(6)) : null, stateBSkinInfluence: stateB ? Number((stateB.skinning?.influence ?? 0).toFixed(6)) : null, stateBBoneCount: stateB?.skinning?.boneCount ?? 0, calls: calls.join('|'), anim: { clipCount: anim.clipCount, playingClipCount: anim.playingClipCount, transitionCount: anim.transitionCount, activeWeightSum: Number((anim.activeWeightSum ?? 0).toFixed(6)), blendEventsDispatched: anim.blendEventsDispatched, skinnedClipCount: anim.skinnedClipCount, totalBoneCount: anim.totalBoneCount, skinningInfluenceSum: Number((anim.skinningInfluenceSum ?? 0).toFixed(6)), skinningUpdates: anim.skinningUpdates } }; }; const first = runSample(); const second = runSample(); if (!first || !second) return false; const stableFirst = JSON.stringify({ ...first, anim: { ...first.anim, blendEventsDispatched: first.anim.blendEventsDispatched > 0 ? 1 : 0, skinningUpdates: first.anim.skinningUpdates > 0 ? 1 : 0 } }); const stableSecond = JSON.stringify({ ...second, anim: { ...second.anim, blendEventsDispatched: second.anim.blendEventsDispatched > 0 ? 1 : 0, skinningUpdates: second.anim.skinningUpdates > 0 ? 1 : 0 } }); return stableFirst === stableSecond && first.fade && first.stepA && first.skin && first.stepB && first.early && first.late && first.stateAPlaying && first.stateACompleted && first.stateAWeight === 0 && first.stateBPlaying && first.stateBTime === 0.95 && first.stateBWeight === 1 && first.stateBSkinInfluence === 0.75 && first.stateBBoneCount === 8 && first.calls === 'early:blend_complete:mix:0|late:blend_complete:mix:0' && first.anim.clipCount === 2 && first.anim.playingClipCount === 1 && first.anim.transitionCount === 0 && first.anim.activeWeightSum === 1 && first.anim.skinnedClipCount === 2 && first.anim.totalBoneCount === 12 && first.anim.skinningInfluenceSum === 1.75 && first.anim.blendEventsDispatched >= 1 && first.anim.skinningUpdates >= 1; })()",
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
id: 'scene3d.clip.reason-codes',
|
|
2086
|
+
expression: "(() => { const nodeId = aura.scene3d.createNode(); const invalidOptions = aura.scene3d.createClip(null); const invalidNode = aura.scene3d.createClip({ nodeId: 999999, duration: 1 }); const invalidDuration = aura.scene3d.createClip({ nodeId, duration: 0 }); const invalidName = aura.scene3d.createClip({ nodeId, duration: 1, name: ' ' }); const invalidWeight = aura.scene3d.createClip({ nodeId, duration: 1, weight: 2 }); const invalidSkinInfluence = aura.scene3d.createClip({ nodeId, duration: 1, skinningInfluence: 2 }); const invalidBoneCount = aura.scene3d.createClip({ nodeId, duration: 1, boneCount: -1 }); const createdA = aura.scene3d.createClip({ nodeId, duration: 1 }); const createdB = aura.scene3d.createClip({ nodeId, duration: 1 }); if (!createdA?.ok || !createdB?.ok) return false; const clipAId = createdA.clipId; const clipBId = createdB.clipId; const invalidClipId = aura.scene3d.playClip('oops'); const missingClip = aura.scene3d.playClip(999999); const invalidSeek = aura.scene3d.seekClip(clipAId, -1); const invalidSetWeight = aura.scene3d.setClipWeight(clipAId, 2); const invalidSetLoop = aura.scene3d.setClipLoop(clipAId, 'yes'); const invalidSkinOptions = aura.scene3d.setClipSkinning(clipAId, null); const invalidSetSkinInfluence = aura.scene3d.setClipSkinning(clipAId, { influence: 2 }); const invalidSetBoneCount = aura.scene3d.setClipSkinning(clipAId, { boneCount: -1 }); const invalidCrossfadeOptions = aura.scene3d.crossfadeClips(clipAId, clipBId, null); const invalidCrossfadeDuration = aura.scene3d.crossfadeClips(clipAId, clipBId, { duration: 0 }); const invalidTargetClip = aura.scene3d.crossfadeClips(clipAId, clipAId, { duration: 0.2 }); const missingTargetClip = aura.scene3d.crossfadeClips(clipAId, 999999, { duration: 0.2 }); const invalidOnEvent = aura.scene3d.onClipEvent(clipAId, null); const invalidDt = aura.scene3d.updateClips(0); aura.scene3d.removeNode(nodeId); return invalidOptions?.reason === 'invalid_clip_options' && invalidNode?.reason === 'invalid_node_id' && invalidDuration?.reason === 'invalid_clip_duration' && invalidName?.reason === 'invalid_clip_name' && invalidWeight?.reason === 'invalid_weight' && invalidSkinInfluence?.reason === 'invalid_skinning_influence' && invalidBoneCount?.reason === 'invalid_skinning_bone_count' && invalidClipId?.reason === 'invalid_clip_id' && missingClip?.reason === 'missing_clip' && invalidSeek?.reason === 'invalid_time' && invalidSetWeight?.reason === 'invalid_weight' && invalidSetLoop?.reason === 'invalid_loop_flag' && invalidSkinOptions?.reason === 'invalid_skinning_options' && invalidSetSkinInfluence?.reason === 'invalid_skinning_influence' && invalidSetBoneCount?.reason === 'invalid_skinning_bone_count' && invalidCrossfadeOptions?.reason === 'invalid_crossfade_options' && invalidCrossfadeDuration?.reason === 'invalid_crossfade_duration' && invalidTargetClip?.reason === 'invalid_target_clip' && missingTargetClip?.reason === 'missing_target_clip' && invalidOnEvent?.reason === 'invalid_callback' && invalidDt?.reason === 'invalid_dt'; })()",
|
|
2087
|
+
},
|
|
2088
|
+
],
|
|
2089
|
+
source: `
|
|
2090
|
+
aura.setup = function () {};
|
|
2091
|
+
`,
|
|
2092
|
+
nativeFrames: 2,
|
|
2093
|
+
}
|
|
2094
|
+
];
|