three-cad-viewer 4.3.1 → 4.3.4
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/three-cad-viewer.esm.js +47 -43
- 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 +47 -43
- 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/nestedgroup.ts +19 -7
- /package/{Readme.md → README.md} +0 -0
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
|
*
|
|
@@ -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;
|
|
@@ -84757,7 +84749,7 @@ class NestedGroup {
|
|
|
84757
84749
|
try {
|
|
84758
84750
|
if (resolved && isMaterialXMaterial(resolved)) {
|
|
84759
84751
|
// --- threejs-materials path ---
|
|
84760
|
-
studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(resolved.
|
|
84752
|
+
studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(resolved.values, resolved.textures, resolved.textureRepeat, this._textureCache);
|
|
84761
84753
|
if (materialXHasTextures(resolved)) {
|
|
84762
84754
|
this._texturedMaterialKeys.add(sharingKey);
|
|
84763
84755
|
}
|
|
@@ -84834,6 +84826,18 @@ class NestedGroup {
|
|
|
84834
84826
|
}
|
|
84835
84827
|
studioBack = cachedBack;
|
|
84836
84828
|
}
|
|
84829
|
+
// Compute tangents for anisotropic materials (required by Three.js)
|
|
84830
|
+
if (studioMaterial instanceof MeshPhysicalMaterial &&
|
|
84831
|
+
studioMaterial.anisotropy > 0 &&
|
|
84832
|
+
obj.shapeGeometry?.getAttribute("uv") != null &&
|
|
84833
|
+
obj.shapeGeometry.getAttribute("tangent") == null) {
|
|
84834
|
+
try {
|
|
84835
|
+
obj.shapeGeometry.computeTangents();
|
|
84836
|
+
}
|
|
84837
|
+
catch {
|
|
84838
|
+
logger.debug(`Studio "${path}": tangent computation failed, anisotropy may have artifacts`);
|
|
84839
|
+
}
|
|
84840
|
+
}
|
|
84837
84841
|
// Apply to ObjectGroup
|
|
84838
84842
|
obj.enterStudioMode(studioMaterial instanceof MeshPhysicalMaterial ? studioMaterial : null, studioBack);
|
|
84839
84843
|
}
|
|
@@ -94284,7 +94288,7 @@ class Tools {
|
|
|
94284
94288
|
}
|
|
94285
94289
|
}
|
|
94286
94290
|
|
|
94287
|
-
const version = "4.3.
|
|
94291
|
+
const version = "4.3.4";
|
|
94288
94292
|
|
|
94289
94293
|
/**
|
|
94290
94294
|
* Clean room environment for Studio mode PMREM generation.
|
|
@@ -95909,7 +95913,7 @@ class StudioFloor {
|
|
|
95909
95913
|
* Create a shadow-receiving plane at the floor position.
|
|
95910
95914
|
*/
|
|
95911
95915
|
_createShadowPlane(zPosition, sceneSize) {
|
|
95912
|
-
const floorSize = sceneSize *
|
|
95916
|
+
const floorSize = sceneSize * 6;
|
|
95913
95917
|
const geometry = new PlaneGeometry(floorSize, floorSize);
|
|
95914
95918
|
const material = new ShadowMaterial({ opacity: 0.5, depthWrite: false });
|
|
95915
95919
|
const plane = new Mesh(geometry, material);
|
|
@@ -104756,7 +104760,7 @@ class StudioManager {
|
|
|
104756
104760
|
light.position.copy(bboxCenter).addScaledVector(dir, maxExtent * 3);
|
|
104757
104761
|
light.target.position.copy(bboxCenter);
|
|
104758
104762
|
light.castShadow = true;
|
|
104759
|
-
const frustumSize = maxExtent *
|
|
104763
|
+
const frustumSize = maxExtent * 6.0;
|
|
104760
104764
|
light.shadow.camera.left = -frustumSize;
|
|
104761
104765
|
light.shadow.camera.right = frustumSize;
|
|
104762
104766
|
light.shadow.camera.top = frustumSize;
|