@haiyue/extensions 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/animation/Animation2DComponent.d.ts +46 -0
- package/dist/animation/Animation2DExtensionRegistry.d.ts +27 -0
- package/dist/animation/Animation2DRenderSystem.d.ts +89 -0
- package/dist/animation/Animation2DRuntime.d.ts +99 -0
- package/dist/animation/Animation2DStateMachine.d.ts +13 -0
- package/dist/animation/Animation2DStateMachineComponent.d.ts +54 -0
- package/dist/animation/Animation2DStateMachineRuntime.d.ts +30 -0
- package/dist/animation/Animation2DStateMachineSystem.d.ts +14 -0
- package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +27 -0
- package/dist/animation/Animation2DSystem.d.ts +14 -0
- package/dist/animation/AnimationAssetLoader.d.ts +9 -0
- package/dist/animation/AnimationAudioClip.d.ts +26 -0
- package/dist/animation/AnimationPathTessellator.d.ts +52 -0
- package/dist/animation/AnimationTextRasterizer.d.ts +21 -0
- package/dist/animation/AnimationVisual2D.d.ts +68 -0
- package/dist/animation/HyaAnimation2DClipAdapter.d.ts +16 -0
- package/dist/animation/Particle2DDescriptorAdapter.d.ts +5 -0
- package/dist/animation/runtime/mixer/Action.d.ts +150 -0
- package/dist/animation/runtime/mixer/Mixer.d.ts +54 -0
- package/dist/animation/runtime/mixer/PoseBuffer.d.ts +23 -0
- package/dist/animation/runtime/mixer/Sampler.d.ts +17 -0
- package/dist/animation/runtime/mixer/Types.d.ts +101 -0
- package/dist/animation/runtime/mixer/index.d.ts +6 -0
- package/dist/animation-state-machine/AnimationStateMachine.d.ts +111 -0
- package/dist/animation-state-machine/AnimationStateMachineChannels.d.ts +23 -0
- package/dist/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.d.ts +51 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.d.ts +14 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineCompiler.d.ts +109 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineController.d.ts +103 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineMixerPort.d.ts +35 -0
- package/dist/animation-state-machine/runtime/index.d.ts +7 -0
- package/dist/animation.d.ts +15 -0
- package/dist/animation.js +1171 -0
- package/dist/animation3d/Animation3DAction.d.ts +46 -0
- package/dist/animation3d/Animation3DBinding.d.ts +85 -0
- package/dist/animation3d/Animation3DClip.d.ts +20 -0
- package/dist/animation3d/Animation3DError.d.ts +13 -0
- package/dist/animation3d/Animation3DMixer.d.ts +27 -0
- package/dist/animation3d/Animation3DPose.d.ts +40 -0
- package/dist/animation3d/Animation3DPoseBuffer.d.ts +24 -0
- package/dist/animation3d/Animation3DResource.d.ts +28 -0
- package/dist/animation3d/Animation3DStateMachine.d.ts +95 -0
- package/dist/animation3d/Animation3DStateMachineRuntime.d.ts +70 -0
- package/dist/animation3d/Animation3DTrack.d.ts +19 -0
- package/dist/animation3d/hya/HyaAnimation3DRuntime.d.ts +73 -0
- package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +65 -0
- package/dist/animation3d/hya/HyaAnimation3DTypes.d.ts +170 -0
- package/dist/animation3d/hya/index.d.ts +4 -0
- package/dist/animation3d/index.d.ts +14 -0
- package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +81 -0
- package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +39 -0
- package/dist/animation3d/runtime/integration/index.d.ts +2 -0
- package/dist/animation3d/runtime/mixer/Action.d.ts +150 -0
- package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +6 -0
- package/dist/animation3d/runtime/mixer/Mixer.d.ts +59 -0
- package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +1 -0
- package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +24 -0
- package/dist/animation3d/runtime/mixer/index.d.ts +4 -0
- package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/index.d.ts +6 -0
- package/dist/animation3d.d.ts +5 -0
- package/dist/animation3d.js +1708 -0
- package/dist/benchmark.d.ts +30 -0
- package/dist/benchmark.js +119 -0
- package/dist/canvas-text/CanvasText2DRenderSystem.d.ts +24 -0
- package/dist/canvas-text/CanvasTextComponent.d.ts +18 -0
- package/dist/canvas-text.d.ts +4 -0
- package/dist/canvas-text.js +276 -0
- package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +4 -0
- package/dist/chunks/2d-ui-canvas-text-2d.generated-C0Eaej2m.js +4 -0
- package/dist/chunks/2d-ui-spine2d.generated-CUUi8xjl.js +4 -0
- package/dist/chunks/2d-ui-tilemap2d.generated-gebw4mX7.js +4 -0
- package/dist/chunks/Animation2DStateMachine-9IonguNa.js +3245 -0
- package/dist/chunks/AnimationStateMachineController-BIcAh-sT.js +1438 -0
- package/dist/chunks/GltfAnimation3DAdapter-Cu3rQQsW.js +2240 -0
- package/dist/chunks/GltfAnimationRuntime-CRHsLycN.js +631 -0
- package/dist/chunks/RenderSystem2DBase-DDT61dAu.js +227 -0
- package/dist/chunks/Spine2DRuntime-D9M9LAQR.js +3056 -0
- package/dist/chunks/SpineAssetParser-BSDmPge1.js +378 -0
- package/dist/chunks/gltfLoader-Dm22jLYB.js +2085 -0
- package/dist/chunks/pluginUtils-CvwroRMd.js +33 -0
- package/dist/experimental-gltf-worker.d.ts +4 -0
- package/dist/experimental-gltf-worker.js +138 -0
- package/dist/experimental-spine-worker.d.ts +4 -0
- package/dist/experimental-spine-worker.js +110 -0
- package/dist/gltf/GltfAccessorReader.d.ts +7 -0
- package/dist/gltf/GltfAnimation3DAdapter.d.ts +47 -0
- package/dist/gltf/GltfAnimationRuntime.d.ts +10 -0
- package/dist/gltf/GltfAssetWorkerClient.d.ts +20 -0
- package/dist/gltf/GltfCompatibilityReport.d.ts +70 -0
- package/dist/gltf/GltfConservativeBounds.d.ts +18 -0
- package/dist/gltf/GltfDracoDecoder.d.ts +4 -0
- package/dist/gltf/GltfExtensionAdapter.d.ts +54 -0
- package/dist/gltf/GltfLoaderContract.d.ts +249 -0
- package/dist/gltf/GltfLoaderErrors.d.ts +4 -0
- package/dist/gltf/GltfMaterialDescriptor.d.ts +23 -0
- package/dist/gltf/GltfMaterialEncoder.d.ts +20 -0
- package/dist/gltf/GltfMaterialLoader.d.ts +6 -0
- package/dist/gltf/GltfModelComponent.d.ts +72 -0
- package/dist/gltf/GltfModelSystem.d.ts +37 -0
- package/dist/gltf/GltfPlugin.d.ts +6 -0
- package/dist/gltf/GltfSchema.d.ts +225 -0
- package/dist/gltf/GltfUvSemanticPlanner.d.ts +33 -0
- package/dist/gltf/gltfLoader.d.ts +9 -0
- package/dist/gltf-animation3d.d.ts +3 -0
- package/dist/gltf-animation3d.js +8 -0
- package/dist/gltf-worker-runtime.d.ts +7 -0
- package/dist/gltf-worker-runtime.js +6981 -0
- package/dist/gltf.d.ts +12 -0
- package/dist/gltf.js +517 -0
- package/dist/grid/Grid2DComponent.d.ts +25 -0
- package/dist/grid.d.ts +2 -0
- package/dist/grid.js +56 -0
- package/dist/hya-state-machine.d.ts +7 -0
- package/dist/hya-state-machine.js +2934 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/internal/2d-ui-shader-artifact.js +870 -0
- package/dist/plugins/pluginUtils.d.ts +20 -0
- package/dist/plugins.d.ts +6 -0
- package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +658 -0
- package/dist/spine/Spine2DComponent.d.ts +116 -0
- package/dist/spine/Spine2DGpuRenderer.d.ts +65 -0
- package/dist/spine/Spine2DRenderSystem.d.ts +27 -0
- package/dist/spine/Spine2DRuntime.d.ts +103 -0
- package/dist/spine/SpineAnimationBoneTimelines.d.ts +8 -0
- package/dist/spine/SpineAssetParser.d.ts +18 -0
- package/dist/spine/SpineAssetWorkerClient.d.ts +19 -0
- package/dist/spine/SpineAssetWorkerContract.d.ts +6 -0
- package/dist/spine/SpineAtlasParser.d.ts +21 -0
- package/dist/spine/SpineFloatBuilder.d.ts +15 -0
- package/dist/spine/SpinePathConstraintSolver.d.ts +32 -0
- package/dist/spine/SpinePlugin.d.ts +8 -0
- package/dist/spine/SpineSkeletonRuntime.d.ts +185 -0
- package/dist/spine/SpineSlotAnimation.d.ts +17 -0
- package/dist/spine/SpineTimelineRuntime.d.ts +25 -0
- package/dist/spine/SpineVertexBuilder.d.ts +61 -0
- package/dist/spine.d.ts +7 -0
- package/dist/spine.js +578 -0
- package/dist/tilemap/Tilemap2DComponent.d.ts +110 -0
- package/dist/tilemap/Tilemap2DRenderSystem.d.ts +11 -0
- package/dist/tilemap/Tilemap2DRenderer.d.ts +24 -0
- package/dist/tilemap/TilemapPlugin.d.ts +8 -0
- package/dist/tilemap.d.ts +6 -0
- package/dist/tilemap.js +535 -0
- package/dist/tween/Tween2DComponent.d.ts +70 -0
- package/dist/tween/Tween2DSystem.d.ts +12 -0
- package/dist/tween.d.ts +4 -0
- package/dist/tween.js +138 -0
- package/dist/utils/RenderSystem2DBase.d.ts +42 -0
- package/dist/utils/arrayAccess.d.ts +4 -0
- package/dist/utils/camera2d.d.ts +12 -0
- package/dist/utils/render2dGpu.d.ts +32 -0
- package/package.json +124 -0
- package/src/animation/Animation2DComponent.ts +135 -0
- package/src/animation/Animation2DExtensionRegistry.ts +52 -0
- package/src/animation/Animation2DRenderSystem.ts +1026 -0
- package/src/animation/Animation2DRuntime.ts +1326 -0
- package/src/animation/Animation2DStateMachine.ts +39 -0
- package/src/animation/Animation2DStateMachineComponent.ts +228 -0
- package/src/animation/Animation2DStateMachineRuntime.ts +141 -0
- package/src/animation/Animation2DStateMachineSystem.ts +60 -0
- package/src/animation/Animation2DStateMachineVisualRuntime.ts +282 -0
- package/src/animation/Animation2DSystem.ts +72 -0
- package/src/animation/AnimationAssetLoader.ts +84 -0
- package/src/animation/AnimationAudioClip.ts +140 -0
- package/src/animation/AnimationPathTessellator.ts +691 -0
- package/src/animation/AnimationTextRasterizer.ts +340 -0
- package/src/animation/AnimationVisual2D.ts +153 -0
- package/src/animation/HyaAnimation2DClipAdapter.ts +845 -0
- package/src/animation/Particle2DDescriptorAdapter.ts +35 -0
- package/src/animation/runtime/mixer/Action.ts +876 -0
- package/src/animation/runtime/mixer/Mixer.ts +505 -0
- package/src/animation/runtime/mixer/PoseBuffer.ts +105 -0
- package/src/animation/runtime/mixer/Sampler.ts +252 -0
- package/src/animation/runtime/mixer/Types.ts +138 -0
- package/src/animation/runtime/mixer/index.ts +31 -0
- package/src/animation-state-machine/AnimationStateMachine.ts +146 -0
- package/src/animation-state-machine/AnimationStateMachineChannels.ts +116 -0
- package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +197 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +503 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +763 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +987 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +54 -0
- package/src/animation-state-machine/runtime/index.ts +47 -0
- package/src/animation.ts +33 -0
- package/src/animation3d/Animation3DAction.ts +73 -0
- package/src/animation3d/Animation3DBinding.ts +116 -0
- package/src/animation3d/Animation3DClip.ts +22 -0
- package/src/animation3d/Animation3DError.ts +34 -0
- package/src/animation3d/Animation3DMixer.ts +82 -0
- package/src/animation3d/Animation3DPose.ts +100 -0
- package/src/animation3d/Animation3DPoseBuffer.ts +85 -0
- package/src/animation3d/Animation3DResource.ts +49 -0
- package/src/animation3d/Animation3DStateMachine.ts +126 -0
- package/src/animation3d/Animation3DStateMachineRuntime.ts +191 -0
- package/src/animation3d/Animation3DTrack.ts +29 -0
- package/src/animation3d/hya/HyaAnimation3DRuntime.ts +756 -0
- package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +618 -0
- package/src/animation3d/hya/HyaAnimation3DTypes.ts +168 -0
- package/src/animation3d/hya/index.ts +10 -0
- package/src/animation3d/index.ts +76 -0
- package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +325 -0
- package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +158 -0
- package/src/animation3d/runtime/integration/index.ts +9 -0
- package/src/animation3d/runtime/mixer/Action.ts +833 -0
- package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +26 -0
- package/src/animation3d/runtime/mixer/Mixer.ts +562 -0
- package/src/animation3d/runtime/mixer/PoseBuffer.ts +1 -0
- package/src/animation3d/runtime/mixer/TrackSampler.ts +399 -0
- package/src/animation3d/runtime/mixer/index.ts +8 -0
- package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +4 -0
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +1 -0
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +1 -0
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +1 -0
- package/src/animation3d/runtime/state-machine/index.ts +45 -0
- package/src/animation3d.ts +5 -0
- package/src/benchmark.ts +168 -0
- package/src/canvas-text/CanvasText2DRenderSystem.ts +292 -0
- package/src/canvas-text/CanvasTextComponent.ts +47 -0
- package/src/canvas-text.ts +4 -0
- package/src/experimental-gltf-worker.ts +18 -0
- package/src/experimental-spine-worker.ts +9 -0
- package/src/gltf/GltfAccessorReader.ts +271 -0
- package/src/gltf/GltfAnimation3DAdapter.ts +680 -0
- package/src/gltf/GltfAnimationRuntime.ts +392 -0
- package/src/gltf/GltfAssetWorkerClient.ts +136 -0
- package/src/gltf/GltfCompatibilityReport.ts +214 -0
- package/src/gltf/GltfConservativeBounds.ts +219 -0
- package/src/gltf/GltfDracoDecoder.ts +251 -0
- package/src/gltf/GltfExtensionAdapter.ts +340 -0
- package/src/gltf/GltfLoaderContract.ts +333 -0
- package/src/gltf/GltfLoaderErrors.ts +59 -0
- package/src/gltf/GltfMaterialDescriptor.ts +134 -0
- package/src/gltf/GltfMaterialEncoder.ts +173 -0
- package/src/gltf/GltfMaterialLoader.ts +293 -0
- package/src/gltf/GltfModelComponent.ts +115 -0
- package/src/gltf/GltfModelSystem.ts +208 -0
- package/src/gltf/GltfPlugin.ts +84 -0
- package/src/gltf/GltfSchema.ts +229 -0
- package/src/gltf/GltfUvSemanticPlanner.ts +166 -0
- package/src/gltf/gltfLoader.ts +910 -0
- package/src/gltf-animation3d.ts +10 -0
- package/src/gltf-worker-runtime.ts +10 -0
- package/src/gltf.ts +56 -0
- package/src/grid/Grid2DComponent.ts +69 -0
- package/src/grid.ts +2 -0
- package/src/hya-state-machine.ts +19 -0
- package/src/index.ts +2 -0
- package/src/plugins/pluginUtils.ts +50 -0
- package/src/plugins.ts +6 -0
- package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +242 -0
- package/src/shaders/generated/2d-ui-artifact.generated.ts +867 -0
- package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +38 -0
- package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +41 -0
- package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +36 -0
- package/src/spine/Spine2DComponent.ts +168 -0
- package/src/spine/Spine2DGpuRenderer.ts +449 -0
- package/src/spine/Spine2DRenderSystem.ts +199 -0
- package/src/spine/Spine2DRuntime.ts +1529 -0
- package/src/spine/SpineAnimationBoneTimelines.ts +30 -0
- package/src/spine/SpineAssetParser.ts +20 -0
- package/src/spine/SpineAssetWorkerClient.ts +112 -0
- package/src/spine/SpineAssetWorkerContract.ts +4 -0
- package/src/spine/SpineAtlasParser.ts +127 -0
- package/src/spine/SpineFloatBuilder.ts +66 -0
- package/src/spine/SpinePathConstraintSolver.ts +582 -0
- package/src/spine/SpinePlugin.ts +76 -0
- package/src/spine/SpineSkeletonRuntime.ts +521 -0
- package/src/spine/SpineSlotAnimation.ts +195 -0
- package/src/spine/SpineTimelineRuntime.ts +453 -0
- package/src/spine/SpineVertexBuilder.ts +686 -0
- package/src/spine.ts +7 -0
- package/src/tilemap/Tilemap2DComponent.ts +232 -0
- package/src/tilemap/Tilemap2DRenderSystem.ts +58 -0
- package/src/tilemap/Tilemap2DRenderer.ts +289 -0
- package/src/tilemap/TilemapPlugin.ts +75 -0
- package/src/tilemap.ts +6 -0
- package/src/tween/Tween2DComponent.ts +83 -0
- package/src/tween/Tween2DSystem.ts +78 -0
- package/src/tween.ts +4 -0
- package/src/types/earcut.d.ts +7 -0
- package/src/types/wgsl.d.ts +4 -0
- package/src/utils/RenderSystem2DBase.ts +164 -0
- package/src/utils/arrayAccess.ts +17 -0
- package/src/utils/camera2d.ts +68 -0
- package/src/utils/render2dGpu.ts +132 -0
- package/test/fixtures/gltf/animation-characterization.gltf +344 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EngineError, EngineErrorCode } from '@haiyue/engine';
|
|
2
|
+
import { ErrorDomain, ErrorRecovery } from '@haiyue/engine/core';
|
|
3
|
+
import { createAbortError } from '@haiyue/engine/experimental/async';
|
|
4
|
+
|
|
5
|
+
export function throwIfGltfLoadAborted(signal: AbortSignal | undefined): void {
|
|
6
|
+
if (!signal?.aborted) return;
|
|
7
|
+
throw createAbortError('glTF load aborted.', signal.reason);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function attachGltfSource(error: unknown, src: string): Error {
|
|
11
|
+
if (error instanceof Error && error.name === 'AbortError') return error;
|
|
12
|
+
if (error instanceof EngineError) {
|
|
13
|
+
return new EngineError(error.code, error.message, {
|
|
14
|
+
domain: error.domain,
|
|
15
|
+
recoverable: error.recoverable,
|
|
16
|
+
recovery: error.recovery,
|
|
17
|
+
context: { url: src, resourceType: 'model/gltf', ...error.context },
|
|
18
|
+
...(error.path === undefined ? {} : { path: error.path }),
|
|
19
|
+
...(error.hint === undefined ? {} : { hint: error.hint }),
|
|
20
|
+
...(error.docsPath === undefined ? {} : { docsPath: error.docsPath }),
|
|
21
|
+
cause: error,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return gltfDataError(error instanceof Error ? error.message : String(error), { url: src }, undefined, error);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function gltfDataError(
|
|
28
|
+
message: string,
|
|
29
|
+
context: Record<string, unknown> = {},
|
|
30
|
+
path?: string,
|
|
31
|
+
cause?: unknown,
|
|
32
|
+
): EngineError {
|
|
33
|
+
const accessor = /[Aa]ccessor\s+(\d+)/.exec(message)?.[1];
|
|
34
|
+
const bufferView = /bufferView\s+(\d+)/i.exec(message)?.[1];
|
|
35
|
+
const buffer = /buffer\s+(\d+)/i.exec(message)?.[1];
|
|
36
|
+
const failedLoad = /^Failed to load/.test(message);
|
|
37
|
+
const resolvedPath = path
|
|
38
|
+
?? (accessor === undefined ? undefined : `gltf.accessors[${accessor}]`)
|
|
39
|
+
?? (bufferView === undefined ? undefined : `gltf.bufferViews[${bufferView}]`)
|
|
40
|
+
?? (buffer === undefined ? undefined : `gltf.buffers[${buffer}]`)
|
|
41
|
+
?? 'gltf';
|
|
42
|
+
return new EngineError(
|
|
43
|
+
failedLoad ? EngineErrorCode.AssetLoadFailed : EngineErrorCode.AssetInvalidData,
|
|
44
|
+
message,
|
|
45
|
+
{
|
|
46
|
+
domain: ErrorDomain.Component,
|
|
47
|
+
recovery: failedLoad ? ErrorRecovery.Retry : ErrorRecovery.ReleaseResource,
|
|
48
|
+
context: {
|
|
49
|
+
resourceType: 'model/gltf',
|
|
50
|
+
...context,
|
|
51
|
+
...(accessor === undefined ? {} : { accessor: Number(accessor) }),
|
|
52
|
+
...(bufferView === undefined ? {} : { bufferView: Number(bufferView) }),
|
|
53
|
+
...(buffer === undefined ? {} : { buffer: Number(buffer) }),
|
|
54
|
+
},
|
|
55
|
+
path: resolvedPath,
|
|
56
|
+
cause,
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ColorSRGB } from '@haiyue/engine';
|
|
2
|
+
import { type MaterialDescriptor, type MaterialTextureSource, type PbrMaterialState, type PbrTextureMappingOptions, type PbrTextureMappings, type PbrTextureSamplers, type PbrTextureSlot } from '@haiyue/engine/material';
|
|
3
|
+
import type { GltfAsset, GltfMaterial, GltfPrimitive, GltfTextureInfo } from './GltfSchema';
|
|
4
|
+
import {
|
|
5
|
+
collectGltfMaterialExtensionPatches,
|
|
6
|
+
type GltfExtensionAdapter,
|
|
7
|
+
type GltfMaterialTextureBinding,
|
|
8
|
+
} from './GltfExtensionAdapter';
|
|
9
|
+
|
|
10
|
+
export interface ResolvedGltfMaterialTexture {
|
|
11
|
+
readonly source: MaterialTextureSource;
|
|
12
|
+
readonly mapping: PbrTextureMappingOptions;
|
|
13
|
+
readonly sampler: GPUSamplerDescriptor | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface GltfMaterialDescriptorInput {
|
|
17
|
+
readonly gltf: GltfAsset;
|
|
18
|
+
readonly material: GltfMaterial | null;
|
|
19
|
+
readonly primitive: GltfPrimitive;
|
|
20
|
+
readonly materialPath: string;
|
|
21
|
+
readonly defaultBaseColor?: readonly [number, number, number, number];
|
|
22
|
+
readonly adapters: readonly GltfExtensionAdapter[];
|
|
23
|
+
readonly resolveTexture: (binding: GltfMaterialTextureBinding) => ResolvedGltfMaterialTexture;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const textureStateKeys: Readonly<Record<PbrTextureSlot, keyof PbrMaterialState>> = Object.freeze({
|
|
27
|
+
baseColor: 'baseColorTexture',
|
|
28
|
+
metallicRoughness: 'metallicRoughnessTexture',
|
|
29
|
+
normal: 'normalTexture',
|
|
30
|
+
occlusion: 'occlusionTexture',
|
|
31
|
+
emissive: 'emissiveTexture',
|
|
32
|
+
clearcoat: 'clearcoatTexture',
|
|
33
|
+
clearcoatRoughness: 'clearcoatRoughnessTexture',
|
|
34
|
+
clearcoatNormal: 'clearcoatNormalTexture',
|
|
35
|
+
specular: 'specularTexture',
|
|
36
|
+
specularColor: 'specularColorTexture',
|
|
37
|
+
sheenColor: 'sheenColorTexture',
|
|
38
|
+
sheenRoughness: 'sheenRoughnessTexture',
|
|
39
|
+
transmission: 'transmissionTexture',
|
|
40
|
+
thickness: 'thicknessTexture',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** Compiles glTF core material data and extension patches into one engine descriptor. */
|
|
44
|
+
export function createGltfMaterialDescriptor(
|
|
45
|
+
input: GltfMaterialDescriptorInput,
|
|
46
|
+
): Extract<MaterialDescriptor, { shadingModel: 'pbr-metallic-roughness' }> {
|
|
47
|
+
const { material } = input;
|
|
48
|
+
const pbr = material?.pbrMetallicRoughness;
|
|
49
|
+
const state: PbrMaterialState = {
|
|
50
|
+
baseColor: pbr?.baseColorFactor
|
|
51
|
+
? linearBaseColorFactorToSrgb(pbr.baseColorFactor)
|
|
52
|
+
: input.defaultBaseColor ?? [1, 1, 1, 1],
|
|
53
|
+
metallic: pbr?.metallicFactor ?? 1,
|
|
54
|
+
roughness: pbr?.roughnessFactor ?? 1,
|
|
55
|
+
normalScale: material?.normalTexture?.scale ?? 1,
|
|
56
|
+
occlusionStrength: material?.occlusionTexture?.strength ?? 1,
|
|
57
|
+
emissiveFactor: material?.emissiveFactor ?? [0, 0, 0],
|
|
58
|
+
alphaMode: material?.alphaMode === 'BLEND' ? 'blend' : material?.alphaMode === 'MASK' ? 'mask' : 'opaque',
|
|
59
|
+
alphaCutoff: material?.alphaCutoff ?? 0.5,
|
|
60
|
+
doubleSided: material?.doubleSided ?? false,
|
|
61
|
+
};
|
|
62
|
+
const patches = collectGltfMaterialExtensionPatches(
|
|
63
|
+
input.gltf,
|
|
64
|
+
material,
|
|
65
|
+
input.primitive,
|
|
66
|
+
input.materialPath,
|
|
67
|
+
input.adapters,
|
|
68
|
+
);
|
|
69
|
+
const textureMappings: Partial<Record<PbrTextureSlot, PbrTextureMappingOptions>> = {};
|
|
70
|
+
const samplers: Partial<Record<PbrTextureSlot, GPUSamplerDescriptor | null>> = {};
|
|
71
|
+
for (const patch of patches) {
|
|
72
|
+
Object.assign(state, patch.state);
|
|
73
|
+
}
|
|
74
|
+
for (const binding of collectGltfMaterialTextureBindings(
|
|
75
|
+
input.gltf,
|
|
76
|
+
material,
|
|
77
|
+
input.primitive,
|
|
78
|
+
input.materialPath,
|
|
79
|
+
input.adapters,
|
|
80
|
+
)) {
|
|
81
|
+
const texture = input.resolveTexture(binding);
|
|
82
|
+
state[textureStateKeys[binding.slot]] = texture.source as never;
|
|
83
|
+
textureMappings[binding.slot] = texture.mapping;
|
|
84
|
+
samplers[binding.slot] = texture.sampler;
|
|
85
|
+
}
|
|
86
|
+
if (Object.keys(textureMappings).length > 0) state.textureMappings = textureMappings as PbrTextureMappings;
|
|
87
|
+
if (Object.keys(samplers).length > 0) state.samplers = samplers as PbrTextureSamplers;
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
shadingModel: 'pbr-metallic-roughness',
|
|
90
|
+
state: Object.freeze(state),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Shared semantic source for preload, UV planning, and descriptor compilation. */
|
|
95
|
+
export function collectGltfMaterialTextureBindings(
|
|
96
|
+
gltf: GltfAsset,
|
|
97
|
+
material: GltfMaterial | null,
|
|
98
|
+
primitive: GltfPrimitive,
|
|
99
|
+
materialPath: string,
|
|
100
|
+
adapters: readonly GltfExtensionAdapter[],
|
|
101
|
+
): readonly GltfMaterialTextureBinding[] {
|
|
102
|
+
if (!material) return Object.freeze([]);
|
|
103
|
+
const pbr = material.pbrMetallicRoughness;
|
|
104
|
+
const bindings: GltfMaterialTextureBinding[] = [];
|
|
105
|
+
addTexture(bindings, 'baseColor', pbr?.baseColorTexture, `${materialPath}.pbrMetallicRoughness.baseColorTexture`);
|
|
106
|
+
addTexture(bindings, 'metallicRoughness', pbr?.metallicRoughnessTexture, `${materialPath}.pbrMetallicRoughness.metallicRoughnessTexture`);
|
|
107
|
+
addTexture(bindings, 'normal', material.normalTexture, `${materialPath}.normalTexture`);
|
|
108
|
+
addTexture(bindings, 'occlusion', material.occlusionTexture, `${materialPath}.occlusionTexture`);
|
|
109
|
+
addTexture(bindings, 'emissive', material.emissiveTexture, `${materialPath}.emissiveTexture`);
|
|
110
|
+
for (const patch of collectGltfMaterialExtensionPatches(gltf, material, primitive, materialPath, adapters)) {
|
|
111
|
+
bindings.push(...patch.textures ?? []);
|
|
112
|
+
}
|
|
113
|
+
return Object.freeze(bindings);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function addTexture(
|
|
117
|
+
bindings: GltfMaterialTextureBinding[],
|
|
118
|
+
slot: PbrTextureSlot,
|
|
119
|
+
textureInfo: GltfTextureInfo | undefined,
|
|
120
|
+
path: string,
|
|
121
|
+
): void {
|
|
122
|
+
if (textureInfo) bindings.push(Object.freeze({ slot, textureInfo, path }));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function linearBaseColorFactorToSrgb(
|
|
126
|
+
value: readonly [number, number, number, number],
|
|
127
|
+
): [number, number, number, number] {
|
|
128
|
+
return [
|
|
129
|
+
ColorSRGB.linearToSRGB(value[0]),
|
|
130
|
+
ColorSRGB.linearToSRGB(value[1]),
|
|
131
|
+
ColorSRGB.linearToSRGB(value[2]),
|
|
132
|
+
value[3],
|
|
133
|
+
];
|
|
134
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { MaterialTextureSource, PbrMaterial, PbrTextureSlot } from '@haiyue/engine/material';
|
|
2
|
+
import type { GltfMaterial, GltfTextureInfo, GltfTextureTransform } from './GltfSchema';
|
|
3
|
+
|
|
4
|
+
export interface EncodeGltfPbrMaterialOptions {
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Resolves an engine texture source to the texture index already allocated
|
|
8
|
+
* by the surrounding glTF/GLB exporter.
|
|
9
|
+
*/
|
|
10
|
+
readonly resolveTextureIndex: (slot: PbrTextureSlot, source: MaterialTextureSource) => number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface EncodedGltfPbrMaterial {
|
|
14
|
+
readonly material: GltfMaterial;
|
|
15
|
+
readonly extensionsUsed: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Encodes the standard glTF material portion without taking ownership of image,
|
|
20
|
+
* sampler, or buffer allocation. Scene exporters can share this codec instead
|
|
21
|
+
* of duplicating KHR material semantics.
|
|
22
|
+
*/
|
|
23
|
+
export function encodeGltfPbrMaterial(
|
|
24
|
+
material: PbrMaterial,
|
|
25
|
+
options: EncodeGltfPbrMaterialOptions,
|
|
26
|
+
): EncodedGltfPbrMaterial {
|
|
27
|
+
const extensionsUsed = new Set<string>();
|
|
28
|
+
const texture = (slot: PbrTextureSlot, source: MaterialTextureSource): GltfTextureInfo | undefined => {
|
|
29
|
+
if (!source) return undefined;
|
|
30
|
+
const index = options.resolveTextureIndex(slot, source);
|
|
31
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
32
|
+
throw new RangeError(`glTF texture index for PBR slot "${slot}" must be a non-negative safe integer.`);
|
|
33
|
+
}
|
|
34
|
+
return encodeTextureInfo(material, slot, index, extensionsUsed);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const baseColor = new Float32Array(4);
|
|
38
|
+
material.baseColor.writeLinear(baseColor);
|
|
39
|
+
const baseColorTexture = texture('baseColor', material.baseColorTexture);
|
|
40
|
+
const metallicRoughnessTexture = texture('metallicRoughness', material.metallicRoughnessTexture);
|
|
41
|
+
const normalTexture = texture('normal', material.normalTexture);
|
|
42
|
+
const occlusionTexture = texture('occlusion', material.occlusionTexture);
|
|
43
|
+
const emissiveTexture = texture('emissive', material.emissiveTexture);
|
|
44
|
+
const clearcoatTexture = texture('clearcoat', material.clearcoatTexture);
|
|
45
|
+
const clearcoatRoughnessTexture = texture('clearcoatRoughness', material.clearcoatRoughnessTexture);
|
|
46
|
+
const clearcoatNormalTexture = texture('clearcoatNormal', material.clearcoatNormalTexture);
|
|
47
|
+
const specularTexture = texture('specular', material.specularTexture);
|
|
48
|
+
const specularColorTexture = texture('specularColor', material.specularColorTexture);
|
|
49
|
+
const sheenColorTexture = texture('sheenColor', material.sheenColorTexture);
|
|
50
|
+
const sheenRoughnessTexture = texture('sheenRoughness', material.sheenRoughnessTexture);
|
|
51
|
+
const transmissionTexture = texture('transmission', material.transmissionTexture);
|
|
52
|
+
const thicknessTexture = texture('thickness', material.thicknessTexture);
|
|
53
|
+
|
|
54
|
+
const extensions: NonNullable<GltfMaterial['extensions']> = {};
|
|
55
|
+
if (
|
|
56
|
+
material.clearcoatFactor !== 0
|
|
57
|
+
|| material.clearcoatRoughnessFactor !== 0
|
|
58
|
+
|| clearcoatTexture
|
|
59
|
+
|| clearcoatRoughnessTexture
|
|
60
|
+
|| clearcoatNormalTexture
|
|
61
|
+
) {
|
|
62
|
+
extensions.KHR_materials_clearcoat = {
|
|
63
|
+
clearcoatFactor: material.clearcoatFactor,
|
|
64
|
+
...(clearcoatTexture ? { clearcoatTexture } : {}),
|
|
65
|
+
clearcoatRoughnessFactor: material.clearcoatRoughnessFactor,
|
|
66
|
+
...(clearcoatRoughnessTexture ? { clearcoatRoughnessTexture } : {}),
|
|
67
|
+
...(clearcoatNormalTexture ? {
|
|
68
|
+
clearcoatNormalTexture: { ...clearcoatNormalTexture, scale: material.clearcoatNormalScale },
|
|
69
|
+
} : {}),
|
|
70
|
+
};
|
|
71
|
+
extensionsUsed.add('KHR_materials_clearcoat');
|
|
72
|
+
}
|
|
73
|
+
if (material.ior !== 1.5) {
|
|
74
|
+
extensions.KHR_materials_ior = { ior: material.ior };
|
|
75
|
+
extensionsUsed.add('KHR_materials_ior');
|
|
76
|
+
}
|
|
77
|
+
if (
|
|
78
|
+
material.specularFactor !== 1
|
|
79
|
+
|| material.specularColorFactor.some(channel => channel !== 1)
|
|
80
|
+
|| specularTexture
|
|
81
|
+
|| specularColorTexture
|
|
82
|
+
) {
|
|
83
|
+
extensions.KHR_materials_specular = {
|
|
84
|
+
specularFactor: material.specularFactor,
|
|
85
|
+
...(specularTexture ? { specularTexture } : {}),
|
|
86
|
+
specularColorFactor: [...material.specularColorFactor],
|
|
87
|
+
...(specularColorTexture ? { specularColorTexture } : {}),
|
|
88
|
+
};
|
|
89
|
+
extensionsUsed.add('KHR_materials_specular');
|
|
90
|
+
}
|
|
91
|
+
if (
|
|
92
|
+
material.sheenColorFactor.some(channel => channel !== 0)
|
|
93
|
+
|| material.sheenRoughnessFactor !== 0
|
|
94
|
+
|| sheenColorTexture
|
|
95
|
+
|| sheenRoughnessTexture
|
|
96
|
+
) {
|
|
97
|
+
extensions.KHR_materials_sheen = {
|
|
98
|
+
sheenColorFactor: [...material.sheenColorFactor],
|
|
99
|
+
...(sheenColorTexture ? { sheenColorTexture } : {}),
|
|
100
|
+
sheenRoughnessFactor: material.sheenRoughnessFactor,
|
|
101
|
+
...(sheenRoughnessTexture ? { sheenRoughnessTexture } : {}),
|
|
102
|
+
};
|
|
103
|
+
extensionsUsed.add('KHR_materials_sheen');
|
|
104
|
+
}
|
|
105
|
+
if (material.transmissionFactor !== 0 || transmissionTexture) {
|
|
106
|
+
extensions.KHR_materials_transmission = {
|
|
107
|
+
transmissionFactor: material.transmissionFactor,
|
|
108
|
+
...(transmissionTexture ? { transmissionTexture } : {}),
|
|
109
|
+
};
|
|
110
|
+
extensionsUsed.add('KHR_materials_transmission');
|
|
111
|
+
}
|
|
112
|
+
if (
|
|
113
|
+
material.thicknessFactor !== 0
|
|
114
|
+
|| thicknessTexture
|
|
115
|
+
|| material.attenuationDistance !== Infinity
|
|
116
|
+
|| material.attenuationColor.some(channel => channel !== 1)
|
|
117
|
+
) {
|
|
118
|
+
extensions.KHR_materials_volume = {
|
|
119
|
+
thicknessFactor: material.thicknessFactor,
|
|
120
|
+
...(thicknessTexture ? { thicknessTexture } : {}),
|
|
121
|
+
...(material.attenuationDistance === Infinity ? {} : { attenuationDistance: material.attenuationDistance }),
|
|
122
|
+
attenuationColor: [...material.attenuationColor],
|
|
123
|
+
};
|
|
124
|
+
extensionsUsed.add('KHR_materials_volume');
|
|
125
|
+
if (material.transmissionFactor === 0 && !transmissionTexture) {
|
|
126
|
+
extensions.KHR_materials_transmission = { transmissionFactor: 0 };
|
|
127
|
+
extensionsUsed.add('KHR_materials_transmission');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const gltfMaterial: GltfMaterial = {
|
|
132
|
+
...(options.name ? { name: options.name } : {}),
|
|
133
|
+
pbrMetallicRoughness: {
|
|
134
|
+
baseColorFactor: [baseColor[0]!, baseColor[1]!, baseColor[2]!, baseColor[3]!],
|
|
135
|
+
...(baseColorTexture ? { baseColorTexture } : {}),
|
|
136
|
+
...(metallicRoughnessTexture ? { metallicRoughnessTexture } : {}),
|
|
137
|
+
metallicFactor: material.metallic,
|
|
138
|
+
roughnessFactor: material.roughness,
|
|
139
|
+
},
|
|
140
|
+
emissiveFactor: [...material.emissiveFactor],
|
|
141
|
+
...(emissiveTexture ? { emissiveTexture } : {}),
|
|
142
|
+
...(normalTexture ? { normalTexture: { ...normalTexture, scale: material.normalScale } } : {}),
|
|
143
|
+
...(occlusionTexture ? { occlusionTexture: { ...occlusionTexture, strength: material.occlusionStrength } } : {}),
|
|
144
|
+
alphaMode: material.alphaMode === 'blend' ? 'BLEND' : material.alphaMode === 'mask' ? 'MASK' : 'OPAQUE',
|
|
145
|
+
...(material.alphaMode === 'mask' ? { alphaCutoff: material.alphaCutoff } : {}),
|
|
146
|
+
doubleSided: material.doubleSided,
|
|
147
|
+
...(Object.keys(extensions).length > 0 ? { extensions } : {}),
|
|
148
|
+
};
|
|
149
|
+
return Object.freeze({
|
|
150
|
+
material: gltfMaterial,
|
|
151
|
+
extensionsUsed: Object.freeze([...extensionsUsed]),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function encodeTextureInfo(
|
|
156
|
+
material: PbrMaterial,
|
|
157
|
+
slot: PbrTextureSlot,
|
|
158
|
+
index: number,
|
|
159
|
+
extensionsUsed: Set<string>,
|
|
160
|
+
): GltfTextureInfo {
|
|
161
|
+
const mapping = material.getTextureMapping(slot);
|
|
162
|
+
const transform: GltfTextureTransform = {};
|
|
163
|
+
if (mapping.offset[0] !== 0 || mapping.offset[1] !== 0) transform.offset = [...mapping.offset];
|
|
164
|
+
if (mapping.rotation !== 0) transform.rotation = mapping.rotation;
|
|
165
|
+
if (mapping.scale[0] !== 1 || mapping.scale[1] !== 1) transform.scale = [...mapping.scale];
|
|
166
|
+
const transformed = Object.keys(transform).length > 0;
|
|
167
|
+
if (transformed) extensionsUsed.add('KHR_texture_transform');
|
|
168
|
+
return {
|
|
169
|
+
index,
|
|
170
|
+
...(mapping.texCoord !== 0 ? { texCoord: mapping.texCoord } : {}),
|
|
171
|
+
...(transformed ? { extensions: { KHR_texture_transform: transform } } : {}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import type { PbrMaterial } from '@haiyue/engine';
|
|
2
|
+
import {
|
|
3
|
+
createMaterialFromDescriptor,
|
|
4
|
+
getPbrTextureFormat,
|
|
5
|
+
type MaterialDescriptorVariant,
|
|
6
|
+
type MaterialTextureSource,
|
|
7
|
+
type PbrTextureMappingOptions,
|
|
8
|
+
type PbrTextureSlot,
|
|
9
|
+
} from '@haiyue/engine/material';
|
|
10
|
+
import type { AssetHandle, CompressedTextureSourceDescriptor } from '@haiyue/engine/assets';
|
|
11
|
+
import {
|
|
12
|
+
collectGltfMaterialVariantReferences,
|
|
13
|
+
} from './GltfExtensionAdapter';
|
|
14
|
+
import {
|
|
15
|
+
collectGltfMaterialTextureBindings,
|
|
16
|
+
createGltfMaterialDescriptor,
|
|
17
|
+
} from './GltfMaterialDescriptor';
|
|
18
|
+
import { getGltfUvChannel, type GltfUvSemanticPlan } from './GltfUvSemanticPlanner';
|
|
19
|
+
import { gltfDataError, throwIfGltfLoadAborted } from './GltfLoaderErrors';
|
|
20
|
+
import type { GltfLoadContext, LoadGltfOptions } from './GltfLoaderContract';
|
|
21
|
+
import type {
|
|
22
|
+
GltfAsset,
|
|
23
|
+
GltfImage,
|
|
24
|
+
GltfMaterial,
|
|
25
|
+
GltfPrimitive,
|
|
26
|
+
GltfTextureInfo,
|
|
27
|
+
} from './GltfSchema';
|
|
28
|
+
|
|
29
|
+
export async function createGltfMaterial(gltf: GltfAsset, buffers: ArrayBuffer[], primitive: GltfPrimitive, options: LoadGltfOptions, context: GltfLoadContext, uvPlan: GltfUvSemanticPlan): Promise<PbrMaterial> {
|
|
30
|
+
throwIfGltfLoadAborted(options.signal);
|
|
31
|
+
const materialIndex = primitive.material;
|
|
32
|
+
const gltfMaterial = materialIndex === undefined ? null : gltf.materials?.[materialIndex] ?? null;
|
|
33
|
+
const createDescriptor = (material: GltfMaterial | null, materialPath: string) => createGltfMaterialDescriptor({
|
|
34
|
+
gltf,
|
|
35
|
+
material,
|
|
36
|
+
primitive,
|
|
37
|
+
materialPath,
|
|
38
|
+
...(options.baseColorFactor === undefined ? {} : { defaultBaseColor: options.baseColorFactor }),
|
|
39
|
+
adapters: context.extensionAdapters,
|
|
40
|
+
resolveTexture: binding => ({
|
|
41
|
+
source: createTextureSource(gltf, buffers, binding.textureInfo.index, binding.slot, context),
|
|
42
|
+
mapping: resolveGltfTextureMapping(binding.textureInfo, primitive, binding.path, uvPlan),
|
|
43
|
+
sampler: createSamplerDescriptor(gltf, binding.textureInfo.index),
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
const descriptor = createDescriptor(
|
|
47
|
+
gltfMaterial,
|
|
48
|
+
materialIndex === undefined ? 'gltf.defaultMaterial' : `gltf.materials[${materialIndex}]`,
|
|
49
|
+
);
|
|
50
|
+
const variants: MaterialDescriptorVariant[] = collectGltfMaterialVariantReferences(
|
|
51
|
+
gltf,
|
|
52
|
+
primitive,
|
|
53
|
+
context.extensionAdapters,
|
|
54
|
+
).map(variant => ({
|
|
55
|
+
name: variant.name,
|
|
56
|
+
state: createDescriptor(
|
|
57
|
+
gltf.materials?.[variant.materialIndex] ?? null,
|
|
58
|
+
`gltf.materials[${variant.materialIndex}]`,
|
|
59
|
+
).state,
|
|
60
|
+
}));
|
|
61
|
+
return createMaterialFromDescriptor({ ...descriptor, variants });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resolveGltfTextureMapping(
|
|
65
|
+
textureInfo: GltfTextureInfo,
|
|
66
|
+
primitive: GltfPrimitive,
|
|
67
|
+
path: string,
|
|
68
|
+
uvPlan: GltfUvSemanticPlan,
|
|
69
|
+
): PbrTextureMappingOptions {
|
|
70
|
+
const transform = textureInfo.extensions?.KHR_texture_transform;
|
|
71
|
+
const textureCoord = transform?.texCoord ?? textureInfo.texCoord ?? 0;
|
|
72
|
+
const texCoordPath = transform?.texCoord === undefined
|
|
73
|
+
? `${path}.texCoord`
|
|
74
|
+
: `${path}.extensions.KHR_texture_transform.texCoord`;
|
|
75
|
+
const semantic = `TEXCOORD_${textureCoord}`;
|
|
76
|
+
if (primitive.attributes[semantic] === undefined) {
|
|
77
|
+
throw gltfDataError(
|
|
78
|
+
`Texture references ${semantic}, but the mesh primitive does not provide that attribute.`,
|
|
79
|
+
{ texCoord: textureCoord, semantic },
|
|
80
|
+
texCoordPath,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const channel = getGltfUvChannel(uvPlan, textureCoord);
|
|
84
|
+
if (channel === null) {
|
|
85
|
+
throw gltfDataError(
|
|
86
|
+
`${semantic} was not assigned to a physical PBR UV channel.`,
|
|
87
|
+
{ texCoord: textureCoord, semantic, capacity: uvPlan.capacity },
|
|
88
|
+
texCoordPath,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
texCoord: channel,
|
|
93
|
+
offset: transform?.offset ?? [0, 0],
|
|
94
|
+
rotation: transform?.rotation ?? 0,
|
|
95
|
+
scale: transform?.scale ?? [1, 1],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createSamplerDescriptor(gltf: GltfAsset, textureIndex: number): GPUSamplerDescriptor | null {
|
|
100
|
+
const texture = gltf.textures?.[textureIndex];
|
|
101
|
+
const sampler = texture?.sampler === undefined ? null : gltf.samplers?.[texture.sampler] ?? null;
|
|
102
|
+
return {
|
|
103
|
+
addressModeU: mapWrapMode(sampler?.wrapS),
|
|
104
|
+
addressModeV: mapWrapMode(sampler?.wrapT),
|
|
105
|
+
magFilter: mapMagFilter(sampler?.magFilter),
|
|
106
|
+
minFilter: mapMinFilter(sampler?.minFilter),
|
|
107
|
+
mipmapFilter: mapMipmapFilter(sampler?.minFilter),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function mapWrapMode(mode: number | undefined): GPUAddressMode {
|
|
112
|
+
switch (mode) {
|
|
113
|
+
case 33071: return 'clamp-to-edge';
|
|
114
|
+
case 33648: return 'mirror-repeat';
|
|
115
|
+
case 10497:
|
|
116
|
+
case undefined:
|
|
117
|
+
return 'repeat';
|
|
118
|
+
default:
|
|
119
|
+
return 'repeat';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function mapMagFilter(filter: number | undefined): GPUFilterMode {
|
|
124
|
+
return filter === 9728 ? 'nearest' : 'linear';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function mapMinFilter(filter: number | undefined): GPUFilterMode {
|
|
128
|
+
switch (filter) {
|
|
129
|
+
case 9728:
|
|
130
|
+
case 9984:
|
|
131
|
+
case 9986:
|
|
132
|
+
return 'nearest';
|
|
133
|
+
default:
|
|
134
|
+
return 'linear';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function mapMipmapFilter(filter: number | undefined): GPUMipmapFilterMode {
|
|
139
|
+
switch (filter) {
|
|
140
|
+
case 9984:
|
|
141
|
+
case 9985:
|
|
142
|
+
return 'nearest';
|
|
143
|
+
case 9986:
|
|
144
|
+
case 9987:
|
|
145
|
+
return 'linear';
|
|
146
|
+
default:
|
|
147
|
+
return 'linear';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface GltfTextureReference {
|
|
152
|
+
readonly source: string | CompressedTextureSourceDescriptor;
|
|
153
|
+
readonly cacheKey: string;
|
|
154
|
+
readonly requestKey: string;
|
|
155
|
+
readonly format: 'rgba8unorm-srgb' | 'rgba8unorm';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function preloadGltfTextures(
|
|
159
|
+
gltf: GltfAsset,
|
|
160
|
+
buffers: ArrayBuffer[],
|
|
161
|
+
options: LoadGltfOptions,
|
|
162
|
+
context: GltfLoadContext,
|
|
163
|
+
): Promise<void> {
|
|
164
|
+
const assetManager = options.assetManager;
|
|
165
|
+
if (!assetManager) return;
|
|
166
|
+
const requests = new Map<string, GltfTextureReference>();
|
|
167
|
+
for (const mesh of gltf.meshes ?? []) {
|
|
168
|
+
for (const primitive of mesh.primitives) {
|
|
169
|
+
const materialIndexes = new Set<number>();
|
|
170
|
+
if (primitive.material !== undefined) materialIndexes.add(primitive.material);
|
|
171
|
+
for (const variant of collectGltfMaterialVariantReferences(gltf, primitive, context.extensionAdapters)) {
|
|
172
|
+
materialIndexes.add(variant.materialIndex);
|
|
173
|
+
}
|
|
174
|
+
for (const materialIndex of materialIndexes) {
|
|
175
|
+
const material = gltf.materials?.[materialIndex] ?? null;
|
|
176
|
+
for (const binding of collectGltfMaterialTextureBindings(
|
|
177
|
+
gltf,
|
|
178
|
+
material,
|
|
179
|
+
primitive,
|
|
180
|
+
`gltf.materials[${materialIndex}]`,
|
|
181
|
+
context.extensionAdapters,
|
|
182
|
+
)) {
|
|
183
|
+
const reference = resolveTextureReference(gltf, buffers, binding.textureInfo.index, binding.slot, context);
|
|
184
|
+
if (reference) requests.set(reference.requestKey, reference);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const results = await Promise.allSettled([...requests.values()].map(async reference => ({
|
|
190
|
+
reference,
|
|
191
|
+
handle: await assetManager.loadTexture(reference.source, {
|
|
192
|
+
cacheKey: reference.cacheKey,
|
|
193
|
+
format: reference.format,
|
|
194
|
+
mipmaps: 'generate',
|
|
195
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
196
|
+
}),
|
|
197
|
+
})));
|
|
198
|
+
const loaded: Array<{ reference: GltfTextureReference; handle: AssetHandle<GPUTexture> }> = [];
|
|
199
|
+
let firstError: unknown = null;
|
|
200
|
+
for (const result of results) {
|
|
201
|
+
if (result.status === 'fulfilled') loaded.push(result.value);
|
|
202
|
+
else if (firstError === null) firstError = result.reason;
|
|
203
|
+
}
|
|
204
|
+
if (firstError !== null) {
|
|
205
|
+
for (const { handle } of loaded) handle.release();
|
|
206
|
+
throw firstError;
|
|
207
|
+
}
|
|
208
|
+
for (const { reference, handle } of loaded) {
|
|
209
|
+
context.preloadedTextures.set(reference.requestKey, handle.value);
|
|
210
|
+
context.assetHandles.push(handle);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function createTextureSource(
|
|
215
|
+
gltf: GltfAsset,
|
|
216
|
+
buffers: ArrayBuffer[],
|
|
217
|
+
textureIndex: number,
|
|
218
|
+
slot: PbrTextureSlot,
|
|
219
|
+
context: GltfLoadContext,
|
|
220
|
+
): MaterialTextureSource {
|
|
221
|
+
const reference = resolveTextureReference(gltf, buffers, textureIndex, slot, context);
|
|
222
|
+
if (!reference) return null;
|
|
223
|
+
return context.preloadedTextures.get(reference.requestKey) ?? reference.source;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function resolveTextureReference(
|
|
227
|
+
gltf: GltfAsset,
|
|
228
|
+
buffers: ArrayBuffer[],
|
|
229
|
+
textureIndex: number,
|
|
230
|
+
slot: PbrTextureSlot,
|
|
231
|
+
context: GltfLoadContext,
|
|
232
|
+
): GltfTextureReference | null {
|
|
233
|
+
const texture = gltf.textures?.[textureIndex];
|
|
234
|
+
const basisuSource = texture?.extensions?.KHR_texture_basisu?.source;
|
|
235
|
+
const imageIndex = basisuSource ?? texture?.source;
|
|
236
|
+
if (imageIndex === undefined) return null;
|
|
237
|
+
const image = gltf.images?.[imageIndex] ?? null;
|
|
238
|
+
if (!image) return null;
|
|
239
|
+
const compressed = basisuSource !== undefined || image.mimeType === 'image/ktx2';
|
|
240
|
+
const src = createImageUrl(gltf, buffers, imageIndex, image, context);
|
|
241
|
+
if (!src) return null;
|
|
242
|
+
const source: string | CompressedTextureSourceDescriptor = compressed
|
|
243
|
+
? { kind: 'compressed-texture', type: 'texture/ktx2', src }
|
|
244
|
+
: src;
|
|
245
|
+
const cacheKey = createImageCacheKey(imageIndex, image, context);
|
|
246
|
+
const format = getPbrTextureFormat(slot);
|
|
247
|
+
return {
|
|
248
|
+
source,
|
|
249
|
+
cacheKey,
|
|
250
|
+
requestKey: compressed ? `compressed:${cacheKey}` : `${format}:${cacheKey}`,
|
|
251
|
+
format,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function createImageCacheKey(imageIndex: number, image: GltfImage, context: GltfLoadContext): string {
|
|
256
|
+
if (image.uri) {
|
|
257
|
+
const url = image.uri.startsWith('data:') || image.uri.startsWith('blob:')
|
|
258
|
+
? image.uri
|
|
259
|
+
: new URL(image.uri, context.baseUrl).href;
|
|
260
|
+
return `url:${url}`;
|
|
261
|
+
}
|
|
262
|
+
return `gltf:${context.assetIdentity}:image:${imageIndex}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function createImageUrl(
|
|
266
|
+
gltf: GltfAsset,
|
|
267
|
+
buffers: ArrayBuffer[],
|
|
268
|
+
imageIndex: number,
|
|
269
|
+
image: GltfImage,
|
|
270
|
+
context: GltfLoadContext,
|
|
271
|
+
): string | null {
|
|
272
|
+
const cached = context.imageSources.get(imageIndex);
|
|
273
|
+
if (typeof cached === 'string') return cached;
|
|
274
|
+
if (image.uri) {
|
|
275
|
+
const url = image.uri.startsWith('data:') || image.uri.startsWith('blob:')
|
|
276
|
+
? image.uri
|
|
277
|
+
: new URL(image.uri, context.baseUrl).href;
|
|
278
|
+
context.imageSources.set(imageIndex, url);
|
|
279
|
+
return url;
|
|
280
|
+
}
|
|
281
|
+
if (image.bufferView === undefined) return null;
|
|
282
|
+
const view = gltf.bufferViews?.[image.bufferView];
|
|
283
|
+
if (!view) return null;
|
|
284
|
+
const buffer = buffers[view.buffer];
|
|
285
|
+
if (!buffer) throw gltfDataError(`Image bufferView references missing buffer ${view.buffer}.`);
|
|
286
|
+
const bytes = new Uint8Array(buffer, view.byteOffset ?? 0, view.byteLength);
|
|
287
|
+
const blob = new Blob([bytes.slice()], { type: image.mimeType || 'application/octet-stream' });
|
|
288
|
+
const url = URL.createObjectURL(blob);
|
|
289
|
+
if (!context.sharedImageSources) context.objectUrls.push(url);
|
|
290
|
+
context.imageSources.set(imageIndex, url);
|
|
291
|
+
return url;
|
|
292
|
+
}
|
|
293
|
+
|