@base2datadesign/viewer-kit 0.2.0 → 0.2.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.
Files changed (42) hide show
  1. package/dist/engine/ViewerEngine.d.ts +5 -1
  2. package/dist/engine/ViewerEngine.d.ts.map +1 -1
  3. package/dist/engine/ViewerEngine.js +91 -6
  4. package/dist/engine/types.d.ts +44 -0
  5. package/dist/engine/types.d.ts.map +1 -1
  6. package/dist/exports/download.d.ts +2 -0
  7. package/dist/exports/download.d.ts.map +1 -0
  8. package/dist/exports/download.js +10 -0
  9. package/dist/exports/three-export.d.ts +4 -0
  10. package/dist/exports/three-export.d.ts.map +1 -0
  11. package/dist/exports/three-export.js +25 -0
  12. package/dist/index.d.ts +5 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +3 -0
  15. package/dist/materials/architectural.js +1 -1
  16. package/dist/systems/debugSystem.d.ts.map +1 -1
  17. package/dist/systems/debugSystem.js +188 -35
  18. package/dist/systems/environmentSystem.d.ts +1 -0
  19. package/dist/systems/environmentSystem.d.ts.map +1 -1
  20. package/dist/systems/environmentSystem.js +26 -5
  21. package/dist/systems/lightingSystem.d.ts +2 -0
  22. package/dist/systems/lightingSystem.d.ts.map +1 -1
  23. package/dist/systems/lightingSystem.js +15 -0
  24. package/dist/systems/postfxSystem.d.ts +4 -0
  25. package/dist/systems/postfxSystem.d.ts.map +1 -1
  26. package/dist/systems/postfxSystem.js +51 -6
  27. package/dist/uploads/grasshopper.d.ts +12 -0
  28. package/dist/uploads/grasshopper.d.ts.map +1 -0
  29. package/dist/uploads/grasshopper.js +113 -0
  30. package/dist/uploads/index.d.ts +8 -0
  31. package/dist/uploads/index.d.ts.map +1 -0
  32. package/dist/uploads/index.js +29 -0
  33. package/dist/uploads/mesh.d.ts +8 -0
  34. package/dist/uploads/mesh.d.ts.map +1 -0
  35. package/dist/uploads/mesh.js +109 -0
  36. package/dist/uploads/rhino3dm.d.ts +7 -0
  37. package/dist/uploads/rhino3dm.d.ts.map +1 -0
  38. package/dist/uploads/rhino3dm.js +33 -0
  39. package/dist/uploads/types.d.ts +67 -0
  40. package/dist/uploads/types.d.ts.map +1 -0
  41. package/dist/uploads/types.js +1 -0
  42. package/package.json +4 -1
@@ -2,9 +2,133 @@ import * as THREE from "three";
2
2
  const DEFAULT_EXTRAS = {
3
3
  grid: true,
4
4
  ground: true,
5
- axes: false,
5
+ axes: true,
6
6
  origin: true,
7
7
  };
8
+ const DEFAULT_GRID_SIZE = 80;
9
+ const DEFAULT_GRID_DIVISIONS = 80;
10
+ const DEFAULT_GRID_OPACITY = 0.15;
11
+ const DEFAULT_GRID_HEIGHT_OFFSET = 0.099;
12
+ const HDR_GROUND_SIZE = 60;
13
+ const AXIS_COLORS = {
14
+ x: 0xef4444,
15
+ y: 0x22c55e,
16
+ z: 0x3b82f6,
17
+ };
18
+ function createCanvasLabel(text, color) {
19
+ if (typeof document === "undefined")
20
+ return null;
21
+ const canvas = document.createElement("canvas");
22
+ const size = 128;
23
+ canvas.width = canvas.height = size;
24
+ const ctx = canvas.getContext("2d");
25
+ if (!ctx)
26
+ return null;
27
+ ctx.clearRect(0, 0, size, size);
28
+ ctx.font = 'bold 68px "IBM Plex Mono", monospace';
29
+ ctx.textAlign = "center";
30
+ ctx.textBaseline = "middle";
31
+ ctx.fillStyle = new THREE.Color(color).getStyle();
32
+ ctx.fillText(text, size / 2, size / 2 + 6);
33
+ const texture = new THREE.CanvasTexture(canvas);
34
+ texture.generateMipmaps = false;
35
+ texture.needsUpdate = true;
36
+ return texture;
37
+ }
38
+ function createAxisLabelSprite(text, color, scale) {
39
+ const texture = createCanvasLabel(text, color);
40
+ if (!texture)
41
+ return null;
42
+ const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false, depthWrite: false });
43
+ const sprite = new THREE.Sprite(material);
44
+ sprite.scale.set(scale, scale, scale);
45
+ sprite.renderOrder = 5;
46
+ return sprite;
47
+ }
48
+ function buildAxisHelper(extras) {
49
+ const group = new THREE.Group();
50
+ const length = 1.8;
51
+ const coneHeight = 0.25;
52
+ const coneRadius = 0.07;
53
+ const labelScale = extras.axisLabelScale ?? 0.35;
54
+ const labelConfig = extras.axesLabels ?? {};
55
+ const lowerEnabled = labelConfig.lowerCase !== false;
56
+ const upperEnabled = labelConfig.upperCase !== false;
57
+ const lowerOffset = labelConfig.offset ?? 0.35;
58
+ const upperOffset = lowerOffset + (labelConfig.secondaryOffset ?? 0.2);
59
+ const rhinoAxisMap = { x: "X", y: "Z", z: "Y" };
60
+ ["x", "y", "z"].forEach((axis) => {
61
+ const dir = new THREE.Vector3(axis === "x" ? 1 : 0, axis === "y" ? 1 : 0, axis === "z" ? 1 : 0);
62
+ const color = AXIS_COLORS[axis];
63
+ const lineGeometry = new THREE.BufferGeometry().setFromPoints([
64
+ new THREE.Vector3(0, 0, 0),
65
+ dir.clone().multiplyScalar(length),
66
+ ]);
67
+ const lineMaterial = new THREE.LineBasicMaterial({ color, linewidth: 2 });
68
+ const line = new THREE.Line(lineGeometry, lineMaterial);
69
+ group.add(line);
70
+ const cone = new THREE.Mesh(new THREE.ConeGeometry(coneRadius, coneHeight, 16), new THREE.MeshBasicMaterial({ color }));
71
+ cone.position.copy(dir.clone().multiplyScalar(length));
72
+ cone.lookAt(dir.clone().multiplyScalar(length + 0.5));
73
+ group.add(cone);
74
+ if (lowerEnabled) {
75
+ const lowerLabel = createAxisLabelSprite(axis, color, labelScale);
76
+ if (lowerLabel) {
77
+ lowerLabel.position.copy(dir.clone().multiplyScalar(length + lowerOffset));
78
+ group.add(lowerLabel);
79
+ }
80
+ }
81
+ if (upperEnabled) {
82
+ const rhinoAxis = rhinoAxisMap[axis];
83
+ const upperLabel = createAxisLabelSprite(rhinoAxis, color, labelScale * 1.1);
84
+ if (upperLabel) {
85
+ const offsetDir = dir.clone().multiplyScalar(length + upperOffset);
86
+ upperLabel.position.copy(offsetDir);
87
+ group.add(upperLabel);
88
+ }
89
+ }
90
+ });
91
+ return group;
92
+ }
93
+ function buildOriginCross(extras, gridSize, groundY) {
94
+ const group = new THREE.Group();
95
+ const length = extras.originLineLength ?? gridSize / 2;
96
+ const thickness = extras.originLineThickness ?? 0.06;
97
+ const color = extras.originLineColor ?? 0x94a3b8;
98
+ const material = new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 0.9, depthWrite: false });
99
+ const yPos = groundY + thickness * 0.5;
100
+ const xLine = new THREE.Mesh(new THREE.BoxGeometry(length * 2, thickness, thickness), material);
101
+ xLine.position.y = yPos;
102
+ const zLine = new THREE.Mesh(new THREE.BoxGeometry(thickness, thickness, length * 2), material.clone());
103
+ zLine.position.y = yPos;
104
+ group.add(xLine, zLine);
105
+ const markerCount = 5;
106
+ const markerThickness = thickness * 0.5;
107
+ for (let i = 1; i <= markerCount; i += 1) {
108
+ const offset = i;
109
+ const tickXPos = offset;
110
+ const tickZPos = offset;
111
+ const xTick = new THREE.Mesh(new THREE.BoxGeometry(markerThickness, thickness * 0.75, thickness * 4), material.clone());
112
+ xTick.position.set(tickXPos, yPos, 0);
113
+ const xTickNeg = xTick.clone();
114
+ xTickNeg.position.x = -tickXPos;
115
+ const zTick = new THREE.Mesh(new THREE.BoxGeometry(thickness * 4, thickness * 0.75, markerThickness), material.clone());
116
+ zTick.position.set(0, yPos, tickZPos);
117
+ const zTickNeg = zTick.clone();
118
+ zTickNeg.position.z = -tickZPos;
119
+ group.add(xTick, xTickNeg, zTick, zTickNeg);
120
+ }
121
+ return group;
122
+ }
123
+ function adjustGridColor(groundHex) {
124
+ const base = new THREE.Color(groundHex);
125
+ const hsl = { h: 0, s: 0, l: 0 };
126
+ base.getHSL(hsl);
127
+ hsl.l = Math.min(1, hsl.l + 0.15);
128
+ const adjusted = new THREE.Color();
129
+ adjusted.setHSL(hsl.h, hsl.s, hsl.l);
130
+ return adjusted.getHex();
131
+ }
8
132
  export class DebugSystem {
9
133
  scene;
10
134
  objects = [];
@@ -14,13 +138,21 @@ export class DebugSystem {
14
138
  apply(preset) {
15
139
  const extras = { ...DEFAULT_EXTRAS, ...(preset.sceneExtras ?? {}) };
16
140
  this.clear();
17
- if (extras.ground) {
18
- const size = extras.groundSize ?? 300;
141
+ const skyMode = preset.sky?.mode ?? "gradient";
142
+ const isHdr = skyMode === "hdr";
143
+ const gradientSkyEnabled = !preset.sky?.mode || skyMode === "gradient";
144
+ const groundEnabled = extras.ground !== false;
145
+ const gridEnabled = extras.grid !== false && gradientSkyEnabled && !isHdr;
146
+ const axesEnabled = extras.axes !== false;
147
+ const originEnabled = extras.origin !== false;
148
+ const groundColor = extras.groundColor ?? 0x2c2f33;
149
+ if (groundEnabled) {
150
+ const size = extras.groundSize ?? (isHdr ? HDR_GROUND_SIZE : 400);
19
151
  const groundGeometry = new THREE.PlaneGeometry(size, size);
20
152
  const groundMaterial = new THREE.MeshStandardMaterial({
21
- color: extras.groundColor ?? 0xf5f5f5,
22
- roughness: extras.groundRoughness ?? 0.75,
23
- metalness: extras.groundMetalness ?? 0.0,
153
+ color: groundColor,
154
+ roughness: extras.groundRoughness ?? (isHdr ? 0.6 : 0.8),
155
+ metalness: extras.groundMetalness ?? (isHdr ? 0.02 : 0.05),
24
156
  toneMapped: false,
25
157
  });
26
158
  const ground = new THREE.Mesh(groundGeometry, groundMaterial);
@@ -29,38 +161,35 @@ export class DebugSystem {
29
161
  ground.receiveShadow = true;
30
162
  this.add(ground);
31
163
  }
32
- if (extras.grid) {
33
- const gridSize = extras.gridSize ?? 100;
34
- const divisions = extras.gridDivisions ?? 50;
35
- const gridHelper = new THREE.GridHelper(gridSize, divisions, extras.gridColor ?? 0xdedede, extras.gridColorSecondary ?? 0xeaeaea);
36
- gridHelper.position.y = extras.gridHeightOffset ?? -0.09;
37
- const gridOpacity = extras.gridOpacity;
38
- if (typeof gridOpacity === "number") {
39
- const material = gridHelper.material;
40
- const applyOpacity = (mat) => {
41
- mat.opacity = gridOpacity;
42
- mat.transparent = gridOpacity < 1;
43
- };
44
- if (Array.isArray(material)) {
45
- material.forEach(applyOpacity);
46
- }
47
- else if (material) {
48
- applyOpacity(material);
49
- }
50
- }
51
- this.add(gridHelper);
52
- }
53
- if (extras.axes) {
54
- const axes = new THREE.AxesHelper(3);
55
- if (extras.axesPosition) {
56
- axes.position.set(...extras.axesPosition);
164
+ let lastGridPositionY = 0;
165
+ if (gridEnabled) {
166
+ const gridPrimary = new THREE.Color(extras.gridColor ?? adjustGridColor(groundColor));
167
+ const gridSecondary = new THREE.Color(extras.gridColorSecondary ?? gridPrimary.getHex());
168
+ const gridSize = extras.gridSize ?? DEFAULT_GRID_SIZE;
169
+ const gridDivisions = extras.gridDivisions ?? Math.max(1, gridSize);
170
+ const grid = new THREE.GridHelper(gridSize, gridDivisions, gridPrimary, gridSecondary);
171
+ const offset = extras.gridHeightOffset ?? DEFAULT_GRID_HEIGHT_OFFSET;
172
+ grid.position.y = groundEnabled ? -offset : -offset * 0.9;
173
+ lastGridPositionY = grid.position.y;
174
+ const gridMaterial = grid.material;
175
+ const setTransparency = (mat) => {
176
+ mat.transparent = true;
177
+ mat.opacity = extras.gridOpacity ?? DEFAULT_GRID_OPACITY;
178
+ };
179
+ if (Array.isArray(gridMaterial)) {
180
+ gridMaterial.forEach((mat) => setTransparency(mat));
57
181
  }
58
- else {
59
- axes.position.set(-15, 0.1, -15);
182
+ else if (gridMaterial) {
183
+ setTransparency(gridMaterial);
60
184
  }
61
- this.add(axes);
185
+ this.add(grid);
186
+ }
187
+ const originPlaneY = groundEnabled ? -0.11 : lastGridPositionY || -(extras.gridHeightOffset ?? DEFAULT_GRID_HEIGHT_OFFSET);
188
+ if (extras.originHighlight !== false) {
189
+ const originCross = buildOriginCross(extras, extras.gridSize ?? DEFAULT_GRID_SIZE, originPlaneY);
190
+ this.add(originCross);
62
191
  }
63
- if (extras.origin) {
192
+ if (originEnabled) {
64
193
  const originSize = extras.originSize ?? 0.1;
65
194
  const originGeometry = new THREE.SphereGeometry(originSize, 16, 16);
66
195
  const originMaterial = new THREE.MeshBasicMaterial({ color: extras.originColor ?? 0x666666 });
@@ -68,6 +197,9 @@ export class DebugSystem {
68
197
  origin.position.set(0, 0, 0);
69
198
  this.add(origin);
70
199
  }
200
+ if (axesEnabled) {
201
+ this.add(buildAxisHelper(extras));
202
+ }
71
203
  }
72
204
  dispose() {
73
205
  this.clear();
@@ -89,6 +221,27 @@ export class DebugSystem {
89
221
  else {
90
222
  material.dispose?.();
91
223
  }
224
+ return;
225
+ }
226
+ if (object instanceof THREE.Line) {
227
+ object.geometry.dispose();
228
+ const material = object.material;
229
+ if (Array.isArray(material)) {
230
+ material.forEach((mat) => mat.dispose?.());
231
+ }
232
+ else {
233
+ material?.dispose?.();
234
+ }
235
+ return;
236
+ }
237
+ if (object instanceof THREE.Sprite) {
238
+ const material = object.material;
239
+ if (material) {
240
+ if (material.map) {
241
+ material.map.dispose?.();
242
+ }
243
+ material.dispose?.();
244
+ }
92
245
  }
93
246
  });
94
247
  this.objects = [];
@@ -16,6 +16,7 @@ export declare class EnvironmentSystem {
16
16
  private objectUrls;
17
17
  private loadToken;
18
18
  private state;
19
+ private fallbackGradient;
19
20
  constructor(scene: THREE.Scene, renderer: THREE.WebGLRenderer, assetResolver?: AssetResolver);
20
21
  getState(): EnvironmentState;
21
22
  apply(preset: RenderPresetDefinition): Promise<void>;
@@ -1 +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;IAqCtB,OAAO,CAAC,gBAAgB;YA6CV,YAAY;YAaZ,WAAW;YAsDX,YAAY;IAoD1B,OAAO,CAAC,oBAAoB;CAY7B"}
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;IACrG,OAAO,CAAC,gBAAgB,CAMtB;gBAEU,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;IAuC1D,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,gBAAgB;YA6CV,YAAY;YAaZ,WAAW;YAsDX,YAAY;IAoD1B,OAAO,CAAC,oBAAoB;CAY7B"}
@@ -1,5 +1,5 @@
1
1
  import * as THREE from "three";
2
- import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
2
+ import { HDRLoader } from "three/examples/jsm/loaders/HDRLoader.js";
3
3
  export class EnvironmentSystem {
4
4
  scene;
5
5
  renderer;
@@ -11,6 +11,13 @@ export class EnvironmentSystem {
11
11
  objectUrls = [];
12
12
  loadToken = 0;
13
13
  state = { mode: "none", backgroundEnabled: false, lightingEnabled: false };
14
+ fallbackGradient = {
15
+ topColor: "#f8fafc",
16
+ horizonColor: "#e2e8f0",
17
+ sunPosition: [0.3, 0.8, 0.4],
18
+ sunIntensity: 0.12,
19
+ sunSize: 0.06,
20
+ };
14
21
  constructor(scene, renderer, assetResolver) {
15
22
  this.scene = scene;
16
23
  this.renderer = renderer;
@@ -34,11 +41,25 @@ export class EnvironmentSystem {
34
41
  return;
35
42
  }
36
43
  if (sky.mode === "hdr") {
37
- await this.applyHdrSky(sky.hdr);
44
+ try {
45
+ await this.applyHdrSky(sky.hdr);
46
+ }
47
+ catch (error) {
48
+ console.warn("[viewer-kit] HDR sky failed, falling back to gradient.", error);
49
+ this.applyGradientSky(this.fallbackGradient);
50
+ this.state = { mode: "gradient", backgroundEnabled: true, lightingEnabled: false };
51
+ }
38
52
  return;
39
53
  }
40
54
  if (sky.mode === "cube") {
41
- await this.applyCubeSky(sky.cube);
55
+ try {
56
+ await this.applyCubeSky(sky.cube);
57
+ }
58
+ catch (error) {
59
+ console.warn("[viewer-kit] Cube sky failed, falling back to gradient.", error);
60
+ this.applyGradientSky(this.fallbackGradient);
61
+ this.state = { mode: "gradient", backgroundEnabled: true, lightingEnabled: false };
62
+ }
42
63
  }
43
64
  }
44
65
  dispose() {
@@ -147,11 +168,11 @@ export class EnvironmentSystem {
147
168
  const token = ++this.loadToken;
148
169
  const backgroundEnabled = hdr.showBackground !== false;
149
170
  const lightingEnabled = hdr.showLighting !== false;
150
- const usePmrem = hdr.usePmrem !== false;
171
+ const usePmrem = hdr.usePmrem ?? false;
151
172
  const resolved = await this.resolveAsset(hdr.src, "hdr");
152
173
  if (token !== this.loadToken)
153
174
  return;
154
- const loader = new RGBELoader();
175
+ const loader = new HDRLoader();
155
176
  const texture = await loader.loadAsync(resolved);
156
177
  if (token !== this.loadToken) {
157
178
  texture.dispose();
@@ -3,10 +3,12 @@ import type { RenderPresetDefinition } from "../engine/types";
3
3
  export declare class LightingSystem {
4
4
  private scene;
5
5
  private lights;
6
+ private targets;
6
7
  constructor(scene: THREE.Scene);
7
8
  apply(preset: RenderPresetDefinition): void;
8
9
  dispose(): void;
9
10
  private add;
11
+ private addTarget;
10
12
  private clear;
11
13
  private configureShadow;
12
14
  }
@@ -1 +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;IAkD3C,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,eAAe;CAcxB"}
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;IACnC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,KAAK,EAAE,KAAK,CAAC,KAAK;IAI9B,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAuD3C,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,KAAK;IAWb,OAAO,CAAC,eAAe;CAcxB"}
@@ -9,12 +9,14 @@ const DEFAULT_LIGHTING = {
9
9
  export class LightingSystem {
10
10
  scene;
11
11
  lights = [];
12
+ targets = [];
12
13
  constructor(scene) {
13
14
  this.scene = scene;
14
15
  }
15
16
  apply(preset) {
16
17
  this.clear();
17
18
  const config = preset.lighting ?? DEFAULT_LIGHTING;
19
+ const lightTarget = preset.sceneExtras?.lightTarget ?? [0, 1.5, 0];
18
20
  if (config.rig === "none")
19
21
  return;
20
22
  const ambient = config.ambient ?? DEFAULT_LIGHTING.ambient;
@@ -35,12 +37,16 @@ export class LightingSystem {
35
37
  if (light.castShadow) {
36
38
  this.configureShadow(light, config.shadow);
37
39
  }
40
+ light.target.position.set(lightTarget[0], lightTarget[1], lightTarget[2]);
41
+ this.addTarget(light.target);
38
42
  this.add(light);
39
43
  }
40
44
  const fill = config.fill ?? DEFAULT_LIGHTING.fill;
41
45
  if (fill && fill.intensity > 0) {
42
46
  const light = new THREE.DirectionalLight(new THREE.Color(fill.color), fill.intensity);
43
47
  light.position.set(...fill.position);
48
+ light.target.position.set(lightTarget[0], lightTarget[1], lightTarget[2]);
49
+ this.addTarget(light.target);
44
50
  this.add(light);
45
51
  }
46
52
  if (preset.interiorLights?.length) {
@@ -60,12 +66,21 @@ export class LightingSystem {
60
66
  this.scene.add(light);
61
67
  this.lights.push(light);
62
68
  }
69
+ addTarget(target) {
70
+ this.scene.add(target);
71
+ this.targets.push(target);
72
+ }
63
73
  clear() {
64
74
  this.lights.forEach((light) => {
65
75
  if (light.parent)
66
76
  light.parent.remove(light);
67
77
  });
68
78
  this.lights = [];
79
+ this.targets.forEach((target) => {
80
+ if (target.parent)
81
+ target.parent.remove(target);
82
+ });
83
+ this.targets = [];
69
84
  }
70
85
  configureShadow(light, config) {
71
86
  if (!light.shadow)
@@ -11,11 +11,15 @@ export declare class PostFxSystem {
11
11
  private bloomEffect;
12
12
  private vignetteEffect;
13
13
  private enabled;
14
+ private lastWidth;
15
+ private lastHeight;
16
+ private pendingConfig;
14
17
  constructor(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera);
15
18
  apply(config?: PostFxConfig): void;
16
19
  render(): void;
17
20
  resize(width: number, height: number): void;
18
21
  dispose(): void;
22
+ invalidate(): void;
19
23
  getState(): {
20
24
  aoEnabled: boolean;
21
25
  bloomEnabled: boolean;
@@ -1 +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,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,cAAc,CAA+B;IACrD,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;IA6ElC,MAAM,IAAI,IAAI;IAQd,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAS3C,OAAO,IAAI,IAAI;IAaf,QAAQ,IAAI;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,OAAO,CAAA;KAAE;CAOpF"}
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,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAA6B;gBAEtC,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;IA2GlC,MAAM,IAAI,IAAI;IAUd,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAY3C,OAAO,IAAI,IAAI;IAaf,UAAU,IAAI,IAAI;IAMlB,QAAQ,IAAI;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,OAAO,CAAA;KAAE;CAOpF"}
@@ -1,3 +1,4 @@
1
+ import * as THREE from "three";
1
2
  import { BloomEffect, EffectComposer, EffectPass, RenderPass, VignetteEffect } from "postprocessing";
2
3
  import { N8AOPostPass } from "n8ao";
3
4
  export class PostFxSystem {
@@ -11,6 +12,9 @@ export class PostFxSystem {
11
12
  bloomEffect = null;
12
13
  vignetteEffect = null;
13
14
  enabled = false;
15
+ lastWidth = 0;
16
+ lastHeight = 0;
17
+ pendingConfig = null;
14
18
  constructor(renderer, scene, camera) {
15
19
  this.renderer = renderer;
16
20
  this.scene = scene;
@@ -23,21 +27,32 @@ export class PostFxSystem {
23
27
  const shouldEnable = Boolean(ao?.enabled || bloom?.enabled || vignette?.enabled);
24
28
  if (!shouldEnable) {
25
29
  this.enabled = false;
30
+ this.pendingConfig = null;
26
31
  if (this.aoPass)
27
32
  this.aoPass.enabled = false;
28
33
  if (this.effectPass)
29
34
  this.effectPass.enabled = false;
30
35
  return;
31
36
  }
37
+ this.pendingConfig = config ?? null;
38
+ if (this.lastWidth < 2 || this.lastHeight < 2) {
39
+ this.enabled = false;
40
+ return;
41
+ }
32
42
  if (!this.composer) {
33
43
  this.composer = new EffectComposer(this.renderer);
34
44
  this.renderPass = new RenderPass(this.scene, this.camera);
35
45
  this.composer.addPass(this.renderPass);
46
+ this.composer.setSize(this.lastWidth, this.lastHeight);
36
47
  }
48
+ this.composer.autoRenderToScreen = false;
37
49
  if (!this.aoPass) {
38
- this.aoPass = new N8AOPostPass(this.scene, this.camera, this.renderer.domElement.width, this.renderer.domElement.height);
50
+ this.aoPass = new N8AOPostPass(this.scene, this.camera, this.lastWidth, this.lastHeight);
39
51
  this.composer.addPass(this.aoPass);
40
52
  }
53
+ else {
54
+ this.aoPass.setSize(this.lastWidth, this.lastHeight);
55
+ }
41
56
  if (!this.bloomEffect) {
42
57
  this.bloomEffect = new BloomEffect({
43
58
  intensity: bloom?.intensity ?? 0.0,
@@ -52,11 +67,13 @@ export class PostFxSystem {
52
67
  darkness: vignette?.darkness ?? 0.5,
53
68
  });
54
69
  }
55
- if (!this.effectPass) {
70
+ const enableEffectPass = Boolean(bloom?.enabled || vignette?.enabled);
71
+ if (enableEffectPass && !this.effectPass) {
56
72
  this.effectPass = new EffectPass(this.camera, this.bloomEffect, this.vignetteEffect);
57
73
  this.composer.addPass(this.effectPass);
58
74
  }
59
75
  this.enabled = true;
76
+ this.pendingConfig = null;
60
77
  if (this.aoPass) {
61
78
  this.aoPass.enabled = Boolean(ao?.enabled);
62
79
  if (ao?.intensity !== undefined)
@@ -67,6 +84,12 @@ export class PostFxSystem {
67
84
  this.aoPass.configuration.distanceFalloff = ao.distanceFalloff;
68
85
  if (ao?.halfRes !== undefined)
69
86
  this.aoPass.configuration.halfRes = ao.halfRes;
87
+ const quality = (ao?.quality ?? "high").toLowerCase();
88
+ const qualityMode = quality === "low" ? "Low" : quality === "medium" ? "Medium" : "High";
89
+ this.aoPass.setQualityMode(qualityMode);
90
+ if (ao?.color) {
91
+ this.aoPass.configuration.color = new THREE.Color(ao.color);
92
+ }
70
93
  this.aoPass.firstFrame?.();
71
94
  }
72
95
  if (this.bloomEffect) {
@@ -86,22 +109,39 @@ export class PostFxSystem {
86
109
  this.vignetteEffect.darkness = darkness;
87
110
  }
88
111
  if (this.effectPass) {
89
- this.effectPass.enabled = Boolean(bloom?.enabled || vignette?.enabled);
112
+ this.effectPass.enabled = enableEffectPass;
113
+ }
114
+ // Ensure the last enabled pass renders to screen.
115
+ const passes = [this.renderPass, this.aoPass, this.effectPass].filter(Boolean);
116
+ passes.forEach((pass) => {
117
+ pass.renderToScreen = false;
118
+ });
119
+ const enabledPasses = passes.filter((pass) => pass.enabled);
120
+ const lastPass = enabledPasses[enabledPasses.length - 1];
121
+ if (lastPass) {
122
+ lastPass.renderToScreen = true;
90
123
  }
91
124
  }
92
125
  render() {
93
126
  if (this.enabled && this.composer) {
127
+ // EffectComposer disables autoClear; force a clean frame to avoid AO ghosting.
128
+ this.renderer.clear(true, true, true);
94
129
  this.composer.render();
95
130
  return;
96
131
  }
97
132
  this.renderer.render(this.scene, this.camera);
98
133
  }
99
134
  resize(width, height) {
100
- if (this.composer) {
135
+ this.lastWidth = width;
136
+ this.lastHeight = height;
137
+ if (this.composer)
101
138
  this.composer.setSize(width, height);
102
- }
103
- if (this.aoPass) {
139
+ if (this.aoPass)
104
140
  this.aoPass.setSize(width, height);
141
+ if (this.pendingConfig) {
142
+ const pending = this.pendingConfig;
143
+ this.pendingConfig = null;
144
+ this.apply(pending);
105
145
  }
106
146
  }
107
147
  dispose() {
@@ -116,6 +156,11 @@ export class PostFxSystem {
116
156
  this.composer = null;
117
157
  this.enabled = false;
118
158
  }
159
+ invalidate() {
160
+ if (this.aoPass) {
161
+ this.aoPass.firstFrame?.();
162
+ }
163
+ }
119
164
  getState() {
120
165
  return {
121
166
  aoEnabled: Boolean(this.aoPass?.enabled),
@@ -0,0 +1,12 @@
1
+ import type { GrasshopperInput, GrasshopperSolveResult, GrasshopperValue } from "./types";
2
+ export type SolveGrasshopperOptions = {
3
+ apiRoute?: string;
4
+ values?: GrasshopperValue[];
5
+ preview?: Record<string, unknown>;
6
+ onStatus?: (status: string) => void;
7
+ };
8
+ export declare function solveGrasshopperFile(file: File, { apiRoute, values, preview, onStatus }?: SolveGrasshopperOptions): Promise<GrasshopperSolveResult>;
9
+ export declare const mapGrasshopperInputType: (inputType?: string) => string;
10
+ export declare const buildGrasshopperValues: (inputs: GrasshopperInput[], valuesByKey: Record<string, unknown>, keyResolver?: (input: GrasshopperInput, index: number) => string) => GrasshopperValue[];
11
+ export declare const buildGrasshopperDefaults: (inputs: GrasshopperInput[], keyResolver?: (input: GrasshopperInput, index: number) => string) => Record<string, unknown>;
12
+ //# sourceMappingURL=grasshopper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grasshopper.d.ts","sourceRoot":"","sources":["../../src/uploads/grasshopper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE1F,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAuBF,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,IAAI,EACV,EAAE,QAA0B,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAE,uBAA4B,GACtF,OAAO,CAAC,sBAAsB,CAAC,CA+BjC;AAED,eAAO,MAAM,uBAAuB,GAAI,YAAY,MAAM,KAAG,MAM5D,CAAC;AAsBF,eAAO,MAAM,sBAAsB,GACjC,QAAQ,gBAAgB,EAAE,EAC1B,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,KAC/D,gBAAgB,EAWlB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,gBAAgB,EAAE,EAC1B,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,KAC/D,MAAM,CAAC,MAAM,EAAE,OAAO,CAexB,CAAC"}