three-cad-viewer 4.3.4 → 4.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scene/clipping.d.ts +6 -0
- package/dist/three-cad-viewer.esm.js +20 -5
- package/dist/three-cad-viewer.esm.js.map +1 -1
- package/dist/three-cad-viewer.esm.min.js +1 -1
- package/dist/three-cad-viewer.js +20 -5
- package/dist/three-cad-viewer.min.js +1 -1
- package/package.json +2 -3
- package/src/_version.ts +0 -1
- package/src/camera/camera.ts +0 -445
- package/src/camera/controls/CADOrbitControls.ts +0 -241
- package/src/camera/controls/CADTrackballControls.ts +0 -598
- package/src/camera/controls.ts +0 -380
- package/src/core/patches.ts +0 -16
- package/src/core/studio-manager.ts +0 -652
- package/src/core/types.ts +0 -892
- package/src/core/viewer-state.ts +0 -784
- package/src/core/viewer.ts +0 -4821
- package/src/index.ts +0 -151
- package/src/rendering/environment.ts +0 -840
- package/src/rendering/light-detection.ts +0 -327
- package/src/rendering/material-factory.ts +0 -735
- package/src/rendering/material-presets.ts +0 -289
- package/src/rendering/raycast.ts +0 -291
- package/src/rendering/room-environment.ts +0 -192
- package/src/rendering/studio-composer.ts +0 -577
- package/src/rendering/studio-floor.ts +0 -108
- package/src/rendering/texture-cache.ts +0 -324
- package/src/rendering/tree-model.ts +0 -542
- package/src/rendering/triplanar.ts +0 -329
- package/src/scene/animation.ts +0 -343
- package/src/scene/axes.ts +0 -108
- package/src/scene/bbox.ts +0 -223
- package/src/scene/clipping.ts +0 -640
- package/src/scene/grid.ts +0 -864
- package/src/scene/nestedgroup.ts +0 -1444
- package/src/scene/objectgroup.ts +0 -866
- package/src/scene/orientation.ts +0 -259
- package/src/scene/render-shape.ts +0 -634
- package/src/tools/cad_tools/measure.ts +0 -811
- package/src/tools/cad_tools/select.ts +0 -100
- package/src/tools/cad_tools/tools.ts +0 -231
- package/src/tools/cad_tools/ui.ts +0 -454
- package/src/tools/cad_tools/zebra.ts +0 -369
- package/src/types/html.d.ts +0 -5
- package/src/types/n8ao.d.ts +0 -28
- package/src/types/three-augmentation.d.ts +0 -60
- package/src/ui/display.ts +0 -3295
- package/src/ui/index.html +0 -505
- package/src/ui/info.ts +0 -177
- package/src/ui/slider.ts +0 -206
- package/src/ui/toolbar.ts +0 -347
- package/src/ui/treeview.ts +0 -945
- package/src/utils/decode-instances.ts +0 -233
- package/src/utils/font.ts +0 -60
- package/src/utils/gpu-tracker.ts +0 -265
- package/src/utils/logger.ts +0 -92
- package/src/utils/sizeof.ts +0 -116
- package/src/utils/timer.ts +0 -69
- package/src/utils/utils.ts +0 -446
|
@@ -1,735 +0,0 @@
|
|
|
1
|
-
import * as THREE from "three";
|
|
2
|
-
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
|
|
3
|
-
import type { ColorValue, MaterialAppearance } from "../core/types.js";
|
|
4
|
-
import { gpuTracker } from "../utils/gpu-tracker.js";
|
|
5
|
-
import { logger } from "../utils/logger.js";
|
|
6
|
-
import { getColorSpaceForMap } from "./texture-cache.js";
|
|
7
|
-
|
|
8
|
-
/** threejs-materials property keys that hold [r,g,b] color arrays (linear RGB). */
|
|
9
|
-
const COLOR_ARRAY_KEYS = new Set([
|
|
10
|
-
"color", "specularColor", "sheenColor", "emissive", "attenuationColor",
|
|
11
|
-
]);
|
|
12
|
-
|
|
13
|
-
/** Map from threejs-materials property names to Three.js texture map property names. */
|
|
14
|
-
const PROPERTY_TO_MAP: Record<string, string> = {
|
|
15
|
-
color: "map",
|
|
16
|
-
metalness: "metalnessMap",
|
|
17
|
-
roughness: "roughnessMap",
|
|
18
|
-
normal: "normalMap",
|
|
19
|
-
emissive: "emissiveMap",
|
|
20
|
-
specularIntensity: "specularIntensityMap",
|
|
21
|
-
specularColor: "specularColorMap",
|
|
22
|
-
clearcoat: "clearcoatMap",
|
|
23
|
-
clearcoatRoughness: "clearcoatRoughnessMap",
|
|
24
|
-
clearcoatNormal: "clearcoatNormalMap",
|
|
25
|
-
transmission: "transmissionMap",
|
|
26
|
-
sheenColor: "sheenColorMap",
|
|
27
|
-
sheenRoughness: "sheenRoughnessMap",
|
|
28
|
-
anisotropy: "anisotropyMap",
|
|
29
|
-
iridescence: "iridescenceMap",
|
|
30
|
-
iridescenceThickness: "iridescenceThicknessMap",
|
|
31
|
-
ao: "aoMap",
|
|
32
|
-
occlusion: "aoMap",
|
|
33
|
-
thickness: "thicknessMap",
|
|
34
|
-
opacity: "alphaMap",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Options for MaterialFactory constructor
|
|
39
|
-
*/
|
|
40
|
-
interface MaterialFactoryOptions {
|
|
41
|
-
defaultOpacity?: number;
|
|
42
|
-
metalness?: number;
|
|
43
|
-
roughness?: number;
|
|
44
|
-
edgeColor?: number;
|
|
45
|
-
transparent?: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Base material properties
|
|
50
|
-
*/
|
|
51
|
-
interface BaseProps {
|
|
52
|
-
polygonOffset: boolean;
|
|
53
|
-
polygonOffsetFactor: number;
|
|
54
|
-
polygonOffsetUnits: number;
|
|
55
|
-
transparent: boolean;
|
|
56
|
-
opacity: number;
|
|
57
|
-
depthWrite: boolean;
|
|
58
|
-
depthTest: boolean;
|
|
59
|
-
clipIntersection: boolean;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Options for face materials
|
|
64
|
-
*/
|
|
65
|
-
interface FaceMaterialOptions {
|
|
66
|
-
color: ColorValue;
|
|
67
|
-
alpha: number;
|
|
68
|
-
visible?: boolean;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Options for back face materials
|
|
73
|
-
*/
|
|
74
|
-
interface BackFaceMaterialOptions extends FaceMaterialOptions {
|
|
75
|
-
polygonOffsetUnits?: number;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Options for edge materials
|
|
80
|
-
*/
|
|
81
|
-
interface EdgeMaterialOptions {
|
|
82
|
-
lineWidth: number;
|
|
83
|
-
color?: ColorValue | null;
|
|
84
|
-
vertexColors?: boolean;
|
|
85
|
-
visible?: boolean;
|
|
86
|
-
resolution?: { width: number; height: number };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Options for simple edge materials
|
|
91
|
-
*/
|
|
92
|
-
interface SimpleEdgeMaterialOptions {
|
|
93
|
-
color?: ColorValue | null;
|
|
94
|
-
visible?: boolean;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Options for vertex materials
|
|
99
|
-
*/
|
|
100
|
-
interface VertexMaterialOptions {
|
|
101
|
-
size: number;
|
|
102
|
-
color?: ColorValue | null;
|
|
103
|
-
visible?: boolean;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Options for texture materials
|
|
108
|
-
*/
|
|
109
|
-
interface TextureMaterialOptions {
|
|
110
|
-
texture: THREE.Texture;
|
|
111
|
-
visible?: boolean;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Interface for the TextureCache dependency.
|
|
116
|
-
* The actual TextureCache class is defined in texture-cache.ts.
|
|
117
|
-
* We depend only on its get() method for loose coupling.
|
|
118
|
-
*/
|
|
119
|
-
interface TextureCacheInterface {
|
|
120
|
-
get(ref: string, textureRole: string): Promise<THREE.Texture | null>;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Options for Studio mode materials.
|
|
125
|
-
*/
|
|
126
|
-
interface StudioMaterialOptions {
|
|
127
|
-
/** Resolved MaterialAppearance definition (already looked up from materials table / presets) */
|
|
128
|
-
materialDef: MaterialAppearance;
|
|
129
|
-
/** Fallback CSS hex color from the leaf node (e.g., "#cc0000") */
|
|
130
|
-
fallbackColor: ColorValue;
|
|
131
|
-
/** Fallback alpha from the leaf node (0-1) */
|
|
132
|
-
fallbackAlpha: number;
|
|
133
|
-
/** TextureCache for resolving texture references */
|
|
134
|
-
textureCache: TextureCacheInterface | null;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Options for updating factory settings
|
|
139
|
-
*/
|
|
140
|
-
interface UpdateOptions {
|
|
141
|
-
metalness?: number;
|
|
142
|
-
roughness?: number;
|
|
143
|
-
transparent?: boolean;
|
|
144
|
-
defaultOpacity?: number;
|
|
145
|
-
edgeColor?: number;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Factory for creating THREE.js materials with consistent CAD viewer settings.
|
|
150
|
-
* Centralizes material creation for testability and consistency.
|
|
151
|
-
*/
|
|
152
|
-
class MaterialFactory {
|
|
153
|
-
defaultOpacity: number;
|
|
154
|
-
metalness: number;
|
|
155
|
-
roughness: number;
|
|
156
|
-
edgeColor: number;
|
|
157
|
-
transparent: boolean;
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Create a MaterialFactory instance.
|
|
161
|
-
*/
|
|
162
|
-
constructor(options: MaterialFactoryOptions = {}) {
|
|
163
|
-
this.defaultOpacity = options.defaultOpacity ?? 1.0;
|
|
164
|
-
this.metalness = options.metalness ?? 0.3;
|
|
165
|
-
this.roughness = options.roughness ?? 0.65;
|
|
166
|
-
this.edgeColor = options.edgeColor ?? 0x707070;
|
|
167
|
-
this.transparent = options.transparent ?? false;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Create base properties shared by all face materials.
|
|
172
|
-
*/
|
|
173
|
-
private _createBaseProps(alpha: number): BaseProps {
|
|
174
|
-
return {
|
|
175
|
-
polygonOffset: true,
|
|
176
|
-
polygonOffsetFactor: 1.0,
|
|
177
|
-
polygonOffsetUnits: 1.0,
|
|
178
|
-
transparent: true,
|
|
179
|
-
opacity: this.transparent ? this.defaultOpacity * alpha : alpha,
|
|
180
|
-
depthWrite: !this.transparent,
|
|
181
|
-
depthTest: true,
|
|
182
|
-
clipIntersection: false,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Create a standard material for front faces with PBR properties.
|
|
188
|
-
*/
|
|
189
|
-
createFrontFaceMaterial({ color, alpha, visible = true }: FaceMaterialOptions, label?: string): THREE.MeshStandardMaterial {
|
|
190
|
-
const material = new THREE.MeshStandardMaterial({
|
|
191
|
-
...this._createBaseProps(alpha),
|
|
192
|
-
color: color,
|
|
193
|
-
metalness: this.metalness,
|
|
194
|
-
roughness: this.roughness,
|
|
195
|
-
flatShading: false,
|
|
196
|
-
side: THREE.FrontSide,
|
|
197
|
-
visible: visible,
|
|
198
|
-
});
|
|
199
|
-
gpuTracker.track("material", material, label ?? "MeshStandardMaterial (front face)");
|
|
200
|
-
return material;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Create a standard material for back faces with PBR properties.
|
|
205
|
-
* Used for polygon rendering where back faces need full shading.
|
|
206
|
-
*/
|
|
207
|
-
createBackFaceStandardMaterial({ color, alpha, polygonOffsetUnits = 2.0, visible = true }: BackFaceMaterialOptions, label?: string): THREE.MeshStandardMaterial {
|
|
208
|
-
const material = new THREE.MeshStandardMaterial({
|
|
209
|
-
...this._createBaseProps(alpha),
|
|
210
|
-
color: color,
|
|
211
|
-
metalness: this.metalness,
|
|
212
|
-
roughness: this.roughness,
|
|
213
|
-
flatShading: false,
|
|
214
|
-
side: THREE.BackSide,
|
|
215
|
-
visible: visible,
|
|
216
|
-
});
|
|
217
|
-
material.polygonOffsetUnits = polygonOffsetUnits;
|
|
218
|
-
gpuTracker.track("material", material, label ?? "MeshStandardMaterial (back face)");
|
|
219
|
-
return material;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Create a basic material for back faces (no lighting/PBR).
|
|
224
|
-
* Used for shape rendering where back faces are simple fills.
|
|
225
|
-
*/
|
|
226
|
-
createBackFaceBasicMaterial({ color, alpha, polygonOffsetUnits = 2.0, visible = true }: BackFaceMaterialOptions, label?: string): THREE.MeshBasicMaterial {
|
|
227
|
-
const material = new THREE.MeshBasicMaterial({
|
|
228
|
-
...this._createBaseProps(alpha),
|
|
229
|
-
color: color,
|
|
230
|
-
side: THREE.BackSide,
|
|
231
|
-
visible: visible,
|
|
232
|
-
});
|
|
233
|
-
material.polygonOffsetUnits = polygonOffsetUnits;
|
|
234
|
-
gpuTracker.track("material", material, label ?? "MeshBasicMaterial (back face)");
|
|
235
|
-
return material;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Create a basic front face material (no PBR, for simple shapes).
|
|
240
|
-
*/
|
|
241
|
-
createBasicFaceMaterial({ color, alpha, visible = true }: FaceMaterialOptions, label?: string): THREE.MeshBasicMaterial {
|
|
242
|
-
const material = new THREE.MeshBasicMaterial({
|
|
243
|
-
...this._createBaseProps(alpha),
|
|
244
|
-
color: color,
|
|
245
|
-
side: THREE.FrontSide,
|
|
246
|
-
visible: visible,
|
|
247
|
-
});
|
|
248
|
-
gpuTracker.track("material", material, label ?? "MeshBasicMaterial (front face)");
|
|
249
|
-
return material;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Create a fat line material (LineMaterial from Three.js examples).
|
|
254
|
-
*/
|
|
255
|
-
createEdgeMaterial({ lineWidth, color, vertexColors = false, visible = true, resolution }: EdgeMaterialOptions, label?: string): LineMaterial {
|
|
256
|
-
const material = new LineMaterial({
|
|
257
|
-
linewidth: lineWidth,
|
|
258
|
-
transparent: true,
|
|
259
|
-
depthWrite: !this.transparent,
|
|
260
|
-
depthTest: !this.transparent,
|
|
261
|
-
clipIntersection: false,
|
|
262
|
-
vertexColors: vertexColors, // boolean, not string "VertexColors"
|
|
263
|
-
toneMapped: false, // critical for correct vertex colors
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
if (!vertexColors) {
|
|
267
|
-
material.color = new THREE.Color(color ?? this.edgeColor);
|
|
268
|
-
}
|
|
269
|
-
material.visible = visible;
|
|
270
|
-
|
|
271
|
-
if (resolution) {
|
|
272
|
-
material.resolution.set(resolution.width, resolution.height);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
gpuTracker.track("material", material, label ?? "LineMaterial (edges)");
|
|
276
|
-
return material;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Create a basic line material for simple edges (e.g., polygon outlines).
|
|
281
|
-
*/
|
|
282
|
-
createSimpleEdgeMaterial({ color, visible = true }: SimpleEdgeMaterialOptions, label?: string): THREE.LineBasicMaterial {
|
|
283
|
-
const material = new THREE.LineBasicMaterial({
|
|
284
|
-
color: color ?? this.edgeColor,
|
|
285
|
-
depthWrite: !this.transparent,
|
|
286
|
-
depthTest: !this.transparent,
|
|
287
|
-
visible: visible,
|
|
288
|
-
});
|
|
289
|
-
gpuTracker.track("material", material, label ?? "LineBasicMaterial (simple edges)");
|
|
290
|
-
return material;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Create a point material for vertex rendering.
|
|
295
|
-
*/
|
|
296
|
-
createVertexMaterial({ size, color, visible = true }: VertexMaterialOptions, label?: string): THREE.PointsMaterial {
|
|
297
|
-
const material = new THREE.PointsMaterial({
|
|
298
|
-
color: color ?? this.edgeColor,
|
|
299
|
-
sizeAttenuation: false,
|
|
300
|
-
size: size,
|
|
301
|
-
transparent: true,
|
|
302
|
-
clipIntersection: false,
|
|
303
|
-
visible: visible,
|
|
304
|
-
});
|
|
305
|
-
gpuTracker.track("material", material, label ?? "PointsMaterial (vertices)");
|
|
306
|
-
return material;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Create a basic material for texture-mapped surfaces.
|
|
311
|
-
*/
|
|
312
|
-
createTextureMaterial({ texture, visible = true }: TextureMaterialOptions, label?: string): THREE.MeshBasicMaterial {
|
|
313
|
-
const material = new THREE.MeshBasicMaterial({
|
|
314
|
-
color: "#ffffff",
|
|
315
|
-
map: texture,
|
|
316
|
-
side: THREE.DoubleSide,
|
|
317
|
-
visible: visible,
|
|
318
|
-
});
|
|
319
|
-
gpuTracker.track("material", material, label ?? "MeshBasicMaterial (textured)");
|
|
320
|
-
return material;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Create a Studio mode material from a resolved MaterialAppearance.
|
|
325
|
-
*
|
|
326
|
-
* Always creates MeshPhysicalMaterial (except when `unlit: true`, which
|
|
327
|
-
* uses MeshBasicMaterial). MeshPhysicalMaterial is a superset of
|
|
328
|
-
* MeshStandardMaterial; when advanced features are off (transmission=0,
|
|
329
|
-
* clearcoat=0, sheen=0, etc.), the shader compiles to essentially the
|
|
330
|
-
* same cost.
|
|
331
|
-
*
|
|
332
|
-
* @param options - Studio material options
|
|
333
|
-
* @param label - Optional label for GPU tracking
|
|
334
|
-
* @returns Configured MeshPhysicalMaterial (or MeshBasicMaterial if unlit)
|
|
335
|
-
*/
|
|
336
|
-
async createStudioMaterial(
|
|
337
|
-
{ materialDef, fallbackColor, fallbackAlpha, textureCache }: StudioMaterialOptions,
|
|
338
|
-
label?: string,
|
|
339
|
-
): Promise<THREE.MeshPhysicalMaterial | THREE.MeshBasicMaterial> {
|
|
340
|
-
const def = materialDef;
|
|
341
|
-
const side = def.doubleSided ? THREE.DoubleSide : THREE.FrontSide;
|
|
342
|
-
|
|
343
|
-
// --- Resolve base color and opacity ---
|
|
344
|
-
let baseColor: THREE.Color;
|
|
345
|
-
let opacity: number;
|
|
346
|
-
|
|
347
|
-
if (def.color) {
|
|
348
|
-
if (typeof def.color === "string") {
|
|
349
|
-
// CSS hex string (e.g. "#55a0e3") — THREE.Color parses as sRGB
|
|
350
|
-
baseColor = new THREE.Color(def.color);
|
|
351
|
-
opacity = 1.0;
|
|
352
|
-
} else {
|
|
353
|
-
// sRGB RGBA tuple [R, G, B, A?] (0-1)
|
|
354
|
-
baseColor = new THREE.Color().setRGB(
|
|
355
|
-
def.color[0], def.color[1], def.color[2],
|
|
356
|
-
THREE.SRGBColorSpace,
|
|
357
|
-
);
|
|
358
|
-
opacity = def.color[3] ?? 1.0;
|
|
359
|
-
}
|
|
360
|
-
} else {
|
|
361
|
-
// Fall back to leaf node's CSS hex color + alpha.
|
|
362
|
-
// THREE.Color constructor with a hex number or CSS string produces
|
|
363
|
-
// linear-space values in Three.js r152+.
|
|
364
|
-
baseColor = new THREE.Color(fallbackColor);
|
|
365
|
-
opacity = fallbackAlpha;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// --- Unlit path: MeshBasicMaterial ---
|
|
369
|
-
if (def.unlit) {
|
|
370
|
-
const basicMat = new THREE.MeshBasicMaterial({
|
|
371
|
-
...this._createBaseProps(opacity),
|
|
372
|
-
color: baseColor,
|
|
373
|
-
side,
|
|
374
|
-
});
|
|
375
|
-
// Apply alpha mode to basic material too
|
|
376
|
-
this._applyAlphaMode(basicMat, def, opacity);
|
|
377
|
-
// Resolve base color texture
|
|
378
|
-
if (def.map && textureCache) {
|
|
379
|
-
const tex = await textureCache.get(def.map, "baseColorTexture");
|
|
380
|
-
if (tex) basicMat.map = tex;
|
|
381
|
-
}
|
|
382
|
-
gpuTracker.track("material", basicMat, label ?? "MeshBasicMaterial (studio unlit)");
|
|
383
|
-
return basicMat;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// --- PBR path: MeshPhysicalMaterial ---
|
|
387
|
-
// Studio materials default to opaque (transparent: false). Unlike CAD
|
|
388
|
-
// mode, Studio mode has no clipping and doesn't need the global
|
|
389
|
-
// transparent:true flag. Only BLEND alpha mode enables transparency.
|
|
390
|
-
const isBlend = def.alphaMode === "BLEND" || (!def.alphaMode && opacity < 1.0);
|
|
391
|
-
const material = new THREE.MeshPhysicalMaterial({
|
|
392
|
-
color: baseColor,
|
|
393
|
-
metalness: def.metalness ?? 0.0,
|
|
394
|
-
roughness: def.roughness ?? 0.5,
|
|
395
|
-
flatShading: false,
|
|
396
|
-
side,
|
|
397
|
-
transparent: isBlend,
|
|
398
|
-
opacity: opacity,
|
|
399
|
-
depthWrite: !isBlend,
|
|
400
|
-
depthTest: true,
|
|
401
|
-
polygonOffset: true,
|
|
402
|
-
polygonOffsetFactor: 1.0,
|
|
403
|
-
polygonOffsetUnits: 1.0,
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
// --- Alpha mode ---
|
|
407
|
-
this._applyAlphaMode(material, def, opacity);
|
|
408
|
-
|
|
409
|
-
// --- Emissive ---
|
|
410
|
-
if (def.emissive) {
|
|
411
|
-
material.emissive = new THREE.Color(def.emissive[0], def.emissive[1], def.emissive[2]);
|
|
412
|
-
}
|
|
413
|
-
if (def.emissiveIntensity !== undefined) {
|
|
414
|
-
material.emissiveIntensity = def.emissiveIntensity;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// --- Transmission (glass, water) ---
|
|
418
|
-
// Transmission uses a separate render target in Three.js and must NOT
|
|
419
|
-
// be combined with alpha blending (transparent: true). Override here
|
|
420
|
-
// so users don't need to set alphaMode: "OPAQUE" manually.
|
|
421
|
-
if (def.transmission !== undefined) {
|
|
422
|
-
material.transmission = def.transmission;
|
|
423
|
-
if (def.transmission > 0) {
|
|
424
|
-
material.transparent = false;
|
|
425
|
-
material.opacity = 1.0;
|
|
426
|
-
material.depthWrite = true;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// --- Clearcoat (car paint, varnish) ---
|
|
431
|
-
if (def.clearcoat !== undefined) {
|
|
432
|
-
material.clearcoat = def.clearcoat;
|
|
433
|
-
}
|
|
434
|
-
if (def.clearcoatRoughness !== undefined) {
|
|
435
|
-
material.clearcoatRoughness = def.clearcoatRoughness;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// --- Volume (subsurface: jade, wax, skin) ---
|
|
439
|
-
if (def.thickness !== undefined) {
|
|
440
|
-
material.thickness = def.thickness;
|
|
441
|
-
}
|
|
442
|
-
if (def.attenuationDistance !== undefined) {
|
|
443
|
-
material.attenuationDistance = def.attenuationDistance;
|
|
444
|
-
}
|
|
445
|
-
if (def.attenuationColor) {
|
|
446
|
-
material.attenuationColor = new THREE.Color(
|
|
447
|
-
def.attenuationColor[0], def.attenuationColor[1], def.attenuationColor[2],
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// --- IOR ---
|
|
452
|
-
if (def.ior !== undefined) {
|
|
453
|
-
material.ior = def.ior;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// --- Specular ---
|
|
457
|
-
if (def.specularIntensity !== undefined) {
|
|
458
|
-
material.specularIntensity = def.specularIntensity;
|
|
459
|
-
}
|
|
460
|
-
if (def.specularColor) {
|
|
461
|
-
material.specularColor = new THREE.Color(
|
|
462
|
-
def.specularColor[0], def.specularColor[1], def.specularColor[2],
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// --- Sheen (fabric, velvet) ---
|
|
467
|
-
// sheen > 0 enables the sheen layer in Three.js
|
|
468
|
-
if (def.sheen !== undefined && def.sheen > 0) {
|
|
469
|
-
material.sheen = def.sheen;
|
|
470
|
-
if (def.sheenColor) {
|
|
471
|
-
material.sheenColor = new THREE.Color(
|
|
472
|
-
def.sheenColor[0], def.sheenColor[1], def.sheenColor[2],
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
if (def.sheenRoughness !== undefined) {
|
|
476
|
-
material.sheenRoughness = def.sheenRoughness;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// --- Anisotropy (brushed metal) ---
|
|
481
|
-
if (def.anisotropy !== undefined && def.anisotropy > 0) {
|
|
482
|
-
material.anisotropy = def.anisotropy;
|
|
483
|
-
if (def.anisotropyRotation !== undefined) {
|
|
484
|
-
material.anisotropyRotation = def.anisotropyRotation;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// --- Textures ---
|
|
489
|
-
// Resolve all texture references via TextureCache.
|
|
490
|
-
// The TextureCache determines colorSpace internally from the texture role name.
|
|
491
|
-
if (textureCache) {
|
|
492
|
-
await this._applyStudioTextures(material, def, textureCache);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
gpuTracker.track("material", material, label ?? "MeshPhysicalMaterial (studio)");
|
|
496
|
-
return material;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Create a Studio mode material from a threejs-materials format entry.
|
|
501
|
-
*
|
|
502
|
-
* threejs-materials `properties` uses simplified property names (e.g., "color",
|
|
503
|
-
* "roughness", "normal") where each entry has an optional `value` (scalar or
|
|
504
|
-
* [r,g,b] array in **linear RGB**) and/or `texture` (inline data URI).
|
|
505
|
-
*
|
|
506
|
-
* @param values - Scalar PBR values from threejs-materials
|
|
507
|
-
* @param textures - Texture map references from threejs-materials
|
|
508
|
-
* @param textureRepeat - Optional [u, v] texture tiling applied to all loaded textures
|
|
509
|
-
* @param textureCache - TextureCache for resolving data URI textures
|
|
510
|
-
* @param label - Optional label for GPU tracking
|
|
511
|
-
* @returns Configured MeshPhysicalMaterial
|
|
512
|
-
*/
|
|
513
|
-
async createStudioMaterialFromMaterialX(
|
|
514
|
-
values: Record<string, unknown>,
|
|
515
|
-
textures: Record<string, string>,
|
|
516
|
-
textureRepeat: [number, number] | undefined,
|
|
517
|
-
textureCache: TextureCacheInterface | null,
|
|
518
|
-
label?: string,
|
|
519
|
-
): Promise<THREE.MeshPhysicalMaterial> {
|
|
520
|
-
// --- Build material options from scalar values ---
|
|
521
|
-
const matOptions: Record<string, unknown> = {
|
|
522
|
-
flatShading: false,
|
|
523
|
-
side: THREE.FrontSide,
|
|
524
|
-
polygonOffset: true,
|
|
525
|
-
polygonOffsetFactor: 1.0,
|
|
526
|
-
polygonOffsetUnits: 1.0,
|
|
527
|
-
depthTest: true,
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
// Warn once if displacement data is present (not supported in Studio)
|
|
531
|
-
if (textures.displacement || values.displacementScale !== undefined) {
|
|
532
|
-
logger.warn("Displacement not supported by the Studio");
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
for (const [key, value] of Object.entries(values)) {
|
|
536
|
-
// Skip displacement properties (not supported, would waste GPU memory)
|
|
537
|
-
if (key === "displacement" || key === "displacementScale" || key === "displacementBias") continue;
|
|
538
|
-
|
|
539
|
-
// Color arrays → THREE.Color (already linear, no sRGB conversion)
|
|
540
|
-
if (COLOR_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
541
|
-
const [r, g, b] = value as number[];
|
|
542
|
-
matOptions[key] = new THREE.Color(r, g, b);
|
|
543
|
-
} else if ((key === "normalScale" || key === "clearcoatNormalScale") && Array.isArray(value)) {
|
|
544
|
-
matOptions[key] = new THREE.Vector2(value[0], value[1]);
|
|
545
|
-
} else if (key === "iridescenceThicknessRange" && Array.isArray(value)) {
|
|
546
|
-
matOptions[key] = value;
|
|
547
|
-
} else {
|
|
548
|
-
matOptions[key] = value;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// --- Handle transmission ---
|
|
553
|
-
const transmissionVal = values.transmission;
|
|
554
|
-
const opacityVal = values.opacity;
|
|
555
|
-
const transparentVal = values.transparent;
|
|
556
|
-
if (typeof transmissionVal === "number" && transmissionVal > 0) {
|
|
557
|
-
matOptions.transparent = false;
|
|
558
|
-
matOptions.opacity = 1.0;
|
|
559
|
-
matOptions.depthWrite = true;
|
|
560
|
-
} else if (transparentVal === true || (typeof opacityVal === "number" && opacityVal < 1.0)) {
|
|
561
|
-
matOptions.transparent = true;
|
|
562
|
-
matOptions.depthWrite = false;
|
|
563
|
-
} else {
|
|
564
|
-
matOptions.transparent = false;
|
|
565
|
-
matOptions.depthWrite = true;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const material = new THREE.MeshPhysicalMaterial(matOptions);
|
|
569
|
-
|
|
570
|
-
// --- Resolve textures ---
|
|
571
|
-
let hasTextures = false;
|
|
572
|
-
if (textureCache) {
|
|
573
|
-
for (const [key, textureRef] of Object.entries(textures)) {
|
|
574
|
-
const mapName = PROPERTY_TO_MAP[key];
|
|
575
|
-
if (!mapName) continue;
|
|
576
|
-
|
|
577
|
-
// Determine color space from the Three.js map property name.
|
|
578
|
-
// TextureCache.get() expects a role name to decide colorSpace.
|
|
579
|
-
// Bridge from Three.js map name → colorSpace → a proxy role name.
|
|
580
|
-
const colorSpace = getColorSpaceForMap(mapName);
|
|
581
|
-
const roleForCache = colorSpace === THREE.SRGBColorSpace
|
|
582
|
-
? "baseColorTexture"
|
|
583
|
-
: "normalTexture";
|
|
584
|
-
const tex = await textureCache.get(textureRef, roleForCache);
|
|
585
|
-
if (tex) {
|
|
586
|
-
if (textureRepeat) {
|
|
587
|
-
tex.repeat.set(textureRepeat[0], textureRepeat[1]);
|
|
588
|
-
}
|
|
589
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
590
|
-
(material as any)[mapName] = tex;
|
|
591
|
-
hasTextures = true;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Enable alpha cutout when an alphaMap is present
|
|
597
|
-
if (material.alphaMap) {
|
|
598
|
-
material.alphaTest = 0.5;
|
|
599
|
-
material.side = THREE.DoubleSide;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Force shader recompile if textures were assigned post-construction
|
|
603
|
-
if (hasTextures) {
|
|
604
|
-
material.needsUpdate = true;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
gpuTracker.track("material", material, label ?? "MeshPhysicalMaterial (threejs-materials)");
|
|
608
|
-
return material;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Apply alpha mode settings to a material.
|
|
613
|
-
*
|
|
614
|
-
* - OPAQUE: fully opaque, no transparency
|
|
615
|
-
* - MASK: alpha testing with cutoff threshold
|
|
616
|
-
* - BLEND: standard alpha blending
|
|
617
|
-
* - Default (no alphaMode): transparent: true (current viewer behavior)
|
|
618
|
-
*/
|
|
619
|
-
private _applyAlphaMode(
|
|
620
|
-
material: THREE.Material & { opacity: number },
|
|
621
|
-
def: MaterialAppearance,
|
|
622
|
-
opacity: number,
|
|
623
|
-
): void {
|
|
624
|
-
switch (def.alphaMode) {
|
|
625
|
-
case "OPAQUE":
|
|
626
|
-
material.transparent = false;
|
|
627
|
-
material.opacity = 1.0;
|
|
628
|
-
material.depthWrite = true;
|
|
629
|
-
break;
|
|
630
|
-
case "MASK":
|
|
631
|
-
material.transparent = false;
|
|
632
|
-
material.alphaTest = def.alphaCutoff ?? 0.5;
|
|
633
|
-
material.opacity = opacity;
|
|
634
|
-
material.depthWrite = true;
|
|
635
|
-
break;
|
|
636
|
-
case "BLEND":
|
|
637
|
-
material.transparent = true;
|
|
638
|
-
material.opacity = opacity;
|
|
639
|
-
material.depthWrite = false;
|
|
640
|
-
break;
|
|
641
|
-
// default: no alphaMode set -- keep _createBaseProps defaults (transparent: true)
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* Resolve and apply texture references from a MaterialAppearance onto a
|
|
647
|
-
* MeshPhysicalMaterial via the TextureCache.
|
|
648
|
-
*
|
|
649
|
-
* Color-data textures (base color, emissive, sheen color, specular color)
|
|
650
|
-
* are requested with SRGBColorSpace. All other textures (normal, metallic-
|
|
651
|
-
* roughness, occlusion, roughness maps, transmission, thickness) are
|
|
652
|
-
* requested with LinearSRGBColorSpace (the default).
|
|
653
|
-
*
|
|
654
|
-
* The metallicRoughnessTexture is a single combined texture where
|
|
655
|
-
* B channel = metalness and G channel = roughness. It is assigned to
|
|
656
|
-
* both metalnessMap and roughnessMap on the material.
|
|
657
|
-
*/
|
|
658
|
-
private async _applyStudioTextures(
|
|
659
|
-
material: THREE.MeshPhysicalMaterial,
|
|
660
|
-
def: MaterialAppearance,
|
|
661
|
-
textureCache: TextureCacheInterface,
|
|
662
|
-
): Promise<void> {
|
|
663
|
-
// Helper to resolve a texture reference. The TextureCache determines
|
|
664
|
-
// colorSpace internally from the textureRole name (sRGB for color-data
|
|
665
|
-
// textures like baseColorTexture, linear for non-color data like normalTexture).
|
|
666
|
-
const resolve = async (key: string | undefined, textureRole: string): Promise<THREE.Texture | null> => {
|
|
667
|
-
if (!key) return null;
|
|
668
|
-
return textureCache.get(key, textureRole);
|
|
669
|
-
};
|
|
670
|
-
|
|
671
|
-
// --- sRGB color-data textures ---
|
|
672
|
-
const baseColorTex = await resolve(def.map, "baseColorTexture");
|
|
673
|
-
if (baseColorTex) material.map = baseColorTex;
|
|
674
|
-
|
|
675
|
-
const emissiveTex = await resolve(def.emissiveMap, "emissiveTexture");
|
|
676
|
-
if (emissiveTex) material.emissiveMap = emissiveTex;
|
|
677
|
-
|
|
678
|
-
const sheenColorTex = await resolve(def.sheenColorMap, "sheenColorTexture");
|
|
679
|
-
if (sheenColorTex) material.sheenColorMap = sheenColorTex;
|
|
680
|
-
|
|
681
|
-
const specularColorTex = await resolve(def.specularColorMap, "specularColorTexture");
|
|
682
|
-
if (specularColorTex) material.specularColorMap = specularColorTex;
|
|
683
|
-
|
|
684
|
-
// --- Linear non-color data textures ---
|
|
685
|
-
const normalTex = await resolve(def.normalMap, "normalTexture");
|
|
686
|
-
if (normalTex) material.normalMap = normalTex;
|
|
687
|
-
|
|
688
|
-
const occlusionTex = await resolve(def.aoMap, "occlusionTexture");
|
|
689
|
-
if (occlusionTex) material.aoMap = occlusionTex;
|
|
690
|
-
|
|
691
|
-
const metalnessTex = await resolve(def.metalnessMap, "metallicRoughnessTexture");
|
|
692
|
-
if (metalnessTex) material.metalnessMap = metalnessTex;
|
|
693
|
-
|
|
694
|
-
const roughnessTex = await resolve(def.roughnessMap, "metallicRoughnessTexture");
|
|
695
|
-
if (roughnessTex) material.roughnessMap = roughnessTex;
|
|
696
|
-
|
|
697
|
-
const transmissionTex = await resolve(def.transmissionMap, "transmissionTexture");
|
|
698
|
-
if (transmissionTex) material.transmissionMap = transmissionTex;
|
|
699
|
-
|
|
700
|
-
const thicknessTex = await resolve(def.thicknessMap, "thicknessTexture");
|
|
701
|
-
if (thicknessTex) material.thicknessMap = thicknessTex;
|
|
702
|
-
|
|
703
|
-
const clearcoatTex = await resolve(def.clearcoatMap, "clearcoatTexture");
|
|
704
|
-
if (clearcoatTex) material.clearcoatMap = clearcoatTex;
|
|
705
|
-
|
|
706
|
-
const clearcoatRoughnessTex = await resolve(def.clearcoatRoughnessMap, "clearcoatRoughnessTexture");
|
|
707
|
-
if (clearcoatRoughnessTex) material.clearcoatRoughnessMap = clearcoatRoughnessTex;
|
|
708
|
-
|
|
709
|
-
const clearcoatNormalTex = await resolve(def.clearcoatNormalMap, "clearcoatNormalTexture");
|
|
710
|
-
if (clearcoatNormalTex) material.clearcoatNormalMap = clearcoatNormalTex;
|
|
711
|
-
|
|
712
|
-
const specularIntensityTex = await resolve(def.specularIntensityMap, "specularIntensityTexture");
|
|
713
|
-
if (specularIntensityTex) material.specularIntensityMap = specularIntensityTex;
|
|
714
|
-
|
|
715
|
-
const sheenRoughnessTex = await resolve(def.sheenRoughnessMap, "sheenRoughnessTexture");
|
|
716
|
-
if (sheenRoughnessTex) material.sheenRoughnessMap = sheenRoughnessTex;
|
|
717
|
-
|
|
718
|
-
const anisotropyTex = await resolve(def.anisotropyMap, "anisotropyTexture");
|
|
719
|
-
if (anisotropyTex) material.anisotropyMap = anisotropyTex;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Update global settings.
|
|
724
|
-
*/
|
|
725
|
-
update(options: UpdateOptions): void {
|
|
726
|
-
if (options.metalness !== undefined) this.metalness = options.metalness;
|
|
727
|
-
if (options.roughness !== undefined) this.roughness = options.roughness;
|
|
728
|
-
if (options.transparent !== undefined) this.transparent = options.transparent;
|
|
729
|
-
if (options.defaultOpacity !== undefined) this.defaultOpacity = options.defaultOpacity;
|
|
730
|
-
if (options.edgeColor !== undefined) this.edgeColor = options.edgeColor;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
export { MaterialFactory };
|
|
735
|
-
export type { MaterialFactoryOptions, UpdateOptions, StudioMaterialOptions, TextureCacheInterface };
|