@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.
- package/dist/engine/ViewerEngine.d.ts +5 -1
- package/dist/engine/ViewerEngine.d.ts.map +1 -1
- package/dist/engine/ViewerEngine.js +91 -6
- package/dist/engine/types.d.ts +44 -0
- package/dist/engine/types.d.ts.map +1 -1
- package/dist/exports/download.d.ts +2 -0
- package/dist/exports/download.d.ts.map +1 -0
- package/dist/exports/download.js +10 -0
- package/dist/exports/three-export.d.ts +4 -0
- package/dist/exports/three-export.d.ts.map +1 -0
- package/dist/exports/three-export.js +25 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/materials/architectural.js +1 -1
- package/dist/systems/debugSystem.d.ts.map +1 -1
- package/dist/systems/debugSystem.js +188 -35
- package/dist/systems/environmentSystem.d.ts +1 -0
- package/dist/systems/environmentSystem.d.ts.map +1 -1
- package/dist/systems/environmentSystem.js +26 -5
- package/dist/systems/lightingSystem.d.ts +2 -0
- package/dist/systems/lightingSystem.d.ts.map +1 -1
- package/dist/systems/lightingSystem.js +15 -0
- package/dist/systems/postfxSystem.d.ts +4 -0
- package/dist/systems/postfxSystem.d.ts.map +1 -1
- package/dist/systems/postfxSystem.js +51 -6
- package/dist/uploads/grasshopper.d.ts +12 -0
- package/dist/uploads/grasshopper.d.ts.map +1 -0
- package/dist/uploads/grasshopper.js +113 -0
- package/dist/uploads/index.d.ts +8 -0
- package/dist/uploads/index.d.ts.map +1 -0
- package/dist/uploads/index.js +29 -0
- package/dist/uploads/mesh.d.ts +8 -0
- package/dist/uploads/mesh.d.ts.map +1 -0
- package/dist/uploads/mesh.js +109 -0
- package/dist/uploads/rhino3dm.d.ts +7 -0
- package/dist/uploads/rhino3dm.d.ts.map +1 -0
- package/dist/uploads/rhino3dm.js +33 -0
- package/dist/uploads/types.d.ts +67 -0
- package/dist/uploads/types.d.ts.map +1 -0
- package/dist/uploads/types.js +1 -0
- 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:
|
|
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
|
-
|
|
18
|
-
|
|
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:
|
|
22
|
-
roughness: extras.groundRoughness ?? 0.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
182
|
+
else if (gridMaterial) {
|
|
183
|
+
setTransparency(gridMaterial);
|
|
60
184
|
}
|
|
61
|
-
this.add(
|
|
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 (
|
|
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;
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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;
|
|
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;
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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"}
|