@base2datadesign/viewer-kit 0.1.0 → 0.1.2

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.
@@ -0,0 +1,32 @@
1
+ import type { CameraState, ViewerCreateOptions, ViewerHandle, ViewerSnapshot } from "./types";
2
+ export declare class ViewerEngine {
3
+ private container;
4
+ private renderer;
5
+ private scene;
6
+ private camera;
7
+ private controls;
8
+ private rootGroup;
9
+ private environmentSystem;
10
+ private lightingSystem;
11
+ private postFxSystem;
12
+ private debugSystem;
13
+ private presets;
14
+ private presetId;
15
+ private running;
16
+ private animationFrame;
17
+ constructor(options: ViewerCreateOptions);
18
+ getHandle(): ViewerHandle;
19
+ start(): void;
20
+ stop(): void;
21
+ resize(): void;
22
+ dispose(): void;
23
+ setPreset(presetId: string): void;
24
+ setSceneRoot(root: unknown): void;
25
+ setCamera(state: Partial<CameraState>): void;
26
+ getCameraState(): CameraState;
27
+ capturePng(): Promise<Blob>;
28
+ getSnapshot(): ViewerSnapshot;
29
+ private applyPreset;
30
+ private resolvePreset;
31
+ }
32
+ //# sourceMappingURL=ViewerEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewerEngine.d.ts","sourceRoot":"","sources":["../../src/engine/ViewerEngine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,WAAW,EAAyC,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAQpJ,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAc;IAE/B,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,WAAW,CAAc;IAEjC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAuB;gBAEjC,OAAO,EAAE,mBAAmB;IA6CxC,SAAS,IAAI,YAAY;IAezB,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAQZ,MAAM,IAAI,IAAI;IASd,OAAO,IAAI,IAAI;IAef,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIjC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAOjC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAc5C,cAAc,IAAI,WAAW;IAQvB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCjC,WAAW,IAAI,cAAc;IAe7B,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,aAAa;CAKtB"}
@@ -0,0 +1,201 @@
1
+ import * as THREE from "three";
2
+ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
3
+ import { DEFAULT_PRESET_ID, DEFAULT_PRESETS } from "../presets/defaults";
4
+ import { applyRendererConfig } from "../systems/rendererSystem";
5
+ import { EnvironmentSystem } from "../systems/environmentSystem";
6
+ import { LightingSystem } from "../systems/lightingSystem";
7
+ import { PostFxSystem } from "../systems/postfxSystem";
8
+ import { DebugSystem } from "../systems/debugSystem";
9
+ export class ViewerEngine {
10
+ container;
11
+ renderer;
12
+ scene;
13
+ camera;
14
+ controls;
15
+ rootGroup;
16
+ environmentSystem;
17
+ lightingSystem;
18
+ postFxSystem;
19
+ debugSystem;
20
+ presets;
21
+ presetId;
22
+ running = false;
23
+ animationFrame = null;
24
+ constructor(options) {
25
+ this.container = options.container;
26
+ this.presets = options.presets ?? DEFAULT_PRESETS;
27
+ this.presetId = options.presetId ?? DEFAULT_PRESET_ID;
28
+ const width = this.container.clientWidth || 1;
29
+ const height = this.container.clientHeight || 1;
30
+ this.scene = new THREE.Scene();
31
+ this.camera = new THREE.PerspectiveCamera(options.camera?.fov ?? 60, width / height, options.camera?.near ?? 0.1, options.camera?.far ?? 500);
32
+ const camPos = options.camera?.position ?? [15, 12, 15];
33
+ this.camera.position.set(...camPos);
34
+ const rendererOptions = {
35
+ antialias: options.renderer?.antialias ?? true,
36
+ alpha: options.renderer?.alpha ?? true,
37
+ preserveDrawingBuffer: options.renderer?.preserveDrawingBuffer ?? false,
38
+ };
39
+ this.renderer = new THREE.WebGLRenderer(rendererOptions);
40
+ this.renderer.setSize(width, height);
41
+ this.container.appendChild(this.renderer.domElement);
42
+ this.controls = new OrbitControls(this.camera, this.renderer.domElement);
43
+ this.controls.enableDamping = true;
44
+ this.controls.dampingFactor = 0.05;
45
+ const target = options.camera?.target ?? [0, 1.5, 0];
46
+ this.controls.target.set(...target);
47
+ this.controls.update();
48
+ this.rootGroup = new THREE.Group();
49
+ this.scene.add(this.rootGroup);
50
+ this.environmentSystem = new EnvironmentSystem(this.scene, this.renderer, options.assetResolver);
51
+ this.lightingSystem = new LightingSystem(this.scene);
52
+ this.postFxSystem = new PostFxSystem(this.renderer, this.scene, this.camera);
53
+ this.debugSystem = new DebugSystem(this.scene);
54
+ this.applyPreset(this.presetId);
55
+ }
56
+ getHandle() {
57
+ return {
58
+ start: () => this.start(),
59
+ stop: () => this.stop(),
60
+ resize: () => this.resize(),
61
+ dispose: () => this.dispose(),
62
+ setPreset: (presetId) => this.setPreset(presetId),
63
+ setSceneRoot: (root) => this.setSceneRoot(root),
64
+ setCamera: (state) => this.setCamera(state),
65
+ getCameraState: () => this.getCameraState(),
66
+ capturePng: () => this.capturePng(),
67
+ getSnapshot: () => this.getSnapshot(),
68
+ };
69
+ }
70
+ start() {
71
+ if (this.running)
72
+ return;
73
+ this.running = true;
74
+ const loop = () => {
75
+ if (!this.running)
76
+ return;
77
+ this.controls.update();
78
+ this.postFxSystem.render();
79
+ this.animationFrame = requestAnimationFrame(loop);
80
+ };
81
+ this.animationFrame = requestAnimationFrame(loop);
82
+ }
83
+ stop() {
84
+ this.running = false;
85
+ if (this.animationFrame !== null) {
86
+ cancelAnimationFrame(this.animationFrame);
87
+ this.animationFrame = null;
88
+ }
89
+ }
90
+ resize() {
91
+ const width = this.container.clientWidth || 1;
92
+ const height = this.container.clientHeight || 1;
93
+ this.camera.aspect = width / height;
94
+ this.camera.updateProjectionMatrix();
95
+ this.renderer.setSize(width, height);
96
+ this.postFxSystem.resize(width, height);
97
+ }
98
+ dispose() {
99
+ this.stop();
100
+ this.environmentSystem.dispose();
101
+ this.lightingSystem.dispose();
102
+ this.postFxSystem.dispose();
103
+ this.debugSystem.dispose();
104
+ this.scene.clear();
105
+ this.controls.dispose();
106
+ this.renderer.dispose();
107
+ if (this.renderer.domElement.parentElement === this.container) {
108
+ this.container.removeChild(this.renderer.domElement);
109
+ }
110
+ }
111
+ setPreset(presetId) {
112
+ this.applyPreset(presetId);
113
+ }
114
+ setSceneRoot(root) {
115
+ this.rootGroup.clear();
116
+ if (root && root instanceof THREE.Object3D) {
117
+ this.rootGroup.add(root);
118
+ }
119
+ }
120
+ setCamera(state) {
121
+ if (state.position) {
122
+ this.camera.position.set(...state.position);
123
+ }
124
+ if (state.target) {
125
+ this.controls.target.set(...state.target);
126
+ }
127
+ if (typeof state.fov === "number") {
128
+ this.camera.fov = state.fov;
129
+ this.camera.updateProjectionMatrix();
130
+ }
131
+ this.controls.update();
132
+ }
133
+ getCameraState() {
134
+ return {
135
+ position: [this.camera.position.x, this.camera.position.y, this.camera.position.z],
136
+ target: [this.controls.target.x, this.controls.target.y, this.controls.target.z],
137
+ fov: this.camera.fov,
138
+ };
139
+ }
140
+ async capturePng() {
141
+ const width = this.renderer.domElement.width || 1;
142
+ const height = this.renderer.domElement.height || 1;
143
+ const target = new THREE.WebGLRenderTarget(width, height);
144
+ const previousTarget = this.renderer.getRenderTarget();
145
+ this.renderer.setRenderTarget(target);
146
+ // Render without postfx for now (upgrade later)
147
+ this.renderer.render(this.scene, this.camera);
148
+ const buffer = new Uint8Array(width * height * 4);
149
+ this.renderer.readRenderTargetPixels(target, 0, 0, width, height, buffer);
150
+ this.renderer.setRenderTarget(previousTarget);
151
+ target.dispose();
152
+ const canvas = document.createElement("canvas");
153
+ canvas.width = width;
154
+ canvas.height = height;
155
+ const ctx = canvas.getContext("2d");
156
+ if (!ctx) {
157
+ return new Blob();
158
+ }
159
+ const imageData = ctx.createImageData(width, height);
160
+ const rowSize = width * 4;
161
+ for (let y = 0; y < height; y += 1) {
162
+ const src = (height - 1 - y) * rowSize;
163
+ const dst = y * rowSize;
164
+ imageData.data.set(buffer.subarray(src, src + rowSize), dst);
165
+ }
166
+ ctx.putImageData(imageData, 0, 0);
167
+ return new Promise((resolve) => {
168
+ canvas.toBlob((blob) => resolve(blob ?? new Blob()), "image/png");
169
+ });
170
+ }
171
+ getSnapshot() {
172
+ const envState = this.environmentSystem.getState();
173
+ return {
174
+ capturedAt: new Date().toISOString(),
175
+ presetId: this.presetId,
176
+ renderer: {
177
+ toneMapping: String(this.renderer.toneMapping),
178
+ toneMappingExposure: this.renderer.toneMappingExposure,
179
+ outputColorSpace: String(this.renderer.outputColorSpace),
180
+ },
181
+ environment: envState,
182
+ postfx: this.postFxSystem.getState(),
183
+ };
184
+ }
185
+ applyPreset(presetId) {
186
+ const preset = this.resolvePreset(presetId);
187
+ this.presetId = preset.id;
188
+ applyRendererConfig(this.renderer, preset.renderer);
189
+ void this.environmentSystem.apply(preset);
190
+ this.lightingSystem.apply(preset);
191
+ this.postFxSystem.apply(preset.postfx);
192
+ this.debugSystem.apply(preset);
193
+ }
194
+ resolvePreset(presetId) {
195
+ if (presetId in this.presets)
196
+ return this.presets[presetId];
197
+ if (DEFAULT_PRESET_ID in this.presets)
198
+ return this.presets[DEFAULT_PRESET_ID];
199
+ return Object.values(this.presets)[0] ?? DEFAULT_PRESETS[DEFAULT_PRESET_ID];
200
+ }
201
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"createViewer.d.ts","sourceRoot":"","sources":["../../src/engine/createViewer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAkB,MAAM,SAAS,CAAC;AAIjF,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CA0DvE"}
1
+ {"version":3,"file":"createViewer.d.ts","sourceRoot":"","sources":["../../src/engine/createViewer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGjE,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAKvE"}
@@ -1,57 +1,7 @@
1
- // NOTE: Stub engine: this exists to stabilize API and publishing early.
2
- // Rendering systems (Three.js) will be implemented next without breaking the surface area.
1
+ import { ViewerEngine } from "./ViewerEngine";
3
2
  export function createViewer(options) {
4
- const { hooks } = options;
5
- let running = false;
6
- let presetId = options.presetId;
7
- const snapshot = () => ({
8
- capturedAt: new Date().toISOString(),
9
- presetId,
10
- renderer: {
11
- toneMapping: "NeutralToneMapping",
12
- toneMappingExposure: 1.0,
13
- outputColorSpace: "SRGBColorSpace",
14
- },
15
- environment: {
16
- mode: "none",
17
- backgroundEnabled: false,
18
- lightingEnabled: false,
19
- },
20
- });
21
- const handle = {
22
- start() {
23
- running = true;
24
- },
25
- stop() {
26
- running = false;
27
- },
28
- resize() {
29
- void running;
30
- },
31
- dispose() {
32
- running = false;
33
- },
34
- setPreset(nextPresetId) {
35
- presetId = nextPresetId;
36
- },
37
- setSceneRoot(_root) {
38
- // placeholder
39
- },
40
- async capturePng() {
41
- // placeholder blob (transparent 1x1 PNG)
42
- const bytes = Uint8Array.from([
43
- 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
44
- 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00,
45
- 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78,
46
- 0x9c, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0d, 0x0a, 0x2d, 0xb4, 0x00,
47
- 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
48
- ]);
49
- return new Blob([bytes], { type: "image/png" });
50
- },
51
- getSnapshot() {
52
- return snapshot();
53
- },
54
- };
55
- queueMicrotask(() => hooks?.onReady?.(handle));
3
+ const engine = new ViewerEngine(options);
4
+ const handle = engine.getHandle();
5
+ queueMicrotask(() => options.hooks?.onReady?.(handle));
56
6
  return handle;
57
7
  }
@@ -4,6 +4,110 @@ export type AssetResolverResult = {
4
4
  headers?: Record<string, string>;
5
5
  };
6
6
  export type AssetResolver = (src: string, kind: AssetKind) => Promise<AssetResolverResult>;
7
+ export type ToneMappingMode = "neutral" | "aces" | "linear";
8
+ export type RendererConfig = {
9
+ toneMapping?: ToneMappingMode;
10
+ toneMappingExposure?: number;
11
+ outputColorSpace?: "srgb" | "linear";
12
+ maxPixelRatio?: number;
13
+ antialias?: boolean;
14
+ alpha?: boolean;
15
+ preserveDrawingBuffer?: boolean;
16
+ shadowMap?: {
17
+ enabled?: boolean;
18
+ type?: "pcfsoft" | "pcf" | "basic";
19
+ };
20
+ };
21
+ export type SkyGradientConfig = {
22
+ topColor: string;
23
+ horizonColor: string;
24
+ sunPosition: [number, number, number];
25
+ sunIntensity: number;
26
+ sunSize: number;
27
+ };
28
+ export type SkyHdrConfig = {
29
+ src: string;
30
+ exposure?: number;
31
+ rotationY?: number;
32
+ showBackground?: boolean;
33
+ showLighting?: boolean;
34
+ pmremResolution?: 256 | 512 | 1024;
35
+ usePmrem?: boolean;
36
+ };
37
+ export type SkyCubeConfig = {
38
+ px: string;
39
+ nx: string;
40
+ py: string;
41
+ ny: string;
42
+ pz: string;
43
+ nz: string;
44
+ exposure?: number;
45
+ rotationY?: number;
46
+ showBackground?: boolean;
47
+ showLighting?: boolean;
48
+ pmremResolution?: 256 | 512 | 1024;
49
+ usePmrem?: boolean;
50
+ };
51
+ export type SkyDefinition = {
52
+ mode: "none";
53
+ } | {
54
+ mode: "gradient";
55
+ gradient: SkyGradientConfig;
56
+ } | {
57
+ mode: "hdr";
58
+ hdr: SkyHdrConfig;
59
+ } | {
60
+ mode: "cube";
61
+ cube: SkyCubeConfig;
62
+ };
63
+ export type LightingRigConfig = {
64
+ rig?: "studio" | "neutral" | "gallery" | "none";
65
+ ambient?: {
66
+ color: string;
67
+ intensity: number;
68
+ };
69
+ hemisphere?: {
70
+ skyColor: string;
71
+ groundColor: string;
72
+ intensity: number;
73
+ };
74
+ key?: {
75
+ color: string;
76
+ intensity: number;
77
+ position: [number, number, number];
78
+ castShadow?: boolean;
79
+ };
80
+ fill?: {
81
+ color: string;
82
+ intensity: number;
83
+ position: [number, number, number];
84
+ };
85
+ };
86
+ export type PostFxConfig = {
87
+ ao?: {
88
+ enabled: boolean;
89
+ intensity?: number;
90
+ radius?: number;
91
+ distanceFalloff?: number;
92
+ halfRes?: boolean;
93
+ };
94
+ };
95
+ export type SceneExtrasConfig = {
96
+ grid?: boolean;
97
+ ground?: boolean;
98
+ axes?: boolean;
99
+ origin?: boolean;
100
+ };
101
+ export type RenderPresetDefinition = {
102
+ id: string;
103
+ label?: string;
104
+ renderer?: RendererConfig;
105
+ sky?: SkyDefinition;
106
+ lighting?: LightingRigConfig;
107
+ postfx?: PostFxConfig;
108
+ sceneExtras?: SceneExtrasConfig;
109
+ };
110
+ export type PresetCatalog = Record<string, RenderPresetDefinition>;
7
111
  export type ViewerSnapshot = {
8
112
  capturedAt: string;
9
113
  presetId: string;
@@ -17,11 +121,33 @@ export type ViewerSnapshot = {
17
121
  backgroundEnabled: boolean;
18
122
  lightingEnabled: boolean;
19
123
  };
124
+ postfx: {
125
+ aoEnabled: boolean;
126
+ };
127
+ };
128
+ export type CameraState = {
129
+ position: [number, number, number];
130
+ target: [number, number, number];
131
+ fov: number;
20
132
  };
21
133
  export type ViewerCreateOptions = {
22
134
  container: HTMLElement;
23
- presetId: string;
135
+ presetId?: string;
136
+ presets?: PresetCatalog;
24
137
  assetResolver?: AssetResolver;
138
+ renderer?: {
139
+ antialias?: boolean;
140
+ alpha?: boolean;
141
+ preserveDrawingBuffer?: boolean;
142
+ };
143
+ camera?: {
144
+ fov?: number;
145
+ near?: number;
146
+ far?: number;
147
+ position?: [number, number, number];
148
+ target?: [number, number, number];
149
+ };
150
+ usePostprocessing?: boolean;
25
151
  hooks?: {
26
152
  onReady?: (handle: ViewerHandle) => void;
27
153
  onError?: (error: unknown) => void;
@@ -34,6 +160,8 @@ export type ViewerHandle = {
34
160
  dispose(): void;
35
161
  setPreset(presetId: string): void;
36
162
  setSceneRoot(root: unknown): void;
163
+ setCamera(state: Partial<CameraState>): void;
164
+ getCameraState(): CameraState;
37
165
  capturePng(): Promise<Blob>;
38
166
  getSnapshot(): ViewerSnapshot;
39
167
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAE3F,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QACR,WAAW,EAAE,MAAM,CAAC;QACpB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;QAC3C,iBAAiB,EAAE,OAAO,CAAC;QAC3B,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;QACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;KACpC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAEhB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAElC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,cAAc,CAAC;CAC/B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export type { ViewerCreateOptions, ViewerHandle, ViewerSnapshot } from "./engine/types";
1
+ export type { ViewerCreateOptions, ViewerHandle, ViewerSnapshot, CameraState, RenderPresetDefinition, PresetCatalog, } from "./engine/types";
2
2
  export { createViewer } from "./engine/createViewer";
3
+ export { DEFAULT_PRESET_ID, DEFAULT_PRESETS } from "./presets/defaults";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,sBAAsB,EACtB,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { createViewer } from "./engine/createViewer";
2
+ export { DEFAULT_PRESET_ID, DEFAULT_PRESETS } from "./presets/defaults";
@@ -0,0 +1,4 @@
1
+ import type { PresetCatalog } from "../engine/types";
2
+ export declare const DEFAULT_PRESET_ID = "default-studio";
3
+ export declare const DEFAULT_PRESETS: PresetCatalog;
4
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/presets/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAElD,eAAO,MAAM,eAAe,EAAE,aA2C7B,CAAC"}
@@ -0,0 +1,45 @@
1
+ export const DEFAULT_PRESET_ID = "default-studio";
2
+ export const DEFAULT_PRESETS = {
3
+ [DEFAULT_PRESET_ID]: {
4
+ id: DEFAULT_PRESET_ID,
5
+ label: "Default Studio",
6
+ renderer: {
7
+ toneMapping: "neutral",
8
+ toneMappingExposure: 1.0,
9
+ outputColorSpace: "srgb",
10
+ maxPixelRatio: 1.75,
11
+ antialias: true,
12
+ alpha: true,
13
+ shadowMap: {
14
+ enabled: true,
15
+ type: "pcfsoft",
16
+ },
17
+ },
18
+ sky: {
19
+ mode: "gradient",
20
+ gradient: {
21
+ topColor: "#ffffff",
22
+ horizonColor: "#d8d8d8",
23
+ sunPosition: [0.3, 0.8, 0.4],
24
+ sunIntensity: 0.15,
25
+ sunSize: 0.08,
26
+ },
27
+ },
28
+ lighting: {
29
+ rig: "studio",
30
+ ambient: { color: "#ffffff", intensity: 1.0 },
31
+ hemisphere: { skyColor: "#ffffff", groundColor: "#f4f4f4", intensity: 1.2 },
32
+ key: { color: "#ffffff", intensity: 1.6, position: [10, 15, 5], castShadow: true },
33
+ fill: { color: "#ffffff", intensity: 0.6, position: [-10, 10, -5] },
34
+ },
35
+ postfx: {
36
+ ao: { enabled: false, intensity: 1.0, radius: 2.0, distanceFalloff: 1.0, halfRes: false },
37
+ },
38
+ sceneExtras: {
39
+ grid: true,
40
+ ground: true,
41
+ axes: false,
42
+ origin: true,
43
+ },
44
+ },
45
+ };
@@ -0,0 +1,12 @@
1
+ import * as THREE from "three";
2
+ import type { RenderPresetDefinition } from "../engine/types";
3
+ export declare class DebugSystem {
4
+ private scene;
5
+ private objects;
6
+ constructor(scene: THREE.Scene);
7
+ apply(preset: RenderPresetDefinition): void;
8
+ dispose(): void;
9
+ private add;
10
+ private clear;
11
+ }
12
+ //# sourceMappingURL=debugSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debugSystem.d.ts","sourceRoot":"","sources":["../../src/systems/debugSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AASjF,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAAwB;gBAE3B,KAAK,EAAE,KAAK,CAAC,KAAK;IAI9B,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAwC3C,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,KAAK;CAed"}
@@ -0,0 +1,73 @@
1
+ import * as THREE from "three";
2
+ const DEFAULT_EXTRAS = {
3
+ grid: true,
4
+ ground: true,
5
+ axes: false,
6
+ origin: true,
7
+ };
8
+ export class DebugSystem {
9
+ scene;
10
+ objects = [];
11
+ constructor(scene) {
12
+ this.scene = scene;
13
+ }
14
+ apply(preset) {
15
+ const extras = { ...DEFAULT_EXTRAS, ...(preset.sceneExtras ?? {}) };
16
+ this.clear();
17
+ if (extras.ground) {
18
+ const groundGeometry = new THREE.PlaneGeometry(300, 300);
19
+ const groundMaterial = new THREE.MeshStandardMaterial({
20
+ color: 0xf5f5f5,
21
+ roughness: 0.75,
22
+ metalness: 0.0,
23
+ toneMapped: false,
24
+ });
25
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
26
+ ground.rotation.x = -Math.PI / 2;
27
+ ground.position.y = -0.11;
28
+ ground.receiveShadow = true;
29
+ this.add(ground);
30
+ }
31
+ if (extras.grid) {
32
+ const gridHelper = new THREE.GridHelper(100, 50, 0xdedede, 0xeaeaea);
33
+ gridHelper.position.y = -0.09;
34
+ this.add(gridHelper);
35
+ }
36
+ if (extras.axes) {
37
+ const axes = new THREE.AxesHelper(3);
38
+ axes.position.set(-15, 0.1, -15);
39
+ this.add(axes);
40
+ }
41
+ if (extras.origin) {
42
+ const originGeometry = new THREE.SphereGeometry(0.1, 16, 16);
43
+ const originMaterial = new THREE.MeshBasicMaterial({ color: 0x666666 });
44
+ const origin = new THREE.Mesh(originGeometry, originMaterial);
45
+ origin.position.set(0, 0, 0);
46
+ this.add(origin);
47
+ }
48
+ }
49
+ dispose() {
50
+ this.clear();
51
+ }
52
+ add(object) {
53
+ this.scene.add(object);
54
+ this.objects.push(object);
55
+ }
56
+ clear() {
57
+ this.objects.forEach((object) => {
58
+ if (object.parent)
59
+ object.parent.remove(object);
60
+ if (object instanceof THREE.Mesh) {
61
+ object.geometry.dispose();
62
+ const material = object.material;
63
+ if (Array.isArray(material)) {
64
+ material.forEach((mat) => mat.dispose?.());
65
+ }
66
+ else {
67
+ material.dispose?.();
68
+ }
69
+ }
70
+ });
71
+ this.objects = [];
72
+ }
73
+ }
@@ -0,0 +1,29 @@
1
+ import * as THREE from "three";
2
+ import type { AssetResolver, RenderPresetDefinition } from "../engine/types";
3
+ export type EnvironmentState = {
4
+ mode: "none" | "gradient" | "hdr" | "cube";
5
+ backgroundEnabled: boolean;
6
+ lightingEnabled: boolean;
7
+ };
8
+ export declare class EnvironmentSystem {
9
+ private scene;
10
+ private renderer;
11
+ private assetResolver?;
12
+ private pmremGenerator;
13
+ private currentObjects;
14
+ private currentBackground;
15
+ private currentEnvironment;
16
+ private objectUrls;
17
+ private loadToken;
18
+ private state;
19
+ constructor(scene: THREE.Scene, renderer: THREE.WebGLRenderer, assetResolver?: AssetResolver);
20
+ getState(): EnvironmentState;
21
+ apply(preset: RenderPresetDefinition): Promise<void>;
22
+ dispose(): void;
23
+ private disposeCurrent;
24
+ private applyGradientSky;
25
+ private resolveAsset;
26
+ private applyHdrSky;
27
+ private applyCubeSky;
28
+ }
29
+ //# sourceMappingURL=environmentSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environmentSystem.d.ts","sourceRoot":"","sources":["../../src/systems/environmentSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAA8C,MAAM,iBAAiB,CAAC;AAEzH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;IAC3C,iBAAiB,EAAE,OAAO,CAAC;IAC3B,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,iBAAiB,CAAkD;IAC3E,OAAO,CAAC,kBAAkB,CAAkD;IAC5E,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,SAAS,CAAK;IAEtB,OAAO,CAAC,KAAK,CAAwF;gBAEzF,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,aAAa,CAAC,EAAE,aAAa;IAM5F,QAAQ,IAAI,gBAAgB;IAItB,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B1D,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,gBAAgB;YA6CV,YAAY;YAaZ,WAAW;YAqDX,YAAY;CAiD3B"}
@@ -0,0 +1,232 @@
1
+ import * as THREE from "three";
2
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
3
+ export class EnvironmentSystem {
4
+ scene;
5
+ renderer;
6
+ assetResolver;
7
+ pmremGenerator = null;
8
+ currentObjects = [];
9
+ currentBackground = null;
10
+ currentEnvironment = null;
11
+ objectUrls = [];
12
+ loadToken = 0;
13
+ state = { mode: "none", backgroundEnabled: false, lightingEnabled: false };
14
+ constructor(scene, renderer, assetResolver) {
15
+ this.scene = scene;
16
+ this.renderer = renderer;
17
+ this.assetResolver = assetResolver;
18
+ }
19
+ getState() {
20
+ return { ...this.state };
21
+ }
22
+ async apply(preset) {
23
+ const sky = preset.sky;
24
+ this.disposeCurrent();
25
+ if (!sky || sky.mode === "none") {
26
+ this.scene.background = null;
27
+ this.scene.environment = null;
28
+ this.state = { mode: "none", backgroundEnabled: false, lightingEnabled: false };
29
+ return;
30
+ }
31
+ if (sky.mode === "gradient") {
32
+ this.applyGradientSky(sky.gradient);
33
+ this.state = { mode: "gradient", backgroundEnabled: true, lightingEnabled: false };
34
+ return;
35
+ }
36
+ if (sky.mode === "hdr") {
37
+ await this.applyHdrSky(sky.hdr);
38
+ return;
39
+ }
40
+ if (sky.mode === "cube") {
41
+ await this.applyCubeSky(sky.cube);
42
+ }
43
+ }
44
+ dispose() {
45
+ this.disposeCurrent();
46
+ if (this.pmremGenerator) {
47
+ this.pmremGenerator.dispose();
48
+ this.pmremGenerator = null;
49
+ }
50
+ }
51
+ disposeCurrent() {
52
+ this.currentObjects.forEach((obj) => {
53
+ if (obj.parent)
54
+ obj.parent.remove(obj);
55
+ if (obj instanceof THREE.Mesh) {
56
+ obj.geometry.dispose();
57
+ const material = obj.material;
58
+ if (Array.isArray(material)) {
59
+ material.forEach((mat) => mat.dispose?.());
60
+ }
61
+ else {
62
+ material.dispose?.();
63
+ }
64
+ }
65
+ });
66
+ this.currentObjects = [];
67
+ if (this.scene.background === this.currentBackground) {
68
+ this.scene.background = null;
69
+ }
70
+ if (this.scene.environment === this.currentEnvironment) {
71
+ this.scene.environment = null;
72
+ }
73
+ if (this.currentBackground)
74
+ this.currentBackground.dispose();
75
+ if (this.currentEnvironment && this.currentEnvironment !== this.currentBackground) {
76
+ this.currentEnvironment.dispose();
77
+ }
78
+ this.currentBackground = null;
79
+ this.currentEnvironment = null;
80
+ this.objectUrls.forEach((url) => URL.revokeObjectURL(url));
81
+ this.objectUrls = [];
82
+ }
83
+ applyGradientSky(gradient) {
84
+ const skyGeometry = new THREE.SphereGeometry(300, 64, 64);
85
+ const skyMaterial = new THREE.ShaderMaterial({
86
+ uniforms: {
87
+ topColor: { value: new THREE.Color(gradient.topColor) },
88
+ horizonColor: { value: new THREE.Color(gradient.horizonColor) },
89
+ sunPosition: { value: new THREE.Vector3(...gradient.sunPosition) },
90
+ sunIntensity: { value: gradient.sunIntensity },
91
+ sunSize: { value: gradient.sunSize },
92
+ },
93
+ vertexShader: `
94
+ varying vec3 vPosition;
95
+ void main() {
96
+ vPosition = position;
97
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
98
+ }
99
+ `,
100
+ fragmentShader: `
101
+ uniform vec3 topColor;
102
+ uniform vec3 horizonColor;
103
+ uniform vec3 sunPosition;
104
+ uniform float sunIntensity;
105
+ uniform float sunSize;
106
+ varying vec3 vPosition;
107
+ void main() {
108
+ vec3 direction = normalize(vPosition);
109
+ float h = direction.y;
110
+ float gradientFactor = smoothstep(-0.1, 0.4, h);
111
+ vec3 color = mix(horizonColor, topColor, gradientFactor);
112
+ vec3 sunDir = normalize(sunPosition);
113
+ float sunDist = distance(direction, sunDir);
114
+ float sun = smoothstep(sunSize, 0.0, sunDist) * sunIntensity;
115
+ color = mix(color, topColor, sun);
116
+ gl_FragColor = vec4(color, 1.0);
117
+ }
118
+ `,
119
+ side: THREE.BackSide,
120
+ depthWrite: false,
121
+ });
122
+ const sky = new THREE.Mesh(skyGeometry, skyMaterial);
123
+ sky.renderOrder = -1;
124
+ this.scene.add(sky);
125
+ this.currentObjects.push(sky);
126
+ }
127
+ async resolveAsset(src, kind) {
128
+ if (!this.assetResolver)
129
+ return src;
130
+ const { url, headers } = await this.assetResolver(src, kind);
131
+ if (!headers || Object.keys(headers).length === 0)
132
+ return url;
133
+ const response = await fetch(url, { headers });
134
+ if (!response.ok)
135
+ throw new Error(`Asset fetch failed: ${response.status}`);
136
+ const blob = await response.blob();
137
+ const objectUrl = URL.createObjectURL(blob);
138
+ this.objectUrls.push(objectUrl);
139
+ return objectUrl;
140
+ }
141
+ async applyHdrSky(hdr) {
142
+ const token = ++this.loadToken;
143
+ const backgroundEnabled = hdr.showBackground !== false;
144
+ const lightingEnabled = hdr.showLighting !== false;
145
+ const usePmrem = hdr.usePmrem !== false;
146
+ const resolved = await this.resolveAsset(hdr.src, "hdr");
147
+ if (token !== this.loadToken)
148
+ return;
149
+ const loader = new RGBELoader();
150
+ const texture = await loader.loadAsync(resolved);
151
+ if (token !== this.loadToken) {
152
+ texture.dispose();
153
+ return;
154
+ }
155
+ texture.mapping = THREE.EquirectangularReflectionMapping;
156
+ texture.colorSpace = THREE.LinearSRGBColorSpace;
157
+ texture.needsUpdate = true;
158
+ texture.center = new THREE.Vector2(0.5, 0.5);
159
+ if (typeof hdr.rotationY === "number") {
160
+ texture.rotation = hdr.rotationY;
161
+ }
162
+ let environmentTexture = null;
163
+ if (lightingEnabled) {
164
+ if (usePmrem) {
165
+ if (!this.pmremGenerator) {
166
+ this.pmremGenerator = new THREE.PMREMGenerator(this.renderer);
167
+ this.pmremGenerator.compileEquirectangularShader();
168
+ }
169
+ environmentTexture = this.pmremGenerator.fromEquirectangular(texture).texture;
170
+ }
171
+ else {
172
+ environmentTexture = texture;
173
+ }
174
+ }
175
+ if (backgroundEnabled) {
176
+ this.scene.background = texture;
177
+ this.currentBackground = texture;
178
+ }
179
+ if (environmentTexture) {
180
+ this.scene.environment = environmentTexture;
181
+ this.currentEnvironment = environmentTexture;
182
+ if (environmentTexture !== texture) {
183
+ this.currentEnvironment = environmentTexture;
184
+ }
185
+ }
186
+ this.state = { mode: "hdr", backgroundEnabled, lightingEnabled };
187
+ }
188
+ async applyCubeSky(cube) {
189
+ const token = ++this.loadToken;
190
+ const backgroundEnabled = cube.showBackground !== false;
191
+ const lightingEnabled = cube.showLighting !== false;
192
+ const usePmrem = cube.usePmrem !== false;
193
+ const faces = await Promise.all([
194
+ this.resolveAsset(cube.px, "cubeface"),
195
+ this.resolveAsset(cube.nx, "cubeface"),
196
+ this.resolveAsset(cube.py, "cubeface"),
197
+ this.resolveAsset(cube.ny, "cubeface"),
198
+ this.resolveAsset(cube.pz, "cubeface"),
199
+ this.resolveAsset(cube.nz, "cubeface"),
200
+ ]);
201
+ if (token !== this.loadToken)
202
+ return;
203
+ const loader = new THREE.CubeTextureLoader();
204
+ const texture = await loader.loadAsync(faces);
205
+ if (token !== this.loadToken) {
206
+ texture.dispose();
207
+ return;
208
+ }
209
+ texture.needsUpdate = true;
210
+ let environmentTexture = null;
211
+ if (lightingEnabled) {
212
+ if (usePmrem) {
213
+ if (!this.pmremGenerator) {
214
+ this.pmremGenerator = new THREE.PMREMGenerator(this.renderer);
215
+ }
216
+ environmentTexture = this.pmremGenerator.fromCubemap(texture).texture;
217
+ }
218
+ else {
219
+ environmentTexture = texture;
220
+ }
221
+ }
222
+ if (backgroundEnabled) {
223
+ this.scene.background = texture;
224
+ this.currentBackground = texture;
225
+ }
226
+ if (environmentTexture) {
227
+ this.scene.environment = environmentTexture;
228
+ this.currentEnvironment = environmentTexture;
229
+ }
230
+ this.state = { mode: "cube", backgroundEnabled, lightingEnabled };
231
+ }
232
+ }
@@ -0,0 +1,12 @@
1
+ import * as THREE from "three";
2
+ import type { RenderPresetDefinition } from "../engine/types";
3
+ export declare class LightingSystem {
4
+ private scene;
5
+ private lights;
6
+ constructor(scene: THREE.Scene);
7
+ apply(preset: RenderPresetDefinition): void;
8
+ dispose(): void;
9
+ private add;
10
+ private clear;
11
+ }
12
+ //# sourceMappingURL=lightingSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lightingSystem.d.ts","sourceRoot":"","sources":["../../src/systems/lightingSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAUjF,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAqB;gBAEvB,KAAK,EAAE,KAAK,CAAC,KAAK;IAI9B,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAqC3C,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,KAAK;CAMd"}
@@ -0,0 +1,58 @@
1
+ import * as THREE from "three";
2
+ const DEFAULT_LIGHTING = {
3
+ rig: "studio",
4
+ ambient: { color: "#ffffff", intensity: 0.8 },
5
+ hemisphere: { skyColor: "#ffffff", groundColor: "#f4f4f4", intensity: 1.0 },
6
+ key: { color: "#ffffff", intensity: 1.4, position: [10, 15, 5], castShadow: true },
7
+ fill: { color: "#ffffff", intensity: 0.5, position: [-10, 10, -5] },
8
+ };
9
+ export class LightingSystem {
10
+ scene;
11
+ lights = [];
12
+ constructor(scene) {
13
+ this.scene = scene;
14
+ }
15
+ apply(preset) {
16
+ this.clear();
17
+ const config = preset.lighting ?? DEFAULT_LIGHTING;
18
+ if (config.rig === "none")
19
+ return;
20
+ const ambient = config.ambient ?? DEFAULT_LIGHTING.ambient;
21
+ if (ambient && ambient.intensity > 0) {
22
+ const light = new THREE.AmbientLight(new THREE.Color(ambient.color), ambient.intensity);
23
+ this.add(light);
24
+ }
25
+ const hemi = config.hemisphere ?? DEFAULT_LIGHTING.hemisphere;
26
+ if (hemi && hemi.intensity > 0) {
27
+ const light = new THREE.HemisphereLight(new THREE.Color(hemi.skyColor), new THREE.Color(hemi.groundColor), hemi.intensity);
28
+ this.add(light);
29
+ }
30
+ const key = config.key ?? DEFAULT_LIGHTING.key;
31
+ if (key && key.intensity > 0) {
32
+ const light = new THREE.DirectionalLight(new THREE.Color(key.color), key.intensity);
33
+ light.position.set(...key.position);
34
+ light.castShadow = key.castShadow ?? false;
35
+ this.add(light);
36
+ }
37
+ const fill = config.fill ?? DEFAULT_LIGHTING.fill;
38
+ if (fill && fill.intensity > 0) {
39
+ const light = new THREE.DirectionalLight(new THREE.Color(fill.color), fill.intensity);
40
+ light.position.set(...fill.position);
41
+ this.add(light);
42
+ }
43
+ }
44
+ dispose() {
45
+ this.clear();
46
+ }
47
+ add(light) {
48
+ this.scene.add(light);
49
+ this.lights.push(light);
50
+ }
51
+ clear() {
52
+ this.lights.forEach((light) => {
53
+ if (light.parent)
54
+ light.parent.remove(light);
55
+ });
56
+ this.lights = [];
57
+ }
58
+ }
@@ -0,0 +1,20 @@
1
+ import * as THREE from "three";
2
+ import type { PostFxConfig } from "../engine/types";
3
+ export declare class PostFxSystem {
4
+ private renderer;
5
+ private scene;
6
+ private camera;
7
+ private composer;
8
+ private renderPass;
9
+ private aoPass;
10
+ private enabled;
11
+ constructor(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera);
12
+ apply(config?: PostFxConfig): void;
13
+ render(): void;
14
+ resize(width: number, height: number): void;
15
+ dispose(): void;
16
+ getState(): {
17
+ aoEnabled: boolean;
18
+ };
19
+ }
20
+ //# sourceMappingURL=postfxSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postfxSystem.d.ts","sourceRoot":"","sources":["../../src/systems/postfxSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,OAAO,CAAS;gBAEZ,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,iBAAiB;IAM9F,KAAK,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI;IA6BlC,MAAM,IAAI,IAAI;IAQd,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAS3C,OAAO,IAAI,IAAI;IAUf,QAAQ,IAAI;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE;CAGnC"}
@@ -0,0 +1,73 @@
1
+ import { EffectComposer, RenderPass } from "postprocessing";
2
+ import { N8AOPostPass } from "n8ao";
3
+ export class PostFxSystem {
4
+ renderer;
5
+ scene;
6
+ camera;
7
+ composer = null;
8
+ renderPass = null;
9
+ aoPass = null;
10
+ enabled = false;
11
+ constructor(renderer, scene, camera) {
12
+ this.renderer = renderer;
13
+ this.scene = scene;
14
+ this.camera = camera;
15
+ }
16
+ apply(config) {
17
+ const ao = config?.ao;
18
+ const shouldEnable = Boolean(ao?.enabled);
19
+ if (!shouldEnable) {
20
+ this.enabled = false;
21
+ if (this.aoPass)
22
+ this.aoPass.enabled = false;
23
+ return;
24
+ }
25
+ if (!this.composer) {
26
+ this.composer = new EffectComposer(this.renderer);
27
+ this.renderPass = new RenderPass(this.scene, this.camera);
28
+ this.composer.addPass(this.renderPass);
29
+ this.aoPass = new N8AOPostPass(this.scene, this.camera, this.renderer.domElement.width, this.renderer.domElement.height);
30
+ this.composer.addPass(this.aoPass);
31
+ }
32
+ this.enabled = true;
33
+ if (this.aoPass) {
34
+ this.aoPass.enabled = true;
35
+ if (ao?.intensity !== undefined)
36
+ this.aoPass.configuration.intensity = ao.intensity;
37
+ if (ao?.radius !== undefined)
38
+ this.aoPass.configuration.aoRadius = ao.radius;
39
+ if (ao?.distanceFalloff !== undefined)
40
+ this.aoPass.configuration.distanceFalloff = ao.distanceFalloff;
41
+ if (ao?.halfRes !== undefined)
42
+ this.aoPass.configuration.halfRes = ao.halfRes;
43
+ this.aoPass.firstFrame?.();
44
+ }
45
+ }
46
+ render() {
47
+ if (this.enabled && this.composer) {
48
+ this.composer.render();
49
+ return;
50
+ }
51
+ this.renderer.render(this.scene, this.camera);
52
+ }
53
+ resize(width, height) {
54
+ if (this.composer) {
55
+ this.composer.setSize(width, height);
56
+ }
57
+ if (this.aoPass) {
58
+ this.aoPass.setSize(width, height);
59
+ }
60
+ }
61
+ dispose() {
62
+ if (this.composer) {
63
+ this.composer.dispose();
64
+ }
65
+ this.renderPass = null;
66
+ this.aoPass = null;
67
+ this.composer = null;
68
+ this.enabled = false;
69
+ }
70
+ getState() {
71
+ return { aoEnabled: Boolean(this.aoPass?.enabled) };
72
+ }
73
+ }
@@ -0,0 +1,4 @@
1
+ import * as THREE from "three";
2
+ import type { RendererConfig } from "../engine/types";
3
+ export declare function applyRendererConfig(renderer: THREE.WebGLRenderer, config?: RendererConfig): void;
4
+ //# sourceMappingURL=rendererSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendererSystem.d.ts","sourceRoot":"","sources":["../../src/systems/rendererSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,iBAAiB,CAAC;AAsBvE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAuBhG"}
@@ -0,0 +1,42 @@
1
+ import * as THREE from "three";
2
+ const DEFAULT_CONFIG = {
3
+ toneMapping: "neutral",
4
+ toneMappingExposure: 1.0,
5
+ outputColorSpace: "srgb",
6
+ maxPixelRatio: 1.75,
7
+ antialias: true,
8
+ alpha: true,
9
+ preserveDrawingBuffer: false,
10
+ shadowMap: {
11
+ enabled: true,
12
+ type: "pcfsoft",
13
+ },
14
+ };
15
+ const toneMappingMap = {
16
+ neutral: THREE.NeutralToneMapping,
17
+ aces: THREE.ACESFilmicToneMapping,
18
+ linear: THREE.LinearToneMapping,
19
+ };
20
+ export function applyRendererConfig(renderer, config) {
21
+ const merged = {
22
+ ...DEFAULT_CONFIG,
23
+ ...(config ?? {}),
24
+ shadowMap: { ...DEFAULT_CONFIG.shadowMap, ...(config?.shadowMap ?? {}) },
25
+ };
26
+ const targetRatio = typeof window !== "undefined" ? window.devicePixelRatio : 1;
27
+ renderer.setPixelRatio(Math.min(targetRatio, merged.maxPixelRatio));
28
+ renderer.outputColorSpace =
29
+ merged.outputColorSpace === "linear" ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
30
+ renderer.toneMapping = toneMappingMap[merged.toneMapping];
31
+ renderer.toneMappingExposure = merged.toneMappingExposure;
32
+ renderer.shadowMap.enabled = merged.shadowMap.enabled ?? true;
33
+ if (merged.shadowMap.type === "pcf") {
34
+ renderer.shadowMap.type = THREE.PCFShadowMap;
35
+ }
36
+ else if (merged.shadowMap.type === "basic") {
37
+ renderer.shadowMap.type = THREE.BasicShadowMap;
38
+ }
39
+ else {
40
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
41
+ }
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base2datadesign/viewer-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/index.js",
@@ -17,6 +17,17 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
+ "peerDependencies": {
21
+ "three": "^0.180.0",
22
+ "postprocessing": "^6.37.8",
23
+ "n8ao": "^1.10.1"
24
+ },
25
+ "devDependencies": {
26
+ "@types/three": "^0.178.1",
27
+ "three": "^0.180.0",
28
+ "postprocessing": "^6.37.8",
29
+ "n8ao": "^1.10.1"
30
+ },
20
31
  "scripts": {
21
32
  "build": "tsc -b",
22
33
  "typecheck": "tsc -b --pretty false --noEmit"