three-cad-viewer 4.3.2 → 4.3.5
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/core/types.d.ts +10 -11
- package/dist/rendering/material-factory.d.ts +3 -5
- package/dist/scene/clipping.d.ts +6 -0
- package/dist/three-cad-viewer.esm.js +62 -45
- package/dist/three-cad-viewer.esm.js.map +1 -1
- package/dist/three-cad-viewer.esm.min.js +2 -2
- package/dist/three-cad-viewer.js +62 -45
- package/dist/three-cad-viewer.min.js +2 -2
- package/package.json +2 -1
- package/src/_version.ts +1 -1
- package/src/core/studio-manager.ts +1 -1
- package/src/core/types.ts +11 -9
- package/src/rendering/material-factory.ts +26 -31
- package/src/rendering/studio-floor.ts +1 -1
- package/src/scene/clipping.ts +10 -0
- package/src/scene/nestedgroup.ts +25 -9
package/dist/core/types.d.ts
CHANGED
|
@@ -493,26 +493,25 @@ export interface MaterialAppearance {
|
|
|
493
493
|
*
|
|
494
494
|
* This format is produced by the threejs-materials Python library, which catalogs
|
|
495
495
|
* PBR materials from ambientCG, GPUOpen, PolyHaven, and PhysicallyBased.
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
496
|
+
* `values` contains scalar properties (e.g., color as linear RGB array,
|
|
497
|
+
* roughness as float). `textures` contains texture references (inline data URIs
|
|
498
|
+
* or file paths) keyed by property name.
|
|
499
499
|
*
|
|
500
|
-
* Detected by the presence of the `
|
|
500
|
+
* Detected by the presence of the `values` key.
|
|
501
501
|
* Extra keys from threejs-materials (id, name, source, url, license) pass through
|
|
502
502
|
* harmlessly and are not part of this interface.
|
|
503
503
|
*/
|
|
504
504
|
export interface MaterialXMaterial {
|
|
505
|
-
/**
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}>;
|
|
505
|
+
/** Scalar PBR property values (e.g., color, metalness, roughness). */
|
|
506
|
+
values: Record<string, unknown>;
|
|
507
|
+
/** Texture map references keyed by property name (e.g., color, normal). */
|
|
508
|
+
textures: Record<string, string>;
|
|
510
509
|
/** Optional texture tiling [u, v], default [1, 1]. Applied to all textures. */
|
|
511
510
|
textureRepeat?: [number, number];
|
|
512
511
|
}
|
|
513
512
|
/**
|
|
514
513
|
* Type guard to check if a material entry is a threejs-materials format dict.
|
|
515
|
-
* Detected by the presence of the `
|
|
514
|
+
* Detected by the presence of the `values` key.
|
|
516
515
|
*/
|
|
517
516
|
export declare function isMaterialXMaterial(m: unknown): m is MaterialXMaterial;
|
|
518
517
|
/**
|
|
@@ -697,7 +696,7 @@ export interface Shapes {
|
|
|
697
696
|
/** User-defined material library (root node).
|
|
698
697
|
* Values can be:
|
|
699
698
|
* - string: builtin preset reference (e.g., "builtin:car-paint")
|
|
700
|
-
* - MaterialXMaterial: threejs-materials format (detected by `
|
|
699
|
+
* - MaterialXMaterial: threejs-materials format (detected by `values` key)
|
|
701
700
|
* - MaterialAppearance: preset with overrides (e.g., { builtin: "acrylic-clear", color: "#55a0e3" })
|
|
702
701
|
*/
|
|
703
702
|
materials?: Record<string, string | MaterialXMaterial | MaterialAppearance> | undefined;
|
|
@@ -164,16 +164,14 @@ declare class MaterialFactory {
|
|
|
164
164
|
* "roughness", "normal") where each entry has an optional `value` (scalar or
|
|
165
165
|
* [r,g,b] array in **linear RGB**) and/or `texture` (inline data URI).
|
|
166
166
|
*
|
|
167
|
-
* @param
|
|
167
|
+
* @param values - Scalar PBR values from threejs-materials
|
|
168
|
+
* @param textures - Texture map references from threejs-materials
|
|
168
169
|
* @param textureRepeat - Optional [u, v] texture tiling applied to all loaded textures
|
|
169
170
|
* @param textureCache - TextureCache for resolving data URI textures
|
|
170
171
|
* @param label - Optional label for GPU tracking
|
|
171
172
|
* @returns Configured MeshPhysicalMaterial
|
|
172
173
|
*/
|
|
173
|
-
createStudioMaterialFromMaterialX(
|
|
174
|
-
value?: unknown;
|
|
175
|
-
texture?: string;
|
|
176
|
-
}>, textureRepeat: [number, number] | undefined, textureCache: TextureCacheInterface | null, label?: string): Promise<THREE.MeshPhysicalMaterial>;
|
|
174
|
+
createStudioMaterialFromMaterialX(values: Record<string, unknown>, textures: Record<string, string>, textureRepeat: [number, number] | undefined, textureCache: TextureCacheInterface | null, label?: string): Promise<THREE.MeshPhysicalMaterial>;
|
|
177
175
|
/**
|
|
178
176
|
* Apply alpha mode settings to a material.
|
|
179
177
|
*
|
package/dist/scene/clipping.d.ts
CHANGED
|
@@ -20,6 +20,12 @@ declare class CenteredPlane extends THREE.Plane {
|
|
|
20
20
|
* @param value - The centered constant value.
|
|
21
21
|
*/
|
|
22
22
|
setConstant(value: number): void;
|
|
23
|
+
/**
|
|
24
|
+
* Clone this CenteredPlane.
|
|
25
|
+
* Overrides THREE.Plane.clone() which calls `new this.constructor()` without
|
|
26
|
+
* arguments, causing `center` to be undefined during shadow map generation.
|
|
27
|
+
*/
|
|
28
|
+
clone(): CenteredPlane;
|
|
23
29
|
}
|
|
24
30
|
/**
|
|
25
31
|
* A THREE.Group that only contains PlaneMesh children.
|
|
@@ -83121,10 +83121,12 @@ class MaterialFactory {
|
|
|
83121
83121
|
}
|
|
83122
83122
|
}
|
|
83123
83123
|
// --- Anisotropy (brushed metal) ---
|
|
83124
|
-
|
|
83125
|
-
|
|
83126
|
-
|
|
83127
|
-
|
|
83124
|
+
if (def.anisotropy !== undefined && def.anisotropy > 0) {
|
|
83125
|
+
material.anisotropy = def.anisotropy;
|
|
83126
|
+
if (def.anisotropyRotation !== undefined) {
|
|
83127
|
+
material.anisotropyRotation = def.anisotropyRotation;
|
|
83128
|
+
}
|
|
83129
|
+
}
|
|
83128
83130
|
// --- Textures ---
|
|
83129
83131
|
// Resolve all texture references via TextureCache.
|
|
83130
83132
|
// The TextureCache determines colorSpace internally from the texture role name.
|
|
@@ -83141,13 +83143,14 @@ class MaterialFactory {
|
|
|
83141
83143
|
* "roughness", "normal") where each entry has an optional `value` (scalar or
|
|
83142
83144
|
* [r,g,b] array in **linear RGB**) and/or `texture` (inline data URI).
|
|
83143
83145
|
*
|
|
83144
|
-
* @param
|
|
83146
|
+
* @param values - Scalar PBR values from threejs-materials
|
|
83147
|
+
* @param textures - Texture map references from threejs-materials
|
|
83145
83148
|
* @param textureRepeat - Optional [u, v] texture tiling applied to all loaded textures
|
|
83146
83149
|
* @param textureCache - TextureCache for resolving data URI textures
|
|
83147
83150
|
* @param label - Optional label for GPU tracking
|
|
83148
83151
|
* @returns Configured MeshPhysicalMaterial
|
|
83149
83152
|
*/
|
|
83150
|
-
async createStudioMaterialFromMaterialX(
|
|
83153
|
+
async createStudioMaterialFromMaterialX(values, textures, textureRepeat, textureCache, label) {
|
|
83151
83154
|
// --- Build material options from scalar values ---
|
|
83152
83155
|
const matOptions = {
|
|
83153
83156
|
flatShading: false,
|
|
@@ -83158,37 +83161,32 @@ class MaterialFactory {
|
|
|
83158
83161
|
depthTest: true,
|
|
83159
83162
|
};
|
|
83160
83163
|
// Warn once if displacement data is present (not supported in Studio)
|
|
83161
|
-
if (
|
|
83164
|
+
if (textures.displacement || values.displacementScale !== undefined) {
|
|
83162
83165
|
logger.warn("Displacement not supported by the Studio");
|
|
83163
83166
|
}
|
|
83164
|
-
for (const [key,
|
|
83165
|
-
if (prop.value === undefined)
|
|
83166
|
-
continue;
|
|
83167
|
+
for (const [key, value] of Object.entries(values)) {
|
|
83167
83168
|
// Skip displacement properties (not supported, would waste GPU memory)
|
|
83168
83169
|
if (key === "displacement" || key === "displacementScale" || key === "displacementBias")
|
|
83169
83170
|
continue;
|
|
83170
|
-
// Skip anisotropy — requires tangent vectors that CAD meshes don't have
|
|
83171
|
-
if (key === "anisotropy" || key === "anisotropyRotation")
|
|
83172
|
-
continue;
|
|
83173
83171
|
// Color arrays → THREE.Color (already linear, no sRGB conversion)
|
|
83174
|
-
if (COLOR_ARRAY_KEYS.has(key) && Array.isArray(
|
|
83175
|
-
const [r, g, b] =
|
|
83172
|
+
if (COLOR_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
83173
|
+
const [r, g, b] = value;
|
|
83176
83174
|
matOptions[key] = new Color(r, g, b);
|
|
83177
83175
|
}
|
|
83178
|
-
else if ((key === "normalScale" || key === "clearcoatNormalScale") && Array.isArray(
|
|
83179
|
-
matOptions[key] = new Vector2(
|
|
83176
|
+
else if ((key === "normalScale" || key === "clearcoatNormalScale") && Array.isArray(value)) {
|
|
83177
|
+
matOptions[key] = new Vector2(value[0], value[1]);
|
|
83180
83178
|
}
|
|
83181
|
-
else if (key === "iridescenceThicknessRange" && Array.isArray(
|
|
83182
|
-
matOptions[key] =
|
|
83179
|
+
else if (key === "iridescenceThicknessRange" && Array.isArray(value)) {
|
|
83180
|
+
matOptions[key] = value;
|
|
83183
83181
|
}
|
|
83184
83182
|
else {
|
|
83185
|
-
matOptions[key] =
|
|
83183
|
+
matOptions[key] = value;
|
|
83186
83184
|
}
|
|
83187
83185
|
}
|
|
83188
83186
|
// --- Handle transmission ---
|
|
83189
|
-
const transmissionVal =
|
|
83190
|
-
const opacityVal =
|
|
83191
|
-
const transparentVal =
|
|
83187
|
+
const transmissionVal = values.transmission;
|
|
83188
|
+
const opacityVal = values.opacity;
|
|
83189
|
+
const transparentVal = values.transparent;
|
|
83192
83190
|
if (typeof transmissionVal === "number" && transmissionVal > 0) {
|
|
83193
83191
|
matOptions.transparent = false;
|
|
83194
83192
|
matOptions.opacity = 1.0;
|
|
@@ -83206,9 +83204,7 @@ class MaterialFactory {
|
|
|
83206
83204
|
// --- Resolve textures ---
|
|
83207
83205
|
let hasTextures = false;
|
|
83208
83206
|
if (textureCache) {
|
|
83209
|
-
for (const [key,
|
|
83210
|
-
if (!prop.texture)
|
|
83211
|
-
continue;
|
|
83207
|
+
for (const [key, textureRef] of Object.entries(textures)) {
|
|
83212
83208
|
const mapName = PROPERTY_TO_MAP[key];
|
|
83213
83209
|
if (!mapName)
|
|
83214
83210
|
continue;
|
|
@@ -83219,7 +83215,7 @@ class MaterialFactory {
|
|
|
83219
83215
|
const roleForCache = colorSpace === SRGBColorSpace
|
|
83220
83216
|
? "baseColorTexture"
|
|
83221
83217
|
: "normalTexture";
|
|
83222
|
-
const tex = await textureCache.get(
|
|
83218
|
+
const tex = await textureCache.get(textureRef, roleForCache);
|
|
83223
83219
|
if (tex) {
|
|
83224
83220
|
if (textureRepeat) {
|
|
83225
83221
|
tex.repeat.set(textureRepeat[0], textureRepeat[1]);
|
|
@@ -83340,9 +83336,9 @@ class MaterialFactory {
|
|
|
83340
83336
|
const sheenRoughnessTex = await resolve(def.sheenRoughnessMap, "sheenRoughnessTexture");
|
|
83341
83337
|
if (sheenRoughnessTex)
|
|
83342
83338
|
material.sheenRoughnessMap = sheenRoughnessTex;
|
|
83343
|
-
|
|
83344
|
-
|
|
83345
|
-
|
|
83339
|
+
const anisotropyTex = await resolve(def.anisotropyMap, "anisotropyTexture");
|
|
83340
|
+
if (anisotropyTex)
|
|
83341
|
+
material.anisotropyMap = anisotropyTex;
|
|
83346
83342
|
}
|
|
83347
83343
|
/**
|
|
83348
83344
|
* Update global settings.
|
|
@@ -83380,10 +83376,10 @@ var CollapseState;
|
|
|
83380
83376
|
})(CollapseState || (CollapseState = {}));
|
|
83381
83377
|
/**
|
|
83382
83378
|
* Type guard to check if a material entry is a threejs-materials format dict.
|
|
83383
|
-
* Detected by the presence of the `
|
|
83379
|
+
* Detected by the presence of the `values` key.
|
|
83384
83380
|
*/
|
|
83385
83381
|
function isMaterialXMaterial(m) {
|
|
83386
|
-
return typeof m === "object" && m !== null && "
|
|
83382
|
+
return typeof m === "object" && m !== null && "values" in m;
|
|
83387
83383
|
}
|
|
83388
83384
|
/**
|
|
83389
83385
|
* Check if shape uses binary format (has triangles_per_face).
|
|
@@ -84001,13 +83997,9 @@ function materialHasTexture(def) {
|
|
|
84001
83997
|
}
|
|
84002
83998
|
return false;
|
|
84003
83999
|
}
|
|
84004
|
-
/** Check whether a threejs-materials entry has texture references
|
|
84000
|
+
/** Check whether a threejs-materials entry has texture references. */
|
|
84005
84001
|
function materialXHasTextures(entry) {
|
|
84006
|
-
|
|
84007
|
-
if (prop.texture)
|
|
84008
|
-
return true;
|
|
84009
|
-
}
|
|
84010
|
-
return false;
|
|
84002
|
+
return Object.keys(entry.textures).length > 0;
|
|
84011
84003
|
}
|
|
84012
84004
|
class NestedGroup {
|
|
84013
84005
|
/**
|
|
@@ -84125,7 +84117,7 @@ class NestedGroup {
|
|
|
84125
84117
|
logger.warn(`Invalid material string '${entry}' for tag '${tag}' (expected "builtin:" prefix)`);
|
|
84126
84118
|
return null;
|
|
84127
84119
|
}
|
|
84128
|
-
// MaterialXMaterial entry: object with `
|
|
84120
|
+
// MaterialXMaterial entry: object with `values` key
|
|
84129
84121
|
if (isMaterialXMaterial(entry)) {
|
|
84130
84122
|
this.resolvedMaterialX.set(tag, entry);
|
|
84131
84123
|
return entry;
|
|
@@ -84139,11 +84131,15 @@ class NestedGroup {
|
|
|
84139
84131
|
logger.warn(`Unknown builtin preset '${presetName}' referenced by '${tag}' on '${objectPath}'`);
|
|
84140
84132
|
return null;
|
|
84141
84133
|
}
|
|
84142
|
-
|
|
84134
|
+
// Strip preset color unless the user explicitly provides one,
|
|
84135
|
+
// so the leaf node's CAD color is used as fallback.
|
|
84136
|
+
const { color: presetColor, ...presetRest } = preset;
|
|
84137
|
+
const resolved = "color" in appearance
|
|
84138
|
+
? { ...preset, ...appearance }
|
|
84139
|
+
: { ...presetRest, ...appearance };
|
|
84143
84140
|
this.resolvedMaterials.set(tag, resolved);
|
|
84144
84141
|
return resolved;
|
|
84145
84142
|
}
|
|
84146
|
-
// Should not happen with current type, but guard anyway
|
|
84147
84143
|
logger.warn(`Unrecognised material entry for tag '${tag}' on '${objectPath}'`);
|
|
84148
84144
|
return null;
|
|
84149
84145
|
}
|
|
@@ -84757,7 +84753,7 @@ class NestedGroup {
|
|
|
84757
84753
|
try {
|
|
84758
84754
|
if (resolved && isMaterialXMaterial(resolved)) {
|
|
84759
84755
|
// --- threejs-materials path ---
|
|
84760
|
-
studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(resolved.
|
|
84756
|
+
studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(resolved.values, resolved.textures, resolved.textureRepeat, this._textureCache);
|
|
84761
84757
|
if (materialXHasTextures(resolved)) {
|
|
84762
84758
|
this._texturedMaterialKeys.add(sharingKey);
|
|
84763
84759
|
}
|
|
@@ -84834,6 +84830,18 @@ class NestedGroup {
|
|
|
84834
84830
|
}
|
|
84835
84831
|
studioBack = cachedBack;
|
|
84836
84832
|
}
|
|
84833
|
+
// Compute tangents for anisotropic materials (required by Three.js)
|
|
84834
|
+
if (studioMaterial instanceof MeshPhysicalMaterial &&
|
|
84835
|
+
studioMaterial.anisotropy > 0 &&
|
|
84836
|
+
obj.shapeGeometry?.getAttribute("uv") != null &&
|
|
84837
|
+
obj.shapeGeometry.getAttribute("tangent") == null) {
|
|
84838
|
+
try {
|
|
84839
|
+
obj.shapeGeometry.computeTangents();
|
|
84840
|
+
}
|
|
84841
|
+
catch {
|
|
84842
|
+
logger.debug(`Studio "${path}": tangent computation failed, anisotropy may have artifacts`);
|
|
84843
|
+
}
|
|
84844
|
+
}
|
|
84837
84845
|
// Apply to ObjectGroup
|
|
84838
84846
|
obj.enterStudioMode(studioMaterial instanceof MeshPhysicalMaterial ? studioMaterial : null, studioBack);
|
|
84839
84847
|
}
|
|
@@ -87470,6 +87478,15 @@ class CenteredPlane extends Plane {
|
|
|
87470
87478
|
const z = this.distanceToPoint(new Vector3(0, 0, 0));
|
|
87471
87479
|
this.constant = z - c + value;
|
|
87472
87480
|
}
|
|
87481
|
+
/**
|
|
87482
|
+
* Clone this CenteredPlane.
|
|
87483
|
+
* Overrides THREE.Plane.clone() which calls `new this.constructor()` without
|
|
87484
|
+
* arguments, causing `center` to be undefined during shadow map generation.
|
|
87485
|
+
*/
|
|
87486
|
+
// @ts-expect-error -- THREE.Plane.clone() returns `this`, but we need a concrete CenteredPlane
|
|
87487
|
+
clone() {
|
|
87488
|
+
return new CenteredPlane(this.normal.clone(), this.centeredConstant, [...this.center]);
|
|
87489
|
+
}
|
|
87473
87490
|
}
|
|
87474
87491
|
// ============================================================================
|
|
87475
87492
|
// PlaneMesh - Visual representation of a clipping plane
|
|
@@ -94284,7 +94301,7 @@ class Tools {
|
|
|
94284
94301
|
}
|
|
94285
94302
|
}
|
|
94286
94303
|
|
|
94287
|
-
const version = "4.3.
|
|
94304
|
+
const version = "4.3.5";
|
|
94288
94305
|
|
|
94289
94306
|
/**
|
|
94290
94307
|
* Clean room environment for Studio mode PMREM generation.
|
|
@@ -95909,7 +95926,7 @@ class StudioFloor {
|
|
|
95909
95926
|
* Create a shadow-receiving plane at the floor position.
|
|
95910
95927
|
*/
|
|
95911
95928
|
_createShadowPlane(zPosition, sceneSize) {
|
|
95912
|
-
const floorSize = sceneSize *
|
|
95929
|
+
const floorSize = sceneSize * 6;
|
|
95913
95930
|
const geometry = new PlaneGeometry(floorSize, floorSize);
|
|
95914
95931
|
const material = new ShadowMaterial({ opacity: 0.5, depthWrite: false });
|
|
95915
95932
|
const plane = new Mesh(geometry, material);
|
|
@@ -104756,7 +104773,7 @@ class StudioManager {
|
|
|
104756
104773
|
light.position.copy(bboxCenter).addScaledVector(dir, maxExtent * 3);
|
|
104757
104774
|
light.target.position.copy(bboxCenter);
|
|
104758
104775
|
light.castShadow = true;
|
|
104759
|
-
const frustumSize = maxExtent *
|
|
104776
|
+
const frustumSize = maxExtent * 6.0;
|
|
104760
104777
|
light.shadow.camera.left = -frustumSize;
|
|
104761
104778
|
light.shadow.camera.right = frustumSize;
|
|
104762
104779
|
light.shadow.camera.top = frustumSize;
|