@forgeax/engine-render 0.1.34 → 0.1.36
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 +69 -3
- package/dist/assembly/device-feature-admission.d.ts +2 -2
- package/dist/assembly/host-contract.d.ts +3 -2
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-target-host.d.ts +5 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts +1 -0
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +2 -4
- package/dist/authoring.mjs.map +1 -1
- package/dist/chunk-2OZAS3WU.mjs +3489 -0
- package/dist/chunk-2OZAS3WU.mjs.map +1 -0
- package/dist/{chunk-UI5SFODX.mjs → chunk-BZQ4CDRF.mjs} +2338 -1991
- package/dist/chunk-BZQ4CDRF.mjs.map +1 -0
- package/dist/{chunk-MKDIT2EM.mjs → chunk-CJOV2XLO.mjs} +216 -261
- package/dist/chunk-CJOV2XLO.mjs.map +1 -0
- package/dist/chunk-D6WSBXJ2.mjs +1842 -0
- package/dist/chunk-D6WSBXJ2.mjs.map +1 -0
- package/dist/{chunk-HEB564WF.mjs → chunk-DRF6OULX.mjs} +4 -4
- package/dist/{chunk-HEB564WF.mjs.map → chunk-DRF6OULX.mjs.map} +1 -1
- package/dist/{chunk-OCDLEI6I.mjs → chunk-OB5WAFYH.mjs} +117 -5
- package/dist/chunk-OB5WAFYH.mjs.map +1 -0
- package/dist/{chunk-ODZWR6JQ.mjs → chunk-OEYKKMXG.mjs} +2042 -27
- package/dist/chunk-OEYKKMXG.mjs.map +1 -0
- package/dist/chunk-RIPAEGI6.mjs +48 -0
- package/dist/chunk-RIPAEGI6.mjs.map +1 -0
- package/dist/{chunk-ELJFJ2BU.mjs → chunk-X2KA6WHM.mjs} +29 -4
- package/dist/chunk-X2KA6WHM.mjs.map +1 -0
- package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
- package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
- package/dist/cloud/frame-context.d.ts +2 -2
- package/dist/cloud/frame-context.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +936 -2140
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -19
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/errors/render.d.ts +3 -2
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/material-context.d.ts +7 -0
- package/dist/extract/material-context.d.ts.map +1 -0
- package/dist/features/host.d.ts +5 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/particle-mesh-layout.d.ts +19 -0
- package/dist/features/particle-mesh-layout.d.ts.map +1 -0
- package/dist/features/plan.d.ts +2 -0
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/types.d.ts +8 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/glyph-text-layout-system.d.ts +1 -1
- package/dist/glyph-text-layout-system.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +1 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-ownership.d.ts +3 -3
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +1 -1
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +1 -1
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +4 -4
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +542 -9
- package/dist/index.mjs.map +1 -1
- package/dist/instances.d.ts +3 -4
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -8
- package/dist/internal.mjs.map +1 -1
- package/dist/pbr-pipeline.d.ts +3 -3
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
- package/dist/pipeline/standard-output/resources.d.ts +2 -2
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
- package/dist/publication/contract.d.ts +77 -0
- package/dist/publication/contract.d.ts.map +1 -0
- package/dist/publication/dependencies.d.ts +7 -0
- package/dist/publication/dependencies.d.ts.map +1 -0
- package/dist/publication/prepare-geometry.d.ts +9 -0
- package/dist/publication/prepare-geometry.d.ts.map +1 -0
- package/dist/publication/programs.d.ts +5 -0
- package/dist/publication/programs.d.ts.map +1 -0
- package/dist/publication/publisher.d.ts +34 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/receiver.d.ts +29 -0
- package/dist/publication/receiver.d.ts.map +1 -0
- package/dist/publication/resource-scope.d.ts +18 -0
- package/dist/publication/resource-scope.d.ts.map +1 -0
- package/dist/publication/targets.d.ts +33 -0
- package/dist/publication/targets.d.ts.map +1 -0
- package/dist/publication/video.d.ts +5 -0
- package/dist/publication/video.d.ts.map +1 -0
- package/dist/record/fold-buckets.d.ts +3 -3
- package/dist/record/fold-buckets.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +6 -6
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +5 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +2 -2
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +2 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -9
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +3 -16
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +2 -3
- package/dist/record/prepared-material-bindings.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +4 -2
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -2
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/sprite-instance-buffer.d.ts +31 -0
- package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
- package/dist/record/target-capture-graph.d.ts +19 -0
- package/dist/record/target-capture-graph.d.ts.map +1 -0
- package/dist/record/typed-frame-graph.d.ts +2 -13
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +2 -2
- package/dist/record/volume-params.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +5 -12
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +4 -1
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +2 -0
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +2 -2
- package/dist/render-system-presentation.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +3 -2
- package/dist/render-system-projections.d.ts.map +1 -1
- package/dist/render-system-residency.d.ts +2 -2
- package/dist/render-system-residency.d.ts.map +1 -1
- package/dist/render-system.d.ts +3 -7
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +8 -4
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/render-source.d.ts +42 -0
- package/dist/scene/render-source.d.ts.map +1 -0
- package/dist/scene/source-systems.d.ts +6 -0
- package/dist/scene/source-systems.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +10 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +2 -2
- package/dist/systems/transparent-dispatch.d.ts.map +1 -1
- package/dist/systems/transparent-sort-config.d.ts +2 -1
- package/dist/systems/transparent-sort-config.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -7
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
- package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +26 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +3 -4
- package/src/__tests__/gpu-driven-production.integration.test.ts +18 -1
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +23 -0
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +0 -1
- package/src/__tests__/material-vertex-texture.browser.test.ts +7 -0
- package/src/__tests__/material-vertex-texture.dawn.test.ts +7 -0
- package/src/__tests__/material-vertex-texture.fixture.ts +31 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
- package/src/__tests__/physical-clearcoat.integration.test.ts +23 -23
- package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +5 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +107 -5
- package/src/__tests__/runtime-material-program.integration.test.ts +79 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +23 -3
- package/src/assembly/device-feature-admission.ts +2 -2
- package/src/assembly/host-contract.ts +5 -2
- package/src/assembly/recovery/renderer-recover.ts +9 -0
- package/src/assembly/render-target-host.ts +57 -47
- package/src/assembly/renderer-facade.ts +3 -1
- package/src/assembly/webgpu-ready.ts +1 -1
- package/src/assembly/webgpu-renderer.ts +101 -70
- package/src/assembly/webgpu-vertex-layouts.ts +2 -9
- package/src/cloud/frame-context.ts +2 -2
- package/src/device/device-scope.ts +3 -0
- package/src/device/gpu-residency.ts +26 -25
- package/src/errors/render.ts +3 -1
- package/src/extract/material-context.ts +21 -0
- package/src/features/host.ts +13 -5
- package/src/features/particle-mesh-layout.ts +25 -0
- package/src/features/plan.ts +2 -0
- package/src/features/types.ts +8 -0
- package/src/glyph-text-layout-system.ts +2 -2
- package/src/gpu-driven/production-raster.ts +9 -3
- package/src/gpu-driven/shadow-ownership.ts +3 -3
- package/src/gpu-driven/shadow-views.ts +27 -2
- package/src/gpu-driven/view-gpu.ts +5 -0
- package/src/ibl/frame-resources.ts +0 -1
- package/src/ibl/skylight-bind-group.ts +13 -25
- package/src/index.ts +9 -0
- package/src/instances.ts +9 -6
- package/src/pbr-pipeline.ts +15 -18
- package/src/pipeline/standard-output/lut-gpu.ts +3 -3
- package/src/pipeline/standard-output/resources.ts +2 -2
- package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
- package/src/publication/__tests__/features.integration.test.ts +173 -0
- package/src/publication/__tests__/programs.unit.test.ts +25 -0
- package/src/publication/__tests__/publication.integration.test.ts +415 -0
- package/src/publication/__tests__/targets.integration.test.ts +120 -0
- package/src/publication/__tests__/video.browser.test.ts +50 -0
- package/src/publication/contract.ts +104 -0
- package/src/publication/dependencies.ts +54 -0
- package/src/publication/prepare-geometry.ts +94 -0
- package/src/publication/programs.ts +45 -0
- package/src/publication/publisher.ts +645 -0
- package/src/publication/receiver.ts +297 -0
- package/src/publication/resource-scope.ts +31 -0
- package/src/publication/targets.ts +182 -0
- package/src/publication/video.ts +33 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
- package/src/record/__tests__/prepared-runtime-material.integration.test.ts +134 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +8 -10
- package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
- package/src/record/fold-buckets.ts +8 -5
- package/src/record/frame-snapshot.ts +6 -6
- package/src/record/frame.ts +87 -28
- package/src/record/helpers.ts +2 -2
- package/src/record/main-pass-geometry.ts +21 -2
- package/src/record/main-pass-material.ts +37 -33
- package/src/record/main-pass-sprite-draws.ts +6 -152
- package/src/record/main-pass.ts +8 -7
- package/src/record/prepared-material-bindings.ts +16 -37
- package/src/record/render-context.ts +4 -2
- package/src/record/shadow-pass.ts +65 -7
- package/src/record/sprite-instance-buffer.ts +155 -0
- package/src/record/target-capture-graph.ts +258 -0
- package/src/record/typed-frame-graph.ts +11 -207
- package/src/record/view-ubo.ts +1 -1
- package/src/record/volume-params.ts +6 -6
- package/src/recovery/render-system-candidate.ts +10 -18
- package/src/render-contract.ts +6 -1
- package/src/render-system-extract-tail.ts +48 -23
- package/src/render-system-extract.ts +11 -2
- package/src/render-system-presentation.ts +2 -2
- package/src/render-system-projections.ts +3 -2
- package/src/render-system-residency.ts +2 -2
- package/src/render-system.ts +82 -54
- package/src/scene/render-scene.ts +110 -100
- package/src/scene/render-source.ts +90 -0
- package/src/scene/source-systems.ts +33 -0
- package/src/systems/skin-palette-allocator.ts +1 -0
- package/src/systems/skin-palette-types.ts +11 -0
- package/src/systems/transparent-dispatch.ts +2 -2
- package/src/systems/transparent-sort-config.ts +3 -1
- package/src/typed-shadow-passes.ts +36 -5
- package/dist/chunk-BLXGONR2.mjs.map +0 -1
- package/dist/chunk-ELJFJ2BU.mjs.map +0 -1
- package/dist/chunk-ILQ5MEZF.mjs +0 -32
- package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
- package/dist/chunk-MKDIT2EM.mjs.map +0 -1
- package/dist/chunk-OCDLEI6I.mjs.map +0 -1
- package/dist/chunk-ODZWR6JQ.mjs.map +0 -1
- package/dist/chunk-RXZ3RGOU.mjs +0 -53
- package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
- package/dist/chunk-SHFUFJ5N.mjs +0 -1781
- package/dist/chunk-SHFUFJ5N.mjs.map +0 -1
- package/dist/chunk-UI5SFODX.mjs.map +0 -1
- package/dist/chunk-UINHLCZA.mjs +0 -2389
- package/dist/chunk-UINHLCZA.mjs.map +0 -1
- package/dist/chunk-WEX45HKH.mjs +0 -1524
- package/dist/chunk-WEX45HKH.mjs.map +0 -1
|
@@ -1,1257 +1,2394 @@
|
|
|
1
|
-
import { GlyphText } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { R_MIN, ok, err, TextError, toShared, handleSlot, AssetError, ASSET_ERROR_HINTS, materialValuesToLinearRuntime, derive, unpackSlot, materialGuidText } from '@forgeax/engine-types';
|
|
7
|
-
import { err as err$1, ok as ok$1, RhiError } from '@forgeax/engine-rhi';
|
|
8
|
-
import { resolveAssetHandle, walkMaterialPassesOverSharedRefs, selectMaterialPassProgram, materialParametersToParamSchema as materialParametersToParamSchema$1 } from '@forgeax/engine-assets-runtime';
|
|
9
|
-
import { Time } from '@forgeax/engine-ecs';
|
|
1
|
+
import { GlyphText, SpriteRegionOverride, SpriteInstances, selectActiveCameraIndex, getActiveCamera } from './chunk-X2KA6WHM.mjs';
|
|
2
|
+
import { RenderIntentInvalidError, DynamicResolutionRequiresTaaError, ShadowInvalidConfigError, VolumeDensityShapeMismatchError, VolumeInvalidBoundsError, VolumeInvalidParametersError, VolumeOwnerConflictError, MotionBlurValidationError, gpuDrivenShadowDrawKey, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, IES_SLICE_CAPACITY, COOKIE_SLICE_CAPACITY, SHADOW_ATLAS_DEFAULT_LAYERS, MaterialSkinAttrMissingError, isStandardPbrSkinMaterialShader, SkinMaterialMismatchError, validateInstanceTransforms, buildGpuDrivenDraws, deriveInstancesUnionBounds, worldEntityKey, gpuDrivenSourceDrawItemIndex, selectPasses, resolveRenderTargetMaterialSource, isCanonicalStandardPbrMaterialShader, BarrelDistortionInvalidParameterError, DynamicResolutionInvalidParameterError, COOKIE_SLICE_SIZE, EnvironmentSourceConflictError, FogCardinalityError, resolveMotionBlurParams, isStandardPbrMaterialShader, AtmosphereInvalidParameterError } from './chunk-2OZAS3WU.mjs';
|
|
3
|
+
import { defineComponent, Entity, Time } from '@forgeax/engine-ecs';
|
|
4
|
+
import { R_MIN, err, ok, TextError, toShared, handleSlot, AssetError, ASSET_ERROR_HINTS, materialValuesToLinearRuntime, derive, unpackSlot, materialGuidText } from '@forgeax/engine-types';
|
|
5
|
+
import { TONEMAP_SHADER_MODE, standardTextureMask, DEFAULT_STANDARD_PBR_PARAM_SCHEMA, STANDARD_PIPELINE_PARAM_SCHEMA, DEFAULT_STANDARD_SURFACE_MODULE } from '@forgeax/engine-shader';
|
|
10
6
|
import { SpawnLightInvalidBoundsError, routeWorldError, SpriteInstancesMutuallyExclusiveWithInstancesError, SpriteInstancesRequiresSpriteShaderError, SpriteInstancesCountMismatchError, readRenderArrayView } from '@forgeax/engine-ecs/projection';
|
|
7
|
+
import { Transform, projectHierarchy, GlobalTransform, MorphWeights } from '@forgeax/engine-scene';
|
|
11
8
|
import { vec3, mat4, frustum, box3 } from '@forgeax/engine-math';
|
|
9
|
+
import { resolveAssetHandle, walkMaterialPassesOverSharedRefs, selectMaterialPassProgram, materialParametersToParamSchema as materialParametersToParamSchema$1 } from '@forgeax/engine-assets-runtime';
|
|
12
10
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
13
|
-
import {
|
|
14
|
-
import { standardTextureMask, DEFAULT_STANDARD_PBR_PARAM_SCHEMA, STANDARD_PIPELINE_PARAM_SCHEMA, DEFAULT_STANDARD_SURFACE_MODULE } from '@forgeax/engine-shader';
|
|
11
|
+
import { RhiError } from '@forgeax/engine-rhi';
|
|
15
12
|
import { Skin, SkinInstancesCoexistForbiddenError, SkeletonResolveFailedError, JointCountMismatchError, JointEntityDanglingError } from '@forgeax/engine-skinning';
|
|
16
13
|
import { PROCEDURAL_FLOATS_PER_VERTEX } from '@forgeax/engine-geometry';
|
|
17
14
|
import { resetFontConcurrency, trackFontConcurrency, layoutGlyphText, conservativeCubeAabb, bakeGlyphMesh } from '@forgeax/engine-graphics-extras';
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
var Atmosphere = defineComponent("Atmosphere", {
|
|
17
|
+
turbidity: { type: "f32", default: 2 },
|
|
18
|
+
rayleigh: { type: "f32", default: 1 },
|
|
19
|
+
mieCoefficient: { type: "f32", default: 5e-3 },
|
|
20
|
+
mieDirectionalG: { type: "f32", default: 0.8 },
|
|
21
|
+
sunAngularRadius: { type: "f32", default: 4675e-6 },
|
|
22
|
+
/** Independent multiplier for the analytic circumsolar lobe. */
|
|
23
|
+
circumsolarStrength: { type: "f32", default: 1 },
|
|
24
|
+
/** Relative angular width of the analytic circumsolar lobe. */
|
|
25
|
+
circumsolarWidth: { type: "f32", default: 1 }
|
|
26
|
+
});
|
|
27
|
+
var BarrelDistortion = defineComponent("BarrelDistortion", {
|
|
28
|
+
strength: { type: "f32", default: 0 },
|
|
29
|
+
centerX: { type: "f32", default: 0.5 },
|
|
30
|
+
centerY: { type: "f32", default: 0.5 }
|
|
31
|
+
});
|
|
32
|
+
var DEFAULT_BARREL_DISTORTION = Object.freeze({
|
|
33
|
+
strength: 0,
|
|
34
|
+
centerX: 0.5,
|
|
35
|
+
centerY: 0.5
|
|
36
|
+
});
|
|
37
|
+
function invalid(field, value, expected) {
|
|
38
|
+
return new BarrelDistortionInvalidParameterError({ field, value, expected });
|
|
39
|
+
}
|
|
40
|
+
function validateBarrelDistortionParameters(input) {
|
|
41
|
+
const value = input ?? {};
|
|
42
|
+
const strength = value.strength ?? DEFAULT_BARREL_DISTORTION.strength;
|
|
43
|
+
const centerX = value.centerX ?? DEFAULT_BARREL_DISTORTION.centerX;
|
|
44
|
+
const centerY = value.centerY ?? DEFAULT_BARREL_DISTORTION.centerY;
|
|
45
|
+
if (!Number.isFinite(strength) || strength < 0 || strength > 0.35) {
|
|
46
|
+
return err(invalid("strength", strength, "finite and in [0, 0.35]"));
|
|
47
|
+
}
|
|
48
|
+
if (!Number.isFinite(centerX) || centerX < 0 || centerX > 1) {
|
|
49
|
+
return err(invalid("centerX", centerX, "finite and in [0, 1]"));
|
|
50
|
+
}
|
|
51
|
+
if (!Number.isFinite(centerY) || centerY < 0 || centerY > 1) {
|
|
52
|
+
return err(invalid("centerY", centerY, "finite and in [0, 1]"));
|
|
53
|
+
}
|
|
54
|
+
return ok(Object.freeze({ strength, centerX, centerY }));
|
|
55
|
+
}
|
|
56
|
+
var CAMERA_PROJECTION_PERSPECTIVE = 0;
|
|
57
|
+
var CAMERA_PROJECTION_ORTHOGRAPHIC = 1;
|
|
58
|
+
var CAMERA_EXPOSURE_MODE_MANUAL = 0;
|
|
59
|
+
var CAMERA_EXPOSURE_MODE_AUTO = 1;
|
|
60
|
+
var CAMERA_TEMPERATURE_MIN = 1e3;
|
|
61
|
+
var CAMERA_TEMPERATURE_MAX = 4e4;
|
|
62
|
+
var CAMERA_TINT_MIN = -1;
|
|
63
|
+
var CAMERA_TINT_MAX = 1;
|
|
64
|
+
var CameraError = class extends Error {
|
|
65
|
+
code;
|
|
66
|
+
expected;
|
|
67
|
+
hint;
|
|
68
|
+
detail;
|
|
69
|
+
constructor(code, field, actual, expected, hint) {
|
|
70
|
+
super(`${code}: ${field}=${String(actual)}; expected ${expected}`);
|
|
71
|
+
this.name = "CameraError";
|
|
72
|
+
this.code = code;
|
|
73
|
+
this.expected = expected;
|
|
74
|
+
this.hint = hint;
|
|
75
|
+
this.detail = Object.freeze({ field, actual, expected });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function actualValue(value) {
|
|
79
|
+
if (typeof value === "number" || typeof value === "string") return value;
|
|
80
|
+
try {
|
|
81
|
+
return JSON.stringify(value) ?? String(value);
|
|
82
|
+
} catch {
|
|
83
|
+
return String(value);
|
|
27
84
|
}
|
|
28
85
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
width: Math.max(1, Math.floor(extent.width / 2)),
|
|
33
|
-
height: Math.max(1, Math.floor(extent.height / 2))
|
|
34
|
-
};
|
|
86
|
+
function cameraError(code, field, actual, expected, hint) {
|
|
87
|
+
return new CameraError(code, field, actualValue(actual), expected, hint);
|
|
35
88
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
const levels = [];
|
|
39
|
-
let current = { width: extent.width, height: extent.height };
|
|
40
|
-
let level = 0;
|
|
41
|
-
while (true) {
|
|
42
|
-
levels.push({ level, ...current });
|
|
43
|
-
if (current.width === 1 && current.height === 1) break;
|
|
44
|
-
current = nextSsrHiZMipExtent(current);
|
|
45
|
-
level += 1;
|
|
46
|
-
}
|
|
47
|
-
return Object.freeze({
|
|
48
|
-
extent: Object.freeze({ width: extent.width, height: extent.height }),
|
|
49
|
-
format: SSR_HIZ_FORMAT,
|
|
50
|
-
mipLevelCount: levels.length,
|
|
51
|
-
levels: Object.freeze(levels.map((entry) => Object.freeze(entry))),
|
|
52
|
-
graphSize: Object.freeze({ width: extent.width, height: extent.height })
|
|
53
|
-
});
|
|
89
|
+
function finitePositive(value) {
|
|
90
|
+
return Number.isFinite(value) && value > 0;
|
|
54
91
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
92
|
+
function validateCameraExposure(exposure) {
|
|
93
|
+
if (typeof exposure !== "object" || exposure === null) {
|
|
94
|
+
throw cameraError(
|
|
95
|
+
"camera-exposure-invalid",
|
|
96
|
+
"value",
|
|
97
|
+
exposure,
|
|
98
|
+
"an object with kind manual or auto",
|
|
99
|
+
"choose one member of the CameraExposure union"
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (exposure.kind === "manual") {
|
|
103
|
+
if (!finitePositive(exposure.multiplier)) {
|
|
104
|
+
throw cameraError(
|
|
105
|
+
"camera-exposure-invalid",
|
|
106
|
+
"multiplier",
|
|
107
|
+
exposure.multiplier,
|
|
108
|
+
"a finite number greater than zero",
|
|
109
|
+
"set Camera.exposure to a positive finite manual multiplier"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return exposure;
|
|
113
|
+
}
|
|
114
|
+
if (exposure.kind === "auto") {
|
|
115
|
+
const [min, max] = exposure.rangeEv;
|
|
116
|
+
const [up, down] = exposure.rates;
|
|
117
|
+
if (!finitePositive(exposure.fallback) || !Number.isFinite(exposure.compensationEv) || !Number.isFinite(min) || !Number.isFinite(max) || min > max || !Number.isFinite(up) || !Number.isFinite(down) || up < 0 || down < 0) {
|
|
118
|
+
throw cameraError(
|
|
119
|
+
"camera-exposure-invalid",
|
|
120
|
+
"auto",
|
|
121
|
+
JSON.stringify(exposure),
|
|
122
|
+
"finite fallback/compensation, an ordered range, and non-negative rates",
|
|
123
|
+
"inspect fallback, compensationEv, rangeEv, and rates before rebuilding the camera"
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return exposure;
|
|
127
|
+
}
|
|
128
|
+
throw cameraError(
|
|
129
|
+
"camera-exposure-invalid",
|
|
130
|
+
"kind",
|
|
131
|
+
exposure.kind,
|
|
132
|
+
"'manual' or 'auto'",
|
|
133
|
+
"choose one member of the CameraExposure union"
|
|
134
|
+
);
|
|
67
135
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
136
|
+
function validateCameraColorGrading(temperature, tint, colorLutStrength) {
|
|
137
|
+
if (!Number.isFinite(temperature) || temperature < CAMERA_TEMPERATURE_MIN || temperature > CAMERA_TEMPERATURE_MAX) {
|
|
138
|
+
throw cameraError(
|
|
139
|
+
"camera-color-grading-invalid",
|
|
140
|
+
"temperature",
|
|
141
|
+
temperature,
|
|
142
|
+
`a finite value in [${CAMERA_TEMPERATURE_MIN}, ${CAMERA_TEMPERATURE_MAX}]`,
|
|
143
|
+
"set Camera.temperature to a supported Kelvin value"
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
if (!Number.isFinite(tint) || tint < CAMERA_TINT_MIN || tint > CAMERA_TINT_MAX) {
|
|
147
|
+
throw cameraError(
|
|
148
|
+
"camera-color-grading-invalid",
|
|
149
|
+
"tint",
|
|
150
|
+
tint,
|
|
151
|
+
`a finite value in [${CAMERA_TINT_MIN}, ${CAMERA_TINT_MAX}]`,
|
|
152
|
+
"set Camera.tint to a normalized value between -1 and 1"
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (!Number.isFinite(colorLutStrength) || colorLutStrength < 0 || colorLutStrength > 1) {
|
|
156
|
+
throw cameraError(
|
|
157
|
+
"camera-color-grading-invalid",
|
|
158
|
+
"colorLutStrength",
|
|
159
|
+
colorLutStrength,
|
|
160
|
+
"a finite value in [0, 1]",
|
|
161
|
+
"set Camera.colorLutStrength to zero to disable the LUT or a blend in [0, 1]"
|
|
162
|
+
);
|
|
72
163
|
}
|
|
73
|
-
return bytes;
|
|
74
164
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
165
|
+
function cameraProjectionFromF32(value) {
|
|
166
|
+
return value === CAMERA_PROJECTION_ORTHOGRAPHIC ? "orthographic" : "perspective";
|
|
167
|
+
}
|
|
168
|
+
var TONEMAP_NONE = TONEMAP_SHADER_MODE.none;
|
|
169
|
+
var TONEMAP_REINHARD_EXTENDED = TONEMAP_SHADER_MODE.reinhardExtended;
|
|
170
|
+
var TONEMAP_REINHARD = TONEMAP_SHADER_MODE.reinhard;
|
|
171
|
+
var TONEMAP_LINEAR = TONEMAP_SHADER_MODE.linear;
|
|
172
|
+
var TONEMAP_CINEON = TONEMAP_SHADER_MODE.cineon;
|
|
173
|
+
var TONEMAP_ACES_FILMIC = TONEMAP_SHADER_MODE.acesFilmic;
|
|
174
|
+
var TONEMAP_AGX = TONEMAP_SHADER_MODE.agx;
|
|
175
|
+
var TONEMAP_NEUTRAL = TONEMAP_SHADER_MODE.neutral;
|
|
176
|
+
function tonemapFromF32(value) {
|
|
177
|
+
switch (value) {
|
|
178
|
+
case TONEMAP_REINHARD_EXTENDED:
|
|
179
|
+
return "reinhard-extended";
|
|
180
|
+
case TONEMAP_REINHARD:
|
|
181
|
+
return "reinhard";
|
|
182
|
+
case TONEMAP_LINEAR:
|
|
183
|
+
return "linear";
|
|
184
|
+
case TONEMAP_CINEON:
|
|
185
|
+
return "cineon";
|
|
186
|
+
case TONEMAP_ACES_FILMIC:
|
|
187
|
+
return "aces-filmic";
|
|
188
|
+
case TONEMAP_AGX:
|
|
189
|
+
return "agx";
|
|
190
|
+
case TONEMAP_NEUTRAL:
|
|
191
|
+
return "neutral";
|
|
192
|
+
default:
|
|
193
|
+
return "none";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function tonemapToU32(mode) {
|
|
197
|
+
switch (mode) {
|
|
198
|
+
case "reinhard-extended":
|
|
199
|
+
return TONEMAP_REINHARD_EXTENDED;
|
|
200
|
+
case "reinhard":
|
|
201
|
+
return TONEMAP_REINHARD;
|
|
202
|
+
case "linear":
|
|
203
|
+
return TONEMAP_LINEAR;
|
|
204
|
+
case "cineon":
|
|
205
|
+
return TONEMAP_CINEON;
|
|
206
|
+
case "aces-filmic":
|
|
207
|
+
return TONEMAP_ACES_FILMIC;
|
|
208
|
+
case "agx":
|
|
209
|
+
return TONEMAP_AGX;
|
|
210
|
+
case "neutral":
|
|
211
|
+
return TONEMAP_NEUTRAL;
|
|
212
|
+
case "none":
|
|
213
|
+
return TONEMAP_NONE;
|
|
214
|
+
}
|
|
215
|
+
throw cameraError(
|
|
216
|
+
"camera-tonemap-invalid",
|
|
217
|
+
"tonemap",
|
|
218
|
+
mode,
|
|
219
|
+
"one of the closed Tonemap values",
|
|
220
|
+
"select a supported Camera tonemap mode"
|
|
95
221
|
);
|
|
96
|
-
const ssrOwnedBytes = hizBytes + traceBytes + hitReactivityBytes + resolvedBytes + historyBytes + temporalParamsBytes + fallbackInputBytes;
|
|
97
|
-
return Object.freeze({
|
|
98
|
-
width: extent.width,
|
|
99
|
-
height: extent.height,
|
|
100
|
-
halfWidth: half.width,
|
|
101
|
-
halfHeight: half.height,
|
|
102
|
-
hizBytes,
|
|
103
|
-
traceBytes,
|
|
104
|
-
hitReactivityBytes,
|
|
105
|
-
resolvedBytes,
|
|
106
|
-
historyBytes,
|
|
107
|
-
temporalParamsBytes,
|
|
108
|
-
fallbackInputBytes,
|
|
109
|
-
ssrOwnedBytes,
|
|
110
|
-
budgetBytes: SSR_SPATIAL_MEMORY_BUDGET_BYTES,
|
|
111
|
-
withinBudget: ssrOwnedBytes <= SSR_SPATIAL_MEMORY_BUDGET_BYTES
|
|
112
|
-
});
|
|
113
222
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
223
|
+
var ANTIALIAS_NONE = 0;
|
|
224
|
+
var ANTIALIAS_FXAA = 1;
|
|
225
|
+
var ANTIALIAS_MSAA = 2;
|
|
226
|
+
var ANTIALIAS_TAA = 3;
|
|
227
|
+
function antialiasFromF32(value) {
|
|
228
|
+
if (value === ANTIALIAS_NONE) return "none";
|
|
229
|
+
if (value === ANTIALIAS_FXAA) return "fxaa";
|
|
230
|
+
if (value === ANTIALIAS_MSAA) return "msaa";
|
|
231
|
+
if (value === ANTIALIAS_TAA) return "taa";
|
|
232
|
+
throw cameraError(
|
|
233
|
+
"camera-antialias-invalid",
|
|
234
|
+
"antialias",
|
|
235
|
+
value,
|
|
236
|
+
`${ANTIALIAS_NONE}, ${ANTIALIAS_FXAA}, ${ANTIALIAS_MSAA}, or ${ANTIALIAS_TAA}`,
|
|
237
|
+
"select a supported Camera antialias mode"
|
|
238
|
+
);
|
|
130
239
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
function createSsrSpatialResources(graph, extent) {
|
|
153
|
-
const hiz = createSsrHiZResources(graph, extent);
|
|
154
|
-
if (!hiz.ok) return hiz;
|
|
155
|
-
const traceExtent = halfExtent(extent);
|
|
156
|
-
const trace = texture(graph, "ssr-trace", SSR_TRACE_FORMAT, traceExtent);
|
|
157
|
-
if (!trace.ok) return trace;
|
|
158
|
-
const hitTexture = graph.createTexture("ssr-hit-reactivity", {
|
|
159
|
-
format: "r32float",
|
|
160
|
-
size: traceExtent
|
|
161
|
-
});
|
|
162
|
-
if (!hitTexture.ok) return hitTexture;
|
|
163
|
-
const hitReactivity = graph.view(hitTexture.value, {
|
|
164
|
-
label: "ssr-hit-reactivity.view",
|
|
165
|
-
dimension: "2d"
|
|
166
|
-
});
|
|
167
|
-
if (!hitReactivity.ok) return hitReactivity;
|
|
168
|
-
return ok({ hiz: hiz.value, trace: trace.value, hitReactivity: hitReactivity.value });
|
|
240
|
+
var BLOOM_DISABLED = 0;
|
|
241
|
+
var BLOOM_ENABLED = 1;
|
|
242
|
+
var CAMERA_BLOOM_INTENSITY_MIN = 0;
|
|
243
|
+
var CAMERA_BLOOM_INTENSITY_MAX = 8;
|
|
244
|
+
var CAMERA_BLOOM_THRESHOLD_MIN = 0;
|
|
245
|
+
var CAMERA_BLOOM_THRESHOLD_MAX = 65504;
|
|
246
|
+
var CAMERA_BLOOM_SOFT_KNEE_MIN = 0;
|
|
247
|
+
var CAMERA_BLOOM_SOFT_KNEE_MAX = 1;
|
|
248
|
+
var CAMERA_BLOOM_SCATTER_MIN = 0;
|
|
249
|
+
var CAMERA_BLOOM_SCATTER_MAX = 0.95;
|
|
250
|
+
function bloomEnabledFromF32(value) {
|
|
251
|
+
if (value === BLOOM_DISABLED) return "off";
|
|
252
|
+
if (value === BLOOM_ENABLED) return "on";
|
|
253
|
+
throw cameraError(
|
|
254
|
+
"camera-bloom-invalid",
|
|
255
|
+
"bloom",
|
|
256
|
+
value,
|
|
257
|
+
`${BLOOM_DISABLED} or ${BLOOM_ENABLED}`,
|
|
258
|
+
"select Camera bloom off or on"
|
|
259
|
+
);
|
|
169
260
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
resourceCount: input.resourceCount,
|
|
181
|
-
uploadBytes: input.uploadBytes
|
|
182
|
-
});
|
|
261
|
+
function validateBloomRange(field, value, min, max) {
|
|
262
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
263
|
+
throw cameraError(
|
|
264
|
+
"camera-bloom-invalid",
|
|
265
|
+
field,
|
|
266
|
+
value,
|
|
267
|
+
`a finite value in [${min}, ${max}]`,
|
|
268
|
+
`set Camera.${field} to a finite value in [${min}, ${max}]`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
183
271
|
}
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
272
|
+
function validateCameraBloom(bloom, threshold, intensity, softKnee, scatter) {
|
|
273
|
+
const enabled = bloomEnabledFromF32(bloom);
|
|
274
|
+
validateBloomRange(
|
|
275
|
+
"bloomThreshold",
|
|
276
|
+
threshold,
|
|
277
|
+
CAMERA_BLOOM_THRESHOLD_MIN,
|
|
278
|
+
CAMERA_BLOOM_THRESHOLD_MAX
|
|
279
|
+
);
|
|
280
|
+
validateBloomRange(
|
|
281
|
+
"bloomIntensity",
|
|
282
|
+
intensity,
|
|
283
|
+
CAMERA_BLOOM_INTENSITY_MIN,
|
|
284
|
+
CAMERA_BLOOM_INTENSITY_MAX
|
|
285
|
+
);
|
|
286
|
+
validateBloomRange(
|
|
287
|
+
"bloomSoftKnee",
|
|
288
|
+
softKnee,
|
|
289
|
+
CAMERA_BLOOM_SOFT_KNEE_MIN,
|
|
290
|
+
CAMERA_BLOOM_SOFT_KNEE_MAX
|
|
291
|
+
);
|
|
292
|
+
validateBloomRange("bloomScatter", scatter, CAMERA_BLOOM_SCATTER_MIN, CAMERA_BLOOM_SCATTER_MAX);
|
|
293
|
+
return enabled;
|
|
294
|
+
}
|
|
295
|
+
var Camera = defineComponent("Camera", {
|
|
296
|
+
fov: { type: "f32" },
|
|
297
|
+
aspect: { type: "f32" },
|
|
298
|
+
near: { type: "f32" },
|
|
299
|
+
far: { type: "f32" },
|
|
300
|
+
projection: { type: "f32", default: 0 },
|
|
301
|
+
left: { type: "f32", default: -1 },
|
|
302
|
+
right: { type: "f32", default: 1 },
|
|
303
|
+
bottom: { type: "f32", default: -1 },
|
|
304
|
+
top: { type: "f32", default: 1 },
|
|
305
|
+
tonemap: { type: "f32", default: 0 },
|
|
306
|
+
exposure: { type: "f32", default: 1 },
|
|
307
|
+
exposureMode: { type: "f32", default: CAMERA_EXPOSURE_MODE_MANUAL },
|
|
308
|
+
compensationEv: { type: "f32", default: 0 },
|
|
309
|
+
rangeEv: { type: "array<f32, 2>", default: new Float32Array([-8, 8]) },
|
|
310
|
+
rates: { type: "array<f32, 2>", default: new Float32Array([3, 1]) },
|
|
311
|
+
whitePoint: { type: "f32", default: 4 },
|
|
312
|
+
temperature: { type: "f32", default: 6504 },
|
|
313
|
+
tint: { type: "f32", default: 0 },
|
|
314
|
+
colorLut: { type: "shared<TextureAsset>", default: 0 },
|
|
315
|
+
colorLutStrength: { type: "f32", default: 0 },
|
|
316
|
+
antialias: { type: "f32", default: 0 },
|
|
317
|
+
historyVersion: { type: "u32", default: 0 },
|
|
318
|
+
bloom: { type: "f32", default: 0 },
|
|
319
|
+
bloomThreshold: { type: "f32", default: 1 },
|
|
320
|
+
bloomIntensity: { type: "f32", default: 1 },
|
|
321
|
+
bloomSoftKnee: { type: "f32", default: 0.5 },
|
|
322
|
+
bloomScatter: { type: "f32", default: 0.7 },
|
|
323
|
+
// M1 target camera role: zero means this camera is eligible for display;
|
|
324
|
+
// non-zero shared targets are auxiliary producers whose views and receipt
|
|
325
|
+
// promotion remain owned by Renderer.
|
|
326
|
+
target: { type: "shared<RenderTarget>", simulationTransient: true },
|
|
327
|
+
// feat-20260709 M3 / D-3: clear-color is one inline `array<f32,4>` column.
|
|
328
|
+
// The earlier 4-scalar form (clearR/G/B/A) was chosen when this was believed
|
|
329
|
+
// to be the only SoA-safe shape; the Transform (pos/quat/scale) and light
|
|
330
|
+
// (direction/color) precedents disprove that -- an `array<f32,N>` IS an
|
|
331
|
+
// inline stride-N SoA column, read on the hot path as `col[i*N+a]` with zero
|
|
332
|
+
// allocation, so collapsing four scalars into one column removes three field
|
|
333
|
+
// names a reader must track without changing the storage layout or read
|
|
334
|
+
// pattern. The default `[0, 0, 0, 0]` is transparent black; explicit alpha
|
|
335
|
+
// stays visible through the same public array field.
|
|
336
|
+
clearColor: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 0]) },
|
|
337
|
+
// feat-20260617-host-engine-contract-and-video-cutscene / M3 / D-4: the
|
|
338
|
+
// aspect-sync sidecar on the createApp(canvas) path writes
|
|
339
|
+
// canvas.width / canvas.height into `aspect` every frame when this flag is
|
|
340
|
+
// true. Reuses the existing `bool` column tier (AnimationPlayer.paused /
|
|
341
|
+
// AudioSource.playing precedent) -- zero ECS infrastructure change. Default
|
|
342
|
+
// true so demos that never touch aspect track the canvas automatically
|
|
343
|
+
// (charter P1 default-is-correct); set false for render-to-texture /
|
|
344
|
+
// split-screen cameras that drive aspect themselves. Read it via
|
|
345
|
+
// world.get (readRow narrows bool -> JS boolean); the query-bundle path
|
|
346
|
+
// returns a raw 0/1 number (the `!== 0` always-true trap).
|
|
347
|
+
autoAspect: { type: "bool", default: true }
|
|
204
348
|
});
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
349
|
+
function cameraExposureFromColumns(input) {
|
|
350
|
+
if (input.exposureMode === CAMERA_EXPOSURE_MODE_MANUAL) {
|
|
351
|
+
return validateCameraExposure({ kind: "manual", multiplier: input.exposure });
|
|
352
|
+
}
|
|
353
|
+
if (input.exposureMode === CAMERA_EXPOSURE_MODE_AUTO) {
|
|
354
|
+
return validateCameraExposure({
|
|
355
|
+
kind: "auto",
|
|
356
|
+
fallback: input.exposure,
|
|
357
|
+
compensationEv: input.compensationEv,
|
|
358
|
+
rangeEv: [input.rangeEv[0] ?? -8, input.rangeEv[1] ?? 8],
|
|
359
|
+
rates: [input.rates[0] ?? 3, input.rates[1] ?? 1]
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
throw cameraError(
|
|
363
|
+
"camera-exposure-mode-invalid",
|
|
364
|
+
"exposureMode",
|
|
365
|
+
input.exposureMode,
|
|
366
|
+
`${CAMERA_EXPOSURE_MODE_MANUAL} or ${CAMERA_EXPOSURE_MODE_AUTO}`,
|
|
367
|
+
"set Camera.exposureMode to the manual or auto encoding"
|
|
368
|
+
);
|
|
215
369
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
370
|
+
function cameraExposureColumns(exposure) {
|
|
371
|
+
const checked = validateCameraExposure(exposure);
|
|
372
|
+
if (checked.kind === "manual") {
|
|
373
|
+
return {
|
|
374
|
+
exposureMode: CAMERA_EXPOSURE_MODE_MANUAL,
|
|
375
|
+
exposure: checked.multiplier,
|
|
376
|
+
compensationEv: 0,
|
|
377
|
+
rangeEv: new Float32Array([-8, 8]),
|
|
378
|
+
rates: new Float32Array([3, 1])
|
|
379
|
+
};
|
|
380
|
+
}
|
|
219
381
|
return {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
lastKnownGood: void 0,
|
|
226
|
-
resourceCount: 0,
|
|
227
|
-
descriptorBytes: 0,
|
|
228
|
-
uploadCount: 0,
|
|
229
|
-
passCount: 0,
|
|
230
|
-
bindGroupCount: 0,
|
|
231
|
-
failure: void 0,
|
|
232
|
-
failureKeys: []
|
|
382
|
+
exposureMode: CAMERA_EXPOSURE_MODE_AUTO,
|
|
383
|
+
exposure: checked.fallback,
|
|
384
|
+
compensationEv: checked.compensationEv,
|
|
385
|
+
rangeEv: new Float32Array(checked.rangeEv),
|
|
386
|
+
rates: new Float32Array(checked.rates)
|
|
233
387
|
};
|
|
234
388
|
}
|
|
235
|
-
function
|
|
236
|
-
|
|
389
|
+
function cameraColorGradingColumns(opts) {
|
|
390
|
+
const exposure = opts.exposure ?? { kind: "manual", multiplier: 1 };
|
|
391
|
+
const temperature = opts.temperature ?? 6504;
|
|
392
|
+
const tint = opts.tint ?? 0;
|
|
393
|
+
const colorLutStrength = opts.colorLutStrength ?? 0;
|
|
394
|
+
validateCameraColorGrading(temperature, tint, colorLutStrength);
|
|
237
395
|
return {
|
|
238
|
-
...
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
accepted: candidate,
|
|
244
|
-
lastKnownGood: candidate,
|
|
245
|
-
resourceCount: 4,
|
|
246
|
-
descriptorBytes: candidate.descriptorBytes,
|
|
247
|
-
uploadCount: candidate.uploadCount
|
|
396
|
+
...cameraExposureColumns(exposure),
|
|
397
|
+
temperature,
|
|
398
|
+
tint,
|
|
399
|
+
colorLut: opts.colorLut ?? 0,
|
|
400
|
+
colorLutStrength
|
|
248
401
|
};
|
|
249
402
|
}
|
|
250
|
-
function
|
|
251
|
-
const
|
|
252
|
-
|
|
403
|
+
function cameraPodFromDefaults() {
|
|
404
|
+
const base = {};
|
|
405
|
+
for (const [key, field] of Object.entries(Camera.fields)) {
|
|
406
|
+
if ("default" in field && field.default !== void 0) {
|
|
407
|
+
base[key] = field.default;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return base;
|
|
411
|
+
}
|
|
412
|
+
function perspective(opts) {
|
|
253
413
|
return {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
414
|
+
// cameraPodFromDefaults() reads autoAspect's schema default (true), so an
|
|
415
|
+
// omitted opts.autoAspect lands the default-correct value; an explicit
|
|
416
|
+
// false overrides it below (D-4: factory one-step opt-out, charter P1).
|
|
417
|
+
...cameraPodFromDefaults(),
|
|
418
|
+
fov: opts.fov,
|
|
419
|
+
aspect: opts.aspect,
|
|
420
|
+
near: opts.near ?? 0.1,
|
|
421
|
+
far: opts.far ?? 100,
|
|
422
|
+
projection: CAMERA_PROJECTION_PERSPECTIVE,
|
|
423
|
+
...opts.autoAspect !== void 0 ? { autoAspect: opts.autoAspect } : {},
|
|
424
|
+
...cameraColorGradingColumns(opts)
|
|
257
425
|
};
|
|
258
426
|
}
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
var SSR_HISTORY_FORMAT = "rgba16float";
|
|
273
|
-
var HISTORY_USAGE = GPU_TEXTURE_USAGE_COPY_SRC | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_STORAGE_BINDING | GPU_TEXTURE_USAGE_RENDER_ATTACHMENT;
|
|
274
|
-
function positiveDimension(value, field) {
|
|
275
|
-
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
276
|
-
throw new RangeError(`${field} must be a positive safe integer.`);
|
|
277
|
-
}
|
|
278
|
-
return value;
|
|
279
|
-
}
|
|
280
|
-
function createResources(device, scope, width, height) {
|
|
281
|
-
const halfWidth = Math.max(1, Math.floor(width / 2));
|
|
282
|
-
const halfHeight = Math.max(1, Math.floor(height / 2));
|
|
283
|
-
const descriptor = {
|
|
284
|
-
label: "ssr-history-rgba16float",
|
|
285
|
-
size: { width: halfWidth, height: halfHeight, depthOrArrayLayers: 1 },
|
|
286
|
-
format: SSR_HISTORY_FORMAT,
|
|
287
|
-
usage: HISTORY_USAGE,
|
|
288
|
-
mipLevelCount: 1,
|
|
289
|
-
sampleCount: 1,
|
|
290
|
-
dimension: "2d",
|
|
291
|
-
viewFormats: void 0,
|
|
292
|
-
textureBindingViewDimension: void 0
|
|
427
|
+
function orthographic(opts) {
|
|
428
|
+
return {
|
|
429
|
+
...cameraPodFromDefaults(),
|
|
430
|
+
fov: 0,
|
|
431
|
+
aspect: 1,
|
|
432
|
+
near: opts.near ?? 0.1,
|
|
433
|
+
far: opts.far ?? 100,
|
|
434
|
+
projection: CAMERA_PROJECTION_ORTHOGRAPHIC,
|
|
435
|
+
left: opts.left,
|
|
436
|
+
right: opts.right,
|
|
437
|
+
bottom: opts.bottom,
|
|
438
|
+
top: opts.top,
|
|
439
|
+
...cameraColorGradingColumns(opts)
|
|
293
440
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
441
|
+
}
|
|
442
|
+
var CloudQualityValue = Object.freeze({
|
|
443
|
+
low: 0,
|
|
444
|
+
medium: 1,
|
|
445
|
+
high: 2
|
|
446
|
+
});
|
|
447
|
+
var CloudLayer = defineComponent("CloudLayer", {
|
|
448
|
+
/** Stable integer seed for the periodic 3D density field. */
|
|
449
|
+
seed: { type: "u32", default: 1337 },
|
|
450
|
+
/** World-space lower edge of the layer, in metres. */
|
|
451
|
+
baseHeight: { type: "f32", default: 120 },
|
|
452
|
+
/** World-space thickness of the layer, in metres. */
|
|
453
|
+
thickness: { type: "f32", default: 80 },
|
|
454
|
+
/** Horizontal noise scale, in cycles per metre. */
|
|
455
|
+
scale: { type: "f32", default: 4e-3 },
|
|
456
|
+
/** Coverage threshold in [0, 1]. */
|
|
457
|
+
coverage: { type: "f32", default: 0.48 },
|
|
458
|
+
/** Extinction multiplier in inverse metres. */
|
|
459
|
+
density: { type: "f32", default: 1 },
|
|
460
|
+
/** World-space wind velocity in metres per second. */
|
|
461
|
+
wind: { type: "array<f32, 3>", default: new Float32Array([8, 0, 2]) },
|
|
462
|
+
/** Quality controls cache resolution and bounded ray steps. */
|
|
463
|
+
quality: { type: "f32", default: CloudQualityValue.medium },
|
|
464
|
+
/** Maximum world-space distance represented by the light-space shadow map. */
|
|
465
|
+
shadowRange: { type: "f32", default: 512 }
|
|
466
|
+
});
|
|
467
|
+
function cloudQualityFromF32(value) {
|
|
468
|
+
if (value === CloudQualityValue.low) return "low";
|
|
469
|
+
if (value === CloudQualityValue.medium) return "medium";
|
|
470
|
+
if (value === CloudQualityValue.high) return "high";
|
|
471
|
+
return void 0;
|
|
472
|
+
}
|
|
473
|
+
var CUBE_CAMERA_UPDATE_ONCE = 0;
|
|
474
|
+
var CUBE_CAMERA_UPDATE_ON_DEMAND = 1;
|
|
475
|
+
var CUBE_CAMERA_UPDATE_CONTINUOUS = 2;
|
|
476
|
+
var CUBE_CAMERA_FACE_ORDER = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
|
|
477
|
+
function cubeCameraUpdateIntentFromF32(value) {
|
|
478
|
+
switch (value) {
|
|
479
|
+
case CUBE_CAMERA_UPDATE_ONCE:
|
|
480
|
+
return "once";
|
|
481
|
+
case CUBE_CAMERA_UPDATE_ON_DEMAND:
|
|
482
|
+
return "on-demand";
|
|
483
|
+
case CUBE_CAMERA_UPDATE_CONTINUOUS:
|
|
484
|
+
return "continuous";
|
|
485
|
+
default:
|
|
486
|
+
throw new RenderIntentInvalidError("CubeCamera", value);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function cubeCameraUpdateIntentToF32(intent) {
|
|
490
|
+
switch (intent) {
|
|
491
|
+
case "once":
|
|
492
|
+
return CUBE_CAMERA_UPDATE_ONCE;
|
|
493
|
+
case "on-demand":
|
|
494
|
+
return CUBE_CAMERA_UPDATE_ON_DEMAND;
|
|
495
|
+
case "continuous":
|
|
496
|
+
return CUBE_CAMERA_UPDATE_CONTINUOUS;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
var CubeCamera = defineComponent("CubeCamera", {
|
|
500
|
+
target: { type: "shared<RenderTarget>", simulationTransient: true },
|
|
501
|
+
near: { type: "f32", default: 0.1 },
|
|
502
|
+
far: { type: "f32", default: 100 },
|
|
503
|
+
updateIntent: { type: "f32", default: CUBE_CAMERA_UPDATE_ONCE },
|
|
504
|
+
requestVersion: { type: "u32", default: 0 },
|
|
505
|
+
faceBudget: { type: "u32", default: 1 }
|
|
506
|
+
});
|
|
507
|
+
var DepthOfFieldQualityValue = Object.freeze({
|
|
508
|
+
low: 0,
|
|
509
|
+
medium: 1,
|
|
510
|
+
high: 2
|
|
511
|
+
});
|
|
512
|
+
var DepthOfFieldSideValue = Object.freeze({
|
|
513
|
+
both: 0,
|
|
514
|
+
near: 1,
|
|
515
|
+
far: 2
|
|
516
|
+
});
|
|
517
|
+
var DepthOfField = defineComponent("DepthOfField", {
|
|
518
|
+
focusDistance: { type: "f32", default: 8 },
|
|
519
|
+
fStop: { type: "f32", default: 2.8 },
|
|
520
|
+
sensorHeight: { type: "f32", default: 0.024 },
|
|
521
|
+
maxRadiusPixels: { type: "f32", default: 16 },
|
|
522
|
+
quality: { type: "f32", default: DepthOfFieldQualityValue.medium },
|
|
523
|
+
blurSide: { type: "f32", default: DepthOfFieldSideValue.both }
|
|
524
|
+
});
|
|
525
|
+
function depthOfFieldQualityFromF32(value) {
|
|
526
|
+
if (value === DepthOfFieldQualityValue.low) return "low";
|
|
527
|
+
if (value === DepthOfFieldQualityValue.medium) return "medium";
|
|
528
|
+
if (value === DepthOfFieldQualityValue.high) return "high";
|
|
529
|
+
return void 0;
|
|
530
|
+
}
|
|
531
|
+
function depthOfFieldSideFromF32(value) {
|
|
532
|
+
if (value === DepthOfFieldSideValue.both) return "both";
|
|
533
|
+
if (value === DepthOfFieldSideValue.near) return "near";
|
|
534
|
+
if (value === DepthOfFieldSideValue.far) return "far";
|
|
535
|
+
return void 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/components/directional-shadow-filter.ts
|
|
539
|
+
var DirectionalShadowFilterValue = Object.freeze({
|
|
540
|
+
pcf1: 1,
|
|
541
|
+
pcf3: 2,
|
|
542
|
+
pcf5: 3,
|
|
543
|
+
pcssMedium: 4,
|
|
544
|
+
pcssHigh: 5
|
|
545
|
+
});
|
|
546
|
+
function directionalShadowQualityFromF32(value, angularRadiusRadians, maxPenumbraTexels) {
|
|
547
|
+
switch (value) {
|
|
548
|
+
case DirectionalShadowFilterValue.pcf1:
|
|
549
|
+
return { kind: "pcf", kernel: 1 };
|
|
550
|
+
case DirectionalShadowFilterValue.pcf3:
|
|
551
|
+
return { kind: "pcf", kernel: 3 };
|
|
552
|
+
case DirectionalShadowFilterValue.pcf5:
|
|
553
|
+
return { kind: "pcf", kernel: 5 };
|
|
554
|
+
case DirectionalShadowFilterValue.pcssMedium:
|
|
555
|
+
return {
|
|
556
|
+
kind: "pcss",
|
|
557
|
+
preset: "medium",
|
|
558
|
+
angularRadiusRadians,
|
|
559
|
+
maxPenumbraTexels
|
|
560
|
+
};
|
|
561
|
+
case DirectionalShadowFilterValue.pcssHigh:
|
|
562
|
+
return {
|
|
563
|
+
kind: "pcss",
|
|
564
|
+
preset: "high",
|
|
565
|
+
angularRadiusRadians,
|
|
566
|
+
maxPenumbraTexels
|
|
567
|
+
};
|
|
568
|
+
default:
|
|
569
|
+
return void 0;
|
|
353
570
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
571
|
+
}
|
|
572
|
+
var DirectionalLight = defineComponent("DirectionalLight", {
|
|
573
|
+
// direction is the ONLY field with no default (D-5): omitting it lands the
|
|
574
|
+
// array layer-3 all-zero [0,0,0], which the renderer owner rejects -- there is no
|
|
575
|
+
// universal default direction, so "default is illegal" forces an explicit
|
|
576
|
+
// non-zero value. color carries an explicit layer-2 default [1,1,1] (white);
|
|
577
|
+
// the array layer-3 fallback is all-zero, so the default MUST be explicit.
|
|
578
|
+
direction: { type: "array<f32, 3>" },
|
|
579
|
+
color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
580
|
+
intensity: { type: "f32", default: 1 },
|
|
581
|
+
// Shadow opt-out gate: defaults to true so zero-config spawns get shadows.
|
|
582
|
+
castShadow: { type: "bool", default: true },
|
|
583
|
+
// 9 shadow fields migrated from DirectionalLightShadow (feat-20260621 M1).
|
|
584
|
+
cascadeCount: { type: "f32", default: 4 },
|
|
585
|
+
splitLambda: { type: "f32", default: 0.75 },
|
|
586
|
+
cascadeBlend: { type: "f32", default: 0.2 },
|
|
587
|
+
mapSize: { type: "f32", default: 2048 },
|
|
588
|
+
depthBias: { type: "f32", default: 1e-5 },
|
|
589
|
+
normalBias: { type: "f32", default: 0.05 },
|
|
590
|
+
// Sole shadow coverage knob (feat replaces nearPlane/farPlane). The PSSM
|
|
591
|
+
// near end derives from the active camera near; shadowDistance is the far
|
|
592
|
+
// reach. Default 200 world units (matches UE-style "dynamic shadow
|
|
593
|
+
// distance"; the old farPlane default was 50).
|
|
594
|
+
shadowDistance: { type: "f32", default: 200 },
|
|
595
|
+
shadowFilter: {
|
|
596
|
+
type: "enum",
|
|
597
|
+
default: DirectionalShadowFilterValue.pcf3,
|
|
598
|
+
labels: DirectionalShadowFilterValue
|
|
599
|
+
},
|
|
600
|
+
shadowAngularRadius: { type: "f32", default: 465e-5 },
|
|
601
|
+
maxPenumbraTexels: { type: "f32", default: 32 }
|
|
602
|
+
});
|
|
603
|
+
var DynamicResolution = defineComponent("DynamicResolution", {
|
|
604
|
+
targetGpuMs: { type: "f32", default: 16.67 },
|
|
605
|
+
minScale: { type: "f32", default: 0.67 },
|
|
606
|
+
maxScale: { type: "f32", default: 1 }
|
|
607
|
+
});
|
|
608
|
+
var DEFAULT_DYNAMIC_RESOLUTION = Object.freeze({
|
|
609
|
+
targetGpuMs: 16.67,
|
|
610
|
+
minScale: 0.67,
|
|
611
|
+
maxScale: 1
|
|
612
|
+
});
|
|
613
|
+
function invalid2(field, value, expected) {
|
|
614
|
+
return new DynamicResolutionInvalidParameterError({ field, value, expected });
|
|
615
|
+
}
|
|
616
|
+
function validateDynamicResolutionParameters(input) {
|
|
617
|
+
const value = input ?? {};
|
|
618
|
+
const targetGpuMs = value.targetGpuMs ?? DEFAULT_DYNAMIC_RESOLUTION.targetGpuMs;
|
|
619
|
+
const minScale = value.minScale ?? DEFAULT_DYNAMIC_RESOLUTION.minScale;
|
|
620
|
+
const maxScale = value.maxScale ?? DEFAULT_DYNAMIC_RESOLUTION.maxScale;
|
|
621
|
+
if (!Number.isFinite(targetGpuMs) || targetGpuMs <= 0) {
|
|
622
|
+
return err(invalid2("targetGpuMs", targetGpuMs, "finite and greater than 0"));
|
|
623
|
+
}
|
|
624
|
+
if (!Number.isFinite(minScale) || minScale < 0.5 || minScale > 1) {
|
|
625
|
+
return err(invalid2("minScale", minScale, "finite and in [0.5, 1.0]"));
|
|
626
|
+
}
|
|
627
|
+
if (!Number.isFinite(maxScale) || maxScale < 0.5 || maxScale > 1) {
|
|
628
|
+
return err(invalid2("maxScale", maxScale, "finite and in [0.5, 1.0]"));
|
|
629
|
+
}
|
|
630
|
+
if (minScale > maxScale) {
|
|
631
|
+
return err(invalid2("minScale", minScale, "less than or equal to maxScale"));
|
|
632
|
+
}
|
|
633
|
+
return ok(Object.freeze({ targetGpuMs, minScale, maxScale }));
|
|
634
|
+
}
|
|
635
|
+
function validateDynamicResolutionCamera(input, antialias) {
|
|
636
|
+
if (input === void 0) return ok(void 0);
|
|
637
|
+
const parameters = validateDynamicResolutionParameters(input);
|
|
638
|
+
if (!parameters.ok) return parameters;
|
|
639
|
+
if (antialias !== "taa") {
|
|
640
|
+
return err(new DynamicResolutionRequiresTaaError({ antialias }));
|
|
641
|
+
}
|
|
642
|
+
return parameters;
|
|
643
|
+
}
|
|
644
|
+
var Fog = defineComponent("Fog", {
|
|
645
|
+
color: { type: "array<f32, 3>", default: new Float32Array([0.5, 0.5, 0.5]) },
|
|
646
|
+
density: { type: "f32", default: 0.01 },
|
|
647
|
+
heightFalloff: { type: "f32", default: 0 },
|
|
648
|
+
maxOpacity: { type: "f32", default: 1 }
|
|
649
|
+
});
|
|
650
|
+
var Instances = defineComponent("Instances", {
|
|
651
|
+
transforms: { type: "array<f32>" }
|
|
652
|
+
});
|
|
653
|
+
var Layer = defineComponent("Layer", {
|
|
654
|
+
value: { type: "i32", default: 0 }
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// src/components/light-helpers.ts
|
|
658
|
+
var RANGE_ZERO_FALLBACK_INV_R2 = 1e8;
|
|
659
|
+
var DEG_TO_RAD = Math.PI / 180;
|
|
660
|
+
var PCSS_RADIUS_MIN = 1e-4;
|
|
661
|
+
var PCSS_RADIUS_MAX = 0.05;
|
|
662
|
+
var PCSS_PENUMBRA_MIN = 1;
|
|
663
|
+
var PCSS_PENUMBRA_MAX = 64;
|
|
664
|
+
function degToCos(deg) {
|
|
665
|
+
return Math.cos(deg * DEG_TO_RAD);
|
|
666
|
+
}
|
|
667
|
+
function computeInvRangeSquared(range) {
|
|
668
|
+
if (range === Number.POSITIVE_INFINITY) return 0;
|
|
669
|
+
if (range === 0) return RANGE_ZERO_FALLBACK_INV_R2;
|
|
670
|
+
return 1 / (range * range);
|
|
671
|
+
}
|
|
672
|
+
function validateDirection(componentName, direction) {
|
|
673
|
+
const dir = direction;
|
|
674
|
+
if (dir === void 0 || (dir[0] ?? 0) === 0 && (dir[1] ?? 0) === 0 && (dir[2] ?? 0) === 0) {
|
|
675
|
+
return new SpawnLightInvalidBoundsError(
|
|
676
|
+
componentName,
|
|
677
|
+
"direction",
|
|
678
|
+
dir === void 0 ? [0, 0, 0] : [dir[0] ?? 0, dir[1] ?? 0, dir[2] ?? 0]
|
|
679
|
+
);
|
|
361
680
|
}
|
|
362
|
-
return
|
|
363
|
-
width,
|
|
364
|
-
height,
|
|
365
|
-
halfWidth,
|
|
366
|
-
halfHeight,
|
|
367
|
-
bytes: halfWidth * halfHeight * 12 * 2,
|
|
368
|
-
descriptor,
|
|
369
|
-
slots,
|
|
370
|
-
paramsBuffer: params.value,
|
|
371
|
-
childScope
|
|
372
|
-
});
|
|
681
|
+
return null;
|
|
373
682
|
}
|
|
374
|
-
function
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
return
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
683
|
+
function validateDirectionalLightData(data) {
|
|
684
|
+
const directionError = validateDirection(
|
|
685
|
+
"DirectionalLight",
|
|
686
|
+
data.direction
|
|
687
|
+
);
|
|
688
|
+
if (directionError !== null) return err(directionError);
|
|
689
|
+
if (data.castShadow === false) return ok(void 0);
|
|
690
|
+
const mapSize = data.mapSize ?? 2048;
|
|
691
|
+
if (mapSize < 1) {
|
|
692
|
+
return err(new ShadowInvalidConfigError("mapSize", mapSize, 1));
|
|
693
|
+
}
|
|
694
|
+
const cascadeCount = data.cascadeCount ?? 4;
|
|
695
|
+
if (cascadeCount < 1 || cascadeCount > 4 || !Number.isInteger(cascadeCount)) {
|
|
696
|
+
return err(new ShadowInvalidConfigError("cascadeCount", cascadeCount, 1, 4));
|
|
697
|
+
}
|
|
698
|
+
const splitLambda = data.splitLambda ?? 0.75;
|
|
699
|
+
if (splitLambda < 0 || splitLambda > 1) {
|
|
700
|
+
return err(new ShadowInvalidConfigError("splitLambda", splitLambda, 0, 1));
|
|
701
|
+
}
|
|
702
|
+
const cascadeBlend = data.cascadeBlend ?? 0.2;
|
|
703
|
+
if (cascadeBlend < 0 || cascadeBlend > 0.5) {
|
|
704
|
+
return err(new ShadowInvalidConfigError("cascadeBlend", cascadeBlend, 0, 0.5));
|
|
705
|
+
}
|
|
706
|
+
const shadowDistance = data.shadowDistance ?? 200;
|
|
707
|
+
if (shadowDistance <= 0) {
|
|
708
|
+
return err(new ShadowInvalidConfigError("shadowDistance", shadowDistance, 0, ">"));
|
|
709
|
+
}
|
|
710
|
+
const shadowFilter = data.shadowFilter ?? DirectionalShadowFilterValue.pcf3;
|
|
711
|
+
const allowedFilters = Object.values(DirectionalShadowFilterValue);
|
|
712
|
+
if (!Number.isFinite(shadowFilter) || !allowedFilters.includes(shadowFilter)) {
|
|
713
|
+
return err(
|
|
714
|
+
new ShadowInvalidConfigError(
|
|
715
|
+
"shadowFilter",
|
|
716
|
+
shadowFilter,
|
|
717
|
+
{ kind: "allowed-values", values: allowedFilters },
|
|
718
|
+
void 0,
|
|
719
|
+
"one of [pcf1, pcf3, pcf5, pcssMedium, pcssHigh]"
|
|
720
|
+
)
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
if (shadowFilter === DirectionalShadowFilterValue.pcssMedium || shadowFilter === DirectionalShadowFilterValue.pcssHigh) {
|
|
724
|
+
const shadowAngularRadius = data.shadowAngularRadius ?? 465e-5;
|
|
725
|
+
if (!Number.isFinite(shadowAngularRadius)) {
|
|
407
726
|
return err(
|
|
408
|
-
|
|
409
|
-
"
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
)
|
|
727
|
+
new ShadowInvalidConfigError("shadowAngularRadius", shadowAngularRadius, {
|
|
728
|
+
kind: "range",
|
|
729
|
+
min: PCSS_RADIUS_MIN,
|
|
730
|
+
max: PCSS_RADIUS_MAX
|
|
731
|
+
})
|
|
413
732
|
);
|
|
414
733
|
}
|
|
415
|
-
if (
|
|
734
|
+
if (shadowAngularRadius < Math.fround(PCSS_RADIUS_MIN) || shadowAngularRadius > Math.fround(PCSS_RADIUS_MAX)) {
|
|
416
735
|
return err(
|
|
417
|
-
|
|
418
|
-
"
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
)
|
|
736
|
+
new ShadowInvalidConfigError("shadowAngularRadius", shadowAngularRadius, {
|
|
737
|
+
kind: "range",
|
|
738
|
+
min: PCSS_RADIUS_MIN,
|
|
739
|
+
max: PCSS_RADIUS_MAX
|
|
740
|
+
})
|
|
422
741
|
);
|
|
423
742
|
}
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
readSlot: this.valid ? this.readIndex : null,
|
|
427
|
-
writeSlot,
|
|
428
|
-
historyValid: this.valid && this.resetReason === void 0
|
|
429
|
-
});
|
|
430
|
-
this.activeCandidate = candidate;
|
|
431
|
-
this.state = this.valid ? "stable" : this.state === "aborted" ? "aborted" : "first-frame";
|
|
432
|
-
this.lastFailure = void 0;
|
|
433
|
-
return ok(candidate);
|
|
434
|
-
}
|
|
435
|
-
commitFrame(candidate) {
|
|
436
|
-
if (this.activeCandidate !== candidate) {
|
|
743
|
+
const maxPenumbraTexels = data.maxPenumbraTexels ?? 32;
|
|
744
|
+
if (!Number.isFinite(maxPenumbraTexels) || !Number.isInteger(maxPenumbraTexels) || maxPenumbraTexels < PCSS_PENUMBRA_MIN || maxPenumbraTexels > PCSS_PENUMBRA_MAX) {
|
|
437
745
|
return err(
|
|
438
|
-
|
|
439
|
-
"
|
|
440
|
-
|
|
441
|
-
|
|
746
|
+
new ShadowInvalidConfigError(
|
|
747
|
+
"maxPenumbraTexels",
|
|
748
|
+
maxPenumbraTexels,
|
|
749
|
+
{ kind: "range", min: PCSS_PENUMBRA_MIN, max: PCSS_PENUMBRA_MAX },
|
|
750
|
+
void 0,
|
|
751
|
+
"a finite integer in [1, 64]"
|
|
442
752
|
)
|
|
443
753
|
);
|
|
444
754
|
}
|
|
445
|
-
this.readIndex = candidate.writeSlot;
|
|
446
|
-
this.valid = true;
|
|
447
|
-
this.activeCandidate = void 0;
|
|
448
|
-
this.state = "stable";
|
|
449
|
-
this.resetReason = void 0;
|
|
450
|
-
this.lastFailure = void 0;
|
|
451
|
-
return ok(void 0);
|
|
452
755
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
756
|
+
return ok(void 0);
|
|
757
|
+
}
|
|
758
|
+
function validateSpotLightData(data) {
|
|
759
|
+
const directionError = validateDirection(
|
|
760
|
+
"SpotLight",
|
|
761
|
+
data.direction
|
|
762
|
+
);
|
|
763
|
+
if (directionError !== null) return err(directionError);
|
|
764
|
+
if (data.castShadow === false) return ok(void 0);
|
|
765
|
+
const range = data.range ?? 10;
|
|
766
|
+
if (typeof range !== "number" || Number.isNaN(range) || range < 0) {
|
|
767
|
+
return err(new SpawnLightInvalidBoundsError("SpotLight", "range", range));
|
|
768
|
+
}
|
|
769
|
+
const innerConeDeg = data.innerConeDeg ?? 0;
|
|
770
|
+
const outerConeDeg = data.outerConeDeg ?? 45;
|
|
771
|
+
if (outerConeDeg > 90) {
|
|
772
|
+
return err(new SpawnLightInvalidBoundsError("SpotLight", "outerNinety", outerConeDeg));
|
|
773
|
+
}
|
|
774
|
+
if (outerConeDeg <= innerConeDeg) {
|
|
775
|
+
return err(new SpawnLightInvalidBoundsError("SpotLight", "innerOuter", outerConeDeg));
|
|
776
|
+
}
|
|
777
|
+
const mapSize = data.mapSize ?? 2048;
|
|
778
|
+
if (mapSize < 1) return err(new ShadowInvalidConfigError("mapSize", mapSize, 1));
|
|
779
|
+
const nearPlane = data.nearPlane;
|
|
780
|
+
const farPlane = data.farPlane;
|
|
781
|
+
if (nearPlane !== void 0 && farPlane !== void 0 && farPlane <= nearPlane) {
|
|
782
|
+
return err(new ShadowInvalidConfigError("farPlane", farPlane, nearPlane));
|
|
783
|
+
}
|
|
784
|
+
const pcfKernelSize = data.pcfKernelSize ?? 3;
|
|
785
|
+
if (pcfKernelSize < 1 || pcfKernelSize % 2 === 0) {
|
|
786
|
+
return err(
|
|
787
|
+
new ShadowInvalidConfigError(
|
|
788
|
+
"pcfKernelSize",
|
|
789
|
+
pcfKernelSize,
|
|
790
|
+
{ kind: "lower-bound", operator: ">=", value: 1 },
|
|
791
|
+
void 0,
|
|
792
|
+
"an odd integer >= 1"
|
|
793
|
+
)
|
|
474
794
|
);
|
|
475
|
-
if (!next.ok) return next;
|
|
476
|
-
if (this.activeResources !== void 0) this.retiringResources.add(this.activeResources);
|
|
477
|
-
this.activeResources = next.value;
|
|
478
|
-
this.activeCandidate = void 0;
|
|
479
|
-
this.valid = false;
|
|
480
|
-
this.readIndex = 0;
|
|
481
|
-
this.resetReason = "resize";
|
|
482
|
-
this.lastFailure = void 0;
|
|
483
|
-
this.resetCount += 1;
|
|
484
|
-
this.state = "reset";
|
|
485
|
-
return ok(void 0);
|
|
486
795
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
for (const resources of pending) resources.childScope.beginRetire();
|
|
498
|
-
if (pending.length === 0) return;
|
|
499
|
-
this.retirementFencePending = true;
|
|
500
|
-
const finish = () => {
|
|
501
|
-
this.retirementFencePending = false;
|
|
502
|
-
for (const resources of pending) {
|
|
503
|
-
this.retiringResources.delete(resources);
|
|
504
|
-
resources.childScope.retire();
|
|
505
|
-
if (this.activeResources === resources) this.activeResources = void 0;
|
|
506
|
-
}
|
|
507
|
-
if (this.activeResources === void 0 && this.retiringResources.size === 0) {
|
|
508
|
-
this.state = "retired";
|
|
509
|
-
}
|
|
510
|
-
if (this.retiringResources.size > 0) this.retireAfterFence(queue, onFailure);
|
|
511
|
-
};
|
|
512
|
-
void queue.onSubmittedWorkDone().then(finish, (cause) => {
|
|
513
|
-
onFailure(cause);
|
|
514
|
-
finish();
|
|
515
|
-
});
|
|
796
|
+
const shadowIntensity = data.shadowIntensity ?? 1;
|
|
797
|
+
if (!Number.isFinite(shadowIntensity) || shadowIntensity < 0 || shadowIntensity > 1) {
|
|
798
|
+
return err(new ShadowInvalidConfigError("shadowIntensity", shadowIntensity, 0, 1));
|
|
799
|
+
}
|
|
800
|
+
return ok(void 0);
|
|
801
|
+
}
|
|
802
|
+
function validatePointLightData(data) {
|
|
803
|
+
const range = data.range ?? 10;
|
|
804
|
+
if (typeof range !== "number" || Number.isNaN(range) || range < 0) {
|
|
805
|
+
return err(new SpawnLightInvalidBoundsError("PointLight", "range", range));
|
|
516
806
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
807
|
+
return ok(void 0);
|
|
808
|
+
}
|
|
809
|
+
function validatePointLightShadowData(data) {
|
|
810
|
+
const mapSize = data.mapSize ?? 512;
|
|
811
|
+
if (mapSize < 1) return err(new ShadowInvalidConfigError("mapSize", mapSize, 1));
|
|
812
|
+
const nearPlane = data.nearPlane ?? 0.1;
|
|
813
|
+
const farPlane = data.farPlane ?? 25;
|
|
814
|
+
if (farPlane <= nearPlane) {
|
|
815
|
+
return err(new ShadowInvalidConfigError("farPlane", farPlane, nearPlane));
|
|
816
|
+
}
|
|
817
|
+
const pcfKernelSize = data.pcfKernelSize ?? 3;
|
|
818
|
+
if (pcfKernelSize < 1 || pcfKernelSize % 2 === 0) {
|
|
819
|
+
return err(
|
|
820
|
+
new ShadowInvalidConfigError(
|
|
821
|
+
"pcfKernelSize",
|
|
822
|
+
pcfKernelSize,
|
|
823
|
+
{ kind: "lower-bound", operator: ">=", value: 1 },
|
|
824
|
+
void 0,
|
|
825
|
+
"an odd integer >= 1"
|
|
826
|
+
)
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
return ok(void 0);
|
|
830
|
+
}
|
|
831
|
+
function validateRectAreaLightData(data) {
|
|
832
|
+
const intensity = data.intensity ?? 1;
|
|
833
|
+
if (typeof intensity !== "number" || !Number.isFinite(intensity) || intensity < 0) {
|
|
834
|
+
return err(new SpawnLightInvalidBoundsError("RectAreaLight", "intensity", intensity));
|
|
835
|
+
}
|
|
836
|
+
const color = data.color ?? [1, 1, 1];
|
|
837
|
+
if (color.length !== 3 || Array.from(color).some(
|
|
838
|
+
(value) => typeof value !== "number" || !Number.isFinite(value) || value < 0
|
|
839
|
+
)) {
|
|
840
|
+
return err(new SpawnLightInvalidBoundsError("RectAreaLight", "color", Array.from(color)));
|
|
841
|
+
}
|
|
842
|
+
for (const field of ["width", "height", "range"]) {
|
|
843
|
+
const value = data[field] ?? (field === "range" ? 10 : 1);
|
|
844
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
845
|
+
return err(new SpawnLightInvalidBoundsError("RectAreaLight", field, value));
|
|
523
846
|
}
|
|
524
|
-
for (const resources of this.retiringResources) resources.childScope.retire();
|
|
525
|
-
this.retiringResources.clear();
|
|
526
|
-
this.valid = false;
|
|
527
|
-
this.state = "retired";
|
|
528
|
-
}
|
|
529
|
-
dispose() {
|
|
530
|
-
if (this.state === "disposed") return;
|
|
531
|
-
this.retire();
|
|
532
|
-
this.state = "disposed";
|
|
533
|
-
}
|
|
534
|
-
inspect() {
|
|
535
|
-
const active = this.activeResources;
|
|
536
|
-
return Object.freeze({
|
|
537
|
-
state: this.state,
|
|
538
|
-
width: active?.width ?? 0,
|
|
539
|
-
height: active?.height ?? 0,
|
|
540
|
-
halfWidth: active?.halfWidth ?? 0,
|
|
541
|
-
halfHeight: active?.halfHeight ?? 0,
|
|
542
|
-
format: SSR_HISTORY_FORMAT,
|
|
543
|
-
historyCount: active === void 0 ? 0 : 2,
|
|
544
|
-
historyValid: this.valid,
|
|
545
|
-
readSlot: active === void 0 ? null : this.valid ? this.readIndex : null,
|
|
546
|
-
writeSlot: active === void 0 ? 0 : this.valid ? this.readIndex === 0 ? 1 : 0 : 0,
|
|
547
|
-
activeBytes: active?.bytes ?? 0,
|
|
548
|
-
candidateBytes: 0,
|
|
549
|
-
retiringBytes: [...this.retiringResources].filter((entry) => entry !== active).reduce((sum, entry) => sum + entry.bytes, 0),
|
|
550
|
-
resetCount: this.resetCount,
|
|
551
|
-
resetReason: this.resetReason,
|
|
552
|
-
lastFailure: this.lastFailure
|
|
553
|
-
});
|
|
554
847
|
}
|
|
555
|
-
|
|
556
|
-
function createSsrHistoryOwner(input) {
|
|
557
|
-
return SsrHistoryOwner.create(input);
|
|
848
|
+
return ok(void 0);
|
|
558
849
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
identity.attachmentId,
|
|
569
|
-
identity.deviceGeneration,
|
|
570
|
-
identity.worldGeneration,
|
|
571
|
-
identity.primitiveSlot,
|
|
572
|
-
identity.slotGeneration
|
|
573
|
-
].join("|");
|
|
574
|
-
}
|
|
575
|
-
var OcclusionQueryPool = class {
|
|
576
|
-
pages = Array.from(
|
|
577
|
-
{ length: OCCLUSION_QUERY_PAGE_COUNT },
|
|
578
|
-
(_, index) => ({ index, nextQueryIndex: 0, pendingCount: 0, inFlightCount: 0 })
|
|
579
|
-
);
|
|
580
|
-
pending = /* @__PURE__ */ new Map();
|
|
581
|
-
tickets = /* @__PURE__ */ new Map();
|
|
582
|
-
nextReservationId = 1;
|
|
583
|
-
disposed = false;
|
|
584
|
-
reserve(identity) {
|
|
585
|
-
if (this.disposed) return void 0;
|
|
586
|
-
const page = this.pages.find(
|
|
587
|
-
(candidate) => candidate.inFlightCount === 0 && candidate.nextQueryIndex < OCCLUSION_QUERY_PAGE_INDEX_LIMIT
|
|
850
|
+
function validateLightProbeData(data) {
|
|
851
|
+
const irradiance = data.irradiance;
|
|
852
|
+
if (irradiance === void 0 || irradiance.length !== 27 || Array.from(irradiance).some((value) => typeof value !== "number" || !Number.isFinite(value))) {
|
|
853
|
+
return err(
|
|
854
|
+
new SpawnLightInvalidBoundsError(
|
|
855
|
+
"LightProbe",
|
|
856
|
+
"irradiance",
|
|
857
|
+
irradiance === void 0 ? [] : Array.from(irradiance)
|
|
858
|
+
)
|
|
588
859
|
);
|
|
589
|
-
|
|
590
|
-
|
|
860
|
+
}
|
|
861
|
+
const radius = data.radius ?? R_MIN;
|
|
862
|
+
if (typeof radius !== "number" || !Number.isFinite(radius) || radius < R_MIN) {
|
|
863
|
+
return err(new SpawnLightInvalidBoundsError("LightProbe", "radius", radius));
|
|
864
|
+
}
|
|
865
|
+
return ok(void 0);
|
|
866
|
+
}
|
|
867
|
+
var LightProbe = defineComponent("LightProbe", {
|
|
868
|
+
irradiance: { type: "array<f32, 27>" },
|
|
869
|
+
radius: { type: "f32", default: R_MIN }
|
|
870
|
+
});
|
|
871
|
+
var Lines = defineComponent(
|
|
872
|
+
"Lines",
|
|
873
|
+
{
|
|
874
|
+
widthPx: { type: "f32", default: 1 }
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
meta: {
|
|
878
|
+
quickStart: "Attach Lines to render line-list MeshAsset pairs in screen pixels.",
|
|
879
|
+
diagnostics: "Inspect widthPx at the points-lines admission boundary.",
|
|
880
|
+
recovery: "Use points-lines-invalid-style for non-finite or non-positive widthPx values.",
|
|
881
|
+
boundaries: "Lines owns raster width only; MeshFilter and MeshRenderer own geometry and material."
|
|
591
882
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
var MeshFilter = defineComponent("MeshFilter", {
|
|
886
|
+
assetHandle: { type: "shared<MeshAsset>" }
|
|
887
|
+
});
|
|
888
|
+
var MeshRenderer = defineComponent("MeshRenderer", {
|
|
889
|
+
materials: { type: "array<shared<MaterialAsset>>", default: [] }
|
|
890
|
+
});
|
|
891
|
+
var MotionBlur = defineComponent("MotionBlur", {
|
|
892
|
+
shutterAngle: { type: "f32", default: 180 },
|
|
893
|
+
maxRadiusPixels: { type: "f32", default: 32 },
|
|
894
|
+
sampleCount: { type: "f32", default: 8 },
|
|
895
|
+
targetFps: { type: "f32", default: 60 }
|
|
896
|
+
});
|
|
897
|
+
var PointLight = defineComponent("PointLight", {
|
|
898
|
+
// color carries an explicit layer-2 default [1,1,1] (white); the array
|
|
899
|
+
// layer-3 fallback is all-zero, so the default MUST be explicit (D-5).
|
|
900
|
+
color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
901
|
+
intensity: { type: "f32", default: 1 },
|
|
902
|
+
range: { type: "f32", default: 10 }
|
|
903
|
+
});
|
|
904
|
+
var PointLightShadow = defineComponent("PointLightShadow", {
|
|
905
|
+
mapSize: { type: "f32", default: 512 },
|
|
906
|
+
depthBias: { type: "f32", default: 5e-3 },
|
|
907
|
+
normalBias: { type: "f32", default: 0.05 },
|
|
908
|
+
nearPlane: { type: "f32", default: 0.1 },
|
|
909
|
+
farPlane: { type: "f32", default: 25 },
|
|
910
|
+
pcfKernelSize: { type: "f32", default: 3 }
|
|
911
|
+
});
|
|
912
|
+
var PointShapeValue = Object.freeze({
|
|
913
|
+
square: 0,
|
|
914
|
+
circle: 1
|
|
915
|
+
});
|
|
916
|
+
function pointShapeFromU32(value) {
|
|
917
|
+
switch (value) {
|
|
918
|
+
case PointShapeValue.square:
|
|
919
|
+
return "square";
|
|
920
|
+
case PointShapeValue.circle:
|
|
921
|
+
return "circle";
|
|
922
|
+
default:
|
|
606
923
|
return void 0;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
var Points = defineComponent(
|
|
927
|
+
"Points",
|
|
928
|
+
{
|
|
929
|
+
sizePx: { type: "f32", default: 4 },
|
|
930
|
+
shape: { type: "enum", default: PointShapeValue.square, labels: PointShapeValue }
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
meta: {
|
|
934
|
+
quickStart: "Attach Points to render point-list MeshAsset vertices in screen pixels.",
|
|
935
|
+
diagnostics: "Inspect sizePx and decode shape with pointShapeFromU32.",
|
|
936
|
+
recovery: "Use points-lines-invalid-style for non-finite or non-positive sizePx values.",
|
|
937
|
+
boundaries: "Points owns raster size and shape only; MeshFilter and MeshRenderer own geometry and material."
|
|
607
938
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
939
|
+
}
|
|
940
|
+
);
|
|
941
|
+
var schema = {
|
|
942
|
+
shader: "string",
|
|
943
|
+
data: "buffer"
|
|
944
|
+
};
|
|
945
|
+
var PostProcessParams = defineComponent("PostProcessParams", schema);
|
|
946
|
+
var RectAreaLight = defineComponent("RectAreaLight", {
|
|
947
|
+
color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
948
|
+
intensity: { type: "f32", default: 1 },
|
|
949
|
+
width: { type: "f32", default: 1 },
|
|
950
|
+
height: { type: "f32", default: 1 },
|
|
951
|
+
range: { type: "f32", default: 10 }
|
|
952
|
+
});
|
|
953
|
+
var REFLECTION_PROBE_UPDATE_ONCE = 0;
|
|
954
|
+
var REFLECTION_PROBE_UPDATE_ON_CHANGE = 1;
|
|
955
|
+
var REFLECTION_PROBE_UPDATE_CONTINUOUS = 2;
|
|
956
|
+
function reflectionProbeUpdateIntentFromF32(value) {
|
|
957
|
+
switch (value) {
|
|
958
|
+
case REFLECTION_PROBE_UPDATE_ONCE:
|
|
959
|
+
return "once";
|
|
960
|
+
case REFLECTION_PROBE_UPDATE_ON_CHANGE:
|
|
961
|
+
return "on-change";
|
|
962
|
+
case REFLECTION_PROBE_UPDATE_CONTINUOUS:
|
|
963
|
+
return "continuous";
|
|
964
|
+
default:
|
|
965
|
+
throw new RenderIntentInvalidError("ReflectionProbe", value);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function reflectionProbeUpdateIntentToF32(intent) {
|
|
969
|
+
switch (intent) {
|
|
970
|
+
case "once":
|
|
971
|
+
return REFLECTION_PROBE_UPDATE_ONCE;
|
|
972
|
+
case "on-change":
|
|
973
|
+
return REFLECTION_PROBE_UPDATE_ON_CHANGE;
|
|
974
|
+
case "continuous":
|
|
975
|
+
return REFLECTION_PROBE_UPDATE_CONTINUOUS;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
var ReflectionProbe = defineComponent("ReflectionProbe", {
|
|
979
|
+
halfExtents: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
980
|
+
priority: { type: "f32", default: 0 },
|
|
981
|
+
boxProjection: { type: "bool", default: false },
|
|
982
|
+
intensity: { type: "f32", default: 1 },
|
|
983
|
+
resolution: { type: "u32", default: 256 },
|
|
984
|
+
updateIntent: { type: "f32", default: REFLECTION_PROBE_UPDATE_ONCE },
|
|
985
|
+
invalidationVersion: { type: "u32", default: 0 }
|
|
986
|
+
});
|
|
987
|
+
var SceneInstance = defineComponent(
|
|
988
|
+
"SceneInstance",
|
|
989
|
+
{
|
|
990
|
+
source: { type: "shared<SceneAsset>" },
|
|
991
|
+
mapping: { type: "array<entity>" },
|
|
992
|
+
// The unique slot is the ECS storage seam for the instance's structured
|
|
993
|
+
// runtime payload; keep the nested semantic visible to schema consumers.
|
|
994
|
+
state: { type: "unique<SceneInstanceState>", shape: "nested" }
|
|
995
|
+
},
|
|
996
|
+
{ transient: true }
|
|
997
|
+
);
|
|
998
|
+
var ScreenSpaceReflection = defineComponent("ScreenSpaceReflection", {
|
|
999
|
+
maxDistance: { type: "f32", default: 40 },
|
|
1000
|
+
thickness: { type: "f32", default: 0.2 },
|
|
1001
|
+
maxRoughness: { type: "f32", default: 0.6 }
|
|
1002
|
+
});
|
|
1003
|
+
var SKYBOX_MODE_CUBEMAP = 0;
|
|
1004
|
+
var SkyboxBackground = defineComponent("SkyboxBackground", {
|
|
1005
|
+
// The GPU skybox projection is render-owned presentation state; restore
|
|
1006
|
+
// keeps the portable mode/rotation controls and lets the owner re-resolve
|
|
1007
|
+
// the environment asset on the target world.
|
|
1008
|
+
equirect: { type: "shared<EquirectAsset>", simulationTransient: true },
|
|
1009
|
+
mode: { type: "f32", default: SKYBOX_MODE_CUBEMAP },
|
|
1010
|
+
rotation: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) }
|
|
1011
|
+
});
|
|
1012
|
+
var Skylight = defineComponent("Skylight", {
|
|
1013
|
+
// The GPU equirect-to-cubemap projection is render-owned presentation
|
|
1014
|
+
// state. Record/restore preserves the portable lighting controls while the
|
|
1015
|
+
// render owner resolves this asset again on the target world.
|
|
1016
|
+
equirect: { type: "shared<EquirectAsset>", simulationTransient: true },
|
|
1017
|
+
// color carries an explicit layer-2 default [1,1,1] (white); the array
|
|
1018
|
+
// layer-3 fallback is all-zero, so the default MUST be explicit (D-5).
|
|
1019
|
+
color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
1020
|
+
intensity: { type: "f32", default: 1 },
|
|
1021
|
+
rotation: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) }
|
|
1022
|
+
});
|
|
1023
|
+
var SortKey = defineComponent("SortKey", {
|
|
1024
|
+
value: { type: "f32", default: 0 }
|
|
1025
|
+
});
|
|
1026
|
+
var SpotLight = defineComponent("SpotLight", {
|
|
1027
|
+
// direction has no default (D-5): omitting it lands the array layer-3
|
|
1028
|
+
// all-zero, which validate() rejects. color carries an explicit layer-2
|
|
1029
|
+
// default [1,1,1] (white); the array layer-3 fallback is all-zero.
|
|
1030
|
+
direction: { type: "array<f32, 3>" },
|
|
1031
|
+
color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
1032
|
+
intensity: { type: "f32", default: 1 },
|
|
1033
|
+
range: { type: "f32", default: 10 },
|
|
1034
|
+
innerConeDeg: { type: "f32", default: 0 },
|
|
1035
|
+
outerConeDeg: { type: "f32", default: 45 },
|
|
1036
|
+
iesProfile: { type: "shared<IesProfileAsset>" },
|
|
1037
|
+
cookie: { type: "shared<TextureAsset>" },
|
|
1038
|
+
rollDeg: { type: "f32", default: 0 },
|
|
1039
|
+
// Shadow opt-out gate: defaults to true so zero-config spawns cast shadows.
|
|
1040
|
+
castShadow: { type: "bool", default: true },
|
|
1041
|
+
// 7 shadow fields aligned with the shared light shadow vocabulary.
|
|
1042
|
+
mapSize: { type: "f32", default: 2048 },
|
|
1043
|
+
depthBias: { type: "f32", default: 5e-3 },
|
|
1044
|
+
normalBias: { type: "f32", default: 0.05 },
|
|
1045
|
+
nearPlane: { type: "f32", default: 0.1 },
|
|
1046
|
+
farPlane: { type: "f32", default: 50 },
|
|
1047
|
+
pcfKernelSize: { type: "f32", default: 3 },
|
|
1048
|
+
// Three's SpotLight.shadow.intensity is the fraction of visibility applied
|
|
1049
|
+
// to a shadowed contribution. Keep it in the author-facing [0,1] domain;
|
|
1050
|
+
// extract/record transport this one fact to surface and volume consumers.
|
|
1051
|
+
shadowIntensity: { type: "f32", default: 1 },
|
|
1052
|
+
// Shared TextureAsset identity; publication and residency remain owned by
|
|
1053
|
+
// the existing asset and GPU resource owners.
|
|
1054
|
+
projector: { type: "shared<TextureAsset>", simulationTransient: true }
|
|
1055
|
+
});
|
|
1056
|
+
var VisibilityStateValue = Object.freeze({
|
|
1057
|
+
inherited: 0,
|
|
1058
|
+
hidden: 1,
|
|
1059
|
+
visible: 2
|
|
1060
|
+
});
|
|
1061
|
+
function visibilityStateFromU32(value) {
|
|
1062
|
+
switch (value) {
|
|
1063
|
+
case VisibilityStateValue.inherited:
|
|
1064
|
+
return "inherited";
|
|
1065
|
+
case VisibilityStateValue.hidden:
|
|
1066
|
+
return "hidden";
|
|
1067
|
+
case VisibilityStateValue.visible:
|
|
1068
|
+
return "visible";
|
|
1069
|
+
default:
|
|
1070
|
+
return void 0;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
var Visibility = defineComponent(
|
|
1074
|
+
"Visibility",
|
|
1075
|
+
{
|
|
1076
|
+
state: {
|
|
1077
|
+
type: "enum",
|
|
1078
|
+
default: VisibilityStateValue.inherited,
|
|
1079
|
+
labels: VisibilityStateValue
|
|
614
1080
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
if (pending === void 0) return false;
|
|
623
|
-
this.pending.delete(reservation.reservationId);
|
|
624
|
-
const page = this.pages[pending.pageIndex];
|
|
625
|
-
if (page !== void 0) {
|
|
626
|
-
page.pendingCount = Math.max(0, page.pendingCount - 1);
|
|
627
|
-
this.releasePage(pending.pageIndex);
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
meta: {
|
|
1084
|
+
quickStart: "Attach Visibility to author render participation intent.",
|
|
1085
|
+
diagnostics: "Compare the ECS state with resolveVisibility and renderer.visibilityStats.",
|
|
1086
|
+
recovery: "Use structured ECS write errors and repair the owning scene relation.",
|
|
1087
|
+
boundaries: "Visibility does not own camera, picking, lifecycle, assets, or VFX shadow policy."
|
|
628
1088
|
}
|
|
629
|
-
return true;
|
|
630
1089
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
1090
|
+
);
|
|
1091
|
+
var COMPUTED_UV_BOUNDARY_EPSILON = Number.EPSILON * 16;
|
|
1092
|
+
function freezeBarrelDistortionMapping(mapping) {
|
|
1093
|
+
const camera = mapping.camera;
|
|
1094
|
+
return Object.freeze({
|
|
1095
|
+
...mapping,
|
|
1096
|
+
...camera === void 0 ? {} : {
|
|
1097
|
+
camera: Object.freeze({
|
|
1098
|
+
projection: camera.projection,
|
|
1099
|
+
far: camera.far,
|
|
1100
|
+
viewMatrix: Object.freeze(Array.from(camera.viewMatrix)),
|
|
1101
|
+
projectionMatrix: Object.freeze(Array.from(camera.projectionMatrix))
|
|
1102
|
+
})
|
|
638
1103
|
}
|
|
639
|
-
|
|
640
|
-
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
function attachBarrelDistortionCameraFrame(mapping, camera) {
|
|
1107
|
+
return freezeBarrelDistortionMapping({
|
|
1108
|
+
...mapping,
|
|
1109
|
+
camera: {
|
|
1110
|
+
projection: camera.projection,
|
|
1111
|
+
far: camera.far,
|
|
1112
|
+
viewMatrix: camera.viewMatrix,
|
|
1113
|
+
projectionMatrix: camera.projectionMatrix
|
|
641
1114
|
}
|
|
642
|
-
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
function invalidExtent(field, value) {
|
|
1118
|
+
return new BarrelDistortionInvalidParameterError({
|
|
1119
|
+
field,
|
|
1120
|
+
value,
|
|
1121
|
+
expected: "finite and greater than 0"
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
function createBarrelDistortionMapping(width, height, input) {
|
|
1125
|
+
if (!Number.isFinite(width) || width <= 0) return err(invalidExtent("width", width));
|
|
1126
|
+
if (!Number.isFinite(height) || height <= 0) return err(invalidExtent("height", height));
|
|
1127
|
+
const parameters = validateBarrelDistortionParameters(input);
|
|
1128
|
+
if (!parameters.ok) return parameters;
|
|
1129
|
+
const { strength, centerX, centerY } = parameters.value;
|
|
1130
|
+
const aspect = width / height;
|
|
1131
|
+
const radiusSquared = 4 * (aspect * aspect * Math.max(centerX, 1 - centerX) ** 2 + Math.max(centerY, 1 - centerY) ** 2);
|
|
1132
|
+
return ok(
|
|
1133
|
+
Object.freeze({
|
|
1134
|
+
width,
|
|
1135
|
+
height,
|
|
1136
|
+
aspect,
|
|
1137
|
+
strength,
|
|
1138
|
+
centerX,
|
|
1139
|
+
centerY,
|
|
1140
|
+
radiusSquared
|
|
1141
|
+
})
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
function displayToSceneScale(mapping, ux, uy) {
|
|
1145
|
+
const px = 2 * mapping.aspect * (ux - mapping.centerX);
|
|
1146
|
+
const py = 2 * (uy - mapping.centerY);
|
|
1147
|
+
const t = (px * px + py * py) / mapping.radiusSquared;
|
|
1148
|
+
return (1 - mapping.strength) / (1 - mapping.strength * t);
|
|
1149
|
+
}
|
|
1150
|
+
function normalizeComputedUv(out, x, y) {
|
|
1151
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
|
|
1152
|
+
if (x < -COMPUTED_UV_BOUNDARY_EPSILON || x > 1 + COMPUTED_UV_BOUNDARY_EPSILON || y < -COMPUTED_UV_BOUNDARY_EPSILON || y > 1 + COMPUTED_UV_BOUNDARY_EPSILON) {
|
|
1153
|
+
return false;
|
|
643
1154
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1155
|
+
out.x = Math.min(1, Math.max(0, x));
|
|
1156
|
+
out.y = Math.min(1, Math.max(0, y));
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
function mapDisplayUvToSceneUv(out, mapping, ux, uy) {
|
|
1160
|
+
if (!Number.isFinite(ux) || !Number.isFinite(uy) || ux < 0 || ux > 1 || uy < 0 || uy > 1)
|
|
1161
|
+
return false;
|
|
1162
|
+
if (mapping.strength === 0) {
|
|
1163
|
+
out.x = ux;
|
|
1164
|
+
out.y = uy;
|
|
1165
|
+
return ux >= 0 && ux <= 1 && uy >= 0 && uy <= 1;
|
|
1166
|
+
}
|
|
1167
|
+
const scale = displayToSceneScale(mapping, ux, uy);
|
|
1168
|
+
return normalizeComputedUv(
|
|
1169
|
+
out,
|
|
1170
|
+
mapping.centerX + (ux - mapping.centerX) * scale,
|
|
1171
|
+
mapping.centerY + (uy - mapping.centerY) * scale
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
function mapSceneUvToDisplayUv(out, mapping, ux, uy) {
|
|
1175
|
+
if (!Number.isFinite(ux) || !Number.isFinite(uy) || ux < 0 || ux > 1 || uy < 0 || uy > 1)
|
|
1176
|
+
return false;
|
|
1177
|
+
if (mapping.strength === 0) {
|
|
1178
|
+
out.x = ux;
|
|
1179
|
+
out.y = uy;
|
|
1180
|
+
return ux >= 0 && ux <= 1 && uy >= 0 && uy <= 1;
|
|
1181
|
+
}
|
|
1182
|
+
const px = 2 * mapping.aspect * (ux - mapping.centerX);
|
|
1183
|
+
const py = 2 * (uy - mapping.centerY);
|
|
1184
|
+
const sourceRadius = Math.hypot(px, py);
|
|
1185
|
+
if (sourceRadius === 0) {
|
|
1186
|
+
out.x = mapping.centerX;
|
|
1187
|
+
out.y = mapping.centerY;
|
|
1188
|
+
return true;
|
|
656
1189
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
1190
|
+
const discriminant = (1 - mapping.strength) ** 2 + 4 * mapping.strength * sourceRadius * sourceRadius / mapping.radiusSquared;
|
|
1191
|
+
const displayRadius = 2 * sourceRadius / (1 - mapping.strength + Math.sqrt(Math.max(0, discriminant)));
|
|
1192
|
+
const scale = displayRadius / sourceRadius;
|
|
1193
|
+
return normalizeComputedUv(
|
|
1194
|
+
out,
|
|
1195
|
+
mapping.centerX + (ux - mapping.centerX) * scale,
|
|
1196
|
+
mapping.centerY + (uy - mapping.centerY) * scale
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
function validPixel(point) {
|
|
1200
|
+
return Number.isFinite(point.x) && Number.isFinite(point.y);
|
|
1201
|
+
}
|
|
1202
|
+
function mapDisplayToScene(out, mapping, displayX, displayY) {
|
|
1203
|
+
if (!Number.isFinite(displayX) || !Number.isFinite(displayY)) return false;
|
|
1204
|
+
if (displayX < 0 || displayX > mapping.width || displayY < 0 || displayY > mapping.height) {
|
|
1205
|
+
return false;
|
|
665
1206
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
page.nextQueryIndex = 0;
|
|
670
|
-
}
|
|
1207
|
+
const scene = { x: 0, y: 0 };
|
|
1208
|
+
if (!mapDisplayUvToSceneUv(scene, mapping, displayX / mapping.width, displayY / mapping.height)) {
|
|
1209
|
+
return false;
|
|
671
1210
|
}
|
|
672
|
-
|
|
1211
|
+
out.x = scene.x * mapping.width;
|
|
1212
|
+
out.y = scene.y * mapping.height;
|
|
1213
|
+
return validPixel(out);
|
|
1214
|
+
}
|
|
1215
|
+
function mapSceneToDisplay(out, mapping, sceneX, sceneY) {
|
|
1216
|
+
if (!Number.isFinite(sceneX) || !Number.isFinite(sceneY)) return false;
|
|
1217
|
+
if (sceneX < 0 || sceneX > mapping.width || sceneY < 0 || sceneY > mapping.height) {
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
const display = { x: 0, y: 0 };
|
|
1221
|
+
if (!mapSceneUvToDisplayUv(display, mapping, sceneX / mapping.width, sceneY / mapping.height)) {
|
|
1222
|
+
return false;
|
|
1223
|
+
}
|
|
1224
|
+
out.x = display.x * mapping.width;
|
|
1225
|
+
out.y = display.y * mapping.height;
|
|
1226
|
+
return validPixel(out);
|
|
1227
|
+
}
|
|
673
1228
|
|
|
674
|
-
// src/
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1229
|
+
// src/errors/cloud.ts
|
|
1230
|
+
var CloudLayerInvalidParameterError = class extends Error {
|
|
1231
|
+
code = "cloud-layer-invalid-parameter";
|
|
1232
|
+
expected;
|
|
1233
|
+
hint;
|
|
1234
|
+
detail;
|
|
1235
|
+
constructor(field, value, expected) {
|
|
1236
|
+
super(`CloudLayer.${field} is invalid`);
|
|
1237
|
+
this.name = "CloudLayerInvalidParameterError";
|
|
1238
|
+
this.expected = `CloudLayer.${field} must be ${expected}`;
|
|
1239
|
+
this.hint = `set CloudLayer.${field} to ${expected}`;
|
|
1240
|
+
this.detail = { field, value, expected };
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
var CloudLayerOwnerConflictError = class extends Error {
|
|
1244
|
+
code = "cloud-layer-owner-conflict";
|
|
1245
|
+
expected = "at most one CloudLayer owns a World frame";
|
|
1246
|
+
hint = "remove additional CloudLayer owners before extraction";
|
|
1247
|
+
detail;
|
|
1248
|
+
constructor(count) {
|
|
1249
|
+
super(`CloudLayer owner cardinality is ${count}`);
|
|
1250
|
+
this.name = "CloudLayerOwnerConflictError";
|
|
1251
|
+
this.detail = { count };
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
var CloudLayerCacheInvalidError = class extends Error {
|
|
1255
|
+
code = "cloud-layer-cache-invalid";
|
|
1256
|
+
expected = "the density cache payload matches its reconstructible source";
|
|
1257
|
+
hint = "discard the derived cache and rebuild it from CloudLayer source facts";
|
|
1258
|
+
detail;
|
|
1259
|
+
constructor(sourceKey, reason) {
|
|
1260
|
+
super(`CloudLayer cache is invalid: ${reason}`);
|
|
1261
|
+
this.name = "CloudLayerCacheInvalidError";
|
|
1262
|
+
this.detail = { sourceKey, reason };
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
var CloudLayerCapabilityMissingError = class extends Error {
|
|
1266
|
+
code = "cloud-layer-capability-missing";
|
|
1267
|
+
expected = "the selected backend exposes the capability required by the cloud lane";
|
|
1268
|
+
hint = "inspect the capability report, disable the cloud lane on this backend, or retry on a backend with the required capability";
|
|
1269
|
+
detail;
|
|
1270
|
+
constructor(capability) {
|
|
1271
|
+
super(`CloudLayer capability is unavailable: ${capability}`);
|
|
1272
|
+
this.name = "CloudLayerCapabilityMissingError";
|
|
1273
|
+
this.detail = { capability };
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
var CloudLayerResourceFailureError = class extends Error {
|
|
1277
|
+
code = "cloud-layer-resource-failed";
|
|
1278
|
+
expected = "cloud resources are created, submitted and retired as one generation";
|
|
1279
|
+
hint = "keep the last-known-good cloud generation and retry after recovery";
|
|
1280
|
+
detail;
|
|
1281
|
+
constructor(detail) {
|
|
1282
|
+
super(`CloudLayer ${detail.stage} resource failed at generation ${detail.generation}`);
|
|
1283
|
+
this.name = "CloudLayerResourceFailureError";
|
|
1284
|
+
this.detail = detail;
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
var CLOUD_QUALITY_PROFILES = Object.freeze({
|
|
1288
|
+
low: Object.freeze({
|
|
1289
|
+
// Preserve low-profile ray counts while keeping separate puffs from
|
|
1290
|
+
// collapsing into one bilinear cache smear.
|
|
1291
|
+
cacheResolution: 32,
|
|
1292
|
+
viewSteps: 40,
|
|
1293
|
+
shadowSteps: 12,
|
|
1294
|
+
historyWeight: 0.82,
|
|
1295
|
+
viewDistance: 1600
|
|
1296
|
+
}),
|
|
1297
|
+
medium: Object.freeze({
|
|
1298
|
+
cacheResolution: 40,
|
|
1299
|
+
viewSteps: 64,
|
|
1300
|
+
shadowSteps: 20,
|
|
1301
|
+
historyWeight: 0.9,
|
|
1302
|
+
viewDistance: 2400
|
|
1303
|
+
}),
|
|
1304
|
+
high: Object.freeze({
|
|
1305
|
+
cacheResolution: 64,
|
|
1306
|
+
viewSteps: 64,
|
|
1307
|
+
shadowSteps: 32,
|
|
1308
|
+
historyWeight: 0.94,
|
|
1309
|
+
viewDistance: 4e3
|
|
1310
|
+
})
|
|
1311
|
+
});
|
|
1312
|
+
function cloudShadowResolutionForQuality(qualityValue) {
|
|
1313
|
+
return CLOUD_QUALITY_PROFILES[qualityValue].cacheResolution * 4;
|
|
1314
|
+
}
|
|
1315
|
+
function cloudViewDistanceForQuality(qualityValue) {
|
|
1316
|
+
return CLOUD_QUALITY_PROFILES[qualityValue].viewDistance;
|
|
1317
|
+
}
|
|
1318
|
+
var CLOUD_EXTINCTION_COEFFICIENT = 0.03;
|
|
1319
|
+
var DEFAULT_CLOUD_LAYER = Object.freeze({
|
|
1320
|
+
seed: 1337,
|
|
1321
|
+
baseHeight: 120,
|
|
1322
|
+
thickness: 80,
|
|
1323
|
+
scale: 4e-3,
|
|
1324
|
+
coverage: 0.48,
|
|
1325
|
+
density: 1,
|
|
1326
|
+
wind: Object.freeze([8, 0, 2]),
|
|
1327
|
+
quality: "medium",
|
|
1328
|
+
shadowRange: 512
|
|
1329
|
+
});
|
|
1330
|
+
function invalid3(field, value, expected) {
|
|
1331
|
+
return err(new CloudLayerInvalidParameterError(field, value, expected));
|
|
707
1332
|
}
|
|
708
|
-
function
|
|
709
|
-
if (
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
code: "webgpu-runtime-error",
|
|
713
|
-
expected: "query range stays within one 4096-index page",
|
|
714
|
-
hint: "allocate a bounded page before recording occlusion resolve"
|
|
715
|
-
})
|
|
716
|
-
);
|
|
1333
|
+
function finiteRange(field, value, min, max = Number.POSITIVE_INFINITY) {
|
|
1334
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
|
|
1335
|
+
const expected = Number.isFinite(max) ? `finite and in [${min}, ${max}]` : `finite and >= ${min}`;
|
|
1336
|
+
return invalid3(field, value, expected);
|
|
717
1337
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1338
|
+
return ok(value);
|
|
1339
|
+
}
|
|
1340
|
+
function quality(value) {
|
|
1341
|
+
if (typeof value === "string" && (value === "low" || value === "medium" || value === "high")) {
|
|
1342
|
+
return ok(value);
|
|
1343
|
+
}
|
|
1344
|
+
if (typeof value === "number") {
|
|
1345
|
+
const resolved = cloudQualityFromF32(value);
|
|
1346
|
+
if (resolved !== void 0) return ok(resolved);
|
|
1347
|
+
}
|
|
1348
|
+
return invalid3(
|
|
1349
|
+
"quality",
|
|
1350
|
+
value,
|
|
1351
|
+
`one of low, medium, high (codes ${CloudQualityValue.low}, ${CloudQualityValue.medium}, ${CloudQualityValue.high})`
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
function wind(value) {
|
|
1355
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
1356
|
+
return invalid3("wind", value, "three finite world-space components");
|
|
1357
|
+
}
|
|
1358
|
+
const source = value;
|
|
1359
|
+
if (source.length !== 3) return invalid3("wind", value, "three finite world-space components");
|
|
1360
|
+
const result = [
|
|
1361
|
+
source[0] ?? Number.NaN,
|
|
1362
|
+
source[1] ?? Number.NaN,
|
|
1363
|
+
source[2] ?? Number.NaN
|
|
1364
|
+
];
|
|
1365
|
+
if (result.some((entry) => !Number.isFinite(entry) || Math.abs(entry) > 1e3)) {
|
|
1366
|
+
return invalid3("wind", value, "three finite components in [-1000, 1000]");
|
|
1367
|
+
}
|
|
1368
|
+
return ok(Object.freeze(result));
|
|
1369
|
+
}
|
|
1370
|
+
function validateCloudLayer(input) {
|
|
1371
|
+
const value = input ?? {};
|
|
1372
|
+
const seed = value.seed ?? DEFAULT_CLOUD_LAYER.seed;
|
|
1373
|
+
if (typeof seed !== "number" || !Number.isInteger(seed) || seed < 0 || seed > 4294967295) {
|
|
1374
|
+
return invalid3("seed", seed, "an integer in [0, 4294967295]");
|
|
1375
|
+
}
|
|
1376
|
+
const baseHeight = finiteRange(
|
|
1377
|
+
"baseHeight",
|
|
1378
|
+
value.baseHeight ?? DEFAULT_CLOUD_LAYER.baseHeight,
|
|
1379
|
+
-1e5,
|
|
1380
|
+
1e6
|
|
1381
|
+
);
|
|
1382
|
+
if (!baseHeight.ok) return baseHeight;
|
|
1383
|
+
const thickness = finiteRange(
|
|
1384
|
+
"thickness",
|
|
1385
|
+
value.thickness ?? DEFAULT_CLOUD_LAYER.thickness,
|
|
1386
|
+
1e-3,
|
|
1387
|
+
1e6
|
|
1388
|
+
);
|
|
1389
|
+
if (!thickness.ok) return thickness;
|
|
1390
|
+
const scale = finiteRange("scale", value.scale ?? DEFAULT_CLOUD_LAYER.scale, 1e-6, 10);
|
|
1391
|
+
if (!scale.ok) return scale;
|
|
1392
|
+
const coverage = finiteRange("coverage", value.coverage ?? DEFAULT_CLOUD_LAYER.coverage, 0, 1);
|
|
1393
|
+
if (!coverage.ok) return coverage;
|
|
1394
|
+
const density = finiteRange("density", value.density ?? DEFAULT_CLOUD_LAYER.density, 0, 32);
|
|
1395
|
+
if (!density.ok) return density;
|
|
1396
|
+
const resolvedWind = wind(value.wind ?? DEFAULT_CLOUD_LAYER.wind);
|
|
1397
|
+
if (!resolvedWind.ok) return resolvedWind;
|
|
1398
|
+
const resolvedQuality = quality(value.quality ?? DEFAULT_CLOUD_LAYER.quality);
|
|
1399
|
+
if (!resolvedQuality.ok) return resolvedQuality;
|
|
1400
|
+
const shadowRange = finiteRange(
|
|
1401
|
+
"shadowRange",
|
|
1402
|
+
value.shadowRange ?? DEFAULT_CLOUD_LAYER.shadowRange,
|
|
1403
|
+
1e-3,
|
|
1404
|
+
1e7
|
|
724
1405
|
);
|
|
725
|
-
if (!
|
|
726
|
-
|
|
727
|
-
|
|
1406
|
+
if (!shadowRange.ok) return shadowRange;
|
|
1407
|
+
return ok(
|
|
1408
|
+
Object.freeze({
|
|
1409
|
+
seed,
|
|
1410
|
+
baseHeight: baseHeight.value,
|
|
1411
|
+
thickness: thickness.value,
|
|
1412
|
+
scale: scale.value,
|
|
1413
|
+
coverage: coverage.value,
|
|
1414
|
+
density: density.value,
|
|
1415
|
+
wind: resolvedWind.value,
|
|
1416
|
+
quality: resolvedQuality.value,
|
|
1417
|
+
shadowRange: shadowRange.value
|
|
1418
|
+
})
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
function cloudLayerSourceKey(params) {
|
|
1422
|
+
return [
|
|
1423
|
+
"cloud-layer-v1",
|
|
1424
|
+
params.seed,
|
|
1425
|
+
params.baseHeight,
|
|
1426
|
+
params.thickness,
|
|
1427
|
+
params.scale,
|
|
1428
|
+
params.coverage,
|
|
1429
|
+
params.density,
|
|
1430
|
+
params.wind[0],
|
|
1431
|
+
params.wind[1],
|
|
1432
|
+
params.wind[2],
|
|
1433
|
+
params.quality,
|
|
1434
|
+
params.shadowRange
|
|
1435
|
+
].join(":");
|
|
1436
|
+
}
|
|
1437
|
+
function cloudLayerFormationKey(params) {
|
|
1438
|
+
return [
|
|
1439
|
+
"cloud-formation-v5",
|
|
1440
|
+
params.seed,
|
|
1441
|
+
params.baseHeight,
|
|
1442
|
+
params.thickness,
|
|
1443
|
+
params.scale,
|
|
1444
|
+
params.quality
|
|
1445
|
+
].join(":");
|
|
1446
|
+
}
|
|
1447
|
+
var MAX_VOLUMETRIC_FOG_OWNERS = 8;
|
|
1448
|
+
var VolumetricFogSamplingValue = { noise: 0, density: 1 };
|
|
1449
|
+
var VolumetricFog = defineComponent("VolumetricFog", {
|
|
1450
|
+
light: { type: "entity" },
|
|
1451
|
+
spotLight: { type: "entity" },
|
|
1452
|
+
density: { type: "shared<TextureAsset>", simulationTransient: true },
|
|
1453
|
+
boundsMin: { type: "array<f32, 3>" },
|
|
1454
|
+
boundsMax: { type: "array<f32, 3>" },
|
|
1455
|
+
extinction: { type: "array<f32, 3>" },
|
|
1456
|
+
albedo: { type: "array<f32, 3>" },
|
|
1457
|
+
emission: { type: "array<f32, 3>" },
|
|
1458
|
+
anisotropy: { type: "f32", default: 0 },
|
|
1459
|
+
maxDistance: { type: "f32", default: 100 },
|
|
1460
|
+
sampling: { type: "enum", labels: VolumetricFogSamplingValue, default: 0 }
|
|
1461
|
+
});
|
|
1462
|
+
function finiteVector(value) {
|
|
1463
|
+
return value.every(Number.isFinite);
|
|
1464
|
+
}
|
|
1465
|
+
function validBounds(bounds) {
|
|
1466
|
+
if (!finiteVector(bounds.min) || !finiteVector(bounds.max)) return false;
|
|
1467
|
+
return bounds.max.every((value, index) => value > (bounds.min[index] ?? value));
|
|
1468
|
+
}
|
|
1469
|
+
function validRange(value, min, max = Infinity) {
|
|
1470
|
+
return value.every((entry) => Number.isFinite(entry) && entry >= min && entry <= max);
|
|
1471
|
+
}
|
|
1472
|
+
function validateVolumetricFog(input) {
|
|
1473
|
+
if (input.density.shape.viewDimension !== "3d" || input.density.colorSpace === "srgb") {
|
|
1474
|
+
return err(
|
|
1475
|
+
new VolumeDensityShapeMismatchError(input.density.guid, input.density.shape.viewDimension)
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
if (!validBounds(input.bounds)) return err(new VolumeInvalidBoundsError(input.bounds));
|
|
1479
|
+
if (!validRange(input.extinction, 0) || !validRange(input.albedo, 0, 1) || !validRange(input.emission, 0) || !Number.isFinite(input.anisotropy) || input.anisotropy <= -1 || input.anisotropy >= 1 || !Number.isFinite(input.maxDistance) || input.maxDistance <= 0 || input.sampling !== void 0 && input.sampling !== "noise" && input.sampling !== "density") {
|
|
1480
|
+
return err(new VolumeInvalidParametersError(input));
|
|
1481
|
+
}
|
|
1482
|
+
return ok(input);
|
|
728
1483
|
}
|
|
729
1484
|
|
|
730
|
-
// src/
|
|
731
|
-
function
|
|
732
|
-
return
|
|
1485
|
+
// src/environment/signature.ts
|
|
1486
|
+
function sorted(items) {
|
|
1487
|
+
return [...items].sort((left, right) => left.entityKey - right.entityKey);
|
|
1488
|
+
}
|
|
1489
|
+
function environmentSignature(input) {
|
|
1490
|
+
return JSON.stringify({
|
|
1491
|
+
environments: sorted(input.environments),
|
|
1492
|
+
fogs: sorted(input.fogs),
|
|
1493
|
+
suns: sorted(input.suns)
|
|
1494
|
+
});
|
|
733
1495
|
}
|
|
734
|
-
function
|
|
735
|
-
return
|
|
1496
|
+
function environmentOnlySignature(input) {
|
|
1497
|
+
return JSON.stringify({
|
|
1498
|
+
environments: sorted(input.environments),
|
|
1499
|
+
suns: sorted(input.suns)
|
|
1500
|
+
});
|
|
736
1501
|
}
|
|
737
|
-
function
|
|
738
|
-
return
|
|
1502
|
+
function fogOnlySignature(input) {
|
|
1503
|
+
return JSON.stringify({ fogs: sorted(input.fogs) });
|
|
739
1504
|
}
|
|
740
|
-
function
|
|
741
|
-
return
|
|
1505
|
+
function environmentFactSignatures(input) {
|
|
1506
|
+
return {
|
|
1507
|
+
environmentSignature: environmentOnlySignature(input),
|
|
1508
|
+
fogSignature: fogOnlySignature(input),
|
|
1509
|
+
signature: environmentSignature(input)
|
|
1510
|
+
};
|
|
742
1511
|
}
|
|
743
1512
|
|
|
744
|
-
// src/
|
|
745
|
-
var
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
);
|
|
761
|
-
this.projection = {
|
|
762
|
-
get querySet() {
|
|
763
|
-
return runtime.pendingFrame?.resources.querySet;
|
|
764
|
-
},
|
|
765
|
-
get queryIndex() {
|
|
766
|
-
return runtime.pendingFrame?.reservations[0]?.queryIndex ?? 0;
|
|
767
|
-
},
|
|
768
|
-
get queryCount() {
|
|
769
|
-
return runtime.pendingFrame?.reservations.length ?? 0;
|
|
770
|
-
},
|
|
771
|
-
get sampleCount() {
|
|
772
|
-
return runtime.pendingFrame?.sampleCount ?? 1;
|
|
773
|
-
},
|
|
774
|
-
get pageIndex() {
|
|
775
|
-
return runtime.pendingFrame?.reservations[0]?.pageIndex ?? 0;
|
|
776
|
-
},
|
|
777
|
-
get reservationId() {
|
|
778
|
-
return runtime.pendingFrame?.reservations[0]?.reservationId ?? 0;
|
|
779
|
-
},
|
|
780
|
-
encodeProxyBounds: (pass) => {
|
|
781
|
-
const pending = runtime.pendingFrame;
|
|
782
|
-
return pending === void 0 ? err$1(runtimeError("the current RenderSystem frame owns the occlusion candidate")) : runtime.encodeProxyBounds(pass, pending);
|
|
783
|
-
},
|
|
784
|
-
beginCandidate: (pass) => {
|
|
785
|
-
const pending = runtime.pendingFrame;
|
|
786
|
-
if (pending === void 0 || pending.endedCount >= pending.reservations.length) {
|
|
787
|
-
return err$1(runtimeError("the current RenderSystem frame owns the occlusion candidate"));
|
|
788
|
-
}
|
|
789
|
-
return pass.beginOcclusionQuery(pending.reservations[pending.endedCount]?.queryIndex ?? 0);
|
|
790
|
-
},
|
|
791
|
-
endCandidate: (pass) => {
|
|
792
|
-
const pending = runtime.pendingFrame;
|
|
793
|
-
if (pending === void 0 || pending.endedCount >= pending.reservations.length) {
|
|
794
|
-
return err$1(runtimeError("the current RenderSystem frame owns the occlusion candidate"));
|
|
795
|
-
}
|
|
796
|
-
const ended = pass.endOcclusionQuery();
|
|
797
|
-
if (ended.ok) pending.endedCount += 1;
|
|
798
|
-
return ended;
|
|
799
|
-
},
|
|
800
|
-
recordResolve: (encoder) => {
|
|
801
|
-
const pending = runtime.pendingFrame;
|
|
802
|
-
if (pending === void 0 || pending.endedCount !== pending.reservations.length) {
|
|
803
|
-
return err$1(runtimeError("the real geometry pass must end its occlusion query first"));
|
|
804
|
-
}
|
|
805
|
-
return recordOcclusionResolve(
|
|
806
|
-
encoder,
|
|
807
|
-
pending.resources,
|
|
808
|
-
pending.reservations[0]?.queryIndex ?? 0,
|
|
809
|
-
pending.reservations.length
|
|
810
|
-
);
|
|
811
|
-
},
|
|
812
|
-
commit: (submitted, submissionGeneration, profilePhase) => {
|
|
813
|
-
const pending = runtime.pendingFrame;
|
|
814
|
-
if (pending !== void 0)
|
|
815
|
-
runtime.commit(pending, submitted, submissionGeneration, profilePhase);
|
|
816
|
-
}
|
|
1513
|
+
// src/extract/environment.ts
|
|
1514
|
+
var ATMOSPHERE_PARAMETER_RANGES = Object.freeze({
|
|
1515
|
+
turbidity: Object.freeze({ min: 1, max: 20 }),
|
|
1516
|
+
rayleigh: Object.freeze({ min: 0, max: Number.POSITIVE_INFINITY }),
|
|
1517
|
+
mieCoefficient: Object.freeze({ min: 0, max: Number.POSITIVE_INFINITY }),
|
|
1518
|
+
mieDirectionalG: Object.freeze({ min: 0, max: 0.999 }),
|
|
1519
|
+
sunAngularRadius: Object.freeze({ min: 0, max: Number.POSITIVE_INFINITY }),
|
|
1520
|
+
circumsolarStrength: Object.freeze({ min: 0, max: 4 }),
|
|
1521
|
+
circumsolarWidth: Object.freeze({ min: 0.25, max: 4 })
|
|
1522
|
+
});
|
|
1523
|
+
function validateFogParameters(candidate) {
|
|
1524
|
+
if (candidate.color.length !== 3) {
|
|
1525
|
+
return {
|
|
1526
|
+
field: "color",
|
|
1527
|
+
value: candidate.color.length,
|
|
1528
|
+
expected: "exactly three finite channels in [0, 1]"
|
|
817
1529
|
};
|
|
818
1530
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
/** Candidate descriptors are rebuilt only when the extracted frame changes. */
|
|
828
|
-
candidateSource;
|
|
829
|
-
validCandidates = [];
|
|
830
|
-
candidateByKey = /* @__PURE__ */ new Map();
|
|
831
|
-
lastPrepareUnavailable = false;
|
|
832
|
-
pendingFrame;
|
|
833
|
-
disposed = false;
|
|
834
|
-
proxyBuffer;
|
|
835
|
-
proxyScratch;
|
|
836
|
-
proxyPipeline;
|
|
837
|
-
proxyPipelineSampleCount;
|
|
838
|
-
queryLatencySamplesUs = [];
|
|
839
|
-
lastQueryLatencyUs = 0;
|
|
840
|
-
projection;
|
|
841
|
-
prepare(candidate) {
|
|
842
|
-
return this.prepareBatch([candidate], 1, false);
|
|
843
|
-
}
|
|
844
|
-
prepareBatch(candidates, sampleCount = 1, useReadyQueue = true) {
|
|
845
|
-
this.lastPrepareUnavailable = false;
|
|
846
|
-
if (this.disposed || candidates.length === 0) return void 0;
|
|
847
|
-
if (this.pendingFrame !== void 0) {
|
|
848
|
-
this.lastPrepareUnavailable = true;
|
|
849
|
-
return void 0;
|
|
1531
|
+
for (let index = 0; index < candidate.color.length; index += 1) {
|
|
1532
|
+
const value = candidate.color[index];
|
|
1533
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0 || value > 1) {
|
|
1534
|
+
return {
|
|
1535
|
+
field: `color[${index}]`,
|
|
1536
|
+
value: value ?? Number.NaN,
|
|
1537
|
+
expected: "a finite number in [0, 1]"
|
|
1538
|
+
};
|
|
850
1539
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1540
|
+
}
|
|
1541
|
+
if (!Number.isFinite(candidate.density) || candidate.density < 0) {
|
|
1542
|
+
return { field: "density", value: candidate.density, expected: "a finite number >= 0" };
|
|
1543
|
+
}
|
|
1544
|
+
if (!Number.isFinite(candidate.heightFalloff) || candidate.heightFalloff < 0) {
|
|
1545
|
+
return {
|
|
1546
|
+
field: "heightFalloff",
|
|
1547
|
+
value: candidate.heightFalloff,
|
|
1548
|
+
expected: "a finite number >= 0"
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
if (!Number.isFinite(candidate.maxOpacity) || candidate.maxOpacity < 0 || candidate.maxOpacity > 1) {
|
|
1552
|
+
return {
|
|
1553
|
+
field: "maxOpacity",
|
|
1554
|
+
value: candidate.maxOpacity,
|
|
1555
|
+
expected: "a finite number in [0, 1]"
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
return void 0;
|
|
1559
|
+
}
|
|
1560
|
+
function validateAtmosphereParameters(parameters) {
|
|
1561
|
+
for (const field of Object.keys(ATMOSPHERE_PARAMETER_RANGES)) {
|
|
1562
|
+
const value = parameters[field];
|
|
1563
|
+
const range = ATMOSPHERE_PARAMETER_RANGES[field];
|
|
1564
|
+
if (!Number.isFinite(value) || value < range.min || value > range.max) {
|
|
1565
|
+
return new AtmosphereInvalidParameterError(field, value, range);
|
|
860
1566
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1567
|
+
}
|
|
1568
|
+
return void 0;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// src/environment/frame.ts
|
|
1572
|
+
var SKYLIGHT_RECOVERY_FALLBACK = Object.freeze({
|
|
1573
|
+
kind: "skylight",
|
|
1574
|
+
active: false
|
|
1575
|
+
});
|
|
1576
|
+
var EnvironmentSelectionParameterError = class extends Error {
|
|
1577
|
+
code = "environment-selection-invalid";
|
|
1578
|
+
expected;
|
|
1579
|
+
hint;
|
|
1580
|
+
detail;
|
|
1581
|
+
constructor(field, value, expected) {
|
|
1582
|
+
super(`environment selection field ${field} is invalid`);
|
|
1583
|
+
this.name = "EnvironmentSelectionParameterError";
|
|
1584
|
+
this.expected = expected;
|
|
1585
|
+
this.hint = `set ${field} to a finite value satisfying ${expected}`;
|
|
1586
|
+
this.detail = { field, value };
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
var SunCardinalityError = class extends Error {
|
|
1590
|
+
code = "sun-cardinality";
|
|
1591
|
+
expected = "atmosphere has exactly one sun owner";
|
|
1592
|
+
hint = "add exactly one DirectionalLight sun for the atmosphere owner";
|
|
1593
|
+
detail;
|
|
1594
|
+
constructor(value) {
|
|
1595
|
+
super(`atmosphere sun cardinality is ${value}`);
|
|
1596
|
+
this.name = "SunCardinalityError";
|
|
1597
|
+
this.detail = { field: "sun", value };
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
function freezeCandidate(candidate) {
|
|
1601
|
+
return Object.freeze(candidate);
|
|
1602
|
+
}
|
|
1603
|
+
function freezeFog(candidate) {
|
|
1604
|
+
return Object.freeze({
|
|
1605
|
+
...candidate,
|
|
1606
|
+
color: Object.freeze([...candidate.color])
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
function selectEnvironment(input) {
|
|
1610
|
+
if (input.environments.length > 1) {
|
|
1611
|
+
return err(
|
|
1612
|
+
new EnvironmentSourceConflictError(
|
|
1613
|
+
[...input.environments].sort((left, right) => left.entityKey - right.entityKey).map(({ kind, entityKey, sourceKey }) => ({ kind, entityKey, sourceKey }))
|
|
1614
|
+
)
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
if (input.fogs.length > 1) return err(new FogCardinalityError(input.fogs.length));
|
|
1618
|
+
const selectedFog = input.fogs[0];
|
|
1619
|
+
if (selectedFog !== void 0) {
|
|
1620
|
+
const issue = validateFogParameters(selectedFog);
|
|
1621
|
+
if (issue !== void 0) {
|
|
1622
|
+
return err(new EnvironmentSelectionParameterError(issue.field, issue.value, issue.expected));
|
|
882
1623
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1624
|
+
}
|
|
1625
|
+
const selected = input.environments[0];
|
|
1626
|
+
if (selected?.kind === "atmosphere") {
|
|
1627
|
+
const invalid5 = validateAtmosphereParameters(selected.atmosphere);
|
|
1628
|
+
if (invalid5 !== void 0) return err(invalid5);
|
|
1629
|
+
}
|
|
1630
|
+
if (selected?.kind === "atmosphere" && input.suns.length !== 1) {
|
|
1631
|
+
return err(new SunCardinalityError(input.suns.length));
|
|
1632
|
+
}
|
|
1633
|
+
const source = selected === void 0 ? { kind: "none" } : selected.kind === "atmosphere" ? {
|
|
1634
|
+
kind: "atmosphere",
|
|
1635
|
+
entityKey: selected.entityKey,
|
|
1636
|
+
sourceKey: selected.sourceKey,
|
|
1637
|
+
atmosphere: Object.freeze({ ...selected.atmosphere })
|
|
1638
|
+
} : {
|
|
1639
|
+
kind: "image",
|
|
1640
|
+
entityKey: selected.entityKey,
|
|
1641
|
+
sourceKey: selected.sourceKey
|
|
1642
|
+
};
|
|
1643
|
+
const sun = input.suns[0] === void 0 ? void 0 : freezeCandidate({ ...input.suns[0] });
|
|
1644
|
+
const fog = selectedFog === void 0 ? void 0 : freezeFog(selectedFog);
|
|
1645
|
+
const signatureInput = {
|
|
1646
|
+
environments: selected === void 0 ? [] : [selected],
|
|
1647
|
+
fogs: selectedFog === void 0 ? [] : [selectedFog],
|
|
1648
|
+
suns: input.suns
|
|
1649
|
+
};
|
|
1650
|
+
const signatures = environmentFactSignatures(signatureInput);
|
|
1651
|
+
const result = {
|
|
1652
|
+
source: Object.freeze(source),
|
|
1653
|
+
fog,
|
|
1654
|
+
...signatures,
|
|
1655
|
+
revision: 0,
|
|
1656
|
+
sun
|
|
1657
|
+
};
|
|
1658
|
+
Object.defineProperty(result, "lane", { value: input.lane, enumerable: false });
|
|
1659
|
+
return ok(Object.freeze(result));
|
|
1660
|
+
}
|
|
1661
|
+
var DEPTH_OF_FIELD_PARAMS_BYTE_SIZE = 64;
|
|
1662
|
+
var MIN_F_STOP = Math.fround(0.7);
|
|
1663
|
+
var MAX_F_STOP = Math.fround(32);
|
|
1664
|
+
var MAX_LINEAR_DEPTH_RAW = Math.fround(1 - 2 ** -24);
|
|
1665
|
+
var DEFAULT_DEPTH_OF_FIELD_PARAMS = Object.freeze({
|
|
1666
|
+
focusDistance: 8,
|
|
1667
|
+
fStop: 2.8,
|
|
1668
|
+
sensorHeight: 0.024,
|
|
1669
|
+
maxRadiusPixels: 16,
|
|
1670
|
+
quality: "medium",
|
|
1671
|
+
blurSide: "both",
|
|
1672
|
+
focalLength: 0
|
|
1673
|
+
});
|
|
1674
|
+
function quantizeF32(value) {
|
|
1675
|
+
return Math.fround(value);
|
|
1676
|
+
}
|
|
1677
|
+
var DepthOfFieldValidationError = class extends Error {
|
|
1678
|
+
code;
|
|
1679
|
+
expected;
|
|
1680
|
+
hint;
|
|
1681
|
+
detail;
|
|
1682
|
+
constructor(code, detail, expected, hint) {
|
|
1683
|
+
super(`${code}: ${expected}`);
|
|
1684
|
+
this.name = "DepthOfFieldValidationError";
|
|
1685
|
+
this.code = code;
|
|
1686
|
+
this.expected = expected;
|
|
1687
|
+
this.hint = hint;
|
|
1688
|
+
this.detail = detail;
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
function depthOfFieldRequestFailure(error) {
|
|
1692
|
+
return Object.freeze({
|
|
1693
|
+
code: error.code,
|
|
1694
|
+
detail: error.detail,
|
|
1695
|
+
expected: error.expected,
|
|
1696
|
+
hint: error.hint
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
function resolveDepthOfFieldFrameParams(requested, requestFailure, accepted) {
|
|
1700
|
+
if (requested !== void 0) return requested;
|
|
1701
|
+
if (requestFailure === void 0 || requestFailure.code === "depth-of-field-orthographic-unsupported") {
|
|
1702
|
+
return void 0;
|
|
1703
|
+
}
|
|
1704
|
+
return accepted;
|
|
1705
|
+
}
|
|
1706
|
+
function invalid4(field, value, expected, bound) {
|
|
1707
|
+
return new DepthOfFieldValidationError(
|
|
1708
|
+
"depth-of-field-invalid-params",
|
|
1709
|
+
{ field, value, expected, ...bound === void 0 ? {} : { bound } },
|
|
1710
|
+
expected,
|
|
1711
|
+
`set ${field} to ${expected}`
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
function focalLength(sensorHeight, fov) {
|
|
1715
|
+
return sensorHeight / (2 * Math.tan(fov / 2));
|
|
1716
|
+
}
|
|
1717
|
+
function opticalCocScale(params) {
|
|
1718
|
+
return params.focalLength / params.sensorHeight / (2 * params.fStop) * (params.focalLength / (params.focusDistance - params.focalLength));
|
|
1719
|
+
}
|
|
1720
|
+
function frameCocCoefficient(params, outputHeight) {
|
|
1721
|
+
return quantizeF32(quantizeF32(outputHeight) * opticalCocScale(params));
|
|
1722
|
+
}
|
|
1723
|
+
function resolveDepthOfFieldFrameValues(params, input) {
|
|
1724
|
+
const focusDistance = quantizeF32(params.focusDistance);
|
|
1725
|
+
const fStop = quantizeF32(params.fStop);
|
|
1726
|
+
const sensorHeight = quantizeF32(params.sensorHeight);
|
|
1727
|
+
const focal = quantizeF32(params.focalLength);
|
|
1728
|
+
const maxRadiusPixels = quantizeF32(params.maxRadiusPixels);
|
|
1729
|
+
const outputHeight = quantizeF32(input.outputHeight);
|
|
1730
|
+
const near = quantizeF32(input.near);
|
|
1731
|
+
const far = quantizeF32(input.far);
|
|
1732
|
+
const finitePositiveFields = [
|
|
1733
|
+
["focusDistance", focusDistance, params.focusDistance],
|
|
1734
|
+
["sensorHeight", sensorHeight, params.sensorHeight],
|
|
1735
|
+
["focalLength", focal, params.focalLength],
|
|
1736
|
+
["near", near, input.near],
|
|
1737
|
+
["far", far, input.far]
|
|
1738
|
+
];
|
|
1739
|
+
for (const [field, value, raw] of finitePositiveFields) {
|
|
1740
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
1741
|
+
return err(invalid4(field, raw, "a finite positive f32 value"));
|
|
908
1742
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1743
|
+
}
|
|
1744
|
+
if (!Number.isFinite(fStop) || fStop < MIN_F_STOP || fStop > MAX_F_STOP) {
|
|
1745
|
+
return err(
|
|
1746
|
+
invalid4("fStop", params.fStop, "a finite f32 value in [0.7, 32]", [MIN_F_STOP, MAX_F_STOP])
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
if (!Number.isFinite(maxRadiusPixels) || maxRadiusPixels < 0 || maxRadiusPixels > 32) {
|
|
1750
|
+
return err(
|
|
1751
|
+
invalid4("maxRadiusPixels", params.maxRadiusPixels, "a finite f32 value in [0, 32]", [0, 32])
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
if (!Number.isFinite(outputHeight) || outputHeight <= 0) {
|
|
1755
|
+
return err(invalid4("outputHeight", input.outputHeight, "a finite positive f32 value"));
|
|
1756
|
+
}
|
|
1757
|
+
if (far <= near) {
|
|
1758
|
+
return err(invalid4("near/far", [input.near, input.far], "finite f32 near > 0 and far > near"));
|
|
1759
|
+
}
|
|
1760
|
+
if (focusDistance <= focal) {
|
|
1761
|
+
return err(invalid4("focusDistance", params.focusDistance, "greater than focalLength in f32"));
|
|
1762
|
+
}
|
|
1763
|
+
const depthRatio = quantizeF32(near / far);
|
|
1764
|
+
if (!Number.isFinite(depthRatio) || depthRatio <= 0) {
|
|
1765
|
+
return err(invalid4("near/far ratio", depthRatio, "a finite positive f32 value"));
|
|
1766
|
+
}
|
|
1767
|
+
for (const raw of [quantizeF32(2 ** -149), 0.5, MAX_LINEAR_DEPTH_RAW]) {
|
|
1768
|
+
const oneMinusRaw = quantizeF32(1 - raw);
|
|
1769
|
+
const weightedRatio = quantizeF32(raw * depthRatio);
|
|
1770
|
+
const denominator = quantizeF32(oneMinusRaw + weightedRatio);
|
|
1771
|
+
const distance = quantizeF32(near / denominator);
|
|
1772
|
+
if (!Number.isFinite(oneMinusRaw) || oneMinusRaw <= 0 || !Number.isFinite(weightedRatio) || !Number.isFinite(denominator) || denominator <= 0 || !Number.isFinite(distance) || distance <= 0) {
|
|
1773
|
+
return err(invalid4("linearDepth", distance, "finite positive f32 reconstruction"));
|
|
914
1774
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1775
|
+
}
|
|
1776
|
+
const cocCoefficient = frameCocCoefficient(
|
|
1777
|
+
{
|
|
1778
|
+
focusDistance,
|
|
1779
|
+
fStop,
|
|
1780
|
+
sensorHeight,
|
|
1781
|
+
focalLength: focal
|
|
1782
|
+
},
|
|
1783
|
+
outputHeight
|
|
1784
|
+
);
|
|
1785
|
+
if (!Number.isFinite(cocCoefficient) || cocCoefficient <= 0) {
|
|
1786
|
+
return err(
|
|
1787
|
+
invalid4("cocCoefficient", cocCoefficient, "a finite positive value representable as f32")
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
for (const [field, depth] of [
|
|
1791
|
+
["near", near],
|
|
1792
|
+
["far", far]
|
|
1793
|
+
]) {
|
|
1794
|
+
const focusOverDepth = quantizeF32(focusDistance / depth);
|
|
1795
|
+
const depthFactor = quantizeF32(1 - focusOverDepth);
|
|
1796
|
+
const radius = quantizeF32(cocCoefficient * depthFactor);
|
|
1797
|
+
if (!Number.isFinite(focusOverDepth) || !Number.isFinite(depthFactor) || !Number.isFinite(radius)) {
|
|
1798
|
+
return err(invalid4(`coc.${field}`, radius, "a finite value representable as f32"));
|
|
921
1799
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1800
|
+
}
|
|
1801
|
+
return ok({
|
|
1802
|
+
focusDistance,
|
|
1803
|
+
fStop,
|
|
1804
|
+
sensorHeight,
|
|
1805
|
+
focalLength: focal,
|
|
1806
|
+
maxRadiusPixels,
|
|
1807
|
+
outputHeight,
|
|
1808
|
+
near,
|
|
1809
|
+
far,
|
|
1810
|
+
cocCoefficient
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
function validateDepthOfFieldFrameParams(params, input) {
|
|
1814
|
+
const resolved = resolveDepthOfFieldFrameValues(params, input);
|
|
1815
|
+
return resolved.ok ? ok(void 0) : resolved;
|
|
1816
|
+
}
|
|
1817
|
+
function validateDepthOfFieldParams(input, camera) {
|
|
1818
|
+
if (camera?.projection === "orthographic") {
|
|
1819
|
+
return err(
|
|
1820
|
+
new DepthOfFieldValidationError(
|
|
1821
|
+
"depth-of-field-orthographic-unsupported",
|
|
1822
|
+
{ projection: "orthographic" },
|
|
1823
|
+
"DepthOfField requires a perspective Camera",
|
|
1824
|
+
"use a perspective Camera or remove the DepthOfField component"
|
|
1825
|
+
)
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
const value = input ?? {};
|
|
1829
|
+
const focusDistance = quantizeF32(
|
|
1830
|
+
value.focusDistance ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.focusDistance
|
|
1831
|
+
);
|
|
1832
|
+
const fStop = quantizeF32(value.fStop ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.fStop);
|
|
1833
|
+
const sensorHeight = quantizeF32(
|
|
1834
|
+
value.sensorHeight ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.sensorHeight
|
|
1835
|
+
);
|
|
1836
|
+
const maxRadiusPixels = quantizeF32(
|
|
1837
|
+
value.maxRadiusPixels ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.maxRadiusPixels
|
|
1838
|
+
);
|
|
1839
|
+
const qualityValue = quantizeF32(value.quality ?? DepthOfFieldQualityValue.medium);
|
|
1840
|
+
const blurSideValue = quantizeF32(value.blurSide ?? DepthOfFieldSideValue.both);
|
|
1841
|
+
const quality2 = depthOfFieldQualityFromF32(qualityValue);
|
|
1842
|
+
const blurSide = depthOfFieldSideFromF32(blurSideValue);
|
|
1843
|
+
if (!Number.isFinite(focusDistance) || focusDistance <= 0) {
|
|
1844
|
+
return err(
|
|
1845
|
+
invalid4("focusDistance", value.focusDistance ?? focusDistance, "a finite positive f32 value")
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
if (!Number.isFinite(fStop) || fStop < MIN_F_STOP || fStop > MAX_F_STOP) {
|
|
1849
|
+
return err(
|
|
1850
|
+
invalid4("fStop", value.fStop ?? fStop, "a finite f32 value in [0.7, 32]", [
|
|
1851
|
+
MIN_F_STOP,
|
|
1852
|
+
MAX_F_STOP
|
|
1853
|
+
])
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
if (!Number.isFinite(sensorHeight) || sensorHeight <= 0) {
|
|
1857
|
+
return err(
|
|
1858
|
+
invalid4("sensorHeight", value.sensorHeight ?? sensorHeight, "a finite positive f32 value")
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
if (!Number.isFinite(maxRadiusPixels) || maxRadiusPixels < 0 || maxRadiusPixels > 32) {
|
|
1862
|
+
return err(
|
|
1863
|
+
invalid4(
|
|
1864
|
+
"maxRadiusPixels",
|
|
1865
|
+
value.maxRadiusPixels ?? maxRadiusPixels,
|
|
1866
|
+
"a finite f32 value in [0, 32]",
|
|
1867
|
+
[0, 32]
|
|
1868
|
+
)
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
if (quality2 === void 0) {
|
|
1872
|
+
return err(invalid4("quality", qualityValue, "DepthOfFieldQualityValue.low, medium, or high"));
|
|
1873
|
+
}
|
|
1874
|
+
if (blurSide === void 0) {
|
|
1875
|
+
return err(invalid4("blurSide", blurSideValue, "DepthOfFieldSideValue.both, near, or far"));
|
|
1876
|
+
}
|
|
1877
|
+
let focal = DEFAULT_DEPTH_OF_FIELD_PARAMS.focalLength;
|
|
1878
|
+
if (camera !== void 0) {
|
|
1879
|
+
const fov = quantizeF32(camera.fov);
|
|
1880
|
+
const near = quantizeF32(camera.near);
|
|
1881
|
+
const far = quantizeF32(camera.far);
|
|
1882
|
+
if (!Number.isFinite(fov) || fov <= 0 || fov >= Math.PI) {
|
|
1883
|
+
return err(invalid4("fov", camera.fov, "a finite f32 value in (0, PI)"));
|
|
930
1884
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1885
|
+
if (!Number.isFinite(near) || !Number.isFinite(far) || near <= 0 || far <= near) {
|
|
1886
|
+
return err(
|
|
1887
|
+
invalid4("near/far", [camera.near, camera.far], "finite f32 near > 0 and far > near")
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
focal = quantizeF32(focalLength(sensorHeight, fov));
|
|
1891
|
+
if (!Number.isFinite(focal) || focal <= 0) {
|
|
1892
|
+
return err(invalid4("focalLength", focal, "a finite positive f32 value"));
|
|
1893
|
+
}
|
|
1894
|
+
if (focusDistance < near || focusDistance > far) {
|
|
1895
|
+
return err(
|
|
1896
|
+
invalid4("focusDistance", focusDistance, `a value in [${near}, ${far}]`, [near, far])
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
if (focusDistance <= focal) {
|
|
1900
|
+
return err(
|
|
1901
|
+
invalid4("focusDistance", focusDistance, `greater than focalLength in f32 (${focal})`)
|
|
1902
|
+
);
|
|
948
1903
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1904
|
+
}
|
|
1905
|
+
return ok(
|
|
1906
|
+
Object.freeze({
|
|
1907
|
+
focusDistance,
|
|
1908
|
+
fStop,
|
|
1909
|
+
sensorHeight,
|
|
1910
|
+
maxRadiusPixels,
|
|
1911
|
+
quality: quality2,
|
|
1912
|
+
blurSide,
|
|
1913
|
+
focalLength: focal
|
|
1914
|
+
})
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
function resolveDepthOfFieldParams(input, camera) {
|
|
1918
|
+
if (input === void 0) return ok(void 0);
|
|
1919
|
+
return validateDepthOfFieldParams(input, camera);
|
|
1920
|
+
}
|
|
1921
|
+
function depthOfFieldTapCount(quality2) {
|
|
1922
|
+
switch (quality2) {
|
|
1923
|
+
case "low":
|
|
1924
|
+
return 16;
|
|
1925
|
+
case "medium":
|
|
1926
|
+
return 32;
|
|
1927
|
+
case "high":
|
|
1928
|
+
return 64;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
function depthOfFieldSideCode(side) {
|
|
1932
|
+
switch (side) {
|
|
1933
|
+
case "both":
|
|
1934
|
+
return DepthOfFieldSideValue.both;
|
|
1935
|
+
case "near":
|
|
1936
|
+
return DepthOfFieldSideValue.near;
|
|
1937
|
+
case "far":
|
|
1938
|
+
return DepthOfFieldSideValue.far;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function depthOfFieldQualityCode(quality2) {
|
|
1942
|
+
switch (quality2) {
|
|
1943
|
+
case "low":
|
|
1944
|
+
return DepthOfFieldQualityValue.low;
|
|
1945
|
+
case "medium":
|
|
1946
|
+
return DepthOfFieldQualityValue.medium;
|
|
1947
|
+
case "high":
|
|
1948
|
+
return DepthOfFieldQualityValue.high;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
function signedDepthOfFieldCoC(params, viewDepth, outputHeight) {
|
|
1952
|
+
if (!Number.isFinite(viewDepth) || viewDepth <= 0 || !Number.isFinite(outputHeight) || outputHeight <= 0 || !Number.isFinite(params.focalLength) || params.focalLength <= 0 || !Number.isFinite(params.sensorHeight) || params.sensorHeight <= 0 || !Number.isFinite(params.fStop) || params.fStop <= 0 || !Number.isFinite(params.focusDistance) || params.focusDistance <= params.focalLength) {
|
|
1953
|
+
return 0;
|
|
1954
|
+
}
|
|
1955
|
+
const focusDistance = quantizeF32(params.focusDistance);
|
|
1956
|
+
const depth = quantizeF32(viewDepth);
|
|
1957
|
+
const focalLength2 = quantizeF32(params.focalLength);
|
|
1958
|
+
const sensorHeight = quantizeF32(params.sensorHeight);
|
|
1959
|
+
const fStop = quantizeF32(params.fStop);
|
|
1960
|
+
const coefficient = frameCocCoefficient(
|
|
1961
|
+
{ focusDistance, focalLength: focalLength2, sensorHeight, fStop },
|
|
1962
|
+
outputHeight
|
|
1963
|
+
);
|
|
1964
|
+
const depthFactor = quantizeF32(1 - quantizeF32(focusDistance / depth));
|
|
1965
|
+
const radius = quantizeF32(coefficient * depthFactor);
|
|
1966
|
+
const limit = Number.isFinite(params.maxRadiusPixels) ? quantizeF32(params.maxRadiusPixels) : Number.POSITIVE_INFINITY;
|
|
1967
|
+
return Math.max(-limit, Math.min(limit, Number.isFinite(radius) ? radius : 0));
|
|
1968
|
+
}
|
|
1969
|
+
function packDepthOfFieldParams(params, input) {
|
|
1970
|
+
const frameValues = resolveDepthOfFieldFrameValues(params, input);
|
|
1971
|
+
if (!frameValues.ok) throw frameValues.error;
|
|
1972
|
+
const {
|
|
1973
|
+
focusDistance,
|
|
1974
|
+
fStop,
|
|
1975
|
+
sensorHeight,
|
|
1976
|
+
focalLength: focal,
|
|
1977
|
+
outputHeight,
|
|
1978
|
+
maxRadiusPixels,
|
|
1979
|
+
near,
|
|
1980
|
+
far,
|
|
1981
|
+
cocCoefficient
|
|
1982
|
+
} = frameValues.value;
|
|
1983
|
+
const payload = new Float32Array(DEPTH_OF_FIELD_PARAMS_BYTE_SIZE / 4);
|
|
1984
|
+
payload.set([
|
|
1985
|
+
focusDistance,
|
|
1986
|
+
fStop,
|
|
1987
|
+
sensorHeight,
|
|
1988
|
+
focal,
|
|
1989
|
+
outputHeight,
|
|
1990
|
+
maxRadiusPixels,
|
|
1991
|
+
depthOfFieldSideCode(params.blurSide),
|
|
1992
|
+
depthOfFieldQualityCode(params.quality),
|
|
1993
|
+
near,
|
|
1994
|
+
far,
|
|
1995
|
+
input.useTemporalDepth ? 1 : 0,
|
|
1996
|
+
0,
|
|
1997
|
+
// row2.w reserved
|
|
1998
|
+
cocCoefficient,
|
|
1999
|
+
// row3.x / WGSL reserved.x
|
|
2000
|
+
0,
|
|
2001
|
+
0,
|
|
2002
|
+
0
|
|
2003
|
+
]);
|
|
2004
|
+
return new Uint8Array(payload.buffer);
|
|
2005
|
+
}
|
|
2006
|
+
function hasVolumetricFogCapability(caps, shaders) {
|
|
2007
|
+
return caps.compute && caps.storageTexture && shaders !== void 0;
|
|
2008
|
+
}
|
|
2009
|
+
function resolveVolumetricFogLightPair(world, point, spot) {
|
|
2010
|
+
const expected = "PointLight + SpotLight in the same World";
|
|
2011
|
+
if (!world.get(point, Entity).ok || !world.get(spot, Entity).ok) {
|
|
2012
|
+
return {
|
|
2013
|
+
status: "unresolved",
|
|
2014
|
+
reason: "missing-entity",
|
|
2015
|
+
point,
|
|
2016
|
+
spot,
|
|
2017
|
+
expected,
|
|
2018
|
+
actual: "point or spot entity is absent from this World",
|
|
2019
|
+
hint: "assign two live entities from the same World and retry"
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
if (point === spot) {
|
|
2023
|
+
return {
|
|
2024
|
+
status: "unresolved",
|
|
2025
|
+
reason: "same-entity",
|
|
2026
|
+
point,
|
|
2027
|
+
spot,
|
|
2028
|
+
expected,
|
|
2029
|
+
actual: "PointLight and SpotLight share one EntityHandle",
|
|
2030
|
+
hint: "spawn PointLight and SpotLight on two different entities"
|
|
956
2031
|
};
|
|
957
|
-
this.pendingFrame = pending;
|
|
958
|
-
return this.projection;
|
|
959
2032
|
}
|
|
960
|
-
|
|
961
|
-
|
|
2033
|
+
const pointValid = world.hasComponent(point, PointLight) && world.hasComponent(point, Transform);
|
|
2034
|
+
const spotValid = world.hasComponent(spot, SpotLight) && world.hasComponent(spot, Transform);
|
|
2035
|
+
if (!pointValid || !spotValid) {
|
|
2036
|
+
return {
|
|
2037
|
+
status: "unresolved",
|
|
2038
|
+
reason: "wrong-component",
|
|
2039
|
+
point,
|
|
2040
|
+
spot,
|
|
2041
|
+
expected,
|
|
2042
|
+
actual: `pointValid=${pointValid}; spotValid=${spotValid}`,
|
|
2043
|
+
hint: "attach PointLight and SpotLight with Transform to separate same-World entities"
|
|
2044
|
+
};
|
|
962
2045
|
}
|
|
963
|
-
|
|
964
|
-
|
|
2046
|
+
return { status: "available", mode: "point-spot", point, spot };
|
|
2047
|
+
}
|
|
2048
|
+
function resolveSelectedVolumetricLight(world, entity) {
|
|
2049
|
+
const expected = "DirectionalLight | PointLight | SpotLight";
|
|
2050
|
+
if (!world.get(entity, Entity).ok) {
|
|
2051
|
+
return {
|
|
2052
|
+
status: "unresolved",
|
|
2053
|
+
reason: "missing-entity",
|
|
2054
|
+
entity,
|
|
2055
|
+
expected,
|
|
2056
|
+
actual: "missing or foreign World entity",
|
|
2057
|
+
hint: "assign VolumetricFog.light to a live DirectionalLight or SpotLight in this World"
|
|
2058
|
+
};
|
|
965
2059
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
return { median: 0, p95: 0, last: this.lastQueryLatencyUs };
|
|
970
|
-
}
|
|
971
|
-
const sorted = [...this.queryLatencySamplesUs].sort((left, right) => left - right);
|
|
972
|
-
const rank = (percentile) => sorted[Math.max(0, Math.ceil(sorted.length * percentile) - 1)] ?? 0;
|
|
2060
|
+
const hasDirectional = world.hasComponent(entity, DirectionalLight);
|
|
2061
|
+
const hasSpot = world.hasComponent(entity, SpotLight);
|
|
2062
|
+
if (hasDirectional && hasSpot) {
|
|
973
2063
|
return {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
2064
|
+
status: "unresolved",
|
|
2065
|
+
reason: "ambiguous",
|
|
2066
|
+
entity,
|
|
2067
|
+
expected,
|
|
2068
|
+
actual: "DirectionalLight + SpotLight",
|
|
2069
|
+
hint: "remove one mutually exclusive light component from the selected entity"
|
|
977
2070
|
};
|
|
978
2071
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
* completion also includes the asynchronous map/unmap transition. Keeping
|
|
983
|
-
* that fence here makes `renderer.observe()` publish the latest visibility
|
|
984
|
-
* result and measured map latency instead of racing the readback promise.
|
|
985
|
-
*/
|
|
986
|
-
async waitForCompletions() {
|
|
987
|
-
for (; ; ) {
|
|
988
|
-
const completions = [...this.activeCompletions.values()];
|
|
989
|
-
if (completions.length === 0) return;
|
|
990
|
-
await Promise.all(completions);
|
|
991
|
-
}
|
|
2072
|
+
if (hasDirectional) return { status: "available", entity, kind: "directional" };
|
|
2073
|
+
if (world.hasComponent(entity, PointLight) && world.hasComponent(entity, Transform)) {
|
|
2074
|
+
return { status: "available", entity, kind: "point" };
|
|
992
2075
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
2076
|
+
if (hasSpot && world.hasComponent(entity, Transform)) {
|
|
2077
|
+
return { status: "available", entity, kind: "spot" };
|
|
2078
|
+
}
|
|
2079
|
+
const actual = hasSpot ? "SpotLight without Transform" : world.hasComponent(entity, PointLight) ? "PointLight" : "other component";
|
|
2080
|
+
return {
|
|
2081
|
+
status: "unresolved",
|
|
2082
|
+
reason: "wrong-component",
|
|
2083
|
+
entity,
|
|
2084
|
+
expected,
|
|
2085
|
+
actual,
|
|
2086
|
+
hint: "add exactly one supported light component and a Transform for PointLight or SpotLight in the same World"
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
function resolveIntegratedVolumeConsumer(resource, consumer) {
|
|
2090
|
+
return { consumer, resource };
|
|
2091
|
+
}
|
|
2092
|
+
var FACE_ORIENTATION = [
|
|
2093
|
+
{ direction: [1, 0, 0], up: [0, 1, 0] },
|
|
2094
|
+
{ direction: [-1, 0, 0], up: [0, 1, 0] },
|
|
2095
|
+
{ direction: [0, 1, 0], up: [0, 0, -1] },
|
|
2096
|
+
{ direction: [0, -1, 0], up: [0, 0, 1] },
|
|
2097
|
+
{ direction: [0, 0, 1], up: [0, 1, 0] },
|
|
2098
|
+
{ direction: [0, 0, -1], up: [0, 1, 0] }
|
|
2099
|
+
];
|
|
2100
|
+
function assertRange(near, far) {
|
|
2101
|
+
if (!Number.isFinite(near) || near <= 0 || !Number.isFinite(far) || far <= near) {
|
|
2102
|
+
throw new RangeError("CubeCamera requires finite near > 0 and far > near.");
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
function buildCubeCameraFaceViews(input) {
|
|
2106
|
+
assertRange(input.near, input.far);
|
|
2107
|
+
const views = [];
|
|
2108
|
+
for (const [index, face] of CUBE_CAMERA_FACE_ORDER.entries()) {
|
|
2109
|
+
const orientation = FACE_ORIENTATION[index];
|
|
2110
|
+
if (orientation === void 0)
|
|
2111
|
+
throw new Error(`CubeCamera face orientation missing at ${index}`);
|
|
2112
|
+
const target = [
|
|
2113
|
+
(input.position[0] ?? 0) + orientation.direction[0],
|
|
2114
|
+
(input.position[1] ?? 0) + orientation.direction[1],
|
|
2115
|
+
(input.position[2] ?? 0) + orientation.direction[2]
|
|
2116
|
+
];
|
|
2117
|
+
const view = mat4.lookAt(mat4.create(), input.position, target, orientation.up);
|
|
2118
|
+
const projection = mat4.perspective(mat4.create(), Math.PI / 2, 1, input.near, input.far);
|
|
2119
|
+
const viewProjection = mat4.multiply(mat4.create(), projection, view);
|
|
2120
|
+
views.push({
|
|
2121
|
+
position: input.position,
|
|
2122
|
+
face,
|
|
2123
|
+
direction: orientation.direction,
|
|
2124
|
+
up: orientation.up,
|
|
2125
|
+
near: input.near,
|
|
2126
|
+
far: input.far,
|
|
2127
|
+
view,
|
|
2128
|
+
projection,
|
|
2129
|
+
viewProjection
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
return views;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// src/mesh-material-bindings.ts
|
|
2136
|
+
var EMPTY_RESIDENCY = Object.freeze({
|
|
2137
|
+
readiness: "pending",
|
|
2138
|
+
samplers: Object.freeze([]),
|
|
2139
|
+
textures: Object.freeze([])
|
|
2140
|
+
});
|
|
2141
|
+
function projectMeshMaterialBindingObservation(input) {
|
|
2142
|
+
const residency = input.bindings.map((_, index) => {
|
|
2143
|
+
const value = input.residency?.[index] ?? EMPTY_RESIDENCY;
|
|
2144
|
+
return Object.freeze({
|
|
2145
|
+
...value,
|
|
2146
|
+
samplers: Object.freeze(value.samplers.map((sampler) => Object.freeze({ ...sampler }))),
|
|
2147
|
+
textures: Object.freeze(value.textures.map((texture) => Object.freeze({ ...texture }))),
|
|
2148
|
+
...value.preparationFailure === void 0 ? {} : {
|
|
2149
|
+
preparationFailure: Object.freeze({
|
|
2150
|
+
...value.preparationFailure,
|
|
2151
|
+
...value.preparationFailure.detail === void 0 ? {} : { detail: Object.freeze({ ...value.preparationFailure.detail }) }
|
|
2152
|
+
})
|
|
1007
2153
|
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
submissionGeneration
|
|
1042
|
-
});
|
|
2154
|
+
});
|
|
2155
|
+
});
|
|
2156
|
+
return {
|
|
2157
|
+
worldId: input.worldId,
|
|
2158
|
+
...input.worldIdentity === void 0 ? {} : { worldIdentity: input.worldIdentity },
|
|
2159
|
+
entityKey: input.entityKey,
|
|
2160
|
+
bindings: Object.freeze([...input.bindings]),
|
|
2161
|
+
diagnostics: Object.freeze([...input.diagnostics]),
|
|
2162
|
+
residency: Object.freeze(residency)
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
function summarizeMeshMaterialBindings(observations) {
|
|
2166
|
+
let ready = 0;
|
|
2167
|
+
let pending = 0;
|
|
2168
|
+
let failed = 0;
|
|
2169
|
+
let lastKnownGood = 0;
|
|
2170
|
+
let textureCount = 0;
|
|
2171
|
+
let samplerCount = 0;
|
|
2172
|
+
for (const observation of observations) {
|
|
2173
|
+
for (const residency of observation.residency) {
|
|
2174
|
+
switch (residency.readiness) {
|
|
2175
|
+
case "ready":
|
|
2176
|
+
ready += 1;
|
|
2177
|
+
break;
|
|
2178
|
+
case "pending":
|
|
2179
|
+
pending += 1;
|
|
2180
|
+
break;
|
|
2181
|
+
case "failed":
|
|
2182
|
+
failed += 1;
|
|
2183
|
+
break;
|
|
2184
|
+
case "last-known-good":
|
|
2185
|
+
lastKnownGood += 1;
|
|
2186
|
+
break;
|
|
1043
2187
|
}
|
|
1044
|
-
|
|
2188
|
+
textureCount += residency.textures.length;
|
|
2189
|
+
samplerCount += residency.samplers.length;
|
|
1045
2190
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
2191
|
+
}
|
|
2192
|
+
return {
|
|
2193
|
+
total: ready + pending + failed + lastKnownGood,
|
|
2194
|
+
ready,
|
|
2195
|
+
pending,
|
|
2196
|
+
failed,
|
|
2197
|
+
lastKnownGood,
|
|
2198
|
+
textureCount,
|
|
2199
|
+
samplerCount
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
function resolveMeshMaterialBindings(mesh, rendererOverrides, deps) {
|
|
2203
|
+
const bindings = [];
|
|
2204
|
+
const diagnostics = [];
|
|
2205
|
+
if (!Array.isArray(mesh.materialSlots)) {
|
|
2206
|
+
return { ok: false, code: "mesh-material-slots-missing" };
|
|
2207
|
+
}
|
|
2208
|
+
if (rendererOverrides.length > mesh.materialSlots.length) {
|
|
2209
|
+
diagnostics.push({
|
|
2210
|
+
code: "mesh-renderer-material-override-overflow",
|
|
2211
|
+
slotIndex: mesh.materialSlots.length
|
|
1060
2212
|
});
|
|
1061
2213
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
2214
|
+
for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
|
|
2215
|
+
const override = rendererOverrides[slotIndex] ?? 0;
|
|
2216
|
+
if (override !== 0 && deps.isValidOverride(override)) {
|
|
2217
|
+
bindings.push({ handle: override, source: "renderer-override" });
|
|
2218
|
+
continue;
|
|
1066
2219
|
}
|
|
1067
|
-
if (
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
2220
|
+
if (override !== 0) {
|
|
2221
|
+
diagnostics.push({
|
|
2222
|
+
code: "mesh-renderer-material-override-invalid",
|
|
2223
|
+
slotIndex,
|
|
2224
|
+
handle: override
|
|
2225
|
+
});
|
|
1071
2226
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
try {
|
|
1083
|
-
await this.device.queue.onSubmittedWorkDone();
|
|
1084
|
-
const mapStart = performance.now();
|
|
1085
|
-
await runtimeTestHooks?.beforeMap?.();
|
|
1086
|
-
const mapped = await pending.resources.stagingBuffer.mapAsync(
|
|
1087
|
-
GPU_BUFFER_USAGE_MAP_READ,
|
|
1088
|
-
0,
|
|
1089
|
-
pending.reservations.length * 8
|
|
1090
|
-
);
|
|
1091
|
-
if (!mapped.ok) throw mapped.error;
|
|
1092
|
-
this.lastQueryLatencyUs = Math.max(0, Math.round((performance.now() - mapStart) * 1e3));
|
|
1093
|
-
this.queryLatencySamplesUs.push(this.lastQueryLatencyUs);
|
|
1094
|
-
if (this.queryLatencySamplesUs.length > 64) this.queryLatencySamplesUs.shift();
|
|
1095
|
-
const bytes = mapped.value.getMappedRange(0, pending.reservations.length * 8);
|
|
1096
|
-
if (!bytes.ok) throw bytes.error;
|
|
1097
|
-
const data = new Uint8Array(bytes.value.slice(0));
|
|
1098
|
-
const dataView = new DataView(data.buffer);
|
|
1099
|
-
const firstQuery = pending.reservations[0]?.queryIndex ?? 0;
|
|
1100
|
-
mapped.value.unmap();
|
|
1101
|
-
for (let ticketIndex = tickets.length - 1; ticketIndex >= 0; ticketIndex -= 1) {
|
|
1102
|
-
const ticket = tickets[ticketIndex];
|
|
1103
|
-
if (ticket === void 0) continue;
|
|
1104
|
-
const candidateIndex = ticket.queryIndex - firstQuery;
|
|
1105
|
-
const samples = dataView.getUint32(candidateIndex * 8, true);
|
|
1106
|
-
const completion = this.pool.complete(ticket, samples);
|
|
1107
|
-
if (completion.status !== "accepted") continue;
|
|
1108
|
-
const candidate = pending.candidates[candidateIndex];
|
|
1109
|
-
if (candidate === void 0) continue;
|
|
1110
|
-
this.facets.applyConfidence(candidate.view, candidate.primitive, {
|
|
1111
|
-
type: "result",
|
|
1112
|
-
samples,
|
|
1113
|
-
submissionGeneration: ticket.submissionGeneration
|
|
1114
|
-
});
|
|
1115
|
-
this.facets.applyCompletion(
|
|
1116
|
-
candidate.view,
|
|
1117
|
-
candidate.primitive,
|
|
1118
|
-
candidate.epoch,
|
|
1119
|
-
candidate.candidate
|
|
1120
|
-
);
|
|
1121
|
-
}
|
|
1122
|
-
} catch {
|
|
1123
|
-
for (const ticket of tickets) {
|
|
1124
|
-
this.pool.complete(ticket, Number.NaN);
|
|
1125
|
-
const candidate = pending.candidates[ticket.queryIndex - (pending.reservations[0]?.queryIndex ?? 0)];
|
|
1126
|
-
if (candidate !== void 0) {
|
|
1127
|
-
this.facets.applyConfidence(candidate.view, candidate.primitive, {
|
|
1128
|
-
type: "failure",
|
|
1129
|
-
submissionGeneration: ticket.submissionGeneration
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
2227
|
+
const declaredDefault = mesh.materialSlots[slotIndex]?.defaultMaterial;
|
|
2228
|
+
if (declaredDefault !== void 0) {
|
|
2229
|
+
const handle = deps.resolveMeshDefault(declaredDefault);
|
|
2230
|
+
if (handle === void 0) {
|
|
2231
|
+
return {
|
|
2232
|
+
ok: false,
|
|
2233
|
+
code: "mesh-default-material-not-ready",
|
|
2234
|
+
slotIndex,
|
|
2235
|
+
defaultMaterial: declaredDefault
|
|
2236
|
+
};
|
|
1132
2237
|
}
|
|
2238
|
+
bindings.push({ handle, source: "mesh-default" });
|
|
2239
|
+
continue;
|
|
1133
2240
|
}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
2241
|
+
bindings.push({ handle: 0, source: "engine-default" });
|
|
2242
|
+
}
|
|
2243
|
+
return { ok: true, bindings, diagnostics };
|
|
2244
|
+
}
|
|
2245
|
+
function resolveVisibility(world, hierarchy = projectHierarchy(world)) {
|
|
2246
|
+
const query = world.query({ read: [Visibility] });
|
|
2247
|
+
const visitVisibilityRows = (visit) => {
|
|
2248
|
+
if (!query.ok) return;
|
|
2249
|
+
const spans = query.value.spans();
|
|
2250
|
+
if (spans.ok) {
|
|
2251
|
+
for (const span of spans.value) {
|
|
2252
|
+
const states = span.get(Visibility).state;
|
|
2253
|
+
for (let index = 0; index < span.length; index += 1) {
|
|
2254
|
+
visit(span.entities[index], states[index] ?? 0);
|
|
2255
|
+
}
|
|
1146
2256
|
}
|
|
1147
|
-
|
|
1148
|
-
return ok$1(void 0);
|
|
2257
|
+
return;
|
|
1149
2258
|
}
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
return err$1(runtimeError("the backend must allocate the bounded proxy geometry buffer"));
|
|
2259
|
+
for (const row of query.value) {
|
|
2260
|
+
visit(row.entity, row.get(Visibility).state);
|
|
1153
2261
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
if (
|
|
1160
|
-
|
|
1161
|
-
pass.setVertexBuffer(0, buffer);
|
|
1162
|
-
let vertexOffset = 0;
|
|
1163
|
-
for (let index = 0; index < pending.reservations.length; index += 1) {
|
|
1164
|
-
const vertices = pending.candidates[index]?.proxyVertices;
|
|
1165
|
-
if (vertices === void 0 || vertices.length === 0) continue;
|
|
1166
|
-
const began = pass.beginOcclusionQuery(pending.reservations[index]?.queryIndex ?? 0);
|
|
1167
|
-
if (!began.ok) return began;
|
|
1168
|
-
pass.draw(vertices.length / 4, 1, vertexOffset / 4, 0);
|
|
1169
|
-
const ended = pass.endOcclusionQuery();
|
|
1170
|
-
if (!ended.ok) return ended;
|
|
1171
|
-
vertexOffset += vertices.length;
|
|
2262
|
+
};
|
|
2263
|
+
let intentCount = 0;
|
|
2264
|
+
let hasAnyHiddenIntent = false;
|
|
2265
|
+
visitVisibilityRows((_entity, rawState) => {
|
|
2266
|
+
intentCount += 1;
|
|
2267
|
+
if (rawState === VisibilityStateValue.hidden) {
|
|
2268
|
+
hasAnyHiddenIntent = true;
|
|
1172
2269
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
if (
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
2270
|
+
});
|
|
2271
|
+
let resolveEntity;
|
|
2272
|
+
let hasIntent;
|
|
2273
|
+
const ensureResolver = () => {
|
|
2274
|
+
if (resolveEntity !== void 0) return;
|
|
2275
|
+
const intentByEntity = /* @__PURE__ */ new Map();
|
|
2276
|
+
visitVisibilityRows((entity, rawState) => {
|
|
2277
|
+
const intent = visibilityStateFromU32(rawState);
|
|
2278
|
+
if (intent !== void 0) {
|
|
2279
|
+
intentByEntity.set(entity, intent);
|
|
2280
|
+
}
|
|
1182
2281
|
});
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
2282
|
+
hasIntent = (entity) => intentByEntity.has(entity);
|
|
2283
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
2284
|
+
const resolving = /* @__PURE__ */ new Set();
|
|
2285
|
+
resolveEntity = (entity) => {
|
|
2286
|
+
const existing = resolved.get(entity);
|
|
2287
|
+
if (existing !== void 0) return existing;
|
|
2288
|
+
if (resolving.has(entity)) return void 0;
|
|
2289
|
+
const intent = intentByEntity.get(entity) ?? "inherited";
|
|
2290
|
+
resolving.add(entity);
|
|
2291
|
+
let result;
|
|
2292
|
+
if (intent === "hidden") {
|
|
2293
|
+
result = { intent, effective: "hidden", source: "self" };
|
|
2294
|
+
} else if (intent === "visible") {
|
|
2295
|
+
result = { intent, effective: "visible", source: "self" };
|
|
2296
|
+
} else {
|
|
2297
|
+
const parent = hierarchy.getParent(entity);
|
|
2298
|
+
const parentResult = parent === void 0 ? void 0 : resolveEntity?.(parent);
|
|
2299
|
+
result = parentResult === void 0 ? { intent, effective: "visible", source: "default" } : { intent, effective: parentResult.effective, source: "parent" };
|
|
2300
|
+
}
|
|
2301
|
+
resolving.delete(entity);
|
|
2302
|
+
resolved.set(entity, result);
|
|
2303
|
+
return result;
|
|
2304
|
+
};
|
|
2305
|
+
};
|
|
2306
|
+
const snapshot = {
|
|
2307
|
+
diagnostics: hierarchy.diagnostics,
|
|
2308
|
+
hasAnyIntent: intentCount > 0,
|
|
2309
|
+
hasAnyHiddenIntent,
|
|
2310
|
+
get(entity) {
|
|
2311
|
+
ensureResolver();
|
|
2312
|
+
return hasIntent?.(entity) ? resolveEntity?.(entity) : void 0;
|
|
2313
|
+
},
|
|
2314
|
+
effective(entity) {
|
|
2315
|
+
ensureResolver();
|
|
2316
|
+
return resolveEntity?.(entity)?.effective ?? "visible";
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
return snapshot;
|
|
2320
|
+
}
|
|
2321
|
+
function selectCloudLayerFrame(candidates, worldTimeSeconds = 0, sunDirection = void 0, sunRadiance = void 0) {
|
|
2322
|
+
if (candidates.length === 0) return ok(void 0);
|
|
2323
|
+
if (candidates.length > 1) return err(new CloudLayerOwnerConflictError(candidates.length));
|
|
2324
|
+
const candidate = candidates[0];
|
|
2325
|
+
if (candidate === void 0) return ok(void 0);
|
|
2326
|
+
const validated = validateCloudLayer(candidate.data);
|
|
2327
|
+
if (!validated.ok) return validated;
|
|
2328
|
+
return ok(
|
|
2329
|
+
Object.freeze({
|
|
2330
|
+
status: "available",
|
|
2331
|
+
entityKey: candidate.entityKey,
|
|
2332
|
+
sourceKey: cloudLayerSourceKey(validated.value),
|
|
2333
|
+
params: validated.value,
|
|
2334
|
+
worldTimeSeconds: Number.isFinite(worldTimeSeconds) ? Math.max(0, worldTimeSeconds) : 0,
|
|
2335
|
+
sunDirection: sunDirection === void 0 ? void 0 : Object.freeze([...sunDirection]),
|
|
2336
|
+
sunRadiance: sunRadiance === void 0 ? void 0 : Object.freeze([...sunRadiance]),
|
|
2337
|
+
revision: 1
|
|
2338
|
+
})
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
function extractCloudLayer(world) {
|
|
2342
|
+
const candidates = [];
|
|
2343
|
+
try {
|
|
2344
|
+
const query = world.query({ read: [CloudLayer] });
|
|
2345
|
+
if (!query.ok) return ok(void 0);
|
|
2346
|
+
for (const row of query.value)
|
|
2347
|
+
candidates.push({ entityKey: row.entity, data: row.get(CloudLayer) });
|
|
2348
|
+
} catch {
|
|
2349
|
+
return ok(void 0);
|
|
1186
2350
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
2351
|
+
let sunDirection;
|
|
2352
|
+
let sunRadiance;
|
|
2353
|
+
try {
|
|
2354
|
+
const query = world.query({ read: [DirectionalLight] });
|
|
2355
|
+
if (query.ok) {
|
|
2356
|
+
for (const row of query.value) {
|
|
2357
|
+
const light = row.get(DirectionalLight);
|
|
2358
|
+
const intensity = Number.isFinite(light.intensity) ? Math.max(0, light.intensity) : 0;
|
|
2359
|
+
sunDirection = [
|
|
2360
|
+
-(light.direction[0] ?? 0),
|
|
2361
|
+
-(light.direction[1] ?? -1),
|
|
2362
|
+
-(light.direction[2] ?? 0)
|
|
2363
|
+
];
|
|
2364
|
+
sunRadiance = [
|
|
2365
|
+
(light.color[0] ?? 1) * intensity,
|
|
2366
|
+
(light.color[1] ?? 1) * intensity,
|
|
2367
|
+
(light.color[2] ?? 1) * intensity
|
|
2368
|
+
];
|
|
2369
|
+
break;
|
|
2370
|
+
}
|
|
1190
2371
|
}
|
|
1191
|
-
|
|
1192
|
-
const module = this.shaderModuleFactory.createShaderModule({
|
|
1193
|
-
label: "occlusion-bounds-proxy",
|
|
1194
|
-
code: PROXY_BOUNDS_WGSL
|
|
1195
|
-
});
|
|
1196
|
-
if (!module.ok) return void 0;
|
|
1197
|
-
const created = this.device.createRenderPipeline({
|
|
1198
|
-
label: "occlusion-bounds-proxy",
|
|
1199
|
-
layout: "auto",
|
|
1200
|
-
vertex: {
|
|
1201
|
-
module: module.value,
|
|
1202
|
-
entryPoint: "vs_main",
|
|
1203
|
-
buffers: [
|
|
1204
|
-
{
|
|
1205
|
-
arrayStride: 16,
|
|
1206
|
-
stepMode: "vertex",
|
|
1207
|
-
attributes: [{ shaderLocation: 0, offset: 0, format: "float32x4" }]
|
|
1208
|
-
}
|
|
1209
|
-
]
|
|
1210
|
-
},
|
|
1211
|
-
// The proxy is recorded in the typed main pass, whose color attachment
|
|
1212
|
-
// remains RGBA16Float. Keep the pass compatible while suppressing all
|
|
1213
|
-
// color writes; occlusion only consumes depth samples.
|
|
1214
|
-
fragment: {
|
|
1215
|
-
module: module.value,
|
|
1216
|
-
entryPoint: "fs_main",
|
|
1217
|
-
targets: [{ format: "rgba16float", writeMask: 0 }]
|
|
1218
|
-
},
|
|
1219
|
-
primitive: { topology: "triangle-list" },
|
|
1220
|
-
depthStencil: {
|
|
1221
|
-
format: "depth24plus-stencil8",
|
|
1222
|
-
depthWriteEnabled: false,
|
|
1223
|
-
depthCompare: "less-equal"
|
|
1224
|
-
},
|
|
1225
|
-
multisample: { count: sampleCount }
|
|
1226
|
-
});
|
|
1227
|
-
if (!created.ok) return void 0;
|
|
1228
|
-
this.proxyPipeline = created.value;
|
|
1229
|
-
this.proxyPipelineSampleCount = sampleCount;
|
|
1230
|
-
return created.value;
|
|
2372
|
+
} catch {
|
|
1231
2373
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
return new RhiError({
|
|
1251
|
-
code: "webgpu-runtime-error",
|
|
1252
|
-
expected: "RenderSystem owns one active occlusion candidate for the shared frame",
|
|
1253
|
-
hint
|
|
1254
|
-
});
|
|
2374
|
+
return selectCloudLayerFrame(
|
|
2375
|
+
candidates,
|
|
2376
|
+
world.getResource(Time).elapsed,
|
|
2377
|
+
sunDirection,
|
|
2378
|
+
sunRadiance
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
function extractVolumetricFog(inputs) {
|
|
2382
|
+
if (inputs.length === 0) return ok({ status: "off" });
|
|
2383
|
+
if (inputs.length > MAX_VOLUMETRIC_FOG_OWNERS)
|
|
2384
|
+
return err(new VolumeOwnerConflictError(inputs.length));
|
|
2385
|
+
const fogs = [];
|
|
2386
|
+
for (const input of inputs) {
|
|
2387
|
+
const validated = validateVolumetricFog(input);
|
|
2388
|
+
if (!validated.ok) return validated;
|
|
2389
|
+
fogs.push(validated.value);
|
|
2390
|
+
}
|
|
2391
|
+
return ok({ status: "available", fogs });
|
|
1255
2392
|
}
|
|
1256
2393
|
|
|
1257
2394
|
// src/extract/directional-shadow-projection.ts
|
|
@@ -1514,224 +2651,6 @@ function asFontHandle(raw) {
|
|
|
1514
2651
|
return toShared(raw);
|
|
1515
2652
|
}
|
|
1516
2653
|
|
|
1517
|
-
// src/instances-derived-bounds.ts
|
|
1518
|
-
var fingerprintScalar = new Float32Array(1);
|
|
1519
|
-
var fingerprintBits = new Uint32Array(fingerprintScalar.buffer);
|
|
1520
|
-
function fingerprintNumericArray(values) {
|
|
1521
|
-
let hash = 2166136261;
|
|
1522
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
1523
|
-
fingerprintScalar[0] = Number(values[index]);
|
|
1524
|
-
hash = Math.imul(hash ^ (fingerprintBits[0] ?? 0), 16777619) >>> 0;
|
|
1525
|
-
}
|
|
1526
|
-
return hash >>> 0;
|
|
1527
|
-
}
|
|
1528
|
-
function finiteAabb(aabb) {
|
|
1529
|
-
if (aabb === void 0 || aabb.length < 6) return false;
|
|
1530
|
-
const minX = aabb[0];
|
|
1531
|
-
const minY = aabb[1];
|
|
1532
|
-
const minZ = aabb[2];
|
|
1533
|
-
const maxX = aabb[3];
|
|
1534
|
-
const maxY = aabb[4];
|
|
1535
|
-
const maxZ = aabb[5];
|
|
1536
|
-
return Number.isFinite(minX) && Number.isFinite(minY) && Number.isFinite(minZ) && Number.isFinite(maxX) && Number.isFinite(maxY) && Number.isFinite(maxZ) && minX <= maxX && minY <= maxY && minZ <= maxZ;
|
|
1537
|
-
}
|
|
1538
|
-
function finiteMatrix(matrix) {
|
|
1539
|
-
if (matrix.length < 16) return false;
|
|
1540
|
-
for (let index = 0; index < 16; index += 1) {
|
|
1541
|
-
if (!Number.isFinite(matrix[index])) return false;
|
|
1542
|
-
}
|
|
1543
|
-
return true;
|
|
1544
|
-
}
|
|
1545
|
-
function multiplyMat4(out, left, right, rightOffset = 0) {
|
|
1546
|
-
for (let column = 0; column < 4; column += 1) {
|
|
1547
|
-
const rightColumn = rightOffset + column * 4;
|
|
1548
|
-
const right0 = Number(right[rightColumn] ?? 0);
|
|
1549
|
-
const right1 = Number(right[rightColumn + 1] ?? 0);
|
|
1550
|
-
const right2 = Number(right[rightColumn + 2] ?? 0);
|
|
1551
|
-
const right3 = Number(right[rightColumn + 3] ?? 0);
|
|
1552
|
-
const outOffset = column * 4;
|
|
1553
|
-
out[outOffset] = Number(left[0] ?? 0) * right0 + Number(left[4] ?? 0) * right1 + Number(left[8] ?? 0) * right2 + Number(left[12] ?? 0) * right3;
|
|
1554
|
-
out[outOffset + 1] = Number(left[1] ?? 0) * right0 + Number(left[5] ?? 0) * right1 + Number(left[9] ?? 0) * right2 + Number(left[13] ?? 0) * right3;
|
|
1555
|
-
out[outOffset + 2] = Number(left[2] ?? 0) * right0 + Number(left[6] ?? 0) * right1 + Number(left[10] ?? 0) * right2 + Number(left[14] ?? 0) * right3;
|
|
1556
|
-
out[outOffset + 3] = Number(left[3] ?? 0) * right0 + Number(left[7] ?? 0) * right1 + Number(left[11] ?? 0) * right2 + Number(left[15] ?? 0) * right3;
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
function finiteMatrixAt(values, offset) {
|
|
1560
|
-
if (values.length - offset < 16) return false;
|
|
1561
|
-
for (let index = 0; index < 16; index += 1) {
|
|
1562
|
-
if (!Number.isFinite(values[offset + index])) return false;
|
|
1563
|
-
}
|
|
1564
|
-
return true;
|
|
1565
|
-
}
|
|
1566
|
-
function isAffine(matrix) {
|
|
1567
|
-
return matrix[3] === 0 && matrix[7] === 0 && matrix[11] === 0 && matrix[15] === 1;
|
|
1568
|
-
}
|
|
1569
|
-
function includeBox(out, candidate, initialized) {
|
|
1570
|
-
const minX = candidate[0];
|
|
1571
|
-
const minY = candidate[1];
|
|
1572
|
-
const minZ = candidate[2];
|
|
1573
|
-
const maxX = candidate[3];
|
|
1574
|
-
const maxY = candidate[4];
|
|
1575
|
-
const maxZ = candidate[5];
|
|
1576
|
-
if (!initialized) {
|
|
1577
|
-
out[0] = minX;
|
|
1578
|
-
out[1] = minY;
|
|
1579
|
-
out[2] = minZ;
|
|
1580
|
-
out[3] = maxX;
|
|
1581
|
-
out[4] = maxY;
|
|
1582
|
-
out[5] = maxZ;
|
|
1583
|
-
return true;
|
|
1584
|
-
}
|
|
1585
|
-
if (minX < (out[0] ?? Number.POSITIVE_INFINITY)) out[0] = minX;
|
|
1586
|
-
if (minY < (out[1] ?? Number.POSITIVE_INFINITY)) out[1] = minY;
|
|
1587
|
-
if (minZ < (out[2] ?? Number.POSITIVE_INFINITY)) out[2] = minZ;
|
|
1588
|
-
if (maxX > (out[3] ?? Number.NEGATIVE_INFINITY)) out[3] = maxX;
|
|
1589
|
-
if (maxY > (out[4] ?? Number.NEGATIVE_INFINITY)) out[4] = maxY;
|
|
1590
|
-
if (maxZ > (out[5] ?? Number.NEGATIVE_INFINITY)) out[5] = maxZ;
|
|
1591
|
-
return true;
|
|
1592
|
-
}
|
|
1593
|
-
function transformAabb(out, local, matrix) {
|
|
1594
|
-
const minX = local[0];
|
|
1595
|
-
const minY = local[1];
|
|
1596
|
-
const minZ = local[2];
|
|
1597
|
-
const maxX = local[3];
|
|
1598
|
-
const maxY = local[4];
|
|
1599
|
-
const maxZ = local[5];
|
|
1600
|
-
const centerX = (minX + maxX) * 0.5;
|
|
1601
|
-
const centerY = (minY + maxY) * 0.5;
|
|
1602
|
-
const centerZ = (minZ + maxZ) * 0.5;
|
|
1603
|
-
const extentX = (maxX - minX) * 0.5;
|
|
1604
|
-
const extentY = (maxY - minY) * 0.5;
|
|
1605
|
-
const extentZ = (maxZ - minZ) * 0.5;
|
|
1606
|
-
if (isAffine(matrix)) {
|
|
1607
|
-
const centerWorldX = matrix[0] * centerX + matrix[4] * centerY + matrix[8] * centerZ + matrix[12];
|
|
1608
|
-
const centerWorldY = matrix[1] * centerX + matrix[5] * centerY + matrix[9] * centerZ + matrix[13];
|
|
1609
|
-
const centerWorldZ = matrix[2] * centerX + matrix[6] * centerY + matrix[10] * centerZ + matrix[14];
|
|
1610
|
-
const extentWorldX = Math.abs(matrix[0]) * extentX + Math.abs(matrix[4]) * extentY + Math.abs(matrix[8]) * extentZ;
|
|
1611
|
-
const extentWorldY = Math.abs(matrix[1]) * extentX + Math.abs(matrix[5]) * extentY + Math.abs(matrix[9]) * extentZ;
|
|
1612
|
-
const extentWorldZ = Math.abs(matrix[2]) * extentX + Math.abs(matrix[6]) * extentY + Math.abs(matrix[10]) * extentZ;
|
|
1613
|
-
out[0] = centerWorldX - extentWorldX;
|
|
1614
|
-
out[1] = centerWorldY - extentWorldY;
|
|
1615
|
-
out[2] = centerWorldZ - extentWorldZ;
|
|
1616
|
-
out[3] = centerWorldX + extentWorldX;
|
|
1617
|
-
out[4] = centerWorldY + extentWorldY;
|
|
1618
|
-
out[5] = centerWorldZ + extentWorldZ;
|
|
1619
|
-
return finiteAabb(out);
|
|
1620
|
-
}
|
|
1621
|
-
let minimumW = Number.POSITIVE_INFINITY;
|
|
1622
|
-
let maximumW = Number.NEGATIVE_INFINITY;
|
|
1623
|
-
for (let corner = 0; corner < 8; corner += 1) {
|
|
1624
|
-
const x = (corner & 1) === 0 ? minX : maxX;
|
|
1625
|
-
const y = (corner & 2) === 0 ? minY : maxY;
|
|
1626
|
-
const z = (corner & 4) === 0 ? minZ : maxZ;
|
|
1627
|
-
const w = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15];
|
|
1628
|
-
if (!Number.isFinite(w)) return false;
|
|
1629
|
-
if (w < minimumW) minimumW = w;
|
|
1630
|
-
if (w > maximumW) maximumW = w;
|
|
1631
|
-
}
|
|
1632
|
-
if (minimumW <= 0 && maximumW >= 0) return false;
|
|
1633
|
-
let unionMinX = Number.POSITIVE_INFINITY;
|
|
1634
|
-
let unionMinY = Number.POSITIVE_INFINITY;
|
|
1635
|
-
let unionMinZ = Number.POSITIVE_INFINITY;
|
|
1636
|
-
let unionMaxX = Number.NEGATIVE_INFINITY;
|
|
1637
|
-
let unionMaxY = Number.NEGATIVE_INFINITY;
|
|
1638
|
-
let unionMaxZ = Number.NEGATIVE_INFINITY;
|
|
1639
|
-
for (let corner = 0; corner < 8; corner += 1) {
|
|
1640
|
-
const x = (corner & 1) === 0 ? minX : maxX;
|
|
1641
|
-
const y = (corner & 2) === 0 ? minY : maxY;
|
|
1642
|
-
const z = (corner & 4) === 0 ? minZ : maxZ;
|
|
1643
|
-
const rawX = matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12];
|
|
1644
|
-
const rawY = matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13];
|
|
1645
|
-
const rawZ = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14];
|
|
1646
|
-
const w = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15];
|
|
1647
|
-
const projectedX = rawX / w;
|
|
1648
|
-
const projectedY = rawY / w;
|
|
1649
|
-
const projectedZ = rawZ / w;
|
|
1650
|
-
if (!Number.isFinite(projectedX) || !Number.isFinite(projectedY) || !Number.isFinite(projectedZ)) {
|
|
1651
|
-
return false;
|
|
1652
|
-
}
|
|
1653
|
-
if (projectedX < unionMinX) unionMinX = projectedX;
|
|
1654
|
-
if (projectedY < unionMinY) unionMinY = projectedY;
|
|
1655
|
-
if (projectedZ < unionMinZ) unionMinZ = projectedZ;
|
|
1656
|
-
if (projectedX > unionMaxX) unionMaxX = projectedX;
|
|
1657
|
-
if (projectedY > unionMaxY) unionMaxY = projectedY;
|
|
1658
|
-
if (projectedZ > unionMaxZ) unionMaxZ = projectedZ;
|
|
1659
|
-
}
|
|
1660
|
-
out[0] = unionMinX;
|
|
1661
|
-
out[1] = unionMinY;
|
|
1662
|
-
out[2] = unionMinZ;
|
|
1663
|
-
out[3] = unionMaxX;
|
|
1664
|
-
out[4] = unionMaxY;
|
|
1665
|
-
out[5] = unionMaxZ;
|
|
1666
|
-
return finiteAabb(out);
|
|
1667
|
-
}
|
|
1668
|
-
function deriveInstancesUnionBounds(input) {
|
|
1669
|
-
const { meshAabb, entityWorld, transforms } = input;
|
|
1670
|
-
if (!finiteAabb(meshAabb) || !finiteMatrix(entityWorld) || transforms === void 0) {
|
|
1671
|
-
return void 0;
|
|
1672
|
-
}
|
|
1673
|
-
if (transforms.length === 0 || transforms.length % 16 !== 0) return void 0;
|
|
1674
|
-
const local = meshAabb;
|
|
1675
|
-
const union = new Float32Array(6);
|
|
1676
|
-
const composed = new Float32Array(16);
|
|
1677
|
-
const transformed = new Float32Array(6);
|
|
1678
|
-
let initialized = false;
|
|
1679
|
-
for (let offset = 0; offset < transforms.length; offset += 16) {
|
|
1680
|
-
if (!finiteMatrixAt(transforms, offset)) return void 0;
|
|
1681
|
-
multiplyMat4(composed, entityWorld, transforms, offset);
|
|
1682
|
-
if (!finiteMatrix(composed) || !transformAabb(transformed, local, composed)) return void 0;
|
|
1683
|
-
initialized = includeBox(union, transformed, initialized);
|
|
1684
|
-
}
|
|
1685
|
-
return initialized && finiteAabb(union) ? union : void 0;
|
|
1686
|
-
}
|
|
1687
|
-
var InstanceBoundsCache = class {
|
|
1688
|
-
entries = /* @__PURE__ */ new Map();
|
|
1689
|
-
hits = 0;
|
|
1690
|
-
misses = 0;
|
|
1691
|
-
derives = 0;
|
|
1692
|
-
invalidations = 0;
|
|
1693
|
-
key(input) {
|
|
1694
|
-
return `${input.worldId === void 0 ? "" : `${input.worldId}:`}${input.entityKey}`;
|
|
1695
|
-
}
|
|
1696
|
-
get(input) {
|
|
1697
|
-
const previous = this.entries.get(this.key(input));
|
|
1698
|
-
if (previous !== void 0 && previous.meshGeneration === input.meshGeneration && previous.transformGeneration === input.transformGeneration && previous.matrixGeneration === input.matrixGeneration) {
|
|
1699
|
-
this.hits += 1;
|
|
1700
|
-
return previous.bounds;
|
|
1701
|
-
}
|
|
1702
|
-
this.misses += 1;
|
|
1703
|
-
this.derives += 1;
|
|
1704
|
-
const bounds = deriveInstancesUnionBounds(input);
|
|
1705
|
-
this.entries.set(this.key(input), {
|
|
1706
|
-
...input.worldId === void 0 ? {} : { worldId: input.worldId },
|
|
1707
|
-
entityKey: input.entityKey,
|
|
1708
|
-
meshGeneration: input.meshGeneration,
|
|
1709
|
-
transformGeneration: input.transformGeneration,
|
|
1710
|
-
matrixGeneration: input.matrixGeneration,
|
|
1711
|
-
bounds
|
|
1712
|
-
});
|
|
1713
|
-
return bounds;
|
|
1714
|
-
}
|
|
1715
|
-
invalidate(entityKey, worldId) {
|
|
1716
|
-
if (entityKey === void 0) {
|
|
1717
|
-
if (this.entries.size > 0) this.invalidations += this.entries.size;
|
|
1718
|
-
this.entries.clear();
|
|
1719
|
-
} else {
|
|
1720
|
-
if (this.entries.delete(this.key({ entityKey, ...worldId === void 0 ? {} : { worldId } }))) {
|
|
1721
|
-
this.invalidations += 1;
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
inspect() {
|
|
1726
|
-
return {
|
|
1727
|
-
hits: this.hits,
|
|
1728
|
-
misses: this.misses,
|
|
1729
|
-
derives: this.derives,
|
|
1730
|
-
invalidations: this.invalidations
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
|
-
};
|
|
1734
|
-
|
|
1735
2654
|
// src/points-lines/bounds.ts
|
|
1736
2655
|
function expandPointsLinesBounds(bounds, style) {
|
|
1737
2656
|
if (bounds.length < 6 || style === void 0) return new Float32Array(bounds);
|
|
@@ -1909,10 +2828,10 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
|
|
|
1909
2828
|
});
|
|
1910
2829
|
const frame = extractFrame(world, prepared);
|
|
1911
2830
|
framesByWorld.set(wi, frame);
|
|
1912
|
-
} catch (
|
|
1913
|
-
if (
|
|
2831
|
+
} catch (err13) {
|
|
2832
|
+
if (err13 instanceof MotionBlurValidationError) throw err13;
|
|
1914
2833
|
try {
|
|
1915
|
-
createWorldInternalView(world)._routeError(
|
|
2834
|
+
createWorldInternalView(world)._routeError(err13, {
|
|
1916
2835
|
severity: Severity.Error,
|
|
1917
2836
|
systemName: `RenderSystem.extractFrames(world[${wi}])`
|
|
1918
2837
|
});
|
|
@@ -3144,30 +4063,32 @@ function extractFrame(world, context) {
|
|
|
3144
4063
|
if (asset.kind !== "material") {
|
|
3145
4064
|
materialSnap = defaultMaterialSnapshot(handleRaw);
|
|
3146
4065
|
} else {
|
|
4066
|
+
const source = world.sharedRefs.resolve(tagged);
|
|
4067
|
+
const programOwner = source.ok && source.value.kind === "material" ? source.value : asset;
|
|
3147
4068
|
const resolvedResult = walkMaterialPassesOverSharedRefs(world, tagged, assets);
|
|
3148
4069
|
if (!resolvedResult.ok) {
|
|
3149
|
-
const
|
|
3150
|
-
switch (
|
|
4070
|
+
const err13 = resolvedResult.error;
|
|
4071
|
+
switch (err13.code) {
|
|
3151
4072
|
case "material-parent-not-found":
|
|
3152
4073
|
case "material-no-effective-pass":
|
|
3153
4074
|
case "material-value-unknown":
|
|
3154
4075
|
case "material-value-type-mismatch":
|
|
3155
4076
|
case "material-contract-program-mismatch":
|
|
3156
|
-
worldInternal._routeError(
|
|
4077
|
+
worldInternal._routeError(err13, {
|
|
3157
4078
|
severity: Severity.Error,
|
|
3158
|
-
systemName: `RenderSystem.extract (${
|
|
4079
|
+
systemName: `RenderSystem.extract (${err13.code})`
|
|
3159
4080
|
});
|
|
3160
4081
|
break;
|
|
3161
4082
|
case "material-circular-inheritance":
|
|
3162
|
-
worldInternal._routeError(
|
|
4083
|
+
worldInternal._routeError(err13, {
|
|
3163
4084
|
severity: Severity.Error,
|
|
3164
4085
|
systemName: "RenderSystem.extract (material-circular-inheritance)"
|
|
3165
4086
|
});
|
|
3166
4087
|
break;
|
|
3167
4088
|
default:
|
|
3168
|
-
worldInternal._routeError(
|
|
4089
|
+
worldInternal._routeError(err13, {
|
|
3169
4090
|
severity: Severity.Error,
|
|
3170
|
-
systemName: `RenderSystem.extract (_materialWalk: ${
|
|
4091
|
+
systemName: `RenderSystem.extract (_materialWalk: ${err13.code})`
|
|
3171
4092
|
});
|
|
3172
4093
|
}
|
|
3173
4094
|
continue;
|
|
@@ -3180,17 +4101,21 @@ function extractFrame(world, context) {
|
|
|
3180
4101
|
};
|
|
3181
4102
|
try {
|
|
3182
4103
|
materialProgramSelections = materialProgramSelectionsForMaterial(
|
|
3183
|
-
|
|
4104
|
+
programOwner,
|
|
3184
4105
|
assets,
|
|
3185
4106
|
materialContext
|
|
3186
4107
|
);
|
|
3187
4108
|
} catch (error) {
|
|
3188
4109
|
if (isMissingMaterialProgram(error) && materialContext?.geometry === "mesh") {
|
|
3189
4110
|
try {
|
|
3190
|
-
const skinnedSelections = materialProgramSelectionsForMaterial(
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
4111
|
+
const skinnedSelections = materialProgramSelectionsForMaterial(
|
|
4112
|
+
programOwner,
|
|
4113
|
+
assets,
|
|
4114
|
+
{
|
|
4115
|
+
...materialContext,
|
|
4116
|
+
geometry: "skinned"
|
|
4117
|
+
}
|
|
4118
|
+
);
|
|
3194
4119
|
if (skinnedSelections?.some(materialProgramSelectionRequiresSkin) === true && gpuDrivenMesh !== void 0) {
|
|
3195
4120
|
const hasSkinIndex = gpuDrivenMesh.attributes.skinIndex !== void 0;
|
|
3196
4121
|
const hasSkinWeight = gpuDrivenMesh.attributes.skinWeight !== void 0;
|
|
@@ -3215,7 +4140,7 @@ function extractFrame(world, context) {
|
|
|
3215
4140
|
])
|
|
3216
4141
|
);
|
|
3217
4142
|
const materialSceneIndexProgramKeys = materialSceneIndexProgramKeysForMaterial(
|
|
3218
|
-
|
|
4143
|
+
programOwner,
|
|
3219
4144
|
assets,
|
|
3220
4145
|
materialContext
|
|
3221
4146
|
);
|
|
@@ -3539,6 +4464,7 @@ function extractFrame(world, context) {
|
|
|
3539
4464
|
}
|
|
3540
4465
|
if (isRenderable) {
|
|
3541
4466
|
let skinSlice;
|
|
4467
|
+
let skinPose;
|
|
3542
4468
|
const skinIdentity = hasSkin ? skinPaletteIdentity(world, entity) : void 0;
|
|
3543
4469
|
const releaseSkinIdentity = () => {
|
|
3544
4470
|
if (skinIdentity !== void 0) {
|
|
@@ -3624,10 +4550,19 @@ function extractFrame(world, context) {
|
|
|
3624
4550
|
});
|
|
3625
4551
|
skinPaletteAllocator.writePersistentJointPalette(receipt, ibms, jointWorlds);
|
|
3626
4552
|
skinSlice = receipt;
|
|
4553
|
+
} else {
|
|
4554
|
+
skinPose = {
|
|
4555
|
+
identity: skinPaletteIdentity(world, entity),
|
|
4556
|
+
generation: (skeletonHandleRaw >>> 0 ^ skeleton.jointCount) >>> 0,
|
|
4557
|
+
jointCount: skeleton.jointCount,
|
|
4558
|
+
...hasFiniteOrderedLocalAabb(skeleton.bounds) ? { bounds: new Float32Array(skeleton.bounds) } : {},
|
|
4559
|
+
inverseBindMatrices: ibms,
|
|
4560
|
+
jointWorlds
|
|
4561
|
+
};
|
|
3627
4562
|
}
|
|
3628
4563
|
}
|
|
3629
4564
|
}
|
|
3630
|
-
if (skinSlice === void 0 && hasSkin) {
|
|
4565
|
+
if (skinSlice === void 0 && skinPose === void 0 && hasSkin) {
|
|
3631
4566
|
releaseSkinIdentity();
|
|
3632
4567
|
if (materialSnap.materialShaderId === "forgeax::pbr-skin" || materialProgramSelections?.some(materialProgramSelectionRequiresSkin) === true) {
|
|
3633
4568
|
pendingDispatch.length = 0;
|
|
@@ -3781,9 +4716,9 @@ function extractFrame(world, context) {
|
|
|
3781
4716
|
let instancesSnap;
|
|
3782
4717
|
if (hasInstances) {
|
|
3783
4718
|
const transforms = worldInternal._getArrayView(entity, Instances, "transforms") ?? new Float32Array();
|
|
3784
|
-
const
|
|
3785
|
-
if (
|
|
3786
|
-
worldInternal._routeError(
|
|
4719
|
+
const invalid5 = validateInstanceTransforms(transforms);
|
|
4720
|
+
if (invalid5 !== void 0) {
|
|
4721
|
+
worldInternal._routeError(invalid5, {
|
|
3787
4722
|
severity: Severity.Error,
|
|
3788
4723
|
systemName: "RenderSystem.extract (Instances transforms)"
|
|
3789
4724
|
});
|
|
@@ -3816,13 +4751,14 @@ function extractFrame(world, context) {
|
|
|
3816
4751
|
entityKey: entity,
|
|
3817
4752
|
...authorVisible ? {} : { authorVisible: false },
|
|
3818
4753
|
...skinSlice !== void 0 ? { skin: skinSlice } : {},
|
|
4754
|
+
...skinPose === void 0 ? {} : { skinPose },
|
|
3819
4755
|
...skin?.joints === void 0 ? {} : { skinJointEntities: Array.from(skin.joints, (joint) => Number(joint)) },
|
|
3820
4756
|
...morph !== void 0 ? { morph } : {},
|
|
3821
4757
|
...instancesSnap !== void 0 ? { instances: instancesSnap } : {},
|
|
3822
4758
|
...spriteInstancesSnap !== void 0 ? { spriteInstances: spriteInstancesSnap } : {},
|
|
3823
4759
|
...pointsLines !== void 0 ? { pointsLines } : {}
|
|
3824
4760
|
};
|
|
3825
|
-
const gpuDrivenDraws = buildGpuDrivenDraws({
|
|
4761
|
+
const gpuDrivenDraws = skinPose === void 0 ? buildGpuDrivenDraws({
|
|
3826
4762
|
mesh: gpuDrivenMesh,
|
|
3827
4763
|
materials: materialsArr,
|
|
3828
4764
|
fallbackMaterial: materialSnap,
|
|
@@ -3834,7 +4770,7 @@ function extractFrame(world, context) {
|
|
|
3834
4770
|
return lower?.kind === "mesh" ? lower : void 0;
|
|
3835
4771
|
})
|
|
3836
4772
|
}
|
|
3837
|
-
});
|
|
4773
|
+
}) : [];
|
|
3838
4774
|
for (const draw of gpuDrivenDraws) {
|
|
3839
4775
|
if (draw.preparationError === void 0) continue;
|
|
3840
4776
|
worldInternal._routeError(draw.preparationError, {
|
|
@@ -4371,8 +5307,8 @@ function internSharedRefFromGuid(world, assetsRef, guid, brand) {
|
|
|
4371
5307
|
return handle;
|
|
4372
5308
|
}
|
|
4373
5309
|
function resolveVideoFieldHandle(value, world, assetsRef) {
|
|
4374
|
-
const
|
|
4375
|
-
const textureGuid = assetReferenceText(
|
|
5310
|
+
const texture = materialTextureValue(value);
|
|
5311
|
+
const textureGuid = assetReferenceText(texture?.texture ?? value);
|
|
4376
5312
|
if (textureGuid === void 0) return void 0;
|
|
4377
5313
|
const payload = assetsRef.lookup(textureGuid);
|
|
4378
5314
|
if (payload === void 0 || payload.kind !== "video") return void 0;
|
|
@@ -4411,10 +5347,10 @@ function collectMaterialTextureCoordinates(values) {
|
|
|
4411
5347
|
}
|
|
4412
5348
|
return out;
|
|
4413
5349
|
}
|
|
4414
|
-
function materialTextureSourceFields(
|
|
4415
|
-
if (
|
|
5350
|
+
function materialTextureSourceFields(schema2) {
|
|
5351
|
+
if (schema2 === void 0) return void 0;
|
|
4416
5352
|
const fields = /* @__PURE__ */ new Set();
|
|
4417
|
-
for (const entry of
|
|
5353
|
+
for (const entry of schema2) {
|
|
4418
5354
|
if (entry.type.startsWith("texture")) fields.add(entry.name);
|
|
4419
5355
|
}
|
|
4420
5356
|
return fields;
|
|
@@ -4607,9 +5543,15 @@ function resolveMaterialSnapshot(handleRaw, world, assetsRef, materialSnapshotCa
|
|
|
4607
5543
|
if (!resolvedResult.ok) return defaultMaterialSnapshot(handleRaw);
|
|
4608
5544
|
const resolved = resolvedResult.value;
|
|
4609
5545
|
const allPasses = resolved.passes;
|
|
4610
|
-
const
|
|
5546
|
+
const source = world.sharedRefs.resolve(tagged);
|
|
5547
|
+
const programOwner = source.ok && source.value.kind === "material" ? source.value : asset;
|
|
5548
|
+
const materialProgramKeys = materialProgramKeysForMaterial(
|
|
5549
|
+
programOwner,
|
|
5550
|
+
assetsRef,
|
|
5551
|
+
materialContext
|
|
5552
|
+
);
|
|
4611
5553
|
const materialSceneIndexProgramKeys = materialSceneIndexProgramKeysForMaterial(
|
|
4612
|
-
|
|
5554
|
+
programOwner,
|
|
4613
5555
|
assetsRef,
|
|
4614
5556
|
materialContext
|
|
4615
5557
|
);
|
|
@@ -5206,9 +6148,9 @@ function selectFogFrame(world) {
|
|
|
5206
6148
|
if (state.lkg !== void 0) return state.lkg;
|
|
5207
6149
|
throw fogSelection.error;
|
|
5208
6150
|
}
|
|
5209
|
-
function textureDigest(
|
|
6151
|
+
function textureDigest(texture) {
|
|
5210
6152
|
let hash = 2166136261;
|
|
5211
|
-
for (const byte of
|
|
6153
|
+
for (const byte of texture.data) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
|
|
5212
6154
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}`;
|
|
5213
6155
|
}
|
|
5214
6156
|
function extractVolumeSnapshot(world, assets, worldInternal) {
|
|
@@ -5328,602 +6270,7 @@ function extractVolumeSnapshot(world, assets, worldInternal) {
|
|
|
5328
6270
|
additional: validatedMembers.slice(1)
|
|
5329
6271
|
};
|
|
5330
6272
|
}
|
|
5331
|
-
var PROBE_R_MIN = R_MIN;
|
|
5332
|
-
var PROBE_MAX_CONTRIBUTORS = PROBE_BLEND_RECORD_CAPACITY;
|
|
5333
|
-
var SH_C0 = 0.28209479177387814;
|
|
5334
|
-
var SH_C1 = 0.4886025119029199;
|
|
5335
|
-
var SH_C2 = 1.0925484305920792;
|
|
5336
|
-
var SH_C20 = 0.31539156525252005;
|
|
5337
|
-
var SH_C22 = 0.5462742152960396;
|
|
5338
|
-
function finiteVector(values, length) {
|
|
5339
|
-
if (values.length !== length) return false;
|
|
5340
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
5341
|
-
if (!Number.isFinite(values[index])) return false;
|
|
5342
|
-
}
|
|
5343
|
-
return true;
|
|
5344
|
-
}
|
|
5345
|
-
function contributorIsActive(probe, radiusMinimum) {
|
|
5346
|
-
return probe.admitted && Number.isFinite(probe.distance) && probe.distance >= 0 && Number.isFinite(probe.radius) && probe.radius >= radiusMinimum && probe.distance < probe.radius && finiteVector(probe.irradiance, 27);
|
|
5347
|
-
}
|
|
5348
|
-
function emptyReceipt(radiusMinimum) {
|
|
5349
|
-
return {
|
|
5350
|
-
activeIdentities: [],
|
|
5351
|
-
admittedIdentities: [],
|
|
5352
|
-
rejectedIdentities: [],
|
|
5353
|
-
stableOrder: [],
|
|
5354
|
-
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
5355
|
-
scaleRadius: radiusMinimum,
|
|
5356
|
-
finite: true
|
|
5357
|
-
};
|
|
5358
|
-
}
|
|
5359
|
-
function admitProbeContributors(candidates, radiusMinimum = PROBE_R_MIN) {
|
|
5360
|
-
const active = candidates.filter((candidate) => contributorIsActive(candidate, radiusMinimum));
|
|
5361
|
-
const overflowed = active.length > PROBE_MAX_CONTRIBUTORS;
|
|
5362
|
-
const stableActive = active.slice().sort((left, right) => left.identity.localeCompare(right.identity));
|
|
5363
|
-
const admitted = stableActive.slice(0, PROBE_MAX_CONTRIBUTORS);
|
|
5364
|
-
const rejected = stableActive.slice(PROBE_MAX_CONTRIBUTORS);
|
|
5365
|
-
const scaleRadius = active.reduce(
|
|
5366
|
-
(minimum, candidate) => Math.min(minimum, candidate.radius),
|
|
5367
|
-
active[0]?.radius ?? radiusMinimum
|
|
5368
|
-
);
|
|
5369
|
-
const receipt = {
|
|
5370
|
-
activeIdentities: stableActive.map((candidate) => candidate.identity),
|
|
5371
|
-
admittedIdentities: admitted.map((candidate) => candidate.identity),
|
|
5372
|
-
rejectedIdentities: rejected.map((candidate) => candidate.identity),
|
|
5373
|
-
stableOrder: admitted.map((candidate) => candidate.identity),
|
|
5374
|
-
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
5375
|
-
...overflowed ? { overflowReason: "active-count-exceeds-capacity" } : {},
|
|
5376
|
-
scaleRadius,
|
|
5377
|
-
finite: stableActive.every((candidate) => Number.isFinite(candidate.radius))
|
|
5378
|
-
};
|
|
5379
|
-
return {
|
|
5380
|
-
active: stableActive,
|
|
5381
|
-
admitted,
|
|
5382
|
-
rejected,
|
|
5383
|
-
receipt,
|
|
5384
|
-
...overflowed ? {
|
|
5385
|
-
error: {
|
|
5386
|
-
code: "capacity-exceeded",
|
|
5387
|
-
activeCount: active.length,
|
|
5388
|
-
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
5389
|
-
receipt
|
|
5390
|
-
}
|
|
5391
|
-
} : {}
|
|
5392
|
-
};
|
|
5393
|
-
}
|
|
5394
|
-
function invalidInputResult(radiusMinimum, code) {
|
|
5395
|
-
const receipt = emptyReceipt(radiusMinimum);
|
|
5396
|
-
return {
|
|
5397
|
-
terms: [],
|
|
5398
|
-
rStar: radiusMinimum,
|
|
5399
|
-
Q: 0,
|
|
5400
|
-
C: 0,
|
|
5401
|
-
S: 1,
|
|
5402
|
-
SHPreblend: new Array(27).fill(0),
|
|
5403
|
-
skyIrradiance: [0, 0, 0],
|
|
5404
|
-
diffuse: [0, 0, 0],
|
|
5405
|
-
finite: false,
|
|
5406
|
-
receipt,
|
|
5407
|
-
scaledQLogOffset: 0,
|
|
5408
|
-
error: { code, activeCount: 0, capacity: PROBE_MAX_CONTRIBUTORS, receipt }
|
|
5409
|
-
};
|
|
5410
|
-
}
|
|
5411
|
-
function neumaierSum(values) {
|
|
5412
|
-
let sum = 0;
|
|
5413
|
-
let correction = 0;
|
|
5414
|
-
for (const value of values) {
|
|
5415
|
-
const next = sum + value;
|
|
5416
|
-
if (Math.abs(sum) >= Math.abs(value)) correction += sum - next + value;
|
|
5417
|
-
else correction += value - next + sum;
|
|
5418
|
-
sum = next;
|
|
5419
|
-
}
|
|
5420
|
-
return sum + correction;
|
|
5421
|
-
}
|
|
5422
|
-
function roundProbeToF32(value) {
|
|
5423
|
-
return Number.isFinite(value) ? new Float32Array([value])[0] : void 0;
|
|
5424
|
-
}
|
|
5425
|
-
function scaledProbeWeight(distance, radius, radiusMinimum = PROBE_R_MIN) {
|
|
5426
|
-
if (!Number.isFinite(distance) || distance < 0 || !Number.isFinite(radius) || radius < radiusMinimum || distance >= radius) {
|
|
5427
|
-
return { coverage: 0, q: 0 };
|
|
5428
|
-
}
|
|
5429
|
-
const ratio = distance / radius;
|
|
5430
|
-
const coverage = 1 - ratio * ratio;
|
|
5431
|
-
return { coverage, q: coverage / (radius * radius) };
|
|
5432
|
-
}
|
|
5433
|
-
function sh9Irradiance(coefficients, normal) {
|
|
5434
|
-
const x = normal[0];
|
|
5435
|
-
const y = normal[1];
|
|
5436
|
-
const z = normal[2];
|
|
5437
|
-
const basis = [
|
|
5438
|
-
SH_C0,
|
|
5439
|
-
SH_C1 * y,
|
|
5440
|
-
SH_C1 * z,
|
|
5441
|
-
SH_C1 * x,
|
|
5442
|
-
SH_C2 * x * y,
|
|
5443
|
-
SH_C2 * y * z,
|
|
5444
|
-
SH_C20 * (3 * z * z - 1),
|
|
5445
|
-
SH_C2 * x * z,
|
|
5446
|
-
SH_C22 * (x * x - y * y)
|
|
5447
|
-
];
|
|
5448
|
-
const result = [0, 0, 0];
|
|
5449
|
-
for (let band = 0; band < basis.length; band += 1) {
|
|
5450
|
-
const factor = basis[band] ?? 0;
|
|
5451
|
-
const offset = band * 3;
|
|
5452
|
-
result[0] = (result[0] ?? 0) + (coefficients[offset] ?? 0) * factor;
|
|
5453
|
-
result[1] = (result[1] ?? 0) + (coefficients[offset + 1] ?? 0) * factor;
|
|
5454
|
-
result[2] = (result[2] ?? 0) + (coefficients[offset + 2] ?? 0) * factor;
|
|
5455
|
-
}
|
|
5456
|
-
return result;
|
|
5457
|
-
}
|
|
5458
|
-
function vectorNeumaierSum(values, width) {
|
|
5459
|
-
return Array.from(
|
|
5460
|
-
{ length: width },
|
|
5461
|
-
(_, channel) => neumaierSum(values.map((value) => value[channel] ?? 0))
|
|
5462
|
-
);
|
|
5463
|
-
}
|
|
5464
|
-
function blendProbeCoefficients(contributors, radiusMinimum) {
|
|
5465
|
-
if (contributors.length > PROBE_MAX_CONTRIBUTORS || contributors.some((contributor) => !contributorIsActive(contributor, radiusMinimum))) {
|
|
5466
|
-
return void 0;
|
|
5467
|
-
}
|
|
5468
|
-
const rStar = contributors.reduce(
|
|
5469
|
-
(minimum, contributor) => Math.min(minimum, contributor.radius),
|
|
5470
|
-
contributors[0]?.radius ?? radiusMinimum
|
|
5471
|
-
);
|
|
5472
|
-
const rawTerms = contributors.map((contributor) => {
|
|
5473
|
-
const { coverage } = scaledProbeWeight(contributor.distance, contributor.radius, radiusMinimum);
|
|
5474
|
-
const scaledRatio = rStar / contributor.radius;
|
|
5475
|
-
return {
|
|
5476
|
-
contributor,
|
|
5477
|
-
coverage,
|
|
5478
|
-
// Diagnostic q is never used for Q, alpha, C, or SH accumulation.
|
|
5479
|
-
q: coverage / (contributor.radius * contributor.radius),
|
|
5480
|
-
scaledQ: coverage * scaledRatio * scaledRatio
|
|
5481
|
-
};
|
|
5482
|
-
});
|
|
5483
|
-
const scaledQLogOffset = 0;
|
|
5484
|
-
const finiteTerms = rawTerms;
|
|
5485
|
-
if (finiteTerms.some((term) => !Number.isFinite(term.scaledQ))) return void 0;
|
|
5486
|
-
const Q = neumaierSum(finiteTerms.map((term) => term.scaledQ));
|
|
5487
|
-
const terms = finiteTerms.map((term) => ({
|
|
5488
|
-
identity: term.contributor.identity,
|
|
5489
|
-
distance: term.contributor.distance,
|
|
5490
|
-
radius: term.contributor.radius,
|
|
5491
|
-
coverage: term.coverage,
|
|
5492
|
-
q: term.q,
|
|
5493
|
-
scaledQ: term.scaledQ,
|
|
5494
|
-
qHat: term.scaledQ,
|
|
5495
|
-
alpha: Q > 0 ? term.scaledQ / Q : 0
|
|
5496
|
-
}));
|
|
5497
|
-
let product = 1;
|
|
5498
|
-
for (const term of finiteTerms) product *= 1 - term.coverage;
|
|
5499
|
-
const C = 1 - product;
|
|
5500
|
-
const S = 1 - C;
|
|
5501
|
-
const weightedSh = finiteTerms.map(
|
|
5502
|
-
(term) => Array.from(term.contributor.irradiance, (value) => value * (Q > 0 ? term.scaledQ / Q : 0))
|
|
5503
|
-
);
|
|
5504
|
-
const SHPreblend = vectorNeumaierSum(weightedSh, 27).map((value) => value * C);
|
|
5505
|
-
const finite = Number.isFinite(Q) && Number.isFinite(C) && Number.isFinite(S) && SHPreblend.every(Number.isFinite) && finiteTerms.every((term) => Number.isFinite(term.scaledQ));
|
|
5506
|
-
const receipt = {
|
|
5507
|
-
activeIdentities: contributors.map((contributor) => contributor.identity),
|
|
5508
|
-
admittedIdentities: contributors.map((contributor) => contributor.identity),
|
|
5509
|
-
rejectedIdentities: [],
|
|
5510
|
-
stableOrder: contributors.map((contributor) => contributor.identity),
|
|
5511
|
-
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
5512
|
-
scaleRadius: rStar,
|
|
5513
|
-
finite
|
|
5514
|
-
};
|
|
5515
|
-
return {
|
|
5516
|
-
terms,
|
|
5517
|
-
rStar,
|
|
5518
|
-
Q,
|
|
5519
|
-
C,
|
|
5520
|
-
S,
|
|
5521
|
-
SHPreblend,
|
|
5522
|
-
finite,
|
|
5523
|
-
receipt,
|
|
5524
|
-
scaledQLogOffset
|
|
5525
|
-
};
|
|
5526
|
-
}
|
|
5527
|
-
function blendLightProbes(input) {
|
|
5528
|
-
const radiusMinimum = input.radiusMinimum ?? PROBE_R_MIN;
|
|
5529
|
-
const normal = input.normal;
|
|
5530
|
-
const sky = input.skyIrradiance;
|
|
5531
|
-
if (!finiteVector(normal, 3) || !finiteVector(sky, 3)) {
|
|
5532
|
-
return invalidInputResult(radiusMinimum, "invalid-admitted-prefix");
|
|
5533
|
-
}
|
|
5534
|
-
const coefficients = blendProbeCoefficients(input.contributors, radiusMinimum);
|
|
5535
|
-
if (coefficients === void 0)
|
|
5536
|
-
return invalidInputResult(radiusMinimum, "invalid-admitted-prefix");
|
|
5537
|
-
const shIrradiance = sh9Irradiance(coefficients.SHPreblend, normal);
|
|
5538
|
-
const diffuse = [0, 1, 2].map(
|
|
5539
|
-
(channel) => Math.max(shIrradiance[channel] ?? 0, 0) + coefficients.S * (sky[channel] ?? 0)
|
|
5540
|
-
);
|
|
5541
|
-
return {
|
|
5542
|
-
...coefficients,
|
|
5543
|
-
skyIrradiance: [sky[0], sky[1], sky[2]],
|
|
5544
|
-
diffuse,
|
|
5545
|
-
finite: coefficients.finite && diffuse.every(Number.isFinite)
|
|
5546
|
-
};
|
|
5547
|
-
}
|
|
5548
|
-
function vectorDistance(left, right) {
|
|
5549
|
-
const dx = left[0] - right[0];
|
|
5550
|
-
const dy = left[1] - right[1];
|
|
5551
|
-
const dz = left[2] - right[2];
|
|
5552
|
-
return Math.hypot(dx, dy, dz);
|
|
5553
|
-
}
|
|
5554
|
-
function encodeProbeRecord(objectKey, generation, localBlendFraction, shPreblend, candidate, accepted, lastKnownGood, sentinel) {
|
|
5555
|
-
const values = new Float32Array(PROBE_BLEND_RECORD_BYTE_SIZE / Float32Array.BYTES_PER_ELEMENT);
|
|
5556
|
-
values[0] = objectKey;
|
|
5557
|
-
values[1] = generation;
|
|
5558
|
-
values[2] = roundProbeToF32(localBlendFraction) ?? 0;
|
|
5559
|
-
values[3] = (1 ) | (accepted ? 2 : 0) | (lastKnownGood ? 4 : 0);
|
|
5560
|
-
for (let band = 0; band < 9; band += 1) {
|
|
5561
|
-
const lane = 4 + band * 4;
|
|
5562
|
-
values[lane] = roundProbeToF32(shPreblend[band * 3] ?? 0) ?? 0;
|
|
5563
|
-
values[lane + 1] = roundProbeToF32(shPreblend[band * 3 + 1] ?? 0) ?? 0;
|
|
5564
|
-
values[lane + 2] = roundProbeToF32(shPreblend[band * 3 + 2] ?? 0) ?? 0;
|
|
5565
|
-
values[lane + 3] = 0;
|
|
5566
|
-
}
|
|
5567
|
-
const packedSh = new Array(27);
|
|
5568
|
-
for (let band = 0; band < 9; band += 1) {
|
|
5569
|
-
packedSh[band * 3] = values[4 + band * 4] ?? 0;
|
|
5570
|
-
packedSh[band * 3 + 1] = values[5 + band * 4] ?? 0;
|
|
5571
|
-
packedSh[band * 3 + 2] = values[6 + band * 4] ?? 0;
|
|
5572
|
-
}
|
|
5573
|
-
return {
|
|
5574
|
-
objectKey,
|
|
5575
|
-
generation,
|
|
5576
|
-
localBlendFraction: values[2] ?? 0,
|
|
5577
|
-
shPreblend: packedSh,
|
|
5578
|
-
bytes: new Uint8Array(values.buffer),
|
|
5579
|
-
byteLength: values.byteLength,
|
|
5580
|
-
candidate,
|
|
5581
|
-
accepted,
|
|
5582
|
-
lastKnownGood,
|
|
5583
|
-
...sentinel === void 0 ? {} : { sentinel }
|
|
5584
|
-
};
|
|
5585
|
-
}
|
|
5586
|
-
function sameList(left, right) {
|
|
5587
|
-
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
5588
|
-
}
|
|
5589
|
-
function sameVector3(left, right) {
|
|
5590
|
-
return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
|
|
5591
|
-
}
|
|
5592
|
-
function sameProbeFacts(left, right) {
|
|
5593
|
-
if (left.length !== right.length) return false;
|
|
5594
|
-
return left.every((probe, index) => {
|
|
5595
|
-
const other = right[index];
|
|
5596
|
-
if (other === void 0 || probe.identity !== other.identity || probe.radius !== other.radius || probe.admitted !== other.admitted || !sameVector3(probe.position, other.position) || probe.irradiance.length !== other.irradiance.length)
|
|
5597
|
-
return false;
|
|
5598
|
-
for (let value = 0; value < probe.irradiance.length; value += 1) {
|
|
5599
|
-
if (probe.irradiance[value] !== other.irradiance[value]) return false;
|
|
5600
|
-
}
|
|
5601
|
-
return true;
|
|
5602
|
-
});
|
|
5603
|
-
}
|
|
5604
|
-
function sameSkyFacts(left, right) {
|
|
5605
|
-
const leftSky = left.sky;
|
|
5606
|
-
const rightSky = right.sky;
|
|
5607
|
-
if (leftSky === void 0 !== (rightSky === void 0)) return false;
|
|
5608
|
-
if (leftSky !== void 0 && rightSky !== void 0 && (leftSky.available !== rightSky.available || leftSky.identity !== rightSky.identity || leftSky.sourceKey !== rightSky.sourceKey || leftSky.fallbackReason !== rightSky.fallbackReason || !sameVector3(leftSky.irradiance, rightSky.irradiance)))
|
|
5609
|
-
return false;
|
|
5610
|
-
if (left.skyIrradiance === void 0 !== (right.skyIrradiance === void 0)) return false;
|
|
5611
|
-
return left.skyIrradiance === void 0 || right.skyIrradiance !== void 0 && sameVector3(left.skyIrradiance, right.skyIrradiance);
|
|
5612
|
-
}
|
|
5613
|
-
var ProbeBlendSceneProjection = class {
|
|
5614
|
-
previous;
|
|
5615
|
-
records = /* @__PURE__ */ new Map();
|
|
5616
|
-
lastError;
|
|
5617
|
-
lastReceipt = emptyReceipt(PROBE_R_MIN);
|
|
5618
|
-
lastActiveContributorCount = 0;
|
|
5619
|
-
lastAdmittedProbeCount = 0;
|
|
5620
|
-
lastCoverage = 0;
|
|
5621
|
-
lastFinite = true;
|
|
5622
|
-
lastSky = {
|
|
5623
|
-
available: false,
|
|
5624
|
-
irradiance: [0, 0, 0],
|
|
5625
|
-
fallbackReason: "no-skylight"
|
|
5626
|
-
};
|
|
5627
|
-
lastDirtyVisitCount = 0;
|
|
5628
|
-
lastDirtyReasons = [];
|
|
5629
|
-
bufferProjectionSource = Object.freeze({});
|
|
5630
|
-
bufferProjection = Object.freeze({
|
|
5631
|
-
sourceIdentity: this.bufferProjectionSource,
|
|
5632
|
-
revision: 0,
|
|
5633
|
-
baseRevision: 0,
|
|
5634
|
-
capacity: 1,
|
|
5635
|
-
records: Object.freeze([]),
|
|
5636
|
-
dirtyRecords: Object.freeze([]),
|
|
5637
|
-
removedCacheKeys: Object.freeze([])
|
|
5638
|
-
});
|
|
5639
|
-
resultRecords = Object.freeze([]);
|
|
5640
|
-
recordFacts = /* @__PURE__ */ new Map();
|
|
5641
|
-
getRecord(objectKey) {
|
|
5642
|
-
return this.records.get(objectKey);
|
|
5643
|
-
}
|
|
5644
|
-
hasRecords() {
|
|
5645
|
-
return this.records.size > 0;
|
|
5646
|
-
}
|
|
5647
|
-
/** Stable upload projection; rebuilt only when probe/object facts change. */
|
|
5648
|
-
recordBufferProjection() {
|
|
5649
|
-
return this.bufferProjection;
|
|
5650
|
-
}
|
|
5651
|
-
inspect() {
|
|
5652
|
-
return {
|
|
5653
|
-
activeContributorCount: this.lastActiveContributorCount,
|
|
5654
|
-
admittedProbeCount: this.lastAdmittedProbeCount,
|
|
5655
|
-
coverage: this.lastCoverage,
|
|
5656
|
-
skyResidualFraction: 1 - this.lastCoverage,
|
|
5657
|
-
finite: this.lastFinite,
|
|
5658
|
-
errorCode: this.lastError?.code,
|
|
5659
|
-
records: [...this.records.values()].map((record) => {
|
|
5660
|
-
const facts = this.recordFacts.get(record.objectKey);
|
|
5661
|
-
return {
|
|
5662
|
-
objectKey: record.objectKey,
|
|
5663
|
-
generation: record.generation,
|
|
5664
|
-
localBlendFraction: record.localBlendFraction,
|
|
5665
|
-
shPreblend: [...record.shPreblend],
|
|
5666
|
-
byteLength: record.byteLength,
|
|
5667
|
-
candidate: record.candidate,
|
|
5668
|
-
accepted: record.accepted,
|
|
5669
|
-
lastKnownGood: record.lastKnownGood,
|
|
5670
|
-
sentinel: record.sentinel,
|
|
5671
|
-
probeBlendIndex: record.objectKey,
|
|
5672
|
-
contributors: (facts?.contributors ?? []).map((term) => ({
|
|
5673
|
-
identity: term.identity,
|
|
5674
|
-
distance: term.distance,
|
|
5675
|
-
radius: term.radius,
|
|
5676
|
-
coverage: term.coverage,
|
|
5677
|
-
q: term.q,
|
|
5678
|
-
qHat: term.qHat,
|
|
5679
|
-
alpha: term.alpha
|
|
5680
|
-
})),
|
|
5681
|
-
qHatSum: facts?.qHatSum ?? 0,
|
|
5682
|
-
rStar: facts?.rStar ?? PROBE_R_MIN,
|
|
5683
|
-
fallbackReason: facts?.fallbackReason
|
|
5684
|
-
};
|
|
5685
|
-
}),
|
|
5686
|
-
sky: {
|
|
5687
|
-
available: this.lastSky?.available ?? false,
|
|
5688
|
-
identity: this.lastSky?.identity,
|
|
5689
|
-
sourceKey: this.lastSky?.sourceKey,
|
|
5690
|
-
irradiance: [...this.lastSky?.irradiance ?? [0, 0, 0]],
|
|
5691
|
-
fallbackReason: this.lastSky?.fallbackReason
|
|
5692
|
-
},
|
|
5693
|
-
dirtyVisitCount: this.lastDirtyVisitCount,
|
|
5694
|
-
dirtyReasons: [...this.lastDirtyReasons],
|
|
5695
|
-
receipt: {
|
|
5696
|
-
activeIdentities: [...this.lastReceipt.activeIdentities],
|
|
5697
|
-
admittedIdentities: [...this.lastReceipt.admittedIdentities],
|
|
5698
|
-
rejectedIdentities: [...this.lastReceipt.rejectedIdentities],
|
|
5699
|
-
stableOrder: [...this.lastReceipt.stableOrder],
|
|
5700
|
-
capacity: this.lastReceipt.capacity,
|
|
5701
|
-
scaleRadius: this.lastReceipt.scaleRadius,
|
|
5702
|
-
finite: this.lastReceipt.finite,
|
|
5703
|
-
overflowReason: this.lastReceipt.overflowReason
|
|
5704
|
-
}
|
|
5705
|
-
};
|
|
5706
|
-
}
|
|
5707
|
-
apply(input) {
|
|
5708
|
-
const dirtyReasons = [];
|
|
5709
|
-
const affectedObjectKeys = /* @__PURE__ */ new Set();
|
|
5710
|
-
const previous = this.previous;
|
|
5711
|
-
if (previous !== void 0 && previous.objects === input.objects && input.worldRevision === previous.worldRevision && input.retainSkyResidualRecords === previous.retainSkyResidualRecords && sameProbeFacts(input.probes, previous.probes) && sameSkyFacts(input, previous)) {
|
|
5712
|
-
return {
|
|
5713
|
-
records: this.resultRecords,
|
|
5714
|
-
contributors: [],
|
|
5715
|
-
activeContributorCount: this.lastActiveContributorCount,
|
|
5716
|
-
admittedProbeCount: this.lastAdmittedProbeCount,
|
|
5717
|
-
coverage: this.lastCoverage,
|
|
5718
|
-
skyResidualFraction: 1 - this.lastCoverage,
|
|
5719
|
-
dirtyReasons: [],
|
|
5720
|
-
affectedObjectKeys: [],
|
|
5721
|
-
visits: 0,
|
|
5722
|
-
allocations: 0,
|
|
5723
|
-
uploads: 0,
|
|
5724
|
-
receipt: this.lastReceipt,
|
|
5725
|
-
...this.lastError === void 0 ? {} : { error: this.lastError }
|
|
5726
|
-
};
|
|
5727
|
-
}
|
|
5728
|
-
const objectKeys = input.objects.map((object) => String(object.objectKey)).sort();
|
|
5729
|
-
const previousObjectKeys = previous?.objects.map((object) => String(object.objectKey)).sort() ?? [];
|
|
5730
|
-
if (previous === void 0 || !sameList(objectKeys, previousObjectKeys)) {
|
|
5731
|
-
dirtyReasons.push("object-boundary");
|
|
5732
|
-
for (const object of input.objects) affectedObjectKeys.add(object.objectKey);
|
|
5733
|
-
}
|
|
5734
|
-
if (previous !== void 0 && input.worldRevision !== previous.worldRevision)
|
|
5735
|
-
dirtyReasons.push("world");
|
|
5736
|
-
if (previous !== void 0 && (!sameProbeFacts(input.probes, previous.probes) || input.retainSkyResidualRecords !== previous.retainSkyResidualRecords))
|
|
5737
|
-
dirtyReasons.push("probe-fact");
|
|
5738
|
-
if (previous !== void 0 && !sameSkyFacts(input, previous)) dirtyReasons.push("sky");
|
|
5739
|
-
const previousObjects = new Map(previous?.objects.map((object) => [object.objectKey, object]));
|
|
5740
|
-
for (const object of input.objects) {
|
|
5741
|
-
const oldObject = previousObjects.get(object.objectKey);
|
|
5742
|
-
if (oldObject === void 0) {
|
|
5743
|
-
affectedObjectKeys.add(object.objectKey);
|
|
5744
|
-
continue;
|
|
5745
|
-
}
|
|
5746
|
-
if (!sameVector3(object.position, oldObject.position)) {
|
|
5747
|
-
dirtyReasons.push("object-position");
|
|
5748
|
-
affectedObjectKeys.add(object.objectKey);
|
|
5749
|
-
}
|
|
5750
|
-
if (object.generation !== oldObject.generation) {
|
|
5751
|
-
dirtyReasons.push("generation");
|
|
5752
|
-
affectedObjectKeys.add(object.objectKey);
|
|
5753
|
-
}
|
|
5754
|
-
}
|
|
5755
|
-
if (dirtyReasons.includes("world") || dirtyReasons.includes("probe-fact") || dirtyReasons.includes("sky"))
|
|
5756
|
-
for (const object of input.objects) affectedObjectKeys.add(object.objectKey);
|
|
5757
|
-
const uniqueDirtyReasons = [...new Set(dirtyReasons)];
|
|
5758
|
-
if (previous !== void 0 && uniqueDirtyReasons.length === 0) {
|
|
5759
|
-
return {
|
|
5760
|
-
records: [...this.records.values()],
|
|
5761
|
-
contributors: [],
|
|
5762
|
-
activeContributorCount: this.lastActiveContributorCount,
|
|
5763
|
-
admittedProbeCount: this.lastAdmittedProbeCount,
|
|
5764
|
-
coverage: this.lastCoverage,
|
|
5765
|
-
skyResidualFraction: 1 - this.lastCoverage,
|
|
5766
|
-
dirtyReasons: [],
|
|
5767
|
-
affectedObjectKeys: [],
|
|
5768
|
-
visits: 0,
|
|
5769
|
-
allocations: 0,
|
|
5770
|
-
uploads: 0,
|
|
5771
|
-
receipt: this.lastReceipt,
|
|
5772
|
-
...this.lastError === void 0 ? {} : { error: this.lastError }
|
|
5773
|
-
};
|
|
5774
|
-
}
|
|
5775
|
-
const nextRecords = new Map(this.records);
|
|
5776
|
-
const currentObjectKeys = new Set(input.objects.map((object) => object.objectKey));
|
|
5777
|
-
for (const objectKey of nextRecords.keys()) {
|
|
5778
|
-
if (!currentObjectKeys.has(objectKey)) {
|
|
5779
|
-
nextRecords.delete(objectKey);
|
|
5780
|
-
this.recordFacts.delete(objectKey);
|
|
5781
|
-
}
|
|
5782
|
-
}
|
|
5783
|
-
let firstTerms = [];
|
|
5784
|
-
let firstReceipt = emptyReceipt(PROBE_R_MIN);
|
|
5785
|
-
let firstError;
|
|
5786
|
-
let firstActiveCount = 0;
|
|
5787
|
-
let firstAdmittedCount = 0;
|
|
5788
|
-
let firstCoverage = 0;
|
|
5789
|
-
let firstObjectSeen = false;
|
|
5790
|
-
let visited = 0;
|
|
5791
|
-
for (const object of input.objects) {
|
|
5792
|
-
if (!affectedObjectKeys.has(object.objectKey)) continue;
|
|
5793
|
-
visited += 1;
|
|
5794
|
-
const candidates = input.probes.map((probe) => ({
|
|
5795
|
-
identity: probe.identity,
|
|
5796
|
-
admitted: probe.admitted,
|
|
5797
|
-
distance: vectorDistance(object.position, probe.position),
|
|
5798
|
-
radius: probe.radius,
|
|
5799
|
-
irradiance: probe.irradiance
|
|
5800
|
-
}));
|
|
5801
|
-
const admission = admitProbeContributors(candidates);
|
|
5802
|
-
if (!firstObjectSeen) {
|
|
5803
|
-
firstObjectSeen = true;
|
|
5804
|
-
firstActiveCount = admission.active.length;
|
|
5805
|
-
firstAdmittedCount = admission.admitted.length;
|
|
5806
|
-
firstReceipt = admission.receipt;
|
|
5807
|
-
}
|
|
5808
|
-
const previousRecord = this.records.get(object.objectKey);
|
|
5809
|
-
const lastKnownGood = input.lastKnownGood ?? previousRecord?.lastKnownGood ?? false;
|
|
5810
|
-
let record;
|
|
5811
|
-
if (admission.error !== void 0) firstError ??= admission.error;
|
|
5812
|
-
if (admission.admitted.length === 0) {
|
|
5813
|
-
if (input.probes.length === 0 && input.retainSkyResidualRecords !== true) {
|
|
5814
|
-
nextRecords.delete(object.objectKey);
|
|
5815
|
-
this.recordFacts.delete(object.objectKey);
|
|
5816
|
-
continue;
|
|
5817
|
-
}
|
|
5818
|
-
const sentinel = lastKnownGood ? "no-active" : "no-lkg";
|
|
5819
|
-
record = encodeProbeRecord(
|
|
5820
|
-
object.objectKey,
|
|
5821
|
-
object.generation,
|
|
5822
|
-
0,
|
|
5823
|
-
new Array(27).fill(0),
|
|
5824
|
-
true,
|
|
5825
|
-
false,
|
|
5826
|
-
lastKnownGood,
|
|
5827
|
-
sentinel
|
|
5828
|
-
);
|
|
5829
|
-
this.recordFacts.set(object.objectKey, {
|
|
5830
|
-
contributors: [],
|
|
5831
|
-
qHatSum: 0,
|
|
5832
|
-
rStar: PROBE_R_MIN,
|
|
5833
|
-
fallbackReason: admission.error?.code ?? sentinel
|
|
5834
|
-
});
|
|
5835
|
-
} else {
|
|
5836
|
-
const blend = blendProbeCoefficients(admission.admitted, PROBE_R_MIN);
|
|
5837
|
-
if (blend === void 0) {
|
|
5838
|
-
firstError ??= {
|
|
5839
|
-
code: "invalid-admitted-prefix",
|
|
5840
|
-
activeCount: admission.active.length,
|
|
5841
|
-
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
5842
|
-
receipt: admission.receipt
|
|
5843
|
-
};
|
|
5844
|
-
firstReceipt = admission.receipt;
|
|
5845
|
-
continue;
|
|
5846
|
-
}
|
|
5847
|
-
record = encodeProbeRecord(
|
|
5848
|
-
object.objectKey,
|
|
5849
|
-
object.generation,
|
|
5850
|
-
blend.C,
|
|
5851
|
-
blend.SHPreblend,
|
|
5852
|
-
true,
|
|
5853
|
-
true,
|
|
5854
|
-
true,
|
|
5855
|
-
void 0
|
|
5856
|
-
);
|
|
5857
|
-
this.recordFacts.set(object.objectKey, {
|
|
5858
|
-
contributors: blend.terms,
|
|
5859
|
-
qHatSum: blend.Q,
|
|
5860
|
-
rStar: blend.rStar,
|
|
5861
|
-
fallbackReason: admission.error === void 0 ? void 0 : "capacity-exceeded-stable-prefix"
|
|
5862
|
-
});
|
|
5863
|
-
if (firstTerms.length === 0) firstTerms = blend.terms;
|
|
5864
|
-
firstCoverage = blend.C;
|
|
5865
|
-
}
|
|
5866
|
-
nextRecords.set(object.objectKey, record);
|
|
5867
|
-
firstError ??= admission.error;
|
|
5868
|
-
}
|
|
5869
|
-
const allocations = previous === void 0 ? nextRecords.size : 0;
|
|
5870
|
-
const resultRecords = Object.freeze([...nextRecords.values()]);
|
|
5871
|
-
const result = {
|
|
5872
|
-
records: resultRecords,
|
|
5873
|
-
contributors: firstTerms,
|
|
5874
|
-
activeContributorCount: firstActiveCount,
|
|
5875
|
-
admittedProbeCount: firstAdmittedCount,
|
|
5876
|
-
coverage: firstCoverage,
|
|
5877
|
-
skyResidualFraction: 1 - firstCoverage,
|
|
5878
|
-
dirtyReasons: uniqueDirtyReasons,
|
|
5879
|
-
affectedObjectKeys: [...affectedObjectKeys].sort((left, right) => left - right),
|
|
5880
|
-
visits: visited,
|
|
5881
|
-
allocations,
|
|
5882
|
-
uploads: nextRecords.size,
|
|
5883
|
-
receipt: firstReceipt,
|
|
5884
|
-
...firstError === void 0 ? {} : { error: firstError }
|
|
5885
|
-
};
|
|
5886
|
-
const projectedRecords = Object.freeze(
|
|
5887
|
-
[...nextRecords].map(([objectKey, record]) => Object.freeze({ cacheKey: objectKey, record }))
|
|
5888
|
-
);
|
|
5889
|
-
const dirtyRecords = Object.freeze(
|
|
5890
|
-
projectedRecords.filter(({ record }) => affectedObjectKeys.has(record.objectKey))
|
|
5891
|
-
);
|
|
5892
|
-
const removedCacheKeys = Object.freeze(
|
|
5893
|
-
[...this.records.keys()].filter((objectKey) => !nextRecords.has(objectKey))
|
|
5894
|
-
);
|
|
5895
|
-
const capacity = projectedRecords.reduce(
|
|
5896
|
-
(value, { record }) => Math.max(value, record.objectKey + 2),
|
|
5897
|
-
1
|
|
5898
|
-
);
|
|
5899
|
-
this.previous = input;
|
|
5900
|
-
this.records = nextRecords;
|
|
5901
|
-
this.resultRecords = resultRecords;
|
|
5902
|
-
const baseRevision = this.bufferProjection.revision;
|
|
5903
|
-
this.bufferProjection = Object.freeze({
|
|
5904
|
-
sourceIdentity: this.bufferProjectionSource,
|
|
5905
|
-
revision: baseRevision + 1,
|
|
5906
|
-
baseRevision,
|
|
5907
|
-
capacity,
|
|
5908
|
-
records: projectedRecords,
|
|
5909
|
-
dirtyRecords,
|
|
5910
|
-
removedCacheKeys
|
|
5911
|
-
});
|
|
5912
|
-
this.lastError = firstError;
|
|
5913
|
-
this.lastReceipt = firstReceipt;
|
|
5914
|
-
this.lastActiveContributorCount = firstActiveCount;
|
|
5915
|
-
this.lastAdmittedProbeCount = firstAdmittedCount;
|
|
5916
|
-
this.lastCoverage = firstCoverage;
|
|
5917
|
-
this.lastFinite = this.resultRecords.every(
|
|
5918
|
-
(record) => record.bytes.length === PROBE_BLEND_RECORD_BYTE_SIZE
|
|
5919
|
-
);
|
|
5920
|
-
this.lastSky = input.sky ?? (input.skyIrradiance === void 0 ? { available: false, irradiance: [0, 0, 0], fallbackReason: "no-skylight" } : { available: true, irradiance: input.skyIrradiance });
|
|
5921
|
-
this.lastDirtyVisitCount = visited;
|
|
5922
|
-
this.lastDirtyReasons = uniqueDirtyReasons;
|
|
5923
|
-
return result;
|
|
5924
|
-
}
|
|
5925
|
-
};
|
|
5926
6273
|
|
|
5927
|
-
export {
|
|
5928
|
-
//# sourceMappingURL=chunk-
|
|
5929
|
-
//# sourceMappingURL=chunk-
|
|
6274
|
+
export { ANTIALIAS_FXAA, ANTIALIAS_MSAA, ANTIALIAS_NONE, ANTIALIAS_TAA, Atmosphere, BLOOM_DISABLED, BLOOM_ENABLED, BarrelDistortion, CAMERA_BLOOM_INTENSITY_MAX, CAMERA_BLOOM_INTENSITY_MIN, CAMERA_BLOOM_SCATTER_MAX, CAMERA_BLOOM_SCATTER_MIN, CAMERA_BLOOM_SOFT_KNEE_MAX, CAMERA_BLOOM_SOFT_KNEE_MIN, CAMERA_BLOOM_THRESHOLD_MAX, CAMERA_BLOOM_THRESHOLD_MIN, CAMERA_EXPOSURE_MODE_AUTO, CAMERA_EXPOSURE_MODE_MANUAL, CAMERA_PROJECTION_ORTHOGRAPHIC, CAMERA_PROJECTION_PERSPECTIVE, CAMERA_TEMPERATURE_MAX, CAMERA_TEMPERATURE_MIN, CAMERA_TINT_MAX, CAMERA_TINT_MIN, CLOUD_EXTINCTION_COEFFICIENT, CLOUD_QUALITY_PROFILES, CUBE_CAMERA_FACE_ORDER, CUBE_CAMERA_UPDATE_CONTINUOUS, CUBE_CAMERA_UPDATE_ONCE, CUBE_CAMERA_UPDATE_ON_DEMAND, Camera, CameraError, CloudLayer, CloudLayerCacheInvalidError, CloudLayerCapabilityMissingError, CloudLayerInvalidParameterError, CloudLayerOwnerConflictError, CloudLayerResourceFailureError, CloudQualityValue, CubeCamera, DEFAULT_CLOUD_LAYER, DEFAULT_DEPTH_OF_FIELD_PARAMS, DEPTH_OF_FIELD_PARAMS_BYTE_SIZE, DepthOfField, DepthOfFieldQualityValue, DepthOfFieldSideValue, DepthOfFieldValidationError, DirectionalLight, DirectionalShadowFilterValue, DynamicResolution, Fog, Instances, Layer, LightProbe, Lines, MAX_VOLUMETRIC_FOG_OWNERS, MeshFilter, MeshRenderer, MotionBlur, PointLight, PointLightShadow, PointShapeValue, Points, PostProcessParams, REFLECTION_PROBE_UPDATE_CONTINUOUS, REFLECTION_PROBE_UPDATE_ONCE, REFLECTION_PROBE_UPDATE_ON_CHANGE, RectAreaLight, ReflectionProbe, SKYBOX_MODE_CUBEMAP, SKYLIGHT_RECOVERY_FALLBACK, SceneInstance, ScreenSpaceReflection, SkyboxBackground, Skylight, SortKey, SpotLight, SunCardinalityError, TONEMAP_ACES_FILMIC, TONEMAP_AGX, TONEMAP_CINEON, TONEMAP_LINEAR, TONEMAP_NEUTRAL, TONEMAP_NONE, TONEMAP_REINHARD, TONEMAP_REINHARD_EXTENDED, Visibility, VisibilityStateValue, VolumetricFog, VolumetricFogSamplingValue, attachBarrelDistortionCameraFrame, buildCubeCameraFaceViews, buildRectAreaWorldFrame, buildShadowFrusta, cameraExposureFromColumns, cameraProjectionFromF32, cloudLayerFormationKey, cloudLayerSourceKey, cloudQualityFromF32, cloudShadowResolutionForQuality, cloudViewDistanceForQuality, computeInvRangeSquared, createBarrelDistortionMapping, createCookieProjectionMatrixData, cubeCameraUpdateIntentFromF32, cubeCameraUpdateIntentToF32, degToCos, depthOfFieldQualityCode, depthOfFieldQualityFromF32, depthOfFieldRequestFailure, depthOfFieldSideCode, depthOfFieldSideFromF32, depthOfFieldTapCount, directionalShadowQualityFromF32, extractCloudLayer, extractFrames, extractVolumetricFog, freezeBarrelDistortionMapping, glyphTextLayoutSystem, hasVolumetricFogCapability, internSharedRefFromGuid, mapDisplayToScene, mapDisplayUvToSceneUv, mapSceneToDisplay, mapSceneUvToDisplayUv, orthographic, packDepthOfFieldParams, perspective, pointShapeFromU32, projectMeshMaterialBindingObservation, rectAreaFacesPoint, reflectionProbeUpdateIntentFromF32, reflectionProbeUpdateIntentToF32, resolveDepthOfFieldFrameParams, resolveDepthOfFieldParams, resolveIntegratedVolumeConsumer, resolveMaterialSnapshot, resolveSelectedVolumetricLight, resolveVisibility, resolveVolumetricFogLightPair, selectCloudLayerFrame, signedDepthOfFieldCoC, summarizeMeshMaterialBindings, validateBarrelDistortionParameters, validateCameraBloom, validateCameraColorGrading, validateCameraExposure, validateCloudLayer, validateDepthOfFieldFrameParams, validateDepthOfFieldParams, validateDirection, validateDirectionalLightData, validateDynamicResolutionCamera, validateDynamicResolutionParameters, validateLightProbeData, validatePointLightData, validatePointLightShadowData, validateRectAreaLightData, validateSpotLightData, validateVolumetricFog, visibilityStateFromU32 };
|
|
6275
|
+
//# sourceMappingURL=chunk-BZQ4CDRF.mjs.map
|
|
6276
|
+
//# sourceMappingURL=chunk-BZQ4CDRF.mjs.map
|