@aura3d/workflows 1.4.0 → 1.4.1

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 ADDED
@@ -0,0 +1,28 @@
1
+ # @aura3d/workflows
2
+
3
+ `@aura3d/workflows` owns asset viewer, product configurator, material studio,
4
+ scene showcase, interactive scene, animation lab, comparison, and production
5
+ workflow helpers for Aura3D.
6
+
7
+ ## Public API
8
+
9
+ - `createAssetViewerWorkflow`
10
+ - `createProductConfiguratorWorkflow`
11
+ - `createMaterialStudioWorkflow`
12
+ - `createSceneShowcaseWorkflow`
13
+ - `createInteractiveSceneWorkflow`
14
+ - `createAnimationLabWorkflow`
15
+ - `createComparisonWorkflow`
16
+ - `createWorkflowDiagnostics`
17
+ - `externalParityWorkflows`
18
+
19
+ Production-runtime helpers include `PRODUCTION_WORKFLOWS`,
20
+ `createProductionWorkflowPlan`, `runProductionExample`,
21
+ `createProductionAssetPreflight`, `createProductionRendererDefaults`, and
22
+ `createProductionVisualQAResult`.
23
+
24
+ ## Package Boundary
25
+
26
+ This package creates workflow data and production-runtime workflow plans. Public
27
+ claims still need matching evidence for the workflow output and the renderer/API
28
+ path used to display it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aura3d/workflows",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -11,12 +11,43 @@
11
11
  "main": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts",
13
13
  "dependencies": {
14
- "@aura3d/animation": "1.4.0",
15
- "@aura3d/assets": "1.4.0",
16
- "@aura3d/product-studio": "1.4.0",
17
- "@aura3d/engine": "1.4.0",
18
- "@aura3d/rendering": "1.4.0",
19
- "@aura3d/scene": "1.4.0"
14
+ "@aura3d/animation": "1.4.1",
15
+ "@aura3d/assets": "1.4.1",
16
+ "@aura3d/engine": "1.4.1",
17
+ "@aura3d/product-studio": "1.4.1",
18
+ "@aura3d/scene": "1.4.1",
19
+ "@aura3d/rendering": "1.4.1"
20
20
  },
21
- "sideEffects": false
21
+ "sideEffects": false,
22
+ "description": "Asset viewer, product configurator, material studio, scene showcase, interactive scene, animation lab, comparison, and production workflow helpers for Aura3D.",
23
+ "keywords": [
24
+ "aura3d",
25
+ "3d",
26
+ "browser-3d",
27
+ "web-3d",
28
+ "typescript",
29
+ "workflows",
30
+ "asset-viewer",
31
+ "product-configurator",
32
+ "material-studio",
33
+ "scene-showcase",
34
+ "production-runtime"
35
+ ],
36
+ "homepage": "https://aura3d.auraone.ai",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/auraoneai/aura3d.git",
40
+ "directory": "packages/workflows"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/auraoneai/aura3d/issues"
44
+ },
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "files": [
50
+ "README.md",
51
+ "dist"
52
+ ]
22
53
  }
@@ -1,165 +0,0 @@
1
- import { AnimationClip, AnimationTrack } from "@aura3d/animation";
2
- import { composeMat4 } from "@aura3d/scene";
3
- import { Geometry, PBRMaterial, createLightingDefault } from "@aura3d/rendering";
4
- import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
5
- import type { AnimationLabWorkflowOptions, AnimationLabWorkflowResult } from "./WorkflowTypes";
6
-
7
- type Vec3 = readonly [number, number, number];
8
-
9
- export function createAnimationLabWorkflow(options: AnimationLabWorkflowOptions = {}): AnimationLabWorkflowResult {
10
- const speed = Number.isFinite(options.speed) && options.speed !== undefined && options.speed > 0 ? options.speed : 1;
11
- const clips = createAnimationClips();
12
- const activeClip = clips.find((clip) => clip.name === (options.clip ?? "walk")) ?? clips[1]!;
13
- const lighting = createLightingDefault("studioProduct");
14
- const material = new PBRMaterial({ name: "animation-lab-character", baseColor: [0.72, 0.55, 0.32, 1], metallic: 0.18, roughness: 0.42 });
15
- const accent = new PBRMaterial({ name: "animation-lab-accent", baseColor: [0.18, 0.3, 0.28, 1], metallic: 0.08, roughness: 0.5 });
16
-
17
- const sample = (timeSeconds: number) => {
18
- const t = wrapTime(timeSeconds * speed, activeClip.duration);
19
- const root = sampleVec3(activeClip, "root.position", t);
20
- const armSwing = sampleScalar(activeClip, "arms.swing", t);
21
- const legSwing = sampleScalar(activeClip, "legs.swing", t);
22
- const headBob = sampleScalar(activeClip, "head.bob", t);
23
- const renderItems = [
24
- {
25
- label: "animation-lab-body",
26
- geometry: Geometry.capsule({ radius: 0.34, height: 1.25, segments: 24, rings: 12, textured: true }),
27
- material,
28
- modelMatrix: composeMat4([root[0], 0.78 + root[1], root[2]], [0, 0, 0, 1], [1.05, 1, 0.85])
29
- },
30
- {
31
- label: "animation-lab-head",
32
- geometry: Geometry.uvSphere(0.28, 32, 16, { textured: true }),
33
- material,
34
- modelMatrix: composeMat4([root[0], 1.66 + headBob, root[2]], [0, 0, 0, 1], [1, 1, 1])
35
- },
36
- {
37
- label: "animation-lab-left-arm",
38
- geometry: Geometry.cylinder({ radius: 0.09, height: 0.78, segments: 18, textured: true }),
39
- material: accent,
40
- modelMatrix: composeMat4([root[0] - 0.44, 0.9, root[2] + armSwing], [0.18, 0, 0, 0.98], [1, 1, 1])
41
- },
42
- {
43
- label: "animation-lab-right-arm",
44
- geometry: Geometry.cylinder({ radius: 0.09, height: 0.78, segments: 18, textured: true }),
45
- material: accent,
46
- modelMatrix: composeMat4([root[0] + 0.44, 0.9, root[2] - armSwing], [-0.18, 0, 0, 0.98], [1, 1, 1])
47
- },
48
- {
49
- label: "animation-lab-left-leg",
50
- geometry: Geometry.cylinder({ radius: 0.12, height: 0.78, segments: 18, textured: true }),
51
- material: accent,
52
- modelMatrix: composeMat4([root[0] - 0.18, 0.18, root[2] - legSwing], [0.12, 0, 0, 0.99], [1, 1, 1])
53
- },
54
- {
55
- label: "animation-lab-right-leg",
56
- geometry: Geometry.cylinder({ radius: 0.12, height: 0.78, segments: 18, textured: true }),
57
- material: accent,
58
- modelMatrix: composeMat4([root[0] + 0.18, 0.18, root[2] + legSwing], [-0.12, 0, 0, 0.99], [1, 1, 1])
59
- }
60
- ];
61
- return {
62
- renderItems,
63
- cameraPolicy: "auto-frame" as const,
64
- cameraFrameOptions: { paddingRatio: 0.24, yawRadians: -0.28, pitchRadians: -0.08 },
65
- environmentLighting: lighting.environmentLighting,
66
- shadow: false,
67
- postprocess: { ...lighting.postprocess, targetFormat: "rgba8" as const }
68
- };
69
- };
70
-
71
- const source = sample(0);
72
- return {
73
- kind: "animation-lab",
74
- source,
75
- renderItems: source.renderItems,
76
- clips,
77
- mixer: {
78
- timeScale: speed,
79
- actionCount: 1,
80
- actions: [{ clipName: activeClip.name, duration: activeClip.duration, time: 0, weight: 1, timeScale: speed, playing: true, paused: false, loopMode: "repeat" }],
81
- layers: [],
82
- values: {
83
- "root.position": [0, 0, 0],
84
- "arms.swing": 0,
85
- "legs.swing": 0,
86
- "head.bob": 0
87
- },
88
- applyErrors: []
89
- },
90
- diagnostics: createWorkflowDiagnostics("animation-lab", {
91
- featureChecklist: ["keyframe-clips", "clip-switching", "animated-transforms", "timeline-sampling", "camera-framing", "postprocess"]
92
- }),
93
- update: sample,
94
- dispose: () => undefined
95
- };
96
- }
97
-
98
- function createAnimationClips(): readonly AnimationClip[] {
99
- return [
100
- createClip("idle", 1.6, [0, 0, 0], 0.05, 0.02, 0.03),
101
- createClip("walk", 1.2, [0, 0, 0.1], 0.24, 0.22, 0.06),
102
- createClip("run", 0.8, [0, 0, 0.16], 0.34, 0.32, 0.09)
103
- ];
104
- }
105
-
106
- function createClip(name: string, duration: number, rootEnd: Vec3, arm: number, leg: number, bob: number): AnimationClip {
107
- return new AnimationClip({
108
- name,
109
- duration,
110
- tracks: [
111
- new AnimationTrack<Vec3>({
112
- target: "root.position",
113
- valueType: "vector3",
114
- keyframes: [
115
- { time: 0, value: [0, 0, 0] },
116
- { time: duration / 2, value: [rootEnd[0], bob, rootEnd[2] / 2] },
117
- { time: duration, value: rootEnd }
118
- ]
119
- }),
120
- new AnimationTrack<number>({
121
- target: "arms.swing",
122
- valueType: "scalar",
123
- keyframes: [
124
- { time: 0, value: -arm },
125
- { time: duration / 2, value: arm },
126
- { time: duration, value: -arm }
127
- ]
128
- }),
129
- new AnimationTrack<number>({
130
- target: "legs.swing",
131
- valueType: "scalar",
132
- keyframes: [
133
- { time: 0, value: leg },
134
- { time: duration / 2, value: -leg },
135
- { time: duration, value: leg }
136
- ]
137
- }),
138
- new AnimationTrack<number>({
139
- target: "head.bob",
140
- valueType: "scalar",
141
- keyframes: [
142
- { time: 0, value: 0 },
143
- { time: duration / 2, value: bob },
144
- { time: duration, value: 0 }
145
- ]
146
- })
147
- ]
148
- });
149
- }
150
-
151
- function sampleVec3(clip: AnimationClip, target: string, time: number): Vec3 {
152
- const value = clip.tracks.find((track) => track.target === target)?.sample(time);
153
- return Array.isArray(value) && value.length === 3 ? [value[0]!, value[1]!, value[2]!] : [0, 0, 0];
154
- }
155
-
156
- function sampleScalar(clip: AnimationClip, target: string, time: number): number {
157
- const value = clip.tracks.find((track) => track.target === target)?.sample(time);
158
- return typeof value === "number" ? value : 0;
159
- }
160
-
161
- function wrapTime(timeSeconds: number, duration: number): number {
162
- if (duration <= 0) return 0;
163
- const wrapped = timeSeconds % duration;
164
- return wrapped < 0 ? wrapped + duration : wrapped;
165
- }
@@ -1,37 +0,0 @@
1
- import { createRenderableScene, loadRenderableAsset } from "@aura3d/assets";
2
- import type { LoadRenderableAssetOptions } from "@aura3d/assets";
3
- import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
4
- import type { AssetViewerWorkflowOptions, AssetViewerWorkflowResult } from "./WorkflowTypes";
5
-
6
- export async function createAssetViewerWorkflow(options: AssetViewerWorkflowOptions): Promise<AssetViewerWorkflowResult> {
7
- const loadOptions: LoadRenderableAssetOptions = {
8
- baseUrl: options.baseUrl,
9
- signal: options.signal,
10
- dependencyChain: options.dependencyChain,
11
- manager: options.manager,
12
- type: options.type
13
- };
14
- const asset = await loadRenderableAsset(options.url, loadOptions);
15
- const scene = await createRenderableScene(asset, {
16
- camera: options.camera ?? "auto-frame",
17
- lighting: options.lighting ?? "studioProduct",
18
- shadows: options.shadows ?? true,
19
- postprocess: options.postprocess ?? "product-default",
20
- viewport: options.viewport,
21
- qualityPreset: options.qualityPreset,
22
- renderResources: options.renderResources
23
- });
24
- return {
25
- kind: "asset-viewer",
26
- asset,
27
- scene,
28
- source: scene.source,
29
- camera: scene.rendererInput?.camera,
30
- diagnostics: createWorkflowDiagnostics("asset-viewer", {
31
- asset,
32
- warnings: scene.warnings,
33
- featureChecklist: ["asset-loading", "render-resources", "auto-camera", "lighting", "shadows", "postprocess"]
34
- }),
35
- dispose: () => scene.dispose()
36
- };
37
- }
@@ -1,68 +0,0 @@
1
- import { composeMat4 } from "@aura3d/scene";
2
- import { Geometry, PBRMaterial, createLightingDefault } from "@aura3d/rendering";
3
- import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
4
- import type { ComparisonWorkflowOptions, ComparisonWorkflowResult } from "./WorkflowTypes";
5
-
6
- export function createComparisonWorkflow(options: ComparisonWorkflowOptions = {}): ComparisonWorkflowResult {
7
- const focus = options.focus ?? "setup";
8
- const lighting = createLightingDefault("studioProduct");
9
- const a3dMaterial = new PBRMaterial({ name: "comparison-a3d", baseColor: [0.12, 0.48, 0.82, 1], metallic: 0.1, roughness: 0.34 });
10
- const threeMaterial = new PBRMaterial({ name: "comparison-three-baseline", baseColor: [0.75, 0.75, 0.75, 1], metallic: 0.05, roughness: 0.5 });
11
- const migrationMaterial = new PBRMaterial({ name: "comparison-migration", baseColor: [0.88, 0.56, 0.2, 1], metallic: 0.18, roughness: 0.38 });
12
- const renderItems = [
13
- {
14
- label: "comparison-a3d-workflow",
15
- geometry: Geometry.uvSphere(0.48, 40, 20, { textured: true }),
16
- material: a3dMaterial,
17
- modelMatrix: composeMat4([-1.15, 0.05, 0], [0, 0, 0, 1], [1, 1, 1])
18
- },
19
- {
20
- label: "comparison-threejs-reference",
21
- geometry: Geometry.texturedCube(0.88),
22
- material: threeMaterial,
23
- modelMatrix: composeMat4([0, 0, 0], [0, 0.25, 0, 0.97], [1, 1, 1])
24
- },
25
- {
26
- label: "comparison-migration-output",
27
- geometry: Geometry.cylinder({ radius: 0.36, height: 1, segments: 32, textured: true }),
28
- material: migrationMaterial,
29
- modelMatrix: composeMat4([1.15, 0, 0], [0, 0, 0, 1], [1, 1, 1])
30
- }
31
- ];
32
- const comparison = {
33
- focus,
34
- a3dSteps: [
35
- "create workflow render source",
36
- "load optional asset/environment through public package APIs",
37
- "render through Renderer.create with diagnostics"
38
- ],
39
- threeJsSteps: [
40
- "create scene, camera, renderer, lights, controls, loader, material setup, and animation loop separately",
41
- "wire resource disposal and resize handling manually",
42
- "add diagnostics through user-land instrumentation"
43
- ],
44
- migrationNotes: [
45
- "A3D comparison workflow renders a A3D-owned parity scene; it does not embed Three.js at runtime.",
46
- "Use @aura3d/three-compat for adapter coverage and warnings when moving Three.js example code."
47
- ]
48
- };
49
- const source = {
50
- renderItems,
51
- cameraPolicy: "auto-frame" as const,
52
- cameraFrameOptions: { paddingRatio: 0.22, yawRadians: -0.36, pitchRadians: -0.1 },
53
- environmentLighting: lighting.environmentLighting,
54
- shadow: false,
55
- postprocess: { ...lighting.postprocess, targetFormat: "rgba8" as const }
56
- };
57
- return {
58
- kind: "comparison",
59
- source,
60
- renderItems,
61
- comparison,
62
- diagnostics: createWorkflowDiagnostics("comparison", {
63
- warnings: ["Comparison workflow summarizes migration/setup parity; same-scene Three.js visual superiority still requires benchmark reports."],
64
- featureChecklist: ["a3d-workflow-source", "threejs-setup-comparison", "migration-notes", "render-diagnostics", "postprocess"]
65
- }),
66
- dispose: () => undefined
67
- };
68
- }
@@ -1,44 +0,0 @@
1
- import { composeMat4 } from "@aura3d/scene";
2
- import { Geometry, PBRMaterial, createLightingDefault } from "@aura3d/rendering";
3
- import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
4
- import type { InteractiveSceneWorkflowOptions, InteractiveSceneWorkflowResult } from "./WorkflowTypes";
5
-
6
- export function createInteractiveSceneWorkflow(_options: InteractiveSceneWorkflowOptions = {}): InteractiveSceneWorkflowResult {
7
- const lighting = createLightingDefault("studioProduct");
8
- const material = new PBRMaterial({ name: "interactive-workflow-material", baseColor: [0.95, 0.62, 0.18, 1], metallic: 0.25, roughness: 0.36 });
9
- const createSource = (timeSeconds: number) => {
10
- const renderItems = [
11
- {
12
- label: "interactive-orbit-a",
13
- geometry: Geometry.uvSphere(0.42, 32, 16, { textured: true }),
14
- material,
15
- modelMatrix: composeMat4([Math.cos(timeSeconds) * 0.9, 0, Math.sin(timeSeconds) * 0.35], [0, 0, 0, 1], [1, 1, 1])
16
- },
17
- {
18
- label: "interactive-orbit-b",
19
- geometry: Geometry.texturedCube(1),
20
- material: new PBRMaterial({ name: "interactive-workflow-blue", baseColor: [0.2, 0.48, 0.92, 1], metallic: 0.1, roughness: 0.44 }),
21
- modelMatrix: composeMat4([Math.cos(timeSeconds + Math.PI) * 0.9, 0, Math.sin(timeSeconds + Math.PI) * 0.35], [0, 0, 0, 1], [0.62, 0.62, 0.62])
22
- }
23
- ];
24
- return {
25
- renderItems,
26
- cameraPolicy: "auto-frame" as const,
27
- cameraFrameOptions: { paddingRatio: 0.28, yawRadians: -0.5, pitchRadians: -0.14 },
28
- environmentLighting: lighting.environmentLighting,
29
- shadow: false,
30
- postprocess: { ...lighting.postprocess, targetFormat: "rgba8" as const }
31
- };
32
- };
33
- const source = createSource(0);
34
- return {
35
- kind: "interactive-scene",
36
- source,
37
- renderItems: source.renderItems,
38
- diagnostics: createWorkflowDiagnostics("interactive-scene", {
39
- featureChecklist: ["update-loop", "animated-transforms", "input-ready-scene", "camera-framing", "postprocess"]
40
- }),
41
- update: createSource,
42
- dispose: () => undefined
43
- };
44
- }
@@ -1,48 +0,0 @@
1
- import { composeMat4 } from "@aura3d/scene";
2
- import { Geometry, NormalMappedPBRMaterial, PBRMaterial, TexturedPBRMaterial, Texture, createLightingDefault } from "@aura3d/rendering";
3
- import { createWorkflowDiagnostics } from "./WorkflowDiagnostics";
4
- import type { MaterialStudioWorkflowOptions, MaterialStudioWorkflowResult } from "./WorkflowTypes";
5
-
6
- export function createMaterialStudioWorkflow(options: MaterialStudioWorkflowOptions = {}): MaterialStudioWorkflowResult {
7
- const white = new Texture({ width: 1, height: 1, colorSpace: "srgb", data: new Uint8Array([240, 236, 220, 255]) });
8
- const rough = new Texture({ width: 1, height: 1, colorSpace: "linear", data: new Uint8Array([255, 128, 255, 255]) });
9
- const normal = new Texture({ width: 1, height: 1, colorSpace: "linear", data: new Uint8Array([128, 128, 255, 255]) });
10
- const lighting = createLightingDefault(options.mode === "transparent" ? "interiorGallery" : "studioProduct");
11
- const renderItems = [
12
- {
13
- label: "workflow-polished-metal",
14
- geometry: Geometry.uvSphere(0.58, 48, 24, { textured: true }),
15
- material: new PBRMaterial({ name: "workflow-polished-metal", baseColor: [0.86, 0.82, 0.72, 1], metallic: 1, roughness: 0.18 }),
16
- modelMatrix: composeMat4([-1.25, 0, 0], [0, 0, 0, 1], [1, 1, 1])
17
- },
18
- {
19
- label: "workflow-textured-pbr",
20
- geometry: Geometry.uvSphere(0.58, 48, 24, { textured: true }),
21
- material: new TexturedPBRMaterial({ name: "workflow-textured-pbr", baseColorTexture: white, metallicRoughnessTexture: rough, normalTexture: normal, roughness: 0.45 }),
22
- modelMatrix: composeMat4([0, 0, 0], [0, 0, 0, 1], [1, 1, 1])
23
- },
24
- {
25
- label: "workflow-normal-material",
26
- geometry: Geometry.texturedCube(1),
27
- material: new NormalMappedPBRMaterial({ name: "workflow-normal-material", normalTexture: normal, baseColor: [0.28, 0.58, 0.9, 1], normalScale: 0.8 }),
28
- modelMatrix: composeMat4([1.25, 0, 0], [0, 0, 0, 1], [0.85, 0.85, 0.85])
29
- }
30
- ];
31
- const source = {
32
- renderItems,
33
- cameraPolicy: "auto-frame" as const,
34
- cameraFrameOptions: { paddingRatio: 0.18, yawRadians: -0.36, pitchRadians: -0.12 },
35
- environmentLighting: lighting.environmentLighting,
36
- shadow: false,
37
- postprocess: { ...lighting.postprocess, targetFormat: "rgba8" as const }
38
- };
39
- return {
40
- kind: "material-studio",
41
- source,
42
- renderItems,
43
- diagnostics: createWorkflowDiagnostics("material-studio", {
44
- featureChecklist: ["pbr", "textured-pbr", "normal-mapped-pbr", "material-comparison", "postprocess"]
45
- }),
46
- dispose: () => undefined
47
- };
48
- }
@@ -1,32 +0,0 @@
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
- }
@@ -1,45 +0,0 @@
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
- }
@@ -1,47 +0,0 @@
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
- }
@@ -1,113 +0,0 @@
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 DELETED
@@ -1,34 +0,0 @@
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";
@@ -1,2 +0,0 @@
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' }; }
@@ -1,2 +0,0 @@
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' }; }
@@ -1,2 +0,0 @@
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' }; }
@@ -1,2 +0,0 @@
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' }; }
@@ -1,2 +0,0 @@
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' }; }
@@ -1,267 +0,0 @@
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
- }
@@ -1,203 +0,0 @@
1
- export type ProductionWorkflowId = "product" | "asset" | "material" | "architecture" | "cinematic";
2
-
3
- export interface ProductionWorkflowDefinition {
4
- readonly id: ProductionWorkflowId;
5
- readonly label: string;
6
- readonly requiredAssetClasses: readonly string[];
7
- readonly requiredRendererFeatures: readonly string[];
8
- readonly requiredProof: readonly string[];
9
- readonly differentiation: readonly string[];
10
- }
11
-
12
- export interface ProductionAssetPreflightInput {
13
- readonly id: string;
14
- readonly localPath?: string;
15
- readonly sourceUri?: string;
16
- readonly sha256?: string;
17
- readonly bytes?: number;
18
- readonly license?: string;
19
- readonly tags?: readonly string[];
20
- readonly renderRequirements?: readonly string[];
21
- }
22
-
23
- export interface ProductionAssetPreflightResult {
24
- readonly assetId: string;
25
- readonly pass: boolean;
26
- readonly missing: readonly string[];
27
- readonly warnings: readonly string[];
28
- }
29
-
30
- export interface ProductionVisualQAInput {
31
- readonly screenshotPath: string;
32
- readonly rendererBackend: string;
33
- readonly realRendererProof: boolean;
34
- readonly width: number;
35
- readonly height: number;
36
- readonly nonBlackPixels: number;
37
- readonly uniqueColorBuckets: number;
38
- readonly drawCalls: number;
39
- readonly textureMemory: number;
40
- }
41
-
42
- export interface ProductionVisualQAResult {
43
- readonly screenshotPath: string;
44
- readonly pass: boolean;
45
- readonly score: number;
46
- readonly failures: readonly string[];
47
- }
48
-
49
- export interface ProductionRendererDefaults {
50
- readonly workflowId: ProductionWorkflowId;
51
- readonly backend: "webgl2";
52
- readonly hdrEnvironmentId: "studio-small-08";
53
- readonly qualityPreset: "hdr-studio-preview";
54
- readonly camera: {
55
- readonly policy: "require";
56
- readonly paddingRatio: number;
57
- readonly yawRadians: number;
58
- readonly pitchRadians: number;
59
- };
60
- readonly postprocess: readonly string[];
61
- readonly runtimeMetrics: readonly string[];
62
- }
63
-
64
- export interface ProductionWorkflowPlan {
65
- readonly workflow: ProductionWorkflowDefinition;
66
- readonly defaults: ProductionRendererDefaults;
67
- readonly preflightRequired: true;
68
- readonly visualQARequired: true;
69
- }
70
-
71
- export const PRODUCTION_WORKFLOWS: readonly ProductionWorkflowDefinition[] = [
72
- {
73
- id: "product",
74
- label: "Product Configurator",
75
- requiredAssetClasses: ["product"],
76
- requiredRendererFeatures: ["real-webgl2-context", "pbr-materials", "hdr-ibl-ready", "pixel-readback"],
77
- requiredProof: ["imported-glb", "hdr-environment", "draw-calls", "texture-memory", "canvas-screenshot"],
78
- differentiation: ["runtime proof bundle", "material variant workflow", "developer metrics"]
79
- },
80
- {
81
- id: "asset",
82
- label: "Asset Inspector",
83
- requiredAssetClasses: ["product", "character", "material"],
84
- requiredRendererFeatures: ["real-webgl2-context", "imported-gltf-render-source", "pixel-readback"],
85
- requiredProof: ["asset metadata", "skinning-or-morph-visibility", "corpus provenance"],
86
- differentiation: ["asset preflight", "metadata and visual proof in one report"]
87
- },
88
- {
89
- id: "material",
90
- label: "Material Studio",
91
- requiredAssetClasses: ["material"],
92
- requiredRendererFeatures: ["pbr-materials", "hdr-ibl-ready", "texture-upload-diagnostics"],
93
- requiredProof: ["material-extension-metadata", "hdr-lighting", "texture bindings"],
94
- differentiation: ["extension coverage reporting", "visual QA score per material"]
95
- },
96
- {
97
- id: "architecture",
98
- label: "Architecture Viewer",
99
- requiredAssetClasses: ["architecture", "product"],
100
- requiredRendererFeatures: ["hdr-ibl-ready", "camera-framing", "pixel-readback"],
101
- requiredProof: ["day-night-environment", "camera defaults", "visual quality metrics"],
102
- differentiation: ["environment comparison", "inspection-ready camera defaults"]
103
- },
104
- {
105
- id: "cinematic",
106
- label: "Cinematic Postprocess",
107
- requiredAssetClasses: ["product"],
108
- requiredRendererFeatures: ["postprocess-presentation", "tone-mapping", "bloom", "fxaa"],
109
- requiredProof: ["postprocess-chain", "nonblank diffable screenshot", "renderer diagnostics"],
110
- differentiation: ["proof-backed postprocess presets", "gallery-ready output"]
111
- }
112
- ];
113
-
114
- export function listProductionWorkflowDefinitions(): readonly ProductionWorkflowDefinition[] {
115
- return PRODUCTION_WORKFLOWS;
116
- }
117
-
118
- export function createProductionAssetPreflight(input: ProductionAssetPreflightInput): ProductionAssetPreflightResult {
119
- const renderRequirements = input.renderRequirements ?? [];
120
- const missing = [
121
- ...(!input.id ? ["id"] : []),
122
- ...(!input.localPath ? ["localPath"] : []),
123
- ...(!input.sourceUri ? ["sourceUri"] : []),
124
- ...(!input.sha256 ? ["sha256"] : []),
125
- ...(!input.license ? ["license"] : []),
126
- ...(!input.bytes || input.bytes <= 0 ? ["bytes"] : []),
127
- ...(renderRequirements.length === 0 ? ["renderRequirements"] : [])
128
- ];
129
- const warnings = [
130
- ...(!input.tags?.includes("pbr") ? ["Asset is not tagged as PBR; workflow may need material fallback."] : []),
131
- ...(!renderRequirements.includes("hdrIbl") ? ["Asset does not declare HDR IBL as an asset-specific requirement; renderer workflow must provide fallback HDR lighting proof."] : []),
132
- ...((input.bytes ?? 0) < 10_000 ? ["Asset is very small for visual parity proof."] : [])
133
- ];
134
- return {
135
- assetId: input.id,
136
- pass: missing.length === 0,
137
- missing,
138
- warnings
139
- };
140
- }
141
-
142
- export function createProductionVisualQAResult(input: ProductionVisualQAInput): ProductionVisualQAResult {
143
- const totalPixels = Math.max(1, input.width * input.height);
144
- const coverage = input.nonBlackPixels / totalPixels;
145
- const score = Number(Math.min(100, (
146
- (input.realRendererProof ? 22 : 0) +
147
- (input.rendererBackend === "webgl2" ? 12 : 0) +
148
- Math.min(24, coverage * 30) +
149
- Math.min(24, input.uniqueColorBuckets / 8) +
150
- Math.min(10, input.drawCalls * 1.5) +
151
- Math.min(8, input.textureMemory / 4_000_000)
152
- )).toFixed(3));
153
- const failures = [
154
- ...(!input.realRendererProof ? ["missing-real-renderer-proof"] : []),
155
- ...(input.rendererBackend !== "webgl2" ? ["unexpected-renderer-backend"] : []),
156
- ...(input.width < 128 || input.height < 128 ? ["screenshot-too-small"] : []),
157
- ...(input.nonBlackPixels <= 1000 ? ["blank-or-nearly-blank"] : []),
158
- ...(input.uniqueColorBuckets <= 4 ? ["low-color-entropy"] : []),
159
- ...(input.drawCalls <= 0 ? ["missing-draw-calls"] : [])
160
- ];
161
- return {
162
- screenshotPath: input.screenshotPath,
163
- pass: failures.length === 0 && score >= 40,
164
- score,
165
- failures
166
- };
167
- }
168
-
169
- export function createProductionRendererDefaults(workflowId: ProductionWorkflowId): ProductionRendererDefaults {
170
- const cameraByWorkflow: Record<ProductionWorkflowId, { readonly yawRadians: number; readonly pitchRadians: number; readonly paddingRatio: number }> = {
171
- product: { yawRadians: -0.38, pitchRadians: -0.16, paddingRatio: 0.18 },
172
- asset: { yawRadians: -0.34, pitchRadians: -0.14, paddingRatio: 0.2 },
173
- material: { yawRadians: -0.42, pitchRadians: -0.16, paddingRatio: 0.22 },
174
- architecture: { yawRadians: -0.52, pitchRadians: -0.2, paddingRatio: 0.24 },
175
- cinematic: { yawRadians: -0.36, pitchRadians: -0.14, paddingRatio: 0.18 }
176
- };
177
- const camera = cameraByWorkflow[workflowId];
178
- return {
179
- workflowId,
180
- backend: "webgl2",
181
- hdrEnvironmentId: "studio-small-08",
182
- qualityPreset: "hdr-studio-preview",
183
- camera: {
184
- policy: "require",
185
- paddingRatio: camera.paddingRatio,
186
- yawRadians: camera.yawRadians,
187
- pitchRadians: camera.pitchRadians
188
- },
189
- postprocess: ["tone-mapping", "color-grade", "bloom", "fxaa"],
190
- runtimeMetrics: ["appId", "sceneId", "rendererBackend", "assetIds", "hdrEnvironmentId", "drawCalls", "triangleCount", "materialCount", "textureCount", "textureMemoryEstimate", "frameTime"]
191
- };
192
- }
193
-
194
- export function createProductionWorkflowPlan(workflowId: ProductionWorkflowId): ProductionWorkflowPlan {
195
- const workflow = PRODUCTION_WORKFLOWS.find((item) => item.id === workflowId);
196
- if (!workflow) throw new Error(`Unknown Production workflow: ${workflowId}`);
197
- return {
198
- workflow,
199
- defaults: createProductionRendererDefaults(workflowId),
200
- preflightRequired: true,
201
- visualQARequired: true
202
- };
203
- }
@@ -1,2 +0,0 @@
1
- export interface WorkflowDiagnosticsSummary { readonly id: string; readonly ready: boolean; readonly workflow: string; }
2
- export function createWorkflowDiagnosticsSummary(): WorkflowDiagnosticsSummary { return { id: 'diagnostics', ready: true, workflow: 'WorkflowDiagnostics' }; }
@@ -1,32 +0,0 @@
1
- export {
2
- PRODUCTION_WORKFLOWS,
3
- createProductionAssetPreflight,
4
- createProductionRendererDefaults,
5
- createProductionVisualQAResult,
6
- createProductionWorkflowPlan,
7
- listProductionWorkflowDefinitions
8
- } from "./ProductionWorkflows";
9
- export type {
10
- ProductionAssetPreflightInput,
11
- ProductionAssetPreflightResult,
12
- ProductionRendererDefaults,
13
- ProductionVisualQAInput,
14
- ProductionVisualQAResult,
15
- ProductionWorkflowDefinition,
16
- ProductionWorkflowId,
17
- ProductionWorkflowPlan
18
- } from "./ProductionWorkflows";
19
- export { runProductionExample } from "./ProductionExampleRuntime";
20
- export type {
21
- ProductionExampleAsset,
22
- ProductionExampleDefinition,
23
- ProductionExampleEnvironment,
24
- ProductionExampleRuntime,
25
- ProductionExampleRuntimeMetrics
26
- } from "./ProductionExampleRuntime";
27
- export * from './ProductRenderWorkflow';
28
- export * from './AssetInspectionWorkflow';
29
- export * from './MaterialAuthoringWorkflow';
30
- export * from './ArchitectureWorkflow';
31
- export * from './CinematicWorkflow';
32
- export * from './WorkflowDiagnostics';
@@ -1,27 +0,0 @@
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 type * from "../WorkflowTypes";
10
-
11
- import { createAnimationLabWorkflow } from "../AnimationLabWorkflow";
12
- import { createAssetViewerWorkflow } from "../AssetViewerWorkflow";
13
- import { createComparisonWorkflow } from "../ComparisonWorkflow";
14
- import { createInteractiveSceneWorkflow } from "../InteractiveSceneWorkflow";
15
- import { createMaterialStudioWorkflow } from "../MaterialStudioWorkflow";
16
- import { createProductConfiguratorWorkflow } from "../ProductConfiguratorWorkflow";
17
- import { createSceneShowcaseWorkflow } from "../SceneShowcaseWorkflow";
18
-
19
- export const workflows = {
20
- assetViewer: createAssetViewerWorkflow,
21
- productConfigurator: createProductConfiguratorWorkflow,
22
- materialStudio: createMaterialStudioWorkflow,
23
- sceneShowcase: createSceneShowcaseWorkflow,
24
- interactiveScene: createInteractiveSceneWorkflow,
25
- animationLab: createAnimationLabWorkflow,
26
- comparison: createComparisonWorkflow
27
- } as const;