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.
@@ -83127,10 +83127,12 @@ void main() {
83127
83127
  }
83128
83128
  }
83129
83129
  // --- Anisotropy (brushed metal) ---
83130
- // Skipped: anisotropic reflections require tangent vectors on the mesh.
83131
- // CAD tessellation never provides tangents, so Three.js falls back to
83132
- // screen-space derivative tangents which produce visible diamond-shaped
83133
- // facet artifacts on coarse meshes.
83130
+ if (def.anisotropy !== undefined && def.anisotropy > 0) {
83131
+ material.anisotropy = def.anisotropy;
83132
+ if (def.anisotropyRotation !== undefined) {
83133
+ material.anisotropyRotation = def.anisotropyRotation;
83134
+ }
83135
+ }
83134
83136
  // --- Textures ---
83135
83137
  // Resolve all texture references via TextureCache.
83136
83138
  // The TextureCache determines colorSpace internally from the texture role name.
@@ -83147,13 +83149,14 @@ void main() {
83147
83149
  * "roughness", "normal") where each entry has an optional `value` (scalar or
83148
83150
  * [r,g,b] array in **linear RGB**) and/or `texture` (inline data URI).
83149
83151
  *
83150
- * @param properties - Material properties from threejs-materials
83152
+ * @param values - Scalar PBR values from threejs-materials
83153
+ * @param textures - Texture map references from threejs-materials
83151
83154
  * @param textureRepeat - Optional [u, v] texture tiling applied to all loaded textures
83152
83155
  * @param textureCache - TextureCache for resolving data URI textures
83153
83156
  * @param label - Optional label for GPU tracking
83154
83157
  * @returns Configured MeshPhysicalMaterial
83155
83158
  */
83156
- async createStudioMaterialFromMaterialX(properties, textureRepeat, textureCache, label) {
83159
+ async createStudioMaterialFromMaterialX(values, textures, textureRepeat, textureCache, label) {
83157
83160
  // --- Build material options from scalar values ---
83158
83161
  const matOptions = {
83159
83162
  flatShading: false,
@@ -83164,37 +83167,32 @@ void main() {
83164
83167
  depthTest: true,
83165
83168
  };
83166
83169
  // Warn once if displacement data is present (not supported in Studio)
83167
- if (properties.displacement?.texture || properties.displacementScale?.value !== undefined) {
83170
+ if (textures.displacement || values.displacementScale !== undefined) {
83168
83171
  logger.warn("Displacement not supported by the Studio");
83169
83172
  }
83170
- for (const [key, prop] of Object.entries(properties)) {
83171
- if (prop.value === undefined)
83172
- continue;
83173
+ for (const [key, value] of Object.entries(values)) {
83173
83174
  // Skip displacement properties (not supported, would waste GPU memory)
83174
83175
  if (key === "displacement" || key === "displacementScale" || key === "displacementBias")
83175
83176
  continue;
83176
- // Skip anisotropy — requires tangent vectors that CAD meshes don't have
83177
- if (key === "anisotropy" || key === "anisotropyRotation")
83178
- continue;
83179
83177
  // Color arrays → THREE.Color (already linear, no sRGB conversion)
83180
- if (COLOR_ARRAY_KEYS.has(key) && Array.isArray(prop.value)) {
83181
- const [r, g, b] = prop.value;
83178
+ if (COLOR_ARRAY_KEYS.has(key) && Array.isArray(value)) {
83179
+ const [r, g, b] = value;
83182
83180
  matOptions[key] = new Color(r, g, b);
83183
83181
  }
83184
- else if ((key === "normalScale" || key === "clearcoatNormalScale") && Array.isArray(prop.value)) {
83185
- matOptions[key] = new Vector2(prop.value[0], prop.value[1]);
83182
+ else if ((key === "normalScale" || key === "clearcoatNormalScale") && Array.isArray(value)) {
83183
+ matOptions[key] = new Vector2(value[0], value[1]);
83186
83184
  }
83187
- else if (key === "iridescenceThicknessRange" && Array.isArray(prop.value)) {
83188
- matOptions[key] = prop.value;
83185
+ else if (key === "iridescenceThicknessRange" && Array.isArray(value)) {
83186
+ matOptions[key] = value;
83189
83187
  }
83190
83188
  else {
83191
- matOptions[key] = prop.value;
83189
+ matOptions[key] = value;
83192
83190
  }
83193
83191
  }
83194
83192
  // --- Handle transmission ---
83195
- const transmissionVal = properties.transmission?.value;
83196
- const opacityVal = properties.opacity?.value;
83197
- const transparentVal = properties.transparent?.value;
83193
+ const transmissionVal = values.transmission;
83194
+ const opacityVal = values.opacity;
83195
+ const transparentVal = values.transparent;
83198
83196
  if (typeof transmissionVal === "number" && transmissionVal > 0) {
83199
83197
  matOptions.transparent = false;
83200
83198
  matOptions.opacity = 1.0;
@@ -83212,9 +83210,7 @@ void main() {
83212
83210
  // --- Resolve textures ---
83213
83211
  let hasTextures = false;
83214
83212
  if (textureCache) {
83215
- for (const [key, prop] of Object.entries(properties)) {
83216
- if (!prop.texture)
83217
- continue;
83213
+ for (const [key, textureRef] of Object.entries(textures)) {
83218
83214
  const mapName = PROPERTY_TO_MAP[key];
83219
83215
  if (!mapName)
83220
83216
  continue;
@@ -83225,7 +83221,7 @@ void main() {
83225
83221
  const roleForCache = colorSpace === SRGBColorSpace
83226
83222
  ? "baseColorTexture"
83227
83223
  : "normalTexture";
83228
- const tex = await textureCache.get(prop.texture, roleForCache);
83224
+ const tex = await textureCache.get(textureRef, roleForCache);
83229
83225
  if (tex) {
83230
83226
  if (textureRepeat) {
83231
83227
  tex.repeat.set(textureRepeat[0], textureRepeat[1]);
@@ -83346,9 +83342,9 @@ void main() {
83346
83342
  const sheenRoughnessTex = await resolve(def.sheenRoughnessMap, "sheenRoughnessTexture");
83347
83343
  if (sheenRoughnessTex)
83348
83344
  material.sheenRoughnessMap = sheenRoughnessTex;
83349
- // Anisotropy texture skipped CAD meshes lack tangent vectors.
83350
- // const anisotropyTex = await resolve(def.anisotropyMap, "anisotropyTexture");
83351
- // if (anisotropyTex) material.anisotropyMap = anisotropyTex;
83345
+ const anisotropyTex = await resolve(def.anisotropyMap, "anisotropyTexture");
83346
+ if (anisotropyTex)
83347
+ material.anisotropyMap = anisotropyTex;
83352
83348
  }
83353
83349
  /**
83354
83350
  * Update global settings.
@@ -83386,10 +83382,10 @@ void main() {
83386
83382
  })(exports.CollapseState || (exports.CollapseState = {}));
83387
83383
  /**
83388
83384
  * Type guard to check if a material entry is a threejs-materials format dict.
83389
- * Detected by the presence of the `properties` key.
83385
+ * Detected by the presence of the `values` key.
83390
83386
  */
83391
83387
  function isMaterialXMaterial(m) {
83392
- return typeof m === "object" && m !== null && "properties" in m;
83388
+ return typeof m === "object" && m !== null && "values" in m;
83393
83389
  }
83394
83390
  /**
83395
83391
  * Check if shape uses binary format (has triangles_per_face).
@@ -84007,13 +84003,9 @@ float metalnessFactor = metalness;
84007
84003
  }
84008
84004
  return false;
84009
84005
  }
84010
- /** Check whether a threejs-materials entry has texture references in its properties. */
84006
+ /** Check whether a threejs-materials entry has texture references. */
84011
84007
  function materialXHasTextures(entry) {
84012
- for (const [, prop] of Object.entries(entry.properties)) {
84013
- if (prop.texture)
84014
- return true;
84015
- }
84016
- return false;
84008
+ return Object.keys(entry.textures).length > 0;
84017
84009
  }
84018
84010
  class NestedGroup {
84019
84011
  /**
@@ -84131,7 +84123,7 @@ float metalnessFactor = metalness;
84131
84123
  logger.warn(`Invalid material string '${entry}' for tag '${tag}' (expected "builtin:" prefix)`);
84132
84124
  return null;
84133
84125
  }
84134
- // MaterialXMaterial entry: object with `properties` key
84126
+ // MaterialXMaterial entry: object with `values` key
84135
84127
  if (isMaterialXMaterial(entry)) {
84136
84128
  this.resolvedMaterialX.set(tag, entry);
84137
84129
  return entry;
@@ -84145,11 +84137,15 @@ float metalnessFactor = metalness;
84145
84137
  logger.warn(`Unknown builtin preset '${presetName}' referenced by '${tag}' on '${objectPath}'`);
84146
84138
  return null;
84147
84139
  }
84148
- const resolved = { ...preset, ...appearance };
84140
+ // Strip preset color unless the user explicitly provides one,
84141
+ // so the leaf node's CAD color is used as fallback.
84142
+ const { color: presetColor, ...presetRest } = preset;
84143
+ const resolved = "color" in appearance
84144
+ ? { ...preset, ...appearance }
84145
+ : { ...presetRest, ...appearance };
84149
84146
  this.resolvedMaterials.set(tag, resolved);
84150
84147
  return resolved;
84151
84148
  }
84152
- // Should not happen with current type, but guard anyway
84153
84149
  logger.warn(`Unrecognised material entry for tag '${tag}' on '${objectPath}'`);
84154
84150
  return null;
84155
84151
  }
@@ -84763,7 +84759,7 @@ float metalnessFactor = metalness;
84763
84759
  try {
84764
84760
  if (resolved && isMaterialXMaterial(resolved)) {
84765
84761
  // --- threejs-materials path ---
84766
- studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(resolved.properties, resolved.textureRepeat, this._textureCache);
84762
+ studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(resolved.values, resolved.textures, resolved.textureRepeat, this._textureCache);
84767
84763
  if (materialXHasTextures(resolved)) {
84768
84764
  this._texturedMaterialKeys.add(sharingKey);
84769
84765
  }
@@ -84840,6 +84836,18 @@ float metalnessFactor = metalness;
84840
84836
  }
84841
84837
  studioBack = cachedBack;
84842
84838
  }
84839
+ // Compute tangents for anisotropic materials (required by Three.js)
84840
+ if (studioMaterial instanceof MeshPhysicalMaterial &&
84841
+ studioMaterial.anisotropy > 0 &&
84842
+ obj.shapeGeometry?.getAttribute("uv") != null &&
84843
+ obj.shapeGeometry.getAttribute("tangent") == null) {
84844
+ try {
84845
+ obj.shapeGeometry.computeTangents();
84846
+ }
84847
+ catch {
84848
+ logger.debug(`Studio "${path}": tangent computation failed, anisotropy may have artifacts`);
84849
+ }
84850
+ }
84843
84851
  // Apply to ObjectGroup
84844
84852
  obj.enterStudioMode(studioMaterial instanceof MeshPhysicalMaterial ? studioMaterial : null, studioBack);
84845
84853
  }
@@ -87476,6 +87484,15 @@ float metalnessFactor = metalness;
87476
87484
  const z = this.distanceToPoint(new Vector3(0, 0, 0));
87477
87485
  this.constant = z - c + value;
87478
87486
  }
87487
+ /**
87488
+ * Clone this CenteredPlane.
87489
+ * Overrides THREE.Plane.clone() which calls `new this.constructor()` without
87490
+ * arguments, causing `center` to be undefined during shadow map generation.
87491
+ */
87492
+ // @ts-expect-error -- THREE.Plane.clone() returns `this`, but we need a concrete CenteredPlane
87493
+ clone() {
87494
+ return new CenteredPlane(this.normal.clone(), this.centeredConstant, [...this.center]);
87495
+ }
87479
87496
  }
87480
87497
  // ============================================================================
87481
87498
  // PlaneMesh - Visual representation of a clipping plane
@@ -94290,7 +94307,7 @@ float metalnessFactor = metalness;
94290
94307
  }
94291
94308
  }
94292
94309
 
94293
- const version = "4.3.2";
94310
+ const version = "4.3.5";
94294
94311
 
94295
94312
  /**
94296
94313
  * Clean room environment for Studio mode PMREM generation.
@@ -95915,7 +95932,7 @@ float metalnessFactor = metalness;
95915
95932
  * Create a shadow-receiving plane at the floor position.
95916
95933
  */
95917
95934
  _createShadowPlane(zPosition, sceneSize) {
95918
- const floorSize = sceneSize * 4;
95935
+ const floorSize = sceneSize * 6;
95919
95936
  const geometry = new PlaneGeometry(floorSize, floorSize);
95920
95937
  const material = new ShadowMaterial({ opacity: 0.5, depthWrite: false });
95921
95938
  const plane = new Mesh(geometry, material);
@@ -104762,7 +104779,7 @@ void mainImage(const in vec4 inputColor, const in vec2 uv, const in float depth,
104762
104779
  light.position.copy(bboxCenter).addScaledVector(dir, maxExtent * 3);
104763
104780
  light.target.position.copy(bboxCenter);
104764
104781
  light.castShadow = true;
104765
- const frustumSize = maxExtent * 4.0;
104782
+ const frustumSize = maxExtent * 6.0;
104766
104783
  light.shadow.camera.left = -frustumSize;
104767
104784
  light.shadow.camera.right = frustumSize;
104768
104785
  light.shadow.camera.top = frustumSize;