three-cad-viewer 4.1.2 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +12 -5
- package/dist/camera/camera.d.ts +14 -2
- package/dist/core/studio-manager.d.ts +91 -0
- package/dist/core/types.d.ts +260 -9
- package/dist/core/viewer-state.d.ts +28 -2
- package/dist/core/viewer.d.ts +200 -6
- package/dist/index.d.ts +7 -2
- package/dist/rendering/environment.d.ts +239 -0
- package/dist/rendering/light-detection.d.ts +44 -0
- package/dist/rendering/material-factory.d.ts +77 -2
- package/dist/rendering/material-presets.d.ts +32 -0
- package/dist/rendering/room-environment.d.ts +13 -0
- package/dist/rendering/studio-composer.d.ts +130 -0
- package/dist/rendering/studio-floor.d.ts +53 -0
- package/dist/rendering/texture-cache.d.ts +142 -0
- package/dist/rendering/triplanar.d.ts +37 -0
- package/dist/scene/animation.d.ts +1 -1
- package/dist/scene/clipping.d.ts +31 -0
- package/dist/scene/nestedgroup.d.ts +64 -27
- package/dist/scene/objectgroup.d.ts +47 -0
- package/dist/three-cad-viewer.css +339 -29
- package/dist/three-cad-viewer.esm.js +27567 -11874
- package/dist/three-cad-viewer.esm.js.map +1 -1
- package/dist/three-cad-viewer.esm.min.js +10 -4
- package/dist/three-cad-viewer.js +27486 -11787
- package/dist/three-cad-viewer.min.js +10 -4
- package/dist/ui/display.d.ts +147 -0
- package/dist/utils/decode-instances.d.ts +60 -0
- package/dist/utils/utils.d.ts +10 -0
- package/package.json +4 -2
- package/src/_version.ts +1 -1
- package/src/camera/camera.ts +27 -10
- package/src/core/studio-manager.ts +682 -0
- package/src/core/types.ts +328 -9
- package/src/core/viewer-state.ts +84 -4
- package/src/core/viewer.ts +453 -22
- package/src/index.ts +25 -1
- package/src/rendering/environment.ts +840 -0
- package/src/rendering/light-detection.ts +327 -0
- package/src/rendering/material-factory.ts +456 -2
- package/src/rendering/material-presets.ts +303 -0
- package/src/rendering/raycast.ts +2 -2
- package/src/rendering/room-environment.ts +192 -0
- package/src/rendering/studio-composer.ts +577 -0
- package/src/rendering/studio-floor.ts +108 -0
- package/src/rendering/texture-cache.ts +1020 -0
- package/src/rendering/triplanar.ts +329 -0
- package/src/scene/animation.ts +3 -2
- package/src/scene/clipping.ts +59 -0
- package/src/scene/nestedgroup.ts +399 -0
- package/src/scene/objectgroup.ts +186 -11
- package/src/scene/orientation.ts +12 -0
- package/src/scene/render-shape.ts +55 -21
- package/src/types/n8ao.d.ts +28 -0
- package/src/ui/display.ts +1032 -27
- package/src/ui/index.html +181 -44
- package/src/utils/decode-instances.ts +233 -0
- package/src/utils/utils.ts +33 -20
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
|
|
3
|
-
import type { ColorValue } from "../core/types.js";
|
|
3
|
+
import type { ColorValue, MaterialAppearance } from "../core/types.js";
|
|
4
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
|
+
};
|
|
5
36
|
|
|
6
37
|
/**
|
|
7
38
|
* Options for MaterialFactory constructor
|
|
@@ -80,6 +111,29 @@ interface TextureMaterialOptions {
|
|
|
80
111
|
visible?: boolean;
|
|
81
112
|
}
|
|
82
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
|
+
|
|
83
137
|
/**
|
|
84
138
|
* Options for updating factory settings
|
|
85
139
|
*/
|
|
@@ -266,6 +320,406 @@ class MaterialFactory {
|
|
|
266
320
|
return material;
|
|
267
321
|
}
|
|
268
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) {
|
|
482
|
+
material.anisotropy = def.anisotropy;
|
|
483
|
+
}
|
|
484
|
+
if (def.anisotropyRotation !== undefined) {
|
|
485
|
+
material.anisotropyRotation = def.anisotropyRotation;
|
|
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 properties - Material properties from threejs-materials
|
|
507
|
+
* @param textureRepeat - Optional [u, v] texture tiling applied to all loaded textures
|
|
508
|
+
* @param textureCache - TextureCache for resolving data URI textures
|
|
509
|
+
* @param label - Optional label for GPU tracking
|
|
510
|
+
* @returns Configured MeshPhysicalMaterial
|
|
511
|
+
*/
|
|
512
|
+
async createStudioMaterialFromMaterialX(
|
|
513
|
+
properties: Record<string, { value?: unknown; texture?: string }>,
|
|
514
|
+
textureRepeat: [number, number] | undefined,
|
|
515
|
+
textureCache: TextureCacheInterface | null,
|
|
516
|
+
label?: string,
|
|
517
|
+
): Promise<THREE.MeshPhysicalMaterial> {
|
|
518
|
+
// --- Build material options from scalar values ---
|
|
519
|
+
const matOptions: Record<string, unknown> = {
|
|
520
|
+
flatShading: false,
|
|
521
|
+
side: THREE.FrontSide,
|
|
522
|
+
polygonOffset: true,
|
|
523
|
+
polygonOffsetFactor: 1.0,
|
|
524
|
+
polygonOffsetUnits: 1.0,
|
|
525
|
+
depthTest: true,
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// Warn once if displacement data is present (not supported in Studio)
|
|
529
|
+
if (properties.displacement?.texture || properties.displacementScale?.value !== undefined) {
|
|
530
|
+
logger.warn("Displacement not supported by the Studio");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
534
|
+
if (prop.value === undefined) continue;
|
|
535
|
+
|
|
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(prop.value)) {
|
|
541
|
+
const [r, g, b] = prop.value as number[];
|
|
542
|
+
matOptions[key] = new THREE.Color(r, g, b);
|
|
543
|
+
} else if (key === "iridescenceThicknessRange" && Array.isArray(prop.value)) {
|
|
544
|
+
matOptions[key] = prop.value;
|
|
545
|
+
} else {
|
|
546
|
+
matOptions[key] = prop.value;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// --- Handle transmission ---
|
|
551
|
+
const transmissionVal = properties.transmission?.value;
|
|
552
|
+
const opacityVal = properties.opacity?.value;
|
|
553
|
+
const transparentVal = properties.transparent?.value;
|
|
554
|
+
if (typeof transmissionVal === "number" && transmissionVal > 0) {
|
|
555
|
+
matOptions.transparent = false;
|
|
556
|
+
matOptions.opacity = 1.0;
|
|
557
|
+
matOptions.depthWrite = true;
|
|
558
|
+
} else if (transparentVal === true || (typeof opacityVal === "number" && opacityVal < 1.0)) {
|
|
559
|
+
matOptions.transparent = true;
|
|
560
|
+
matOptions.depthWrite = false;
|
|
561
|
+
} else {
|
|
562
|
+
matOptions.transparent = false;
|
|
563
|
+
matOptions.depthWrite = true;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const material = new THREE.MeshPhysicalMaterial(matOptions);
|
|
567
|
+
|
|
568
|
+
// --- Resolve textures ---
|
|
569
|
+
let hasTextures = false;
|
|
570
|
+
if (textureCache) {
|
|
571
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
572
|
+
if (!prop.texture) continue;
|
|
573
|
+
|
|
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(prop.texture, 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
|
+
|
|
597
|
+
// Enable alpha cutout when an alphaMap is present
|
|
598
|
+
if (material.alphaMap) {
|
|
599
|
+
material.alphaTest = 0.5;
|
|
600
|
+
material.side = THREE.DoubleSide;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Force shader recompile if textures were assigned post-construction
|
|
604
|
+
if (hasTextures) {
|
|
605
|
+
material.needsUpdate = true;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
gpuTracker.track("material", material, label ?? "MeshPhysicalMaterial (threejs-materials)");
|
|
609
|
+
return material;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Apply alpha mode settings to a material.
|
|
614
|
+
*
|
|
615
|
+
* - OPAQUE: fully opaque, no transparency
|
|
616
|
+
* - MASK: alpha testing with cutoff threshold
|
|
617
|
+
* - BLEND: standard alpha blending
|
|
618
|
+
* - Default (no alphaMode): transparent: true (current viewer behavior)
|
|
619
|
+
*/
|
|
620
|
+
private _applyAlphaMode(
|
|
621
|
+
material: THREE.Material & { opacity: number },
|
|
622
|
+
def: MaterialAppearance,
|
|
623
|
+
opacity: number,
|
|
624
|
+
): void {
|
|
625
|
+
switch (def.alphaMode) {
|
|
626
|
+
case "OPAQUE":
|
|
627
|
+
material.transparent = false;
|
|
628
|
+
material.opacity = 1.0;
|
|
629
|
+
material.depthWrite = true;
|
|
630
|
+
break;
|
|
631
|
+
case "MASK":
|
|
632
|
+
material.transparent = false;
|
|
633
|
+
material.alphaTest = def.alphaCutoff ?? 0.5;
|
|
634
|
+
material.opacity = opacity;
|
|
635
|
+
material.depthWrite = true;
|
|
636
|
+
break;
|
|
637
|
+
case "BLEND":
|
|
638
|
+
material.transparent = true;
|
|
639
|
+
material.opacity = opacity;
|
|
640
|
+
material.depthWrite = false;
|
|
641
|
+
break;
|
|
642
|
+
// default: no alphaMode set -- keep _createBaseProps defaults (transparent: true)
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Resolve and apply texture references from a MaterialAppearance onto a
|
|
648
|
+
* MeshPhysicalMaterial via the TextureCache.
|
|
649
|
+
*
|
|
650
|
+
* Color-data textures (base color, emissive, sheen color, specular color)
|
|
651
|
+
* are requested with SRGBColorSpace. All other textures (normal, metallic-
|
|
652
|
+
* roughness, occlusion, roughness maps, transmission, thickness) are
|
|
653
|
+
* requested with LinearSRGBColorSpace (the default).
|
|
654
|
+
*
|
|
655
|
+
* The metallicRoughnessTexture is a single combined texture where
|
|
656
|
+
* B channel = metalness and G channel = roughness. It is assigned to
|
|
657
|
+
* both metalnessMap and roughnessMap on the material.
|
|
658
|
+
*/
|
|
659
|
+
private async _applyStudioTextures(
|
|
660
|
+
material: THREE.MeshPhysicalMaterial,
|
|
661
|
+
def: MaterialAppearance,
|
|
662
|
+
textureCache: TextureCacheInterface,
|
|
663
|
+
): Promise<void> {
|
|
664
|
+
// Helper to resolve a texture reference. The TextureCache determines
|
|
665
|
+
// colorSpace internally from the textureRole name (sRGB for color-data
|
|
666
|
+
// textures like baseColorTexture, linear for non-color data like normalTexture).
|
|
667
|
+
const resolve = async (key: string | undefined, textureRole: string): Promise<THREE.Texture | null> => {
|
|
668
|
+
if (!key) return null;
|
|
669
|
+
return textureCache.get(key, textureRole);
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// --- sRGB color-data textures ---
|
|
673
|
+
const baseColorTex = await resolve(def.map, "baseColorTexture");
|
|
674
|
+
if (baseColorTex) material.map = baseColorTex;
|
|
675
|
+
|
|
676
|
+
const emissiveTex = await resolve(def.emissiveMap, "emissiveTexture");
|
|
677
|
+
if (emissiveTex) material.emissiveMap = emissiveTex;
|
|
678
|
+
|
|
679
|
+
const sheenColorTex = await resolve(def.sheenColorMap, "sheenColorTexture");
|
|
680
|
+
if (sheenColorTex) material.sheenColorMap = sheenColorTex;
|
|
681
|
+
|
|
682
|
+
const specularColorTex = await resolve(def.specularColorMap, "specularColorTexture");
|
|
683
|
+
if (specularColorTex) material.specularColorMap = specularColorTex;
|
|
684
|
+
|
|
685
|
+
// --- Linear non-color data textures ---
|
|
686
|
+
const normalTex = await resolve(def.normalMap, "normalTexture");
|
|
687
|
+
if (normalTex) material.normalMap = normalTex;
|
|
688
|
+
|
|
689
|
+
const occlusionTex = await resolve(def.aoMap, "occlusionTexture");
|
|
690
|
+
if (occlusionTex) material.aoMap = occlusionTex;
|
|
691
|
+
|
|
692
|
+
const metalnessTex = await resolve(def.metalnessMap, "metallicRoughnessTexture");
|
|
693
|
+
if (metalnessTex) material.metalnessMap = metalnessTex;
|
|
694
|
+
|
|
695
|
+
const roughnessTex = await resolve(def.roughnessMap, "metallicRoughnessTexture");
|
|
696
|
+
if (roughnessTex) material.roughnessMap = roughnessTex;
|
|
697
|
+
|
|
698
|
+
const transmissionTex = await resolve(def.transmissionMap, "transmissionTexture");
|
|
699
|
+
if (transmissionTex) material.transmissionMap = transmissionTex;
|
|
700
|
+
|
|
701
|
+
const thicknessTex = await resolve(def.thicknessMap, "thicknessTexture");
|
|
702
|
+
if (thicknessTex) material.thicknessMap = thicknessTex;
|
|
703
|
+
|
|
704
|
+
const clearcoatTex = await resolve(def.clearcoatMap, "clearcoatTexture");
|
|
705
|
+
if (clearcoatTex) material.clearcoatMap = clearcoatTex;
|
|
706
|
+
|
|
707
|
+
const clearcoatRoughnessTex = await resolve(def.clearcoatRoughnessMap, "clearcoatRoughnessTexture");
|
|
708
|
+
if (clearcoatRoughnessTex) material.clearcoatRoughnessMap = clearcoatRoughnessTex;
|
|
709
|
+
|
|
710
|
+
const clearcoatNormalTex = await resolve(def.clearcoatNormalMap, "clearcoatNormalTexture");
|
|
711
|
+
if (clearcoatNormalTex) material.clearcoatNormalMap = clearcoatNormalTex;
|
|
712
|
+
|
|
713
|
+
const specularIntensityTex = await resolve(def.specularIntensityMap, "specularIntensityTexture");
|
|
714
|
+
if (specularIntensityTex) material.specularIntensityMap = specularIntensityTex;
|
|
715
|
+
|
|
716
|
+
const sheenRoughnessTex = await resolve(def.sheenRoughnessMap, "sheenRoughnessTexture");
|
|
717
|
+
if (sheenRoughnessTex) material.sheenRoughnessMap = sheenRoughnessTex;
|
|
718
|
+
|
|
719
|
+
const anisotropyTex = await resolve(def.anisotropyMap, "anisotropyTexture");
|
|
720
|
+
if (anisotropyTex) material.anisotropyMap = anisotropyTex;
|
|
721
|
+
}
|
|
722
|
+
|
|
269
723
|
/**
|
|
270
724
|
* Update global settings.
|
|
271
725
|
*/
|
|
@@ -279,4 +733,4 @@ class MaterialFactory {
|
|
|
279
733
|
}
|
|
280
734
|
|
|
281
735
|
export { MaterialFactory };
|
|
282
|
-
export type { MaterialFactoryOptions, UpdateOptions };
|
|
736
|
+
export type { MaterialFactoryOptions, UpdateOptions, StudioMaterialOptions, TextureCacheInterface };
|