@haiyue/extensions 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/animation/Animation2DComponent.d.ts +18 -0
- package/dist/animation/Animation2DExtensionRegistry.d.ts +7 -0
- package/dist/animation/Animation2DRenderSystem.d.ts +6 -0
- package/dist/animation/Animation2DRuntime.d.ts +6 -2
- package/dist/animation/AnimationTextRasterizer.d.ts +4 -0
- package/dist/animation/AnimationVisual2D.d.ts +58 -2
- package/dist/animation/interaction/InteractionRuntime.d.ts +83 -0
- package/dist/animation/interaction/runtime-types.d.ts +257 -0
- package/dist/animation.d.ts +3 -1
- package/dist/animation.js +551 -59
- package/dist/animation3d.js +1 -1
- package/dist/canvas-text.js +1 -1
- package/dist/chunks/2d-ui-animation-2d.generated-FpziAeGJ.js +4 -0
- package/dist/chunks/2d-ui-indexed-sprite.generated-DmKR0CC3.js +4 -0
- package/dist/chunks/Animation2DStateMachine-BHcA6Cu3.js +18212 -0
- package/dist/chunks/{AnimationStateMachineController-BIcAh-sT.js → AnimationStateMachineController-4sKaqh9E.js} +2 -2
- package/dist/chunks/AnimationVisual2D-8y3qp0_U.js +230 -0
- package/dist/chunks/{RenderSystem2DBase-DDT61dAu.js → RenderSystem2DBase-CF2s00vg.js} +2 -2
- package/dist/controls/VirtualJoystickControls.d.ts +46 -0
- package/dist/controls/contracts.d.ts +91 -0
- package/dist/controls.d.ts +2 -0
- package/dist/controls.js +358 -0
- package/dist/deformable-animation/runtime/DeformableMesh2DRuntimeExtension.d.ts +11 -0
- package/dist/deformable-animation/runtime/DeformableMesh2DSampler.d.ts +11 -0
- package/dist/deformable-animation.d.ts +5 -0
- package/dist/deformable-animation.js +316 -0
- package/dist/experimental-indexed-sprite.d.ts +5 -0
- package/dist/experimental-indexed-sprite.js +482 -0
- package/dist/gltf-worker-runtime.js +9 -9
- package/dist/hya-state-machine.js +3 -2
- package/dist/index.js +1 -1
- package/dist/indexed-sprite/AtlasLayout.d.ts +2 -0
- package/dist/indexed-sprite/IndexedSpriteRenderer.d.ts +23 -0
- package/dist/indexed-sprite/contracts.d.ts +89 -0
- package/dist/internal/2d-ui-shader-artifact.js +232 -23
- package/dist/ray-tracing/acceleration/bvh.d.ts +37 -0
- package/dist/ray-tracing/acceleration/index.d.ts +6 -0
- package/dist/ray-tracing/acceleration/pack.d.ts +152 -0
- package/dist/ray-tracing/acceleration/query.d.ts +4 -0
- package/dist/ray-tracing/acceleration/runtime.d.ts +30 -0
- package/dist/ray-tracing/acceleration/tlas.d.ts +4 -0
- package/dist/ray-tracing/acceleration/types.d.ts +165 -0
- package/dist/ray-tracing/denoise/index.d.ts +3 -0
- package/dist/ray-tracing/denoise/layout.d.ts +9 -0
- package/dist/ray-tracing/denoise/runtime.d.ts +23 -0
- package/dist/ray-tracing/denoise/types.d.ts +28 -0
- package/dist/ray-tracing/hybrid/contract.d.ts +2 -0
- package/dist/ray-tracing/hybrid/index.d.ts +5 -0
- package/dist/ray-tracing/hybrid/layout.d.ts +17 -0
- package/dist/ray-tracing/hybrid/plan.d.ts +2 -0
- package/dist/ray-tracing/hybrid/runtime.d.ts +31 -0
- package/dist/ray-tracing/hybrid/shaders/hybrid-artifact.generated.d.ts +625 -0
- package/dist/ray-tracing/hybrid/types.d.ts +144 -0
- package/dist/ray-tracing/lifecycle/device-recovery.d.ts +31 -0
- package/dist/ray-tracing/lifecycle/index.d.ts +2 -0
- package/dist/ray-tracing/lifecycle/types.d.ts +37 -0
- package/dist/ray-tracing/material/index.d.ts +2 -0
- package/dist/ray-tracing/material/pack.d.ts +5 -0
- package/dist/ray-tracing/material/types.d.ts +63 -0
- package/dist/ray-tracing/reference/index.d.ts +122 -0
- package/dist/ray-tracing/renderer/index.d.ts +7 -0
- package/dist/ray-tracing/renderer/layout.d.ts +20 -0
- package/dist/ray-tracing/renderer/plan.d.ts +2 -0
- package/dist/ray-tracing/renderer/reference.d.ts +17 -0
- package/dist/ray-tracing/renderer/runtime.d.ts +39 -0
- package/dist/ray-tracing/renderer/scene.d.ts +4 -0
- package/dist/ray-tracing/renderer/types.d.ts +115 -0
- package/dist/ray-tracing/sampling/index.d.ts +8 -0
- package/dist/ray-tracing/sampling/layout.d.ts +12 -0
- package/dist/ray-tracing/sampling/plan.d.ts +5 -0
- package/dist/ray-tracing/sampling/revision.d.ts +15 -0
- package/dist/ray-tracing/sampling/runtime.d.ts +44 -0
- package/dist/ray-tracing/sampling/sequence.d.ts +2 -0
- package/dist/ray-tracing/sampling/types.d.ts +96 -0
- package/dist/ray-tracing/scene/index.d.ts +41 -0
- package/dist/ray-tracing/traversal/index.d.ts +4 -0
- package/dist/ray-tracing/traversal/layout.d.ts +16 -0
- package/dist/ray-tracing/traversal/plan.d.ts +2 -0
- package/dist/ray-tracing/traversal/runtime.d.ts +25 -0
- package/dist/ray-tracing/traversal/types.d.ts +79 -0
- package/dist/ray-tracing/worker/client.d.ts +25 -0
- package/dist/ray-tracing/worker/index.d.ts +5 -0
- package/dist/ray-tracing/worker/runtime.d.ts +11 -0
- package/dist/ray-tracing/worker/serialization.d.ts +16 -0
- package/dist/ray-tracing/worker/source.d.ts +1 -0
- package/dist/ray-tracing/worker/types.d.ts +91 -0
- package/dist/ray-tracing.d.ts +17 -0
- package/dist/ray-tracing.js +7666 -0
- package/dist/spine.js +1 -1
- package/dist/tilemap.js +1 -1
- package/package.json +26 -23
- package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +0 -27
- package/dist/animation/Particle2DDescriptorAdapter.d.ts +0 -5
- package/dist/animation3d/Animation3DResource.d.ts +0 -28
- package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +0 -65
- package/dist/animation3d/hya/index.d.ts +0 -4
- package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +0 -81
- package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +0 -39
- package/dist/animation3d/runtime/integration/index.d.ts +0 -2
- package/dist/animation3d/runtime/mixer/Action.d.ts +0 -150
- package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +0 -6
- package/dist/animation3d/runtime/mixer/Mixer.d.ts +0 -59
- package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +0 -1
- package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +0 -24
- package/dist/animation3d/runtime/mixer/index.d.ts +0 -4
- package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +0 -1
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +0 -1
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +0 -1
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +0 -1
- package/dist/animation3d/runtime/state-machine/index.d.ts +0 -6
- package/dist/benchmark.d.ts +0 -30
- package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +0 -4
- package/dist/chunks/Animation2DStateMachine-9IonguNa.js +0 -3245
- package/dist/gltf/GltfAccessorReader.d.ts +0 -7
- package/dist/gltf/GltfDracoDecoder.d.ts +0 -4
- package/dist/gltf/GltfLoaderErrors.d.ts +0 -4
- package/dist/gltf/GltfMaterialDescriptor.d.ts +0 -23
- package/dist/gltf/GltfMaterialLoader.d.ts +0 -6
- package/dist/gltf-worker-runtime.d.ts +0 -7
- package/dist/plugins/pluginUtils.d.ts +0 -20
- package/dist/plugins.d.ts +0 -6
- package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +0 -658
- package/dist/spine/Spine2DGpuRenderer.d.ts +0 -65
- package/dist/spine/Spine2DRuntime.d.ts +0 -103
- package/dist/spine/SpineAnimationBoneTimelines.d.ts +0 -8
- package/dist/spine/SpineFloatBuilder.d.ts +0 -15
- package/dist/spine/SpinePathConstraintSolver.d.ts +0 -32
- package/dist/spine/SpineSkeletonRuntime.d.ts +0 -185
- package/dist/spine/SpineSlotAnimation.d.ts +0 -17
- package/dist/spine/SpineTimelineRuntime.d.ts +0 -25
- package/dist/spine/SpineVertexBuilder.d.ts +0 -61
- package/dist/tilemap/Tilemap2DRenderer.d.ts +0 -24
- package/dist/utils/arrayAccess.d.ts +0 -4
- package/dist/utils/camera2d.d.ts +0 -12
- package/src/animation/Animation2DComponent.ts +0 -135
- package/src/animation/Animation2DExtensionRegistry.ts +0 -52
- package/src/animation/Animation2DRenderSystem.ts +0 -1026
- package/src/animation/Animation2DRuntime.ts +0 -1326
- package/src/animation/Animation2DStateMachine.ts +0 -39
- package/src/animation/Animation2DStateMachineComponent.ts +0 -228
- package/src/animation/Animation2DStateMachineRuntime.ts +0 -141
- package/src/animation/Animation2DStateMachineSystem.ts +0 -60
- package/src/animation/Animation2DStateMachineVisualRuntime.ts +0 -282
- package/src/animation/Animation2DSystem.ts +0 -72
- package/src/animation/AnimationAssetLoader.ts +0 -84
- package/src/animation/AnimationAudioClip.ts +0 -140
- package/src/animation/AnimationPathTessellator.ts +0 -691
- package/src/animation/AnimationTextRasterizer.ts +0 -340
- package/src/animation/AnimationVisual2D.ts +0 -153
- package/src/animation/HyaAnimation2DClipAdapter.ts +0 -845
- package/src/animation/Particle2DDescriptorAdapter.ts +0 -35
- package/src/animation/runtime/mixer/Action.ts +0 -876
- package/src/animation/runtime/mixer/Mixer.ts +0 -505
- package/src/animation/runtime/mixer/PoseBuffer.ts +0 -105
- package/src/animation/runtime/mixer/Sampler.ts +0 -252
- package/src/animation/runtime/mixer/Types.ts +0 -138
- package/src/animation/runtime/mixer/index.ts +0 -31
- package/src/animation-state-machine/AnimationStateMachine.ts +0 -146
- package/src/animation-state-machine/AnimationStateMachineChannels.ts +0 -116
- package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +0 -197
- package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +0 -503
- package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +0 -763
- package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +0 -987
- package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +0 -54
- package/src/animation-state-machine/runtime/index.ts +0 -47
- package/src/animation.ts +0 -33
- package/src/animation3d/Animation3DAction.ts +0 -73
- package/src/animation3d/Animation3DBinding.ts +0 -116
- package/src/animation3d/Animation3DClip.ts +0 -22
- package/src/animation3d/Animation3DError.ts +0 -34
- package/src/animation3d/Animation3DMixer.ts +0 -82
- package/src/animation3d/Animation3DPose.ts +0 -100
- package/src/animation3d/Animation3DPoseBuffer.ts +0 -85
- package/src/animation3d/Animation3DResource.ts +0 -49
- package/src/animation3d/Animation3DStateMachine.ts +0 -126
- package/src/animation3d/Animation3DStateMachineRuntime.ts +0 -191
- package/src/animation3d/Animation3DTrack.ts +0 -29
- package/src/animation3d/hya/HyaAnimation3DRuntime.ts +0 -756
- package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +0 -618
- package/src/animation3d/hya/HyaAnimation3DTypes.ts +0 -168
- package/src/animation3d/hya/index.ts +0 -10
- package/src/animation3d/index.ts +0 -76
- package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +0 -325
- package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +0 -158
- package/src/animation3d/runtime/integration/index.ts +0 -9
- package/src/animation3d/runtime/mixer/Action.ts +0 -833
- package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +0 -26
- package/src/animation3d/runtime/mixer/Mixer.ts +0 -562
- package/src/animation3d/runtime/mixer/PoseBuffer.ts +0 -1
- package/src/animation3d/runtime/mixer/TrackSampler.ts +0 -399
- package/src/animation3d/runtime/mixer/index.ts +0 -8
- package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +0 -4
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +0 -1
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +0 -1
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +0 -1
- package/src/animation3d/runtime/state-machine/index.ts +0 -45
- package/src/animation3d.ts +0 -5
- package/src/benchmark.ts +0 -168
- package/src/canvas-text/CanvasText2DRenderSystem.ts +0 -292
- package/src/canvas-text/CanvasTextComponent.ts +0 -47
- package/src/canvas-text.ts +0 -4
- package/src/experimental-gltf-worker.ts +0 -18
- package/src/experimental-spine-worker.ts +0 -9
- package/src/gltf/GltfAccessorReader.ts +0 -271
- package/src/gltf/GltfAnimation3DAdapter.ts +0 -680
- package/src/gltf/GltfAnimationRuntime.ts +0 -392
- package/src/gltf/GltfAssetWorkerClient.ts +0 -136
- package/src/gltf/GltfCompatibilityReport.ts +0 -214
- package/src/gltf/GltfConservativeBounds.ts +0 -219
- package/src/gltf/GltfDracoDecoder.ts +0 -251
- package/src/gltf/GltfExtensionAdapter.ts +0 -340
- package/src/gltf/GltfLoaderContract.ts +0 -333
- package/src/gltf/GltfLoaderErrors.ts +0 -59
- package/src/gltf/GltfMaterialDescriptor.ts +0 -134
- package/src/gltf/GltfMaterialEncoder.ts +0 -173
- package/src/gltf/GltfMaterialLoader.ts +0 -293
- package/src/gltf/GltfModelComponent.ts +0 -115
- package/src/gltf/GltfModelSystem.ts +0 -208
- package/src/gltf/GltfPlugin.ts +0 -84
- package/src/gltf/GltfSchema.ts +0 -229
- package/src/gltf/GltfUvSemanticPlanner.ts +0 -166
- package/src/gltf/gltfLoader.ts +0 -910
- package/src/gltf-animation3d.ts +0 -10
- package/src/gltf-worker-runtime.ts +0 -10
- package/src/gltf.ts +0 -56
- package/src/grid/Grid2DComponent.ts +0 -69
- package/src/grid.ts +0 -2
- package/src/hya-state-machine.ts +0 -19
- package/src/index.ts +0 -2
- package/src/plugins/pluginUtils.ts +0 -50
- package/src/plugins.ts +0 -6
- package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +0 -242
- package/src/shaders/generated/2d-ui-artifact.generated.ts +0 -867
- package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +0 -38
- package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +0 -41
- package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +0 -36
- package/src/spine/Spine2DComponent.ts +0 -168
- package/src/spine/Spine2DGpuRenderer.ts +0 -449
- package/src/spine/Spine2DRenderSystem.ts +0 -199
- package/src/spine/Spine2DRuntime.ts +0 -1529
- package/src/spine/SpineAnimationBoneTimelines.ts +0 -30
- package/src/spine/SpineAssetParser.ts +0 -20
- package/src/spine/SpineAssetWorkerClient.ts +0 -112
- package/src/spine/SpineAssetWorkerContract.ts +0 -4
- package/src/spine/SpineAtlasParser.ts +0 -127
- package/src/spine/SpineFloatBuilder.ts +0 -66
- package/src/spine/SpinePathConstraintSolver.ts +0 -582
- package/src/spine/SpinePlugin.ts +0 -76
- package/src/spine/SpineSkeletonRuntime.ts +0 -521
- package/src/spine/SpineSlotAnimation.ts +0 -195
- package/src/spine/SpineTimelineRuntime.ts +0 -453
- package/src/spine/SpineVertexBuilder.ts +0 -686
- package/src/spine.ts +0 -7
- package/src/tilemap/Tilemap2DComponent.ts +0 -232
- package/src/tilemap/Tilemap2DRenderSystem.ts +0 -58
- package/src/tilemap/Tilemap2DRenderer.ts +0 -289
- package/src/tilemap/TilemapPlugin.ts +0 -75
- package/src/tilemap.ts +0 -6
- package/src/tween/Tween2DComponent.ts +0 -83
- package/src/tween/Tween2DSystem.ts +0 -78
- package/src/tween.ts +0 -4
- package/src/types/earcut.d.ts +0 -7
- package/src/types/wgsl.d.ts +0 -4
- package/src/utils/RenderSystem2DBase.ts +0 -164
- package/src/utils/arrayAccess.ts +0 -17
- package/src/utils/camera2d.ts +0 -68
- package/src/utils/render2dGpu.ts +0 -132
- package/test/fixtures/gltf/animation-characterization.gltf +0 -344
package/src/gltf/gltfLoader.ts
DELETED
|
@@ -1,910 +0,0 @@
|
|
|
1
|
-
import { CartesianTransform3D, Entity, Geometry3D, Mesh3D, PbrMaterial } from '@haiyue/engine';
|
|
2
|
-
import { Transform3D } from '@haiyue/engine/components';
|
|
3
|
-
import { type AssetHandle, type AssetManager } from '@haiyue/engine/assets';
|
|
4
|
-
import { monotonicNow, parseAssetWorkerFirst } from '@haiyue/engine/experimental/async';
|
|
5
|
-
import { mat4 } from 'wgpu-matrix';
|
|
6
|
-
import {
|
|
7
|
-
isGltfAsset,
|
|
8
|
-
type GltfAsset,
|
|
9
|
-
type GltfNode,
|
|
10
|
-
type GltfPrimitive,
|
|
11
|
-
} from './GltfSchema';
|
|
12
|
-
import { GltfConservativeBounds } from './GltfConservativeBounds';
|
|
13
|
-
import {
|
|
14
|
-
createGltfCompatibilityReport,
|
|
15
|
-
type GltfPrimitiveCompatibilityInput,
|
|
16
|
-
} from './GltfCompatibilityReport';
|
|
17
|
-
import {
|
|
18
|
-
parseTextureCoordinateSemantic,
|
|
19
|
-
planGltfUvSemantics,
|
|
20
|
-
} from './GltfUvSemanticPlanner';
|
|
21
|
-
import {
|
|
22
|
-
collectGltfMaterialVariantNames,
|
|
23
|
-
createGltfExtensionCapabilities,
|
|
24
|
-
resolveGltfExtensionAdapters,
|
|
25
|
-
type GltfExtensionCapability,
|
|
26
|
-
} from './GltfExtensionAdapter';
|
|
27
|
-
import {
|
|
28
|
-
attachGltfSource,
|
|
29
|
-
gltfDataError,
|
|
30
|
-
throwIfGltfLoadAborted as throwIfAborted,
|
|
31
|
-
} from './GltfLoaderErrors';
|
|
32
|
-
import {
|
|
33
|
-
generateFlatNormals,
|
|
34
|
-
readAccessorFloat,
|
|
35
|
-
readAccessorIndices,
|
|
36
|
-
readAccessorMat4,
|
|
37
|
-
readAccessorUnsigned,
|
|
38
|
-
} from './GltfAccessorReader';
|
|
39
|
-
import { decodeDracoPrimitive } from './GltfDracoDecoder';
|
|
40
|
-
import {
|
|
41
|
-
applyMorphWeights,
|
|
42
|
-
composeTrsMatrix,
|
|
43
|
-
createAnimationClips,
|
|
44
|
-
registerGltfConservativeBounds,
|
|
45
|
-
updateSkinnedPrimitive,
|
|
46
|
-
} from './GltfAnimationRuntime';
|
|
47
|
-
import { createGltfMaterial, preloadGltfTextures } from './GltfMaterialLoader';
|
|
48
|
-
import type {
|
|
49
|
-
GltfAnimationTarget,
|
|
50
|
-
GltfAssetStats,
|
|
51
|
-
GltfExtensionReport,
|
|
52
|
-
GltfExtensionReportEntry,
|
|
53
|
-
GltfGeometryPayloadMatrix,
|
|
54
|
-
GltfLoadContext,
|
|
55
|
-
GltfLoadWarning,
|
|
56
|
-
GltfParsedAsset,
|
|
57
|
-
GltfPendingSkinnedPrimitive,
|
|
58
|
-
GltfPrimitiveGeometryPayload,
|
|
59
|
-
GltfSkinnedPrimitiveRuntime,
|
|
60
|
-
LoadedGltfModel,
|
|
61
|
-
LoadGltfOptions,
|
|
62
|
-
} from './GltfLoaderContract';
|
|
63
|
-
|
|
64
|
-
export * from './GltfLoaderContract';
|
|
65
|
-
export { applyGltfAnimationClip } from './GltfAnimationRuntime';
|
|
66
|
-
|
|
67
|
-
const sharedImageSourcesByAsset = new WeakMap<GltfParsedAsset, WeakMap<AssetManager, Map<number, string>>>();
|
|
68
|
-
|
|
69
|
-
export async function loadGltfModel(src: string, options: LoadGltfOptions = {}): Promise<LoadedGltfModel> {
|
|
70
|
-
const loadStartedAt = nowMilliseconds();
|
|
71
|
-
throwIfAborted(options.signal);
|
|
72
|
-
const extensionAdapters = resolveGltfExtensionAdapters(options.extensionAdapters);
|
|
73
|
-
const extensionCapabilities = createGltfExtensionCapabilities(extensionAdapters);
|
|
74
|
-
let parsedAssetHandle: AssetHandle<GltfParsedAsset> | null = null;
|
|
75
|
-
let parsedAsset: GltfParsedAsset;
|
|
76
|
-
const assetManager = options.assetManager ?? null;
|
|
77
|
-
try {
|
|
78
|
-
if (assetManager) {
|
|
79
|
-
parsedAssetHandle = await assetManager.load(
|
|
80
|
-
`gltf:parsed:${resolveAssetIdentity(src)}`,
|
|
81
|
-
signal => loadParsedAssetForModel(src, options, signal),
|
|
82
|
-
asset => disposeSharedImageSources(asset, assetManager),
|
|
83
|
-
{ signal: options.signal },
|
|
84
|
-
);
|
|
85
|
-
parsedAsset = parsedAssetHandle.value;
|
|
86
|
-
} else {
|
|
87
|
-
parsedAsset = await loadParsedAssetForModel(src, options, options.signal);
|
|
88
|
-
}
|
|
89
|
-
} catch (error) {
|
|
90
|
-
throw attachGltfSource(error, src);
|
|
91
|
-
}
|
|
92
|
-
const { gltf, buffers, baseUrl, geometryPayloads } = parsedAsset;
|
|
93
|
-
let extensionInspection: GltfExtensionInspection;
|
|
94
|
-
try {
|
|
95
|
-
extensionInspection = validateGltfExtensions(gltf, extensionCapabilities);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
parsedAssetHandle?.release();
|
|
98
|
-
throw attachGltfSource(error, src);
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
for (const warning of extensionInspection.warnings) options.onWarning?.(warning);
|
|
102
|
-
throwIfAborted(options.signal);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
parsedAssetHandle?.release();
|
|
105
|
-
throw attachGltfSource(error, src);
|
|
106
|
-
}
|
|
107
|
-
const animationTargets = new Map<number, GltfAnimationTarget>();
|
|
108
|
-
const context: GltfLoadContext = {
|
|
109
|
-
baseUrl,
|
|
110
|
-
assetIdentity: resolveAssetIdentity(src),
|
|
111
|
-
objectUrls: [],
|
|
112
|
-
imageSources: parsedAssetHandle && assetManager
|
|
113
|
-
? getSharedImageSources(parsedAsset, assetManager)
|
|
114
|
-
: new Map(),
|
|
115
|
-
sharedImageSources: parsedAssetHandle !== null,
|
|
116
|
-
preloadedTextures: new Map(),
|
|
117
|
-
animationTargets,
|
|
118
|
-
skinnedPrimitives: [],
|
|
119
|
-
pendingSkinnedPrimitives: [],
|
|
120
|
-
assetHandles: parsedAssetHandle ? [parsedAssetHandle] : [],
|
|
121
|
-
...(geometryPayloads === undefined ? {} : { geometryPayloads }),
|
|
122
|
-
compatibilityPrimitives: [],
|
|
123
|
-
extensionAdapters,
|
|
124
|
-
};
|
|
125
|
-
const root = new Entity('glTF Model');
|
|
126
|
-
try {
|
|
127
|
-
let mainThreadDracoDecodeMs = 0;
|
|
128
|
-
const geometryOptions: LoadGltfOptions = geometryPayloads === undefined
|
|
129
|
-
? {
|
|
130
|
-
...options,
|
|
131
|
-
diagnostics: {
|
|
132
|
-
onDracoDecode(durationMs, decodedBytes) {
|
|
133
|
-
mainThreadDracoDecodeMs += durationMs;
|
|
134
|
-
options.diagnostics?.onDracoDecode?.(durationMs, decodedBytes);
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
}
|
|
138
|
-
: options;
|
|
139
|
-
let geometryPreparationMs = parsedAsset.metrics?.geometryPreparationMs ?? 0;
|
|
140
|
-
const geometryTask = geometryPayloads === undefined
|
|
141
|
-
? (async () => {
|
|
142
|
-
const startedAt = nowMilliseconds();
|
|
143
|
-
try {
|
|
144
|
-
return await prepareGltfGeometryPayloads(gltf, buffers, geometryOptions);
|
|
145
|
-
} finally {
|
|
146
|
-
geometryPreparationMs = nowMilliseconds() - startedAt;
|
|
147
|
-
}
|
|
148
|
-
})()
|
|
149
|
-
: Promise.resolve(geometryPayloads);
|
|
150
|
-
let textureDecodeTranscodeUploadMs = 0;
|
|
151
|
-
const textureTask = (async () => {
|
|
152
|
-
const startedAt = nowMilliseconds();
|
|
153
|
-
try {
|
|
154
|
-
await preloadGltfTextures(gltf, buffers, options, context);
|
|
155
|
-
} finally {
|
|
156
|
-
textureDecodeTranscodeUploadMs = nowMilliseconds() - startedAt;
|
|
157
|
-
}
|
|
158
|
-
})();
|
|
159
|
-
const [geometryResult, textureResult] = await Promise.all([
|
|
160
|
-
settleTask(geometryTask),
|
|
161
|
-
settleTask(textureTask),
|
|
162
|
-
]);
|
|
163
|
-
if (!geometryResult.ok) throw geometryResult.error;
|
|
164
|
-
if (!textureResult.ok) throw textureResult.error;
|
|
165
|
-
context.geometryPayloads = geometryResult.value;
|
|
166
|
-
const instantiateStartedAt = nowMilliseconds();
|
|
167
|
-
root.addComponent(new CartesianTransform3D());
|
|
168
|
-
const sceneIndex = options.scene ?? gltf.scene ?? 0;
|
|
169
|
-
const scene = gltf.scenes?.[sceneIndex] ?? gltf.scenes?.[0];
|
|
170
|
-
const rootNodes = await Promise.all((scene?.nodes ?? []).map(nodeIndex =>
|
|
171
|
-
instantiateNode(gltf, buffers, nodeIndex, options, animationTargets, context)));
|
|
172
|
-
for (const child of rootNodes) {
|
|
173
|
-
throwIfAborted(options.signal);
|
|
174
|
-
if (child) root.addChild(child);
|
|
175
|
-
}
|
|
176
|
-
resolvePendingSkins(gltf, buffers, context);
|
|
177
|
-
const animationClips = createAnimationClips(gltf, buffers, animationTargets, context.skinnedPrimitives);
|
|
178
|
-
const instantiateMs = nowMilliseconds() - instantiateStartedAt;
|
|
179
|
-
const decodedGeometryBytes = countDecodedGeometryBytes(context.compatibilityPrimitives);
|
|
180
|
-
const parsedMetrics = parsedAsset.metrics;
|
|
181
|
-
const dracoDecodeMs = (parsedMetrics?.dracoDecodeMs ?? 0) + mainThreadDracoDecodeMs;
|
|
182
|
-
const compatibilityReport = createGltfCompatibilityReport(
|
|
183
|
-
gltf,
|
|
184
|
-
extensionInspection.report.entries,
|
|
185
|
-
context.compatibilityPrimitives,
|
|
186
|
-
{
|
|
187
|
-
loadMs: nowMilliseconds() - loadStartedAt,
|
|
188
|
-
decodedGeometryBytes,
|
|
189
|
-
},
|
|
190
|
-
);
|
|
191
|
-
return {
|
|
192
|
-
root,
|
|
193
|
-
animations: animationClips.map(clip => ({ name: clip.name, duration: clip.duration, channelCount: clip.channels.length })),
|
|
194
|
-
animationClips,
|
|
195
|
-
assetStats: collectAssetStats(gltf),
|
|
196
|
-
loadMetrics: Object.freeze({
|
|
197
|
-
timings: Object.freeze({
|
|
198
|
-
fetchMs: parsedMetrics?.fetchMs ?? 0,
|
|
199
|
-
workerParseMs: parsedMetrics?.workerParseMs ?? 0,
|
|
200
|
-
dracoDecodeMs,
|
|
201
|
-
geometryPreparationMs: geometryPayloads === undefined
|
|
202
|
-
? Math.max(0, geometryPreparationMs - mainThreadDracoDecodeMs)
|
|
203
|
-
: parsedMetrics?.geometryPreparationMs ?? 0,
|
|
204
|
-
instantiateMs,
|
|
205
|
-
textureDecodeTranscodeUploadMs,
|
|
206
|
-
totalMs: nowMilliseconds() - loadStartedAt,
|
|
207
|
-
}),
|
|
208
|
-
sourceBytes: parsedMetrics?.sourceBytes ?? countUniqueBufferBytes(buffers),
|
|
209
|
-
decodedGeometryBytes,
|
|
210
|
-
workerTransferBytes: parsedMetrics?.workerTransferBytes ?? 0,
|
|
211
|
-
workerTransferBufferCount: parsedMetrics?.workerTransferBufferCount ?? 0,
|
|
212
|
-
}),
|
|
213
|
-
objectUrls: context.objectUrls,
|
|
214
|
-
assetHandles: context.assetHandles,
|
|
215
|
-
materialVariants: collectGltfMaterialVariantNames(gltf, extensionAdapters),
|
|
216
|
-
warnings: extensionInspection.warnings,
|
|
217
|
-
extensionReport: extensionInspection.report,
|
|
218
|
-
compatibilityReport,
|
|
219
|
-
};
|
|
220
|
-
} catch (error) {
|
|
221
|
-
disposeEntityTree(root);
|
|
222
|
-
revokeObjectUrls(context.objectUrls);
|
|
223
|
-
releaseAssetHandles(context.assetHandles);
|
|
224
|
-
throw attachGltfSource(error, src);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async function loadParsedAssetForModel(
|
|
229
|
-
src: string,
|
|
230
|
-
options: LoadGltfOptions,
|
|
231
|
-
signal: AbortSignal | undefined,
|
|
232
|
-
): Promise<GltfParsedAsset> {
|
|
233
|
-
const assetWorker = options.assetWorker && !isInlineOrBlobUri(src) ? options.assetWorker : null;
|
|
234
|
-
return parseAssetWorkerFirst({
|
|
235
|
-
parser: { type: 'model/gltf', parse: (_input, context) => loadParsedGltfAsset(src, context.signal) },
|
|
236
|
-
input: src,
|
|
237
|
-
context: { ...(signal === undefined ? {} : { signal }), source: src },
|
|
238
|
-
worker: assetWorker
|
|
239
|
-
? (_input, context) => assetWorker.loadParsedAsset(src, {
|
|
240
|
-
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
241
|
-
...(options.dracoDecoderConfig === undefined ? {} : { dracoDecoderConfig: options.dracoDecoderConfig }),
|
|
242
|
-
})
|
|
243
|
-
: null,
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function resolveAssetIdentity(src: string): string {
|
|
248
|
-
try {
|
|
249
|
-
const fallback = typeof globalThis.location?.href === 'string' ? globalThis.location.href : undefined;
|
|
250
|
-
return fallback === undefined ? new URL(src).href : new URL(src, fallback).href;
|
|
251
|
-
} catch {
|
|
252
|
-
return src;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function getSharedImageSources(asset: GltfParsedAsset, manager: AssetManager): Map<number, string> {
|
|
257
|
-
let byManager = sharedImageSourcesByAsset.get(asset);
|
|
258
|
-
if (!byManager) {
|
|
259
|
-
byManager = new WeakMap();
|
|
260
|
-
sharedImageSourcesByAsset.set(asset, byManager);
|
|
261
|
-
}
|
|
262
|
-
let sources = byManager.get(manager);
|
|
263
|
-
if (!sources) {
|
|
264
|
-
sources = new Map();
|
|
265
|
-
byManager.set(manager, sources);
|
|
266
|
-
}
|
|
267
|
-
return sources;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function disposeSharedImageSources(asset: GltfParsedAsset, manager: AssetManager): void {
|
|
271
|
-
const sources = sharedImageSourcesByAsset.get(asset)?.get(manager);
|
|
272
|
-
if (!sources) return;
|
|
273
|
-
for (const url of sources.values()) {
|
|
274
|
-
if (url.startsWith('blob:')) URL.revokeObjectURL(url);
|
|
275
|
-
}
|
|
276
|
-
sources.clear();
|
|
277
|
-
sharedImageSourcesByAsset.get(asset)?.delete(manager);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
type SettledTask<T> = { readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: unknown };
|
|
281
|
-
|
|
282
|
-
async function settleTask<T>(task: Promise<T>): Promise<SettledTask<T>> {
|
|
283
|
-
try {
|
|
284
|
-
return { ok: true, value: await task };
|
|
285
|
-
} catch (error) {
|
|
286
|
-
return { ok: false, error };
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const nowMilliseconds = monotonicNow;
|
|
291
|
-
|
|
292
|
-
function countDecodedGeometryBytes(primitives: readonly GltfPrimitiveCompatibilityInput[]): number {
|
|
293
|
-
const buffers = new Set<ArrayBufferLike>();
|
|
294
|
-
const add = (value: ArrayBufferView<ArrayBufferLike> | null | undefined): void => {
|
|
295
|
-
if (value) buffers.add(value.buffer);
|
|
296
|
-
};
|
|
297
|
-
for (const { geometry } of primitives) {
|
|
298
|
-
add(geometry.positions);
|
|
299
|
-
add(geometry.normals);
|
|
300
|
-
add(geometry.indices);
|
|
301
|
-
add(geometry.morphBasePositions);
|
|
302
|
-
add(geometry.morphBaseNormals);
|
|
303
|
-
for (const value of geometry.textureCoordinates.values()) add(value);
|
|
304
|
-
for (const target of geometry.morphTargets) {
|
|
305
|
-
add(target.positions);
|
|
306
|
-
add(target.normals);
|
|
307
|
-
}
|
|
308
|
-
add(geometry.skinning?.joints);
|
|
309
|
-
add(geometry.skinning?.weights);
|
|
310
|
-
add(geometry.skinning?.jointMatrices);
|
|
311
|
-
}
|
|
312
|
-
let total = 0;
|
|
313
|
-
for (const buffer of buffers) total += buffer.byteLength;
|
|
314
|
-
return total;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function countUniqueBufferBytes(buffers: readonly ArrayBuffer[]): number {
|
|
318
|
-
let total = 0;
|
|
319
|
-
for (const buffer of new Set(buffers)) total += buffer.byteLength;
|
|
320
|
-
return total;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function isInlineOrBlobUri(src: string): boolean {
|
|
324
|
-
return /^(data:|blob:)/i.test(src);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export function disposeGltfModel(model: LoadedGltfModel): void {
|
|
328
|
-
disposeEntityTree(model.root);
|
|
329
|
-
revokeObjectUrls(model.objectUrls);
|
|
330
|
-
releaseAssetHandles(model.assetHandles);
|
|
331
|
-
model.animations.length = 0;
|
|
332
|
-
model.animationClips.length = 0;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
export function setGltfMaterialVariant(model: LoadedGltfModel, name: string | null): void {
|
|
336
|
-
if (name !== null && !model.materialVariants.includes(name)) {
|
|
337
|
-
throw new RangeError(`Unknown glTF material variant "${name}".`);
|
|
338
|
-
}
|
|
339
|
-
visitEntityTree(model.root, entity => {
|
|
340
|
-
const material = entity.getComponent(Mesh3D)?.material;
|
|
341
|
-
if (!(material instanceof PbrMaterial)) return;
|
|
342
|
-
material.setVariant(name !== null && material.variantNames.includes(name) ? name : null);
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function visitEntityTree(entity: Entity, visit: (entity: Entity) => void): void {
|
|
347
|
-
visit(entity);
|
|
348
|
-
for (const child of entity.children as Iterable<Entity>) visitEntityTree(child, visit);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function releaseAssetHandles(handles: AssetHandle<unknown>[]): void {
|
|
352
|
-
for (const handle of handles) handle.release();
|
|
353
|
-
handles.length = 0;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function revokeObjectUrls(urls: string[]): void {
|
|
357
|
-
for (const url of urls) URL.revokeObjectURL(url);
|
|
358
|
-
urls.length = 0;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function disposeEntityTree(entity: Entity): void {
|
|
362
|
-
const children = [...entity.children] as Entity[];
|
|
363
|
-
for (const child of children) disposeEntityTree(child);
|
|
364
|
-
entity.destroy();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function collectAssetStats(gltf: GltfAsset): GltfAssetStats {
|
|
368
|
-
return {
|
|
369
|
-
meshCount: gltf.meshes?.length ?? 0,
|
|
370
|
-
primitiveCount: (gltf.meshes ?? []).reduce((sum, mesh) => sum + mesh.primitives.length, 0),
|
|
371
|
-
materialCount: gltf.materials?.length ?? 0,
|
|
372
|
-
textureCount: gltf.textures?.length ?? 0,
|
|
373
|
-
imageCount: gltf.images?.length ?? 0,
|
|
374
|
-
animationCount: gltf.animations?.length ?? 0,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
async function loadGltfAsset(src: string, signal?: AbortSignal): Promise<{
|
|
379
|
-
gltf: GltfAsset;
|
|
380
|
-
binaryChunk: ArrayBuffer | null;
|
|
381
|
-
baseUrl: string;
|
|
382
|
-
fetchMs: number;
|
|
383
|
-
parseMs: number;
|
|
384
|
-
sourceBytes: number;
|
|
385
|
-
}> {
|
|
386
|
-
const baseUrl = resolveBaseUrl(src);
|
|
387
|
-
const fetchStartedAt = nowMilliseconds();
|
|
388
|
-
const response = await fetch(src, signal ? { signal } : undefined);
|
|
389
|
-
if (!response.ok) throw gltfDataError(
|
|
390
|
-
`Failed to load glTF: ${response.status} ${response.statusText}`,
|
|
391
|
-
{ url: src, status: response.status },
|
|
392
|
-
'gltf',
|
|
393
|
-
);
|
|
394
|
-
throwIfAborted(signal);
|
|
395
|
-
const bytes = await response.arrayBuffer();
|
|
396
|
-
const fetchMs = nowMilliseconds() - fetchStartedAt;
|
|
397
|
-
throwIfAborted(signal);
|
|
398
|
-
const parseStartedAt = nowMilliseconds();
|
|
399
|
-
if (isGlb(bytes)) {
|
|
400
|
-
const parsed = parseGlb(bytes);
|
|
401
|
-
return {
|
|
402
|
-
...parsed,
|
|
403
|
-
baseUrl,
|
|
404
|
-
fetchMs,
|
|
405
|
-
parseMs: nowMilliseconds() - parseStartedAt,
|
|
406
|
-
sourceBytes: bytes.byteLength,
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
const text = new TextDecoder().decode(bytes);
|
|
410
|
-
const gltf = parseGltfJson(text, src, 'gltf');
|
|
411
|
-
return {
|
|
412
|
-
gltf,
|
|
413
|
-
binaryChunk: null,
|
|
414
|
-
baseUrl,
|
|
415
|
-
fetchMs,
|
|
416
|
-
parseMs: nowMilliseconds() - parseStartedAt,
|
|
417
|
-
sourceBytes: bytes.byteLength,
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export async function loadParsedGltfAsset(src: string, signal?: AbortSignal): Promise<GltfParsedAsset> {
|
|
422
|
-
const { gltf, binaryChunk, baseUrl, fetchMs: primaryFetchMs, parseMs, sourceBytes } =
|
|
423
|
-
await loadGltfAsset(src, signal);
|
|
424
|
-
throwIfAborted(signal);
|
|
425
|
-
const buffersStartedAt = nowMilliseconds();
|
|
426
|
-
const buffers = await loadBuffers(gltf, binaryChunk, baseUrl, signal);
|
|
427
|
-
const bufferFetchMs = nowMilliseconds() - buffersStartedAt;
|
|
428
|
-
return {
|
|
429
|
-
gltf,
|
|
430
|
-
binaryChunk,
|
|
431
|
-
buffers,
|
|
432
|
-
baseUrl,
|
|
433
|
-
metrics: Object.freeze({
|
|
434
|
-
fetchMs: primaryFetchMs + bufferFetchMs,
|
|
435
|
-
parseMs,
|
|
436
|
-
workerParseMs: parseMs,
|
|
437
|
-
dracoDecodeMs: 0,
|
|
438
|
-
geometryPreparationMs: 0,
|
|
439
|
-
sourceBytes: sourceBytes + countExternalBufferBytes(gltf, buffers),
|
|
440
|
-
workerTransferBytes: 0,
|
|
441
|
-
workerTransferBufferCount: 0,
|
|
442
|
-
}),
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function countExternalBufferBytes(gltf: GltfAsset, buffers: readonly ArrayBuffer[]): number {
|
|
447
|
-
let total = 0;
|
|
448
|
-
for (const [index, descriptor] of (gltf.buffers ?? []).entries()) {
|
|
449
|
-
if (!descriptor.uri || descriptor.uri.startsWith('data:')) continue;
|
|
450
|
-
total += buffers[index]?.byteLength ?? 0;
|
|
451
|
-
}
|
|
452
|
-
return total;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
interface GltfExtensionInspection {
|
|
456
|
-
readonly warnings: readonly GltfLoadWarning[];
|
|
457
|
-
readonly report: GltfExtensionReport;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function validateGltfExtensions(
|
|
461
|
-
gltf: GltfAsset,
|
|
462
|
-
capabilities: Readonly<Record<string, GltfExtensionCapability>>,
|
|
463
|
-
): GltfExtensionInspection {
|
|
464
|
-
const usedExtensions = new Set(gltf.extensionsUsed ?? []);
|
|
465
|
-
const requiredExtensions = new Set(gltf.extensionsRequired ?? []);
|
|
466
|
-
for (const [index, extension] of (gltf.extensionsRequired ?? []).entries()) {
|
|
467
|
-
if (!usedExtensions.has(extension)) {
|
|
468
|
-
throw gltfDataError(
|
|
469
|
-
`Required glTF extension "${extension}" is missing from extensionsUsed.`,
|
|
470
|
-
{ extension },
|
|
471
|
-
`gltf.extensionsRequired[${index}]`,
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
const capability = capabilities[extension];
|
|
475
|
-
if (capability?.support === 'supported') continue;
|
|
476
|
-
throw gltfDataError(
|
|
477
|
-
`Required glTF extension "${extension}" is not supported.`,
|
|
478
|
-
{ extension, support: capability?.support ?? 'unknown' },
|
|
479
|
-
`gltf.extensionsRequired[${index}]`,
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const warnings: GltfLoadWarning[] = [];
|
|
484
|
-
for (const [index, extension] of (gltf.extensionsUsed ?? []).entries()) {
|
|
485
|
-
const capability = capabilities[extension];
|
|
486
|
-
if (capability?.support === 'supported') continue;
|
|
487
|
-
const partial = capability?.support === 'partial';
|
|
488
|
-
warnings.push(Object.freeze({
|
|
489
|
-
code: partial ? 'W_GLTF_PARTIAL_OPTIONAL_EXTENSION' : 'W_GLTF_UNSUPPORTED_OPTIONAL_EXTENSION',
|
|
490
|
-
message: partial
|
|
491
|
-
? `Optional glTF extension "${extension}" is only partially supported: ${capability.note}`
|
|
492
|
-
: `Optional glTF extension "${extension}" is not supported and was ignored.`,
|
|
493
|
-
extension,
|
|
494
|
-
path: `gltf.extensionsUsed[${index}]`,
|
|
495
|
-
}));
|
|
496
|
-
}
|
|
497
|
-
const entries = [...usedExtensions].map(extension => {
|
|
498
|
-
const capability = capabilities[extension];
|
|
499
|
-
const support = capability?.support ?? 'unsupported';
|
|
500
|
-
return Object.freeze({
|
|
501
|
-
extension,
|
|
502
|
-
required: requiredExtensions.has(extension),
|
|
503
|
-
support,
|
|
504
|
-
disposition: support === 'supported' ? 'supported' : support === 'partial' ? 'partial' : 'ignored',
|
|
505
|
-
note: capability?.note ?? 'No loader capability is registered; optional extension data is ignored.',
|
|
506
|
-
} satisfies GltfExtensionReportEntry);
|
|
507
|
-
});
|
|
508
|
-
const frozenWarnings = Object.freeze(warnings);
|
|
509
|
-
return Object.freeze({
|
|
510
|
-
warnings: frozenWarnings,
|
|
511
|
-
report: Object.freeze({
|
|
512
|
-
fullySupported: entries.every(entry => entry.support === 'supported'),
|
|
513
|
-
entries: Object.freeze(entries),
|
|
514
|
-
}),
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function resolveBaseUrl(src: string): string {
|
|
519
|
-
try {
|
|
520
|
-
return new URL('.', src).href;
|
|
521
|
-
} catch {
|
|
522
|
-
const documentBase = (globalThis as typeof globalThis & {
|
|
523
|
-
document?: { baseURI?: string };
|
|
524
|
-
}).document?.baseURI;
|
|
525
|
-
const candidates = [documentBase, globalThis.location?.href, 'http://localhost/'];
|
|
526
|
-
for (const candidate of candidates) {
|
|
527
|
-
if (typeof candidate !== 'string') continue;
|
|
528
|
-
try {
|
|
529
|
-
return new URL('.', candidate).href;
|
|
530
|
-
} catch {
|
|
531
|
-
// about:srcdoc and other opaque origins cannot resolve relatives.
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
return 'http://localhost/';
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
function isGlb(buffer: ArrayBuffer): boolean {
|
|
539
|
-
const view = new DataView(buffer);
|
|
540
|
-
return view.byteLength >= 12 && view.getUint32(0, true) === 0x46546c67;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
function parseGlb(buffer: ArrayBuffer): { gltf: GltfAsset; binaryChunk: ArrayBuffer | null } {
|
|
544
|
-
if (buffer.byteLength < 12) throw gltfDataError('GLB header is truncated.', {}, 'glb.header');
|
|
545
|
-
const view = new DataView(buffer);
|
|
546
|
-
const version = view.getUint32(4, true);
|
|
547
|
-
if (version !== 2) throw gltfDataError(`Unsupported GLB version: ${version}`, { version }, 'glb.header.version');
|
|
548
|
-
let offset = 12;
|
|
549
|
-
let gltf: GltfAsset | null = null;
|
|
550
|
-
let binaryChunk: ArrayBuffer | null = null;
|
|
551
|
-
while (offset + 8 <= view.byteLength) {
|
|
552
|
-
const chunkLength = view.getUint32(offset, true);
|
|
553
|
-
const chunkType = view.getUint32(offset + 4, true);
|
|
554
|
-
const chunkStart = offset + 8;
|
|
555
|
-
if (chunkStart + chunkLength > view.byteLength) {
|
|
556
|
-
throw gltfDataError('GLB chunk exceeds the file length.', { chunkLength, offset }, `glb.chunks[${offset}]`);
|
|
557
|
-
}
|
|
558
|
-
const chunk = buffer.slice(chunkStart, chunkStart + chunkLength);
|
|
559
|
-
if (chunkType === 0x4e4f534a) {
|
|
560
|
-
gltf = parseGltfJson(new TextDecoder().decode(chunk), 'GLB', 'glb.json');
|
|
561
|
-
} else if (chunkType === 0x004e4942) {
|
|
562
|
-
binaryChunk = chunk;
|
|
563
|
-
}
|
|
564
|
-
offset = chunkStart + chunkLength;
|
|
565
|
-
}
|
|
566
|
-
if (!gltf) throw gltfDataError('GLB is missing JSON chunk.', {}, 'glb.json');
|
|
567
|
-
return { gltf, binaryChunk };
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function parseGltfJson(text: string, source: string, path: string): GltfAsset {
|
|
571
|
-
let value: unknown;
|
|
572
|
-
try {
|
|
573
|
-
value = JSON.parse(text) as unknown;
|
|
574
|
-
} catch (error) {
|
|
575
|
-
throw gltfDataError('glTF JSON is not valid JSON.', { url: source }, path, error);
|
|
576
|
-
}
|
|
577
|
-
if (!isGltfAsset(value)) {
|
|
578
|
-
throw gltfDataError('glTF JSON does not match the supported glTF structure.', { url: source }, path);
|
|
579
|
-
}
|
|
580
|
-
return value;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
async function loadBuffers(gltf: GltfAsset, binaryChunk: ArrayBuffer | null, baseUrl: string, signal?: AbortSignal): Promise<ArrayBuffer[]> {
|
|
584
|
-
return Promise.all((gltf.buffers ?? []).map(async (buffer, index) => {
|
|
585
|
-
throwIfAborted(signal);
|
|
586
|
-
if (!buffer.uri) {
|
|
587
|
-
if (index === 0 && binaryChunk) return binaryChunk;
|
|
588
|
-
throw gltfDataError(`glTF buffer ${index} has no uri and no GLB chunk.`);
|
|
589
|
-
}
|
|
590
|
-
if (buffer.uri.startsWith('data:')) {
|
|
591
|
-
return dataUriToArrayBuffer(buffer.uri);
|
|
592
|
-
}
|
|
593
|
-
const response = await fetch(resolveFetchUri(buffer.uri, baseUrl), signal ? { signal } : undefined);
|
|
594
|
-
if (!response.ok) throw gltfDataError(`Failed to load glTF buffer: ${buffer.uri}`, { uri: buffer.uri }, `gltf.buffers[${index}]`);
|
|
595
|
-
throwIfAborted(signal);
|
|
596
|
-
const result = await response.arrayBuffer();
|
|
597
|
-
throwIfAborted(signal);
|
|
598
|
-
return result;
|
|
599
|
-
}));
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
function resolveFetchUri(uri: string, baseUrl: string): string {
|
|
603
|
-
if (/^(blob:|https?:\/\/)/i.test(uri)) return uri;
|
|
604
|
-
return new URL(uri, baseUrl).href;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function dataUriToArrayBuffer(uri: string): ArrayBuffer {
|
|
608
|
-
const comma = uri.indexOf(',');
|
|
609
|
-
const meta = uri.slice(0, comma);
|
|
610
|
-
const data = uri.slice(comma + 1);
|
|
611
|
-
if (meta.endsWith(';base64')) {
|
|
612
|
-
const binary = atob(data);
|
|
613
|
-
const out = new Uint8Array(binary.length);
|
|
614
|
-
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
615
|
-
return out.buffer;
|
|
616
|
-
}
|
|
617
|
-
return new TextEncoder().encode(decodeURIComponent(data)).buffer;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
async function instantiateNode(
|
|
621
|
-
gltf: GltfAsset,
|
|
622
|
-
buffers: ArrayBuffer[],
|
|
623
|
-
nodeIndex: number,
|
|
624
|
-
options: LoadGltfOptions,
|
|
625
|
-
animationTargets: Map<number, GltfAnimationTarget>,
|
|
626
|
-
context: GltfLoadContext,
|
|
627
|
-
): Promise<Entity | null> {
|
|
628
|
-
throwIfAborted(options.signal);
|
|
629
|
-
const node = gltf.nodes?.[nodeIndex];
|
|
630
|
-
if (!node) return null;
|
|
631
|
-
const entity = new Entity(node.name || `glTF Node ${nodeIndex}`);
|
|
632
|
-
const { transform, target } = createNodeTransform(node, entity);
|
|
633
|
-
entity.addComponent(transform);
|
|
634
|
-
animationTargets.set(nodeIndex, target);
|
|
635
|
-
if (node.mesh !== undefined) {
|
|
636
|
-
const mesh = gltf.meshes?.[node.mesh];
|
|
637
|
-
target.weights = mesh?.weights ? [...mesh.weights] : [];
|
|
638
|
-
const primitiveEntities = await Promise.all((mesh?.primitives ?? []).map((primitive, primitiveIndex) =>
|
|
639
|
-
createPrimitiveEntity(gltf, buffers, primitive, options, `${mesh?.name || 'Mesh'} Primitive ${primitiveIndex}`, target, context, node.skin, entity, node.mesh as number, primitiveIndex)));
|
|
640
|
-
for (const primitiveEntity of primitiveEntities) {
|
|
641
|
-
throwIfAborted(options.signal);
|
|
642
|
-
if (primitiveEntity) entity.addChild(primitiveEntity);
|
|
643
|
-
}
|
|
644
|
-
applyMorphWeights(target, target.weights);
|
|
645
|
-
}
|
|
646
|
-
const children = await Promise.all((node.children ?? []).map(childIndex =>
|
|
647
|
-
instantiateNode(gltf, buffers, childIndex, options, animationTargets, context)));
|
|
648
|
-
for (const child of children) {
|
|
649
|
-
throwIfAborted(options.signal);
|
|
650
|
-
if (child) entity.addChild(child);
|
|
651
|
-
}
|
|
652
|
-
return entity;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function createNodeTransform(node: GltfNode, entity: Entity): { transform: Transform3D; target: GltfAnimationTarget } {
|
|
656
|
-
const translation: [number, number, number] = node.translation ? [...node.translation] : [0, 0, 0];
|
|
657
|
-
const rotation: [number, number, number, number] = node.rotation ? [...node.rotation] : [0, 0, 0, 1];
|
|
658
|
-
const scale: [number, number, number] = node.scale ? [...node.scale] : [1, 1, 1];
|
|
659
|
-
const transform = new Transform3D();
|
|
660
|
-
if (node.matrix?.length === 16) {
|
|
661
|
-
transform.localMatrix = new Float32Array(node.matrix);
|
|
662
|
-
} else {
|
|
663
|
-
transform.localMatrix = composeTrsMatrix(translation, rotation, scale);
|
|
664
|
-
}
|
|
665
|
-
return { transform, target: { entity, transform, translation, rotation, scale, weights: [], morphPrimitives: [] } };
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
async function createPrimitiveEntity(
|
|
669
|
-
gltf: GltfAsset,
|
|
670
|
-
buffers: ArrayBuffer[],
|
|
671
|
-
primitive: GltfPrimitive,
|
|
672
|
-
options: LoadGltfOptions,
|
|
673
|
-
name: string,
|
|
674
|
-
target: GltfAnimationTarget,
|
|
675
|
-
context: GltfLoadContext,
|
|
676
|
-
skinIndex: number | undefined,
|
|
677
|
-
meshEntity: Entity,
|
|
678
|
-
meshIndex: number,
|
|
679
|
-
primitiveIndex: number,
|
|
680
|
-
): Promise<Entity | null> {
|
|
681
|
-
throwIfAborted(options.signal);
|
|
682
|
-
if (primitive.mode !== undefined && primitive.mode !== 4) return null;
|
|
683
|
-
const payload = context.geometryPayloads?.[meshIndex]?.[primitiveIndex]
|
|
684
|
-
?? await createPrimitiveGeometryPayload(gltf, buffers, primitive, options);
|
|
685
|
-
throwIfAborted(options.signal);
|
|
686
|
-
if (!payload) return null;
|
|
687
|
-
const uvPlanResult = planGltfUvSemantics(
|
|
688
|
-
gltf,
|
|
689
|
-
primitive,
|
|
690
|
-
meshIndex,
|
|
691
|
-
primitiveIndex,
|
|
692
|
-
context.extensionAdapters,
|
|
693
|
-
);
|
|
694
|
-
if (!uvPlanResult.ok) {
|
|
695
|
-
throw gltfDataError(uvPlanResult.failure.message, uvPlanResult.failure.context, uvPlanResult.failure.path);
|
|
696
|
-
}
|
|
697
|
-
const uvPlan = uvPlanResult.plan;
|
|
698
|
-
const { positions, indices, normals, textureCoordinates, joints, weights, positionTargets, normalTargets } = payload;
|
|
699
|
-
for (const mapping of uvPlan.mappings) {
|
|
700
|
-
if (textureCoordinates.some(entry => entry.set === mapping.set)) continue;
|
|
701
|
-
throw gltfDataError(
|
|
702
|
-
`Decoded geometry payload is missing ${mapping.semantic}.`,
|
|
703
|
-
{ meshIndex, primitiveIndex, semantic: mapping.semantic, texCoord: mapping.set },
|
|
704
|
-
`${uvPlan.path}.attributes.${mapping.semantic}`,
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
const geometry = new Geometry3D({
|
|
708
|
-
positions,
|
|
709
|
-
...(normals === undefined ? {} : { normals }),
|
|
710
|
-
textureCoordinates,
|
|
711
|
-
textureCoordinateLayout: uvPlan.mappings.map(mapping => mapping.set),
|
|
712
|
-
...(indices === undefined ? {} : { indices }),
|
|
713
|
-
});
|
|
714
|
-
const conservativeBounds = GltfConservativeBounds.fromPrimitive(gltf, primitive);
|
|
715
|
-
if (conservativeBounds) registerGltfConservativeBounds(geometry, conservativeBounds);
|
|
716
|
-
if (positionTargets.length > 0 || normalTargets.length > 0) {
|
|
717
|
-
geometry.setMorphTargets(
|
|
718
|
-
new Array(Math.max(positionTargets.length, normalTargets.length)).fill(null).map((_, index) => ({
|
|
719
|
-
...(positionTargets[index] === undefined ? {} : { positions: positionTargets[index] }),
|
|
720
|
-
...(normalTargets[index] === undefined ? {} : { normals: normalTargets[index] }),
|
|
721
|
-
})),
|
|
722
|
-
target.weights,
|
|
723
|
-
);
|
|
724
|
-
target.morphPrimitives.push({
|
|
725
|
-
geometry,
|
|
726
|
-
basePositions: positions.slice(),
|
|
727
|
-
baseNormals: normals ? normals.slice() : null,
|
|
728
|
-
positionTargets,
|
|
729
|
-
normalTargets,
|
|
730
|
-
});
|
|
731
|
-
if (target.weights.length === 0) target.weights = new Array(Math.max(positionTargets.length, normalTargets.length)).fill(0);
|
|
732
|
-
}
|
|
733
|
-
const material = await createGltfMaterial(gltf, buffers, primitive, options, context, uvPlan);
|
|
734
|
-
throwIfAborted(options.signal);
|
|
735
|
-
const entity = new Entity(name);
|
|
736
|
-
entity.addComponent(new CartesianTransform3D());
|
|
737
|
-
entity.addComponent(new Mesh3D(geometry, material));
|
|
738
|
-
if (skinIndex !== undefined && joints && weights) {
|
|
739
|
-
context.pendingSkinnedPrimitives.push({ skinIndex, geometry, meshEntity, joints, weights });
|
|
740
|
-
}
|
|
741
|
-
context.compatibilityPrimitives.push({
|
|
742
|
-
meshIndex,
|
|
743
|
-
primitiveIndex,
|
|
744
|
-
primitive,
|
|
745
|
-
geometry,
|
|
746
|
-
skinRequested: skinIndex !== undefined,
|
|
747
|
-
uvSemanticPlan: uvPlan,
|
|
748
|
-
});
|
|
749
|
-
return entity;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
async function createPrimitiveGeometryPayload(
|
|
753
|
-
gltf: GltfAsset,
|
|
754
|
-
buffers: ArrayBuffer[],
|
|
755
|
-
primitive: GltfPrimitive,
|
|
756
|
-
options: LoadGltfOptions,
|
|
757
|
-
): Promise<GltfPrimitiveGeometryPayload | null> {
|
|
758
|
-
throwIfAborted(options.signal);
|
|
759
|
-
if (primitive.mode !== undefined && primitive.mode !== 4) return null;
|
|
760
|
-
const dracoExtension = primitive.extensions?.KHR_draco_mesh_compression;
|
|
761
|
-
let decoded = null;
|
|
762
|
-
if (dracoExtension) {
|
|
763
|
-
const decodeStartedAt = nowMilliseconds();
|
|
764
|
-
decoded = await decodeDracoPrimitive(gltf, buffers, dracoExtension, options);
|
|
765
|
-
options.diagnostics?.onDracoDecode?.(
|
|
766
|
-
nowMilliseconds() - decodeStartedAt,
|
|
767
|
-
countDracoGeometryBytes(decoded),
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
throwIfAborted(options.signal);
|
|
771
|
-
const positionAccessor = primitive.attributes.POSITION;
|
|
772
|
-
if (positionAccessor === undefined && !decoded?.positions) return null;
|
|
773
|
-
const positions = decoded?.positions ?? readAccessorFloat(gltf, buffers, positionAccessor as number, 3);
|
|
774
|
-
const indices = decoded?.indices ?? (primitive.indices !== undefined ? readAccessorIndices(gltf, buffers, primitive.indices) : undefined);
|
|
775
|
-
const normals = decoded?.normals ?? (primitive.attributes.NORMAL !== undefined
|
|
776
|
-
? readAccessorFloat(gltf, buffers, primitive.attributes.NORMAL, 3)
|
|
777
|
-
: generateFlatNormals(positions, indices ?? null));
|
|
778
|
-
const textureCoordinates = decoded?.textureCoordinates ?? Object.entries(primitive.attributes)
|
|
779
|
-
.map(([semantic, accessor]) => ({ set: parseTextureCoordinateSemantic(semantic), accessor }))
|
|
780
|
-
.filter((entry): entry is { set: number; accessor: number } => entry.set !== null)
|
|
781
|
-
.sort((a, b) => a.set - b.set)
|
|
782
|
-
.map(entry => ({ set: entry.set, data: readAccessorFloat(gltf, buffers, entry.accessor, 2) }));
|
|
783
|
-
const joints = decoded?.joints ?? (primitive.attributes.JOINTS_0 !== undefined
|
|
784
|
-
? readAccessorUnsigned(gltf, buffers, primitive.attributes.JOINTS_0, 4)
|
|
785
|
-
: undefined);
|
|
786
|
-
const weights = decoded?.weights ?? (primitive.attributes.WEIGHTS_0 !== undefined
|
|
787
|
-
? readAccessorFloat(gltf, buffers, primitive.attributes.WEIGHTS_0, 4)
|
|
788
|
-
: undefined);
|
|
789
|
-
return {
|
|
790
|
-
positions,
|
|
791
|
-
...(indices === undefined ? {} : { indices }),
|
|
792
|
-
...(normals === undefined ? {} : { normals }),
|
|
793
|
-
textureCoordinates,
|
|
794
|
-
...(joints === undefined ? {} : { joints }),
|
|
795
|
-
...(weights === undefined ? {} : { weights }),
|
|
796
|
-
positionTargets: (primitive.targets ?? []).map(item => item.POSITION !== undefined
|
|
797
|
-
? readAccessorFloat(gltf, buffers, item.POSITION, 3)
|
|
798
|
-
: new Float32Array(positions.length)),
|
|
799
|
-
normalTargets: (primitive.targets ?? []).map(item => item.NORMAL !== undefined
|
|
800
|
-
? readAccessorFloat(gltf, buffers, item.NORMAL, 3)
|
|
801
|
-
: new Float32Array(normals.length)),
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
export async function prepareGltfGeometryPayloads(
|
|
806
|
-
gltf: GltfAsset,
|
|
807
|
-
buffers: ArrayBuffer[],
|
|
808
|
-
options: LoadGltfOptions = {},
|
|
809
|
-
): Promise<GltfGeometryPayloadMatrix> {
|
|
810
|
-
return Promise.all((gltf.meshes ?? []).map(mesh => Promise.all(
|
|
811
|
-
mesh.primitives.map(primitive => createPrimitiveGeometryPayload(gltf, buffers, primitive, options)),
|
|
812
|
-
)));
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
function countDracoGeometryBytes(decoded: NonNullable<Awaited<ReturnType<typeof decodeDracoPrimitive>>>): number {
|
|
816
|
-
const buffers = new Set<ArrayBufferLike>();
|
|
817
|
-
const add = (value: ArrayBufferView<ArrayBufferLike> | undefined): void => {
|
|
818
|
-
if (value) buffers.add(value.buffer);
|
|
819
|
-
};
|
|
820
|
-
add(decoded.positions);
|
|
821
|
-
add(decoded.indices);
|
|
822
|
-
add(decoded.normals);
|
|
823
|
-
add(decoded.joints);
|
|
824
|
-
add(decoded.weights);
|
|
825
|
-
for (const entry of decoded.textureCoordinates) add(entry.data);
|
|
826
|
-
let total = 0;
|
|
827
|
-
for (const buffer of buffers) total += buffer.byteLength;
|
|
828
|
-
return total;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function resolvePendingSkins(gltf: GltfAsset, buffers: ArrayBuffer[], context: GltfLoadContext): void {
|
|
832
|
-
for (const pending of context.pendingSkinnedPrimitives) {
|
|
833
|
-
const runtime = createSkinRuntime(gltf, buffers, pending, context);
|
|
834
|
-
if (runtime) {
|
|
835
|
-
updateSkinnedPrimitive(runtime);
|
|
836
|
-
context.skinnedPrimitives.push(runtime);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
context.pendingSkinnedPrimitives.length = 0;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
function createSkinRuntime(
|
|
843
|
-
gltf: GltfAsset,
|
|
844
|
-
buffers: ArrayBuffer[],
|
|
845
|
-
pending: GltfPendingSkinnedPrimitive,
|
|
846
|
-
context: GltfLoadContext,
|
|
847
|
-
): GltfSkinnedPrimitiveRuntime | null {
|
|
848
|
-
const skin = gltf.skins?.[pending.skinIndex];
|
|
849
|
-
if (!skin || skin.joints.length === 0) return null;
|
|
850
|
-
const jointTargets = skin.joints.map(nodeIndex => context.animationTargets.get(nodeIndex) ?? null);
|
|
851
|
-
if (!jointTargets.some(Boolean)) return null;
|
|
852
|
-
const inverseBindMatrices = skin.inverseBindMatrices !== undefined
|
|
853
|
-
? readAccessorMat4(gltf, buffers, skin.inverseBindMatrices)
|
|
854
|
-
: new Array(jointTargets.length).fill(null).map(() => mat4.identity() as Float32Array);
|
|
855
|
-
if (inverseBindMatrices.length < jointTargets.length) {
|
|
856
|
-
while (inverseBindMatrices.length < jointTargets.length) inverseBindMatrices.push(mat4.identity() as Float32Array);
|
|
857
|
-
}
|
|
858
|
-
const jointMatrices = new Float32Array(jointTargets.length * 16);
|
|
859
|
-
const gpuSkinAttributes = createGpuSkinAttributes(pending.joints, pending.weights, jointTargets.length);
|
|
860
|
-
pending.geometry.setSkinning({
|
|
861
|
-
joints: gpuSkinAttributes.joints,
|
|
862
|
-
weights: gpuSkinAttributes.weights,
|
|
863
|
-
jointMatrices,
|
|
864
|
-
});
|
|
865
|
-
return {
|
|
866
|
-
geometry: pending.geometry,
|
|
867
|
-
meshEntity: pending.meshEntity,
|
|
868
|
-
jointTargets,
|
|
869
|
-
inverseBindMatrices,
|
|
870
|
-
jointMatrices,
|
|
871
|
-
inverseMeshWorldScratch: mat4.identity() as Float32Array,
|
|
872
|
-
jointMatrixScratch: mat4.identity() as Float32Array,
|
|
873
|
-
skinMatrixScratch: mat4.identity() as Float32Array,
|
|
874
|
-
lastMeshWorldVersion: -1,
|
|
875
|
-
lastGeometryVersion: -1,
|
|
876
|
-
lastJointWorldVersions: new Int32Array(jointTargets.length).fill(-1),
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
function createGpuSkinAttributes(
|
|
881
|
-
joints: Uint16Array | Uint32Array,
|
|
882
|
-
weights: Float32Array,
|
|
883
|
-
jointCount: number,
|
|
884
|
-
): { joints: Float32Array; weights: Float32Array } {
|
|
885
|
-
const gpuJoints = new Float32Array(joints.length);
|
|
886
|
-
const gpuWeights = new Float32Array(joints.length);
|
|
887
|
-
for (let vertexOffset = 0; vertexOffset < joints.length; vertexOffset += 4) {
|
|
888
|
-
let weightSum = 0;
|
|
889
|
-
for (let influence = 0; influence < 4; influence++) {
|
|
890
|
-
const offset = vertexOffset + influence;
|
|
891
|
-
const jointIndex = joints[offset] ?? 0;
|
|
892
|
-
const weight = weights[offset] ?? 0;
|
|
893
|
-
if (jointIndex >= 0 && jointIndex < jointCount) {
|
|
894
|
-
gpuJoints[offset] = jointIndex;
|
|
895
|
-
gpuWeights[offset] = Number.isFinite(weight) && weight > 0 ? weight : 0;
|
|
896
|
-
weightSum += gpuWeights[offset];
|
|
897
|
-
} else {
|
|
898
|
-
gpuJoints[offset] = 0;
|
|
899
|
-
gpuWeights[offset] = 0;
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
if (weightSum > 0) {
|
|
903
|
-
for (let influence = 0; influence < 4; influence++) {
|
|
904
|
-
const offset = vertexOffset + influence;
|
|
905
|
-
gpuWeights[offset] = (gpuWeights[offset] ?? 0) / weightSum;
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
return { joints: gpuJoints, weights: gpuWeights };
|
|
910
|
-
}
|