@aura3d/workflows 1.0.6

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.
Files changed (101) hide show
  1. package/dist/AnimationLabWorkflow.d.ts +3 -0
  2. package/dist/AnimationLabWorkflow.d.ts.map +1 -0
  3. package/dist/AnimationLabWorkflow.js +156 -0
  4. package/dist/AnimationLabWorkflow.js.map +1 -0
  5. package/dist/AssetViewerWorkflow.d.ts +3 -0
  6. package/dist/AssetViewerWorkflow.d.ts.map +1 -0
  7. package/dist/AssetViewerWorkflow.js +35 -0
  8. package/dist/AssetViewerWorkflow.js.map +1 -0
  9. package/dist/ComparisonWorkflow.d.ts +3 -0
  10. package/dist/ComparisonWorkflow.d.ts.map +1 -0
  11. package/dist/ComparisonWorkflow.js +67 -0
  12. package/dist/ComparisonWorkflow.js.map +1 -0
  13. package/dist/InteractiveSceneWorkflow.d.ts +3 -0
  14. package/dist/InteractiveSceneWorkflow.d.ts.map +1 -0
  15. package/dist/InteractiveSceneWorkflow.js +43 -0
  16. package/dist/InteractiveSceneWorkflow.js.map +1 -0
  17. package/dist/MaterialStudioWorkflow.d.ts +3 -0
  18. package/dist/MaterialStudioWorkflow.d.ts.map +1 -0
  19. package/dist/MaterialStudioWorkflow.js +47 -0
  20. package/dist/MaterialStudioWorkflow.js.map +1 -0
  21. package/dist/ProductConfiguratorWorkflow.d.ts +3 -0
  22. package/dist/ProductConfiguratorWorkflow.d.ts.map +1 -0
  23. package/dist/ProductConfiguratorWorkflow.js +25 -0
  24. package/dist/ProductConfiguratorWorkflow.js.map +1 -0
  25. package/dist/SceneShowcaseWorkflow.d.ts +3 -0
  26. package/dist/SceneShowcaseWorkflow.d.ts.map +1 -0
  27. package/dist/SceneShowcaseWorkflow.js +44 -0
  28. package/dist/SceneShowcaseWorkflow.js.map +1 -0
  29. package/dist/WorkflowDiagnostics.d.ts +11 -0
  30. package/dist/WorkflowDiagnostics.d.ts.map +1 -0
  31. package/dist/WorkflowDiagnostics.js +34 -0
  32. package/dist/WorkflowDiagnostics.js.map +1 -0
  33. package/dist/WorkflowTypes.d.ts +90 -0
  34. package/dist/WorkflowTypes.d.ts.map +1 -0
  35. package/dist/WorkflowTypes.js +2 -0
  36. package/dist/WorkflowTypes.js.map +1 -0
  37. package/dist/index.d.ts +13 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +11 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/production-runtime/ArchitectureWorkflow.d.ts +7 -0
  42. package/dist/production-runtime/ArchitectureWorkflow.d.ts.map +1 -0
  43. package/dist/production-runtime/ArchitectureWorkflow.js +2 -0
  44. package/dist/production-runtime/ArchitectureWorkflow.js.map +1 -0
  45. package/dist/production-runtime/AssetInspectionWorkflow.d.ts +7 -0
  46. package/dist/production-runtime/AssetInspectionWorkflow.d.ts.map +1 -0
  47. package/dist/production-runtime/AssetInspectionWorkflow.js +2 -0
  48. package/dist/production-runtime/AssetInspectionWorkflow.js.map +1 -0
  49. package/dist/production-runtime/CinematicWorkflow.d.ts +7 -0
  50. package/dist/production-runtime/CinematicWorkflow.d.ts.map +1 -0
  51. package/dist/production-runtime/CinematicWorkflow.js +2 -0
  52. package/dist/production-runtime/CinematicWorkflow.js.map +1 -0
  53. package/dist/production-runtime/MaterialAuthoringWorkflow.d.ts +7 -0
  54. package/dist/production-runtime/MaterialAuthoringWorkflow.d.ts.map +1 -0
  55. package/dist/production-runtime/MaterialAuthoringWorkflow.js +2 -0
  56. package/dist/production-runtime/MaterialAuthoringWorkflow.js.map +1 -0
  57. package/dist/production-runtime/ProductRenderWorkflow.d.ts +7 -0
  58. package/dist/production-runtime/ProductRenderWorkflow.d.ts.map +1 -0
  59. package/dist/production-runtime/ProductRenderWorkflow.js +2 -0
  60. package/dist/production-runtime/ProductRenderWorkflow.js.map +1 -0
  61. package/dist/production-runtime/ProductionExampleRuntime.d.ts +66 -0
  62. package/dist/production-runtime/ProductionExampleRuntime.d.ts.map +1 -0
  63. package/dist/production-runtime/ProductionExampleRuntime.js +189 -0
  64. package/dist/production-runtime/ProductionExampleRuntime.js.map +1 -0
  65. package/dist/production-runtime/ProductionWorkflows.d.ts +69 -0
  66. package/dist/production-runtime/ProductionWorkflows.d.ts.map +1 -0
  67. package/dist/production-runtime/ProductionWorkflows.js +128 -0
  68. package/dist/production-runtime/ProductionWorkflows.js.map +1 -0
  69. package/dist/production-runtime/WorkflowDiagnostics.d.ts +7 -0
  70. package/dist/production-runtime/WorkflowDiagnostics.d.ts.map +1 -0
  71. package/dist/production-runtime/WorkflowDiagnostics.js +2 -0
  72. package/dist/production-runtime/WorkflowDiagnostics.js.map +1 -0
  73. package/dist/production-runtime/index.d.ts +11 -0
  74. package/dist/production-runtime/index.d.ts.map +1 -0
  75. package/dist/production-runtime/index.js +9 -0
  76. package/dist/production-runtime/index.js.map +1 -0
  77. package/dist/workflow-foundation/index.d.ts +26 -0
  78. package/dist/workflow-foundation/index.d.ts.map +1 -0
  79. package/dist/workflow-foundation/index.js +25 -0
  80. package/dist/workflow-foundation/index.js.map +1 -0
  81. package/package.json +22 -0
  82. package/src/AnimationLabWorkflow.ts +165 -0
  83. package/src/AssetViewerWorkflow.ts +37 -0
  84. package/src/ComparisonWorkflow.ts +68 -0
  85. package/src/InteractiveSceneWorkflow.ts +44 -0
  86. package/src/MaterialStudioWorkflow.ts +48 -0
  87. package/src/ProductConfiguratorWorkflow.ts +32 -0
  88. package/src/SceneShowcaseWorkflow.ts +45 -0
  89. package/src/WorkflowDiagnostics.ts +47 -0
  90. package/src/WorkflowTypes.ts +113 -0
  91. package/src/index.ts +34 -0
  92. package/src/production-runtime/ArchitectureWorkflow.ts +2 -0
  93. package/src/production-runtime/AssetInspectionWorkflow.ts +2 -0
  94. package/src/production-runtime/CinematicWorkflow.ts +2 -0
  95. package/src/production-runtime/MaterialAuthoringWorkflow.ts +2 -0
  96. package/src/production-runtime/ProductRenderWorkflow.ts +2 -0
  97. package/src/production-runtime/ProductionExampleRuntime.ts +267 -0
  98. package/src/production-runtime/ProductionWorkflows.ts +203 -0
  99. package/src/production-runtime/WorkflowDiagnostics.ts +2 -0
  100. package/src/production-runtime/index.ts +32 -0
  101. package/src/workflow-foundation/index.ts +27 -0
@@ -0,0 +1,32 @@
1
+ import {
2
+ createProductCameraFrame,
3
+ createProductLightingPreset,
4
+ createProductMaterialMode,
5
+ createProductRenderScene,
6
+ loadProductAsset
7
+ } from "@aura3d/product-studio";
8
+ import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
9
+ import type { ProductConfiguratorWorkflowOptions, ProductConfiguratorWorkflowResult } from "./WorkflowTypes";
10
+
11
+ export async function createProductConfiguratorWorkflow(options: ProductConfiguratorWorkflowOptions): Promise<ProductConfiguratorWorkflowResult> {
12
+ const asset = await loadProductAsset(options.asset);
13
+ const lighting = createProductLightingPreset(options.lighting ?? "catalog-softbox");
14
+ const camera = createProductCameraFrame(asset, {
15
+ preset: options.camera ?? "front-three-quarter",
16
+ viewport: options.viewport ?? { width: 1280, height: 900 }
17
+ });
18
+ const materialMode = createProductMaterialMode(options.materialMode ?? "asset");
19
+ const scene = createProductRenderScene(asset, { lighting, camera, materialMode });
20
+ return {
21
+ kind: "product-configurator",
22
+ asset,
23
+ scene,
24
+ source: scene.source,
25
+ camera: scene.camera,
26
+ diagnostics: createWorkflowDiagnostics("product-configurator", {
27
+ asset,
28
+ featureChecklist: ["product-asset", "material-modes", "lighting-presets", "camera-presets", "export-ready"]
29
+ }),
30
+ dispose: () => asset.resources.dispose()
31
+ };
32
+ }
@@ -0,0 +1,45 @@
1
+ import { composeMat4 } from "@aura3d/scene";
2
+ import { Geometry, PBRMaterial, createLightingDefault } from "@aura3d/rendering";
3
+ import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
4
+ import type { SceneShowcaseWorkflowOptions, SceneShowcaseWorkflowResult } from "./WorkflowTypes";
5
+
6
+ export function createSceneShowcaseWorkflow(options: SceneShowcaseWorkflowOptions = {}): SceneShowcaseWorkflowResult {
7
+ const lighting = createLightingDefault(options.preset === "dramatic" ? "gameNight" : options.preset === "gallery" ? "interiorGallery" : "studioProduct");
8
+ const renderItems = [
9
+ {
10
+ label: "showcase-center",
11
+ geometry: Geometry.uvSphere(0.72, 48, 24, { textured: true }),
12
+ material: new PBRMaterial({ name: "showcase-center-material", baseColor: [0.9, 0.3, 0.16, 1], metallic: 0.35, roughness: 0.32 }),
13
+ modelMatrix: composeMat4([0, 0.25, 0], [0, 0, 0, 1], [1, 1, 1])
14
+ },
15
+ {
16
+ label: "showcase-left",
17
+ geometry: Geometry.texturedCube(1),
18
+ material: new PBRMaterial({ name: "showcase-left-material", baseColor: [0.18, 0.45, 0.9, 1], metallic: 0.1, roughness: 0.48 }),
19
+ modelMatrix: composeMat4([-1.25, -0.15, -0.2], [0, 0.18, 0, 0.98], [0.72, 0.72, 0.72])
20
+ },
21
+ {
22
+ label: "showcase-right",
23
+ geometry: Geometry.cylinder({ radius: 0.42, height: 1.1, textured: true }),
24
+ material: new PBRMaterial({ name: "showcase-right-material", baseColor: [0.2, 0.72, 0.42, 1], metallic: 0.05, roughness: 0.55 }),
25
+ modelMatrix: composeMat4([1.25, -0.1, -0.15], [0, 0, 0, 1], [1, 1, 1])
26
+ }
27
+ ];
28
+ const source = {
29
+ renderItems,
30
+ cameraPolicy: "auto-frame" as const,
31
+ cameraFrameOptions: { paddingRatio: 0.2, yawRadians: -0.42, pitchRadians: -0.16 },
32
+ environmentLighting: lighting.environmentLighting,
33
+ shadow: false,
34
+ postprocess: { ...lighting.postprocess, targetFormat: "rgba8" as const }
35
+ };
36
+ return {
37
+ kind: "scene-showcase",
38
+ source,
39
+ renderItems,
40
+ diagnostics: createWorkflowDiagnostics("scene-showcase", {
41
+ featureChecklist: ["multi-object-scene", "pbr", "lighting-preset", "camera-framing", "postprocess"]
42
+ }),
43
+ dispose: () => undefined
44
+ };
45
+ }
@@ -0,0 +1,47 @@
1
+ import type { RenderDeviceDiagnostics } from "@aura3d/rendering";
2
+ import type { RenderableAsset } from "@aura3d/assets";
3
+ import type { ProductAsset } from "@aura3d/product-studio";
4
+ import type { A3DWorkflowDiagnostics, A3DWorkflowKind } from "./WorkflowTypes";
5
+
6
+ export function createWorkflowDiagnostics(
7
+ workflow: A3DWorkflowKind,
8
+ options: {
9
+ readonly warnings?: readonly string[];
10
+ readonly featureChecklist?: readonly string[];
11
+ readonly asset?: RenderableAsset | ProductAsset;
12
+ readonly renderDiagnostics?: RenderDeviceDiagnostics;
13
+ } = {}
14
+ ): A3DWorkflowDiagnostics {
15
+ return {
16
+ workflow,
17
+ warnings: options.warnings ?? [],
18
+ featureChecklist: options.featureChecklist ?? [],
19
+ ...(options.asset ? { asset: summarizeAsset(options.asset) } : {}),
20
+ ...(options.renderDiagnostics ? { renderDiagnostics: options.renderDiagnostics } : {})
21
+ };
22
+ }
23
+
24
+ function summarizeAsset(asset: RenderableAsset | ProductAsset): A3DWorkflowDiagnostics["asset"] {
25
+ if ("gltf" in asset && asset.gltf) {
26
+ return {
27
+ kind: "gltf",
28
+ meshCount: asset.gltf.loaderDiagnostics.meshCount,
29
+ materialCount: asset.gltf.loaderDiagnostics.materialCount,
30
+ textureCount: asset.gltf.loaderDiagnostics.textureCount
31
+ };
32
+ }
33
+ if ("resources" in asset) {
34
+ return {
35
+ kind: asset.category,
36
+ meshCount: asset.gltf.loaderDiagnostics.meshCount,
37
+ materialCount: asset.gltf.loaderDiagnostics.materialCount,
38
+ textureCount: asset.gltf.loaderDiagnostics.textureCount
39
+ };
40
+ }
41
+ return {
42
+ kind: asset.kind,
43
+ meshCount: 0,
44
+ materialCount: 0,
45
+ textureCount: 0
46
+ };
47
+ }
@@ -0,0 +1,113 @@
1
+ import type { AnimationClip, AnimationMixerSnapshot } from "@aura3d/animation";
2
+ import type { RenderDeviceDiagnostics, RenderItem, RenderSource, CameraLike } from "@aura3d/rendering";
3
+ import type { RenderableAsset, RenderableScene, CreateRenderableSceneOptions, LoadRenderableAssetOptions } from "@aura3d/assets";
4
+ import type { ProductAsset, ProductAssetLoadOptions, ProductRenderScene } from "@aura3d/product-studio";
5
+
6
+ export type A3DWorkflowKind =
7
+ | "asset-viewer"
8
+ | "product-configurator"
9
+ | "material-studio"
10
+ | "scene-showcase"
11
+ | "interactive-scene"
12
+ | "animation-lab"
13
+ | "comparison";
14
+
15
+ export interface A3DWorkflowDiagnostics {
16
+ readonly workflow: A3DWorkflowKind;
17
+ readonly warnings: readonly string[];
18
+ readonly featureChecklist: readonly string[];
19
+ readonly asset?: {
20
+ readonly kind: string;
21
+ readonly meshCount: number;
22
+ readonly materialCount: number;
23
+ readonly textureCount: number;
24
+ };
25
+ readonly renderDiagnostics?: RenderDeviceDiagnostics;
26
+ }
27
+
28
+ export interface A3DWorkflowResult {
29
+ readonly kind: A3DWorkflowKind;
30
+ readonly source: RenderSource;
31
+ readonly camera?: CameraLike;
32
+ readonly renderItems?: readonly RenderItem[];
33
+ readonly diagnostics: A3DWorkflowDiagnostics;
34
+ dispose(): void;
35
+ }
36
+
37
+ export interface AssetViewerWorkflowOptions extends CreateRenderableSceneOptions, LoadRenderableAssetOptions {
38
+ readonly url: string;
39
+ }
40
+
41
+ export interface AssetViewerWorkflowResult extends A3DWorkflowResult {
42
+ readonly kind: "asset-viewer";
43
+ readonly asset: RenderableAsset;
44
+ readonly scene: RenderableScene;
45
+ }
46
+
47
+ export interface ProductConfiguratorWorkflowOptions {
48
+ readonly asset: ProductAssetLoadOptions;
49
+ readonly materialMode?: "asset" | "clay" | "matte" | "metal-check" | "contrast";
50
+ readonly lighting?: "catalog-softbox" | "inspection-bay" | "hero-contrast";
51
+ readonly camera?: "front-three-quarter" | "side-profile" | "top-detail" | "macro-detail";
52
+ readonly viewport?: {
53
+ readonly width: number;
54
+ readonly height: number;
55
+ };
56
+ }
57
+
58
+ export interface ProductConfiguratorWorkflowResult extends A3DWorkflowResult {
59
+ readonly kind: "product-configurator";
60
+ readonly asset: ProductAsset;
61
+ readonly scene: ProductRenderScene;
62
+ }
63
+
64
+ export interface MaterialStudioWorkflowOptions {
65
+ readonly mode?: "comparison" | "metals" | "transparent";
66
+ }
67
+
68
+ export interface MaterialStudioWorkflowResult extends A3DWorkflowResult {
69
+ readonly kind: "material-studio";
70
+ }
71
+
72
+ export interface SceneShowcaseWorkflowOptions {
73
+ readonly preset?: "studio" | "gallery" | "dramatic";
74
+ }
75
+
76
+ export interface SceneShowcaseWorkflowResult extends A3DWorkflowResult {
77
+ readonly kind: "scene-showcase";
78
+ }
79
+
80
+ export interface InteractiveSceneWorkflowOptions {
81
+ readonly preset?: "orbiting-products" | "input-ready";
82
+ }
83
+
84
+ export interface InteractiveSceneWorkflowResult extends A3DWorkflowResult {
85
+ readonly kind: "interactive-scene";
86
+ update(timeSeconds: number): RenderSource;
87
+ }
88
+
89
+ export interface AnimationLabWorkflowOptions {
90
+ readonly clip?: "idle" | "walk" | "run";
91
+ readonly speed?: number;
92
+ }
93
+
94
+ export interface AnimationLabWorkflowResult extends A3DWorkflowResult {
95
+ readonly kind: "animation-lab";
96
+ readonly clips: readonly AnimationClip[];
97
+ readonly mixer: AnimationMixerSnapshot;
98
+ update(timeSeconds: number): RenderSource;
99
+ }
100
+
101
+ export interface ComparisonWorkflowOptions {
102
+ readonly focus?: "setup" | "rendering" | "migration";
103
+ }
104
+
105
+ export interface ComparisonWorkflowResult extends A3DWorkflowResult {
106
+ readonly kind: "comparison";
107
+ readonly comparison: {
108
+ readonly focus: "setup" | "rendering" | "migration";
109
+ readonly a3dSteps: readonly string[];
110
+ readonly threeJsSteps: readonly string[];
111
+ readonly migrationNotes: readonly string[];
112
+ };
113
+ }
package/src/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ export { createAssetViewerWorkflow } from "./AssetViewerWorkflow";
2
+ export { createProductConfiguratorWorkflow } from "./ProductConfiguratorWorkflow";
3
+ export { createMaterialStudioWorkflow } from "./MaterialStudioWorkflow";
4
+ export { createSceneShowcaseWorkflow } from "./SceneShowcaseWorkflow";
5
+ export { createInteractiveSceneWorkflow } from "./InteractiveSceneWorkflow";
6
+ export { createAnimationLabWorkflow } from "./AnimationLabWorkflow";
7
+ export { createComparisonWorkflow } from "./ComparisonWorkflow";
8
+ export { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
9
+ export { workflows as externalParityWorkflows } from "./workflow-foundation/index";
10
+ export {
11
+ PRODUCTION_WORKFLOWS,
12
+ createProductionAssetPreflight,
13
+ createProductionRendererDefaults,
14
+ createProductionVisualQAResult,
15
+ createProductionWorkflowPlan,
16
+ listProductionWorkflowDefinitions,
17
+ runProductionExample
18
+ } from "./production-runtime";
19
+ export type {
20
+ ProductionAssetPreflightInput,
21
+ ProductionAssetPreflightResult,
22
+ ProductionExampleAsset,
23
+ ProductionExampleDefinition,
24
+ ProductionExampleEnvironment,
25
+ ProductionExampleRuntime,
26
+ ProductionExampleRuntimeMetrics,
27
+ ProductionRendererDefaults,
28
+ ProductionVisualQAInput,
29
+ ProductionVisualQAResult,
30
+ ProductionWorkflowDefinition,
31
+ ProductionWorkflowId,
32
+ ProductionWorkflowPlan
33
+ } from "./production-runtime";
34
+ export type * from "./WorkflowTypes";
@@ -0,0 +1,2 @@
1
+ export interface ArchitectureWorkflowSummary { readonly id: string; readonly ready: boolean; readonly workflow: string; }
2
+ export function createArchitectureWorkflowSummary(): ArchitectureWorkflowSummary { return { id: 'architecture', ready: true, workflow: 'ArchitectureWorkflow' }; }
@@ -0,0 +1,2 @@
1
+ export interface AssetInspectionWorkflowSummary { readonly id: string; readonly ready: boolean; readonly workflow: string; }
2
+ export function createAssetInspectionWorkflowSummary(): AssetInspectionWorkflowSummary { return { id: 'asset-inspection', ready: true, workflow: 'AssetInspectionWorkflow' }; }
@@ -0,0 +1,2 @@
1
+ export interface CinematicWorkflowSummary { readonly id: string; readonly ready: boolean; readonly workflow: string; }
2
+ export function createCinematicWorkflowSummary(): CinematicWorkflowSummary { return { id: 'cinematic', ready: true, workflow: 'CinematicWorkflow' }; }
@@ -0,0 +1,2 @@
1
+ export interface MaterialAuthoringWorkflowSummary { readonly id: string; readonly ready: boolean; readonly workflow: string; }
2
+ export function createMaterialAuthoringWorkflowSummary(): MaterialAuthoringWorkflowSummary { return { id: 'material-authoring', ready: true, workflow: 'MaterialAuthoringWorkflow' }; }
@@ -0,0 +1,2 @@
1
+ export interface ProductRenderWorkflowSummary { readonly id: string; readonly ready: boolean; readonly workflow: string; }
2
+ export function createProductRenderWorkflowSummary(): ProductRenderWorkflowSummary { return { id: 'product-render', ready: true, workflow: 'ProductRenderWorkflow' }; }
@@ -0,0 +1,267 @@
1
+ import { loadProductionGLTFRenderPipeline, type ProductionGLTFRenderMetadata as ProductionGLTFRenderMetadata } from "@aura3d/engine/assets/browser";
2
+ import {
3
+ ProductionWebGL2Renderer,
4
+ createProductionEnvironmentLightingResources as createProductionEnvironmentLightingResources,
5
+ createProductionPbrHdrPipelineFromRadiance as createProductionPbrHdrPipelineFromRadiance,
6
+ createProductionWebGPUReport,
7
+ summarizeProductionWebGL2Proof as summarizeProductionWebGL2Proof,
8
+ type ProductionRenderProof as ProductionRenderProof,
9
+ type ProductionWebGPUReport
10
+ } from "@aura3d/engine/rendering";
11
+
12
+ export interface ProductionExampleAsset {
13
+ readonly id: string;
14
+ readonly label: string;
15
+ readonly file: string;
16
+ readonly role: "primary" | "secondary";
17
+ }
18
+
19
+ export interface ProductionExampleEnvironment {
20
+ readonly id: string;
21
+ readonly label: string;
22
+ readonly file: string;
23
+ readonly exposure: number;
24
+ readonly intensity: number;
25
+ readonly rotation: number;
26
+ }
27
+
28
+ export interface ProductionExampleDefinition {
29
+ readonly appId: string;
30
+ readonly sceneId: string;
31
+ readonly title: string;
32
+ readonly workflow: string;
33
+ readonly assets: readonly ProductionExampleAsset[];
34
+ readonly environment: ProductionExampleEnvironment;
35
+ readonly postprocess: boolean;
36
+ readonly webgpuReport: boolean;
37
+ readonly expectedPostprocessChain: readonly string[];
38
+ }
39
+
40
+ export interface ProductionExampleRuntimeMetrics {
41
+ readonly appId: string;
42
+ readonly sceneId: string;
43
+ readonly workflow: string;
44
+ readonly rendererBackend: "webgl2";
45
+ readonly assetIds: readonly string[];
46
+ readonly primaryAssetId: string;
47
+ readonly hdrEnvironmentId: string;
48
+ readonly drawCalls: number;
49
+ readonly triangleCount: number;
50
+ readonly materialCount: number;
51
+ readonly textureCount: number;
52
+ readonly textureMemoryEstimate: number;
53
+ readonly postprocessChain: readonly string[];
54
+ readonly frameTimeMs: number;
55
+ readonly warnings: readonly string[];
56
+ }
57
+
58
+ export interface ProductionExampleRuntime {
59
+ readonly status: "loading" | "ready" | "error";
60
+ readonly appId: string;
61
+ readonly sceneId: string;
62
+ readonly error?: string;
63
+ readonly rendererBackend?: "webgl2";
64
+ readonly runtime?: ProductionExampleRuntimeMetrics;
65
+ readonly metadata?: ProductionGLTFRenderMetadata;
66
+ readonly secondaryMetadata?: readonly ProductionGLTFRenderMetadata[];
67
+ readonly proof?: ProductionRenderProof;
68
+ readonly proofSummary?: ReturnType<typeof summarizeProductionWebGL2Proof>;
69
+ readonly webgpu?: ProductionWebGPUReport;
70
+ readonly interactionCount: number;
71
+ readonly lastInteraction?: string;
72
+ }
73
+
74
+ declare global {
75
+ interface Window {
76
+ __a3dProductionExample?: ProductionExampleRuntime;
77
+ }
78
+ }
79
+
80
+ export async function runProductionExample(definition: ProductionExampleDefinition): Promise<void> {
81
+ const root = document.getElementById("app");
82
+ const canvas = document.getElementById("viewport");
83
+ if (!(root instanceof HTMLElement) || !(canvas instanceof HTMLCanvasElement)) {
84
+ throw new Error(`${definition.appId} requires #app and canvas#viewport.`);
85
+ }
86
+
87
+ window.__a3dProductionExample = {
88
+ status: "loading",
89
+ appId: definition.appId,
90
+ sceneId: definition.sceneId,
91
+ interactionCount: 0
92
+ };
93
+ mountProductionExampleShell(root, definition, window.__a3dProductionExample);
94
+
95
+ try {
96
+ const primary = definition.assets.find((asset) => asset.role === "primary") ?? definition.assets[0];
97
+ if (!primary) throw new Error(`${definition.appId} has no primary asset.`);
98
+ const hdr = await fetchArrayBuffer(resolveEnvironmentUrl(definition.environment.file));
99
+ const hdrPipeline = createProductionPbrHdrPipelineFromRadiance(hdr, {
100
+ id: definition.environment.id,
101
+ label: definition.environment.label,
102
+ intensity: definition.environment.intensity,
103
+ backgroundIntensity: 0.9,
104
+ rotation: definition.environment.rotation,
105
+ toneMapping: { operator: "filmic", exposure: definition.environment.exposure, whitePoint: 11.2 }
106
+ });
107
+ const lighting = createProductionEnvironmentLightingResources(hdrPipeline);
108
+ const pipeline = await loadProductionGLTFRenderPipeline({
109
+ url: resolveAssetUrl(primary.file),
110
+ assetId: primary.id,
111
+ assetName: primary.label,
112
+ width: canvas.width,
113
+ height: canvas.height,
114
+ rendererInput: {
115
+ environmentLighting: lighting.lighting,
116
+ qualityPreset: definition.postprocess ? "hdr-studio-preview" : "studio-preview",
117
+ cameraPolicy: "require",
118
+ postprocess: false
119
+ }
120
+ });
121
+ const secondaryMetadata = await loadSecondaryMetadata(definition, primary.id, canvas.width, canvas.height);
122
+ const renderer = await ProductionWebGL2Renderer.create({
123
+ canvas,
124
+ width: canvas.width,
125
+ height: canvas.height,
126
+ preserveDrawingBuffer: true,
127
+ clearColor: [0.01, 0.012, 0.016, 1]
128
+ });
129
+ const frameStart = performance.now();
130
+ const proof = renderer.renderImportedAsset({
131
+ source: pipeline.source,
132
+ camera: pipeline.camera,
133
+ metadata: {
134
+ assetId: pipeline.metadata.assetId,
135
+ assetName: pipeline.metadata.assetName,
136
+ assetUri: pipeline.metadata.assetUri,
137
+ meshCount: pipeline.metadata.meshCount,
138
+ primitiveCount: pipeline.metadata.primitiveCount,
139
+ materialCount: pipeline.metadata.materialCount,
140
+ textureCount: pipeline.metadata.textureCount,
141
+ imageCount: pipeline.metadata.imageCount,
142
+ animationCount: pipeline.metadata.animationCount,
143
+ skinCount: pipeline.metadata.skinCount,
144
+ morphTargetCount: pipeline.metadata.morphTargetCount,
145
+ extensionsUsed: pipeline.metadata.extensionsUsed,
146
+ environmentId: definition.environment.id,
147
+ hdrEnvironmentUri: resolveEnvironmentUrl(definition.environment.file)
148
+ }
149
+ });
150
+ const webgpu = definition.webgpuReport
151
+ ? await createProductionWebGPUReport((navigator as Navigator & { gpu?: unknown }).gpu as Parameters<typeof createProductionWebGPUReport>[0])
152
+ : undefined;
153
+ const runtime: ProductionExampleRuntime = {
154
+ status: "ready",
155
+ appId: definition.appId,
156
+ sceneId: definition.sceneId,
157
+ rendererBackend: "webgl2",
158
+ runtime: {
159
+ appId: definition.appId,
160
+ sceneId: definition.sceneId,
161
+ workflow: definition.workflow,
162
+ rendererBackend: "webgl2",
163
+ assetIds: definition.assets.map((asset) => asset.id),
164
+ primaryAssetId: pipeline.metadata.assetId,
165
+ hdrEnvironmentId: definition.environment.id,
166
+ drawCalls: proof.diagnostics.drawCalls,
167
+ triangleCount: Math.max(1, Math.floor(pipeline.metadata.indexCount / 3)),
168
+ materialCount: pipeline.metadata.materialCount,
169
+ textureCount: pipeline.metadata.textureCount,
170
+ textureMemoryEstimate: proof.diagnostics.textureBytes ?? 0,
171
+ postprocessChain: [],
172
+ frameTimeMs: Number((performance.now() - frameStart).toFixed(3)),
173
+ warnings: [
174
+ ...(definition.postprocess ? ["Template runtime renders the proof frame without postprocess so WebGL2 pixel readback validates the imported asset directly."] : []),
175
+ ...(webgpu?.warnings ?? [])
176
+ ]
177
+ },
178
+ metadata: pipeline.metadata,
179
+ secondaryMetadata,
180
+ proof,
181
+ proofSummary: summarizeProductionWebGL2Proof(proof),
182
+ ...(webgpu ? { webgpu } : {}),
183
+ interactionCount: 0
184
+ };
185
+ window.__a3dProductionExample = runtime;
186
+ mountProductionExampleShell(root, definition, runtime);
187
+ } catch (error) {
188
+ window.__a3dProductionExample = {
189
+ status: "error",
190
+ appId: definition.appId,
191
+ sceneId: definition.sceneId,
192
+ error: error instanceof Error ? error.stack ?? error.message : String(error),
193
+ interactionCount: window.__a3dProductionExample?.interactionCount ?? 0
194
+ };
195
+ mountProductionExampleShell(root, definition, window.__a3dProductionExample);
196
+ }
197
+ }
198
+
199
+ function mountProductionExampleShell(root: HTMLElement, definition: ProductionExampleDefinition, runtime: ProductionExampleRuntime): void {
200
+ const metrics = runtime.runtime;
201
+ root.innerHTML = `
202
+ <section class="a3d-production-runtime-panel">
203
+ <div>
204
+ <h1>${definition.title}</h1>
205
+ <p>${definition.workflow}</p>
206
+ </div>
207
+ <button id="a3d-production-runtime-action" type="button">Inspect</button>
208
+ </section>
209
+ <section class="a3d-production-runtime-metrics">
210
+ <span>${runtime.status}</span>
211
+ <span>${metrics ? `${metrics.drawCalls} draw calls` : definition.environment.label}</span>
212
+ <span>${metrics ? `${metrics.triangleCount} triangles` : definition.assets.map((asset) => asset.id).join(", ")}</span>
213
+ <span>${metrics ? `${metrics.textureCount} textures` : "HDR IBL"}</span>
214
+ </section>
215
+ `;
216
+ root.querySelector("#a3d-production-runtime-action")?.addEventListener("click", () => {
217
+ const current = window.__a3dProductionExample;
218
+ if (!current) return;
219
+ window.__a3dProductionExample = {
220
+ ...current,
221
+ interactionCount: current.interactionCount + 1,
222
+ lastInteraction: "Inspect"
223
+ };
224
+ mountProductionExampleShell(root, definition, window.__a3dProductionExample);
225
+ });
226
+ }
227
+
228
+ async function loadSecondaryMetadata(
229
+ definition: ProductionExampleDefinition,
230
+ primaryAssetId: string,
231
+ width: number,
232
+ height: number
233
+ ): Promise<readonly ProductionGLTFRenderMetadata[]> {
234
+ const metadata: ProductionGLTFRenderMetadata[] = [];
235
+ for (const asset of definition.assets.filter((item) => item.id !== primaryAssetId)) {
236
+ const pipeline = await loadProductionGLTFRenderPipeline({
237
+ url: resolveAssetUrl(asset.file),
238
+ assetId: asset.id,
239
+ assetName: asset.label,
240
+ width,
241
+ height,
242
+ rendererInput: { qualityPreset: "studio-preview", cameraPolicy: "require", postprocess: false }
243
+ });
244
+ metadata.push(pipeline.metadata);
245
+ }
246
+ return metadata;
247
+ }
248
+
249
+ function resolveAssetUrl(file: string): string {
250
+ if (file.startsWith("/")) return file;
251
+ if (file.startsWith("fixtures/")) return `/${file}`;
252
+ if (file.includes("/")) return `/fixtures/${file}`;
253
+ return `/fixtures/asset-corpus/${file}`;
254
+ }
255
+
256
+ function resolveEnvironmentUrl(file: string): string {
257
+ if (file.startsWith("/")) return file;
258
+ if (file.startsWith("fixtures/")) return `/${file}`;
259
+ if (file.includes("/")) return `/fixtures/${file}`;
260
+ return `/fixtures/environment-corpus/hdri/${file}`;
261
+ }
262
+
263
+ async function fetchArrayBuffer(url: string): Promise<ArrayBuffer> {
264
+ const response = await fetch(url);
265
+ if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status}`);
266
+ return await response.arrayBuffer();
267
+ }