@archvisioninc/canvas 2.7.6 → 2.8.1

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_DEV.md CHANGED
@@ -391,6 +391,12 @@ const inboundData = {
391
391
  };
392
392
 
393
393
  updateCanvas(updateArgs, inboundData);
394
+
395
+ // Clearing all texture channels
396
+ const inboundData = {
397
+ id: selectedMaterialId,
398
+ clearTextureChannels: true,
399
+ }
394
400
  ```
395
401
 
396
402
  ### Materials: Clear-coat
@@ -522,6 +528,74 @@ const inboundData = {
522
528
  updateCanvas(removeArgs, inboundData);
523
529
  ```
524
530
 
531
+ ### Materials: UV's
532
+ ```jsx
533
+ // Setting UV X Scaling
534
+ const inboundData = {
535
+ id: selectedMaterialId,
536
+ uvXScale: newVal,
537
+ };
538
+
539
+ updateCanvas(updateArgs, inboundData);
540
+
541
+ // Setting UV Y Scaling
542
+ const inboundData = {
543
+ id: selectedMaterialId,
544
+ uvYScale: newVal,
545
+ };
546
+
547
+ updateCanvas(updateArgs, inboundData);
548
+
549
+ // Locking UV Y to X (or vice-versa) Scaling
550
+ const inboundData = {
551
+ id: selectedMaterialId,
552
+ uvXScale: newVal,
553
+ uvScaleLock: true,
554
+ };
555
+
556
+ updateCanvas(updateArgs, inboundData);
557
+
558
+ // Setting UV X Offset
559
+ const inboundData = {
560
+ id: selectedMaterialId,
561
+ uvXOffset: newVal,
562
+ };
563
+
564
+ updateCanvas(updateArgs, inboundData);
565
+
566
+ // Setting UV Y Offset
567
+ const inboundData = {
568
+ id: selectedMaterialId,
569
+ uvYOffset: newVal,
570
+ };
571
+
572
+ updateCanvas(updateArgs, inboundData);
573
+
574
+ // Setting UV X Rotation
575
+ const inboundData = {
576
+ id: selectedMaterialId,
577
+ uvXRotation: newVal,
578
+ };
579
+
580
+ updateCanvas(updateArgs, inboundData);
581
+
582
+ // Setting UV Y Rotation
583
+ const inboundData = {
584
+ id: selectedMaterialId,
585
+ uvYRotation: newVal,
586
+ };
587
+
588
+ updateCanvas(updateArgs, inboundData);
589
+
590
+ // Setting UV Z Rotation
591
+ const inboundData = {
592
+ id: selectedMaterialId,
593
+ uvZRotation: newVal,
594
+ };
595
+
596
+ updateCanvas(updateArgs, inboundData);
597
+ ```
598
+
525
599
  ### Materials: General
526
600
  ```jsx
527
601
  // Toggling back-face culling
@@ -66,8 +66,8 @@ const performTransform = args => {
66
66
  const userMeshes = getUserMeshes();
67
67
  const updatedPositions = scene.metadata.meshChangeTracking.map(mesh => {
68
68
  const userMesh = userMeshes.find(userMesh => userMesh.id === mesh.meshId);
69
- const parent = userMesh.parent;
70
- userMesh.setParent(null);
69
+ const parent = userMesh?.parent;
70
+ userMesh?.setParent?.(null);
71
71
  const boundingBox = getBoundingMeshData([userMesh]);
72
72
  const {
73
73
  center,
@@ -82,7 +82,7 @@ const performTransform = args => {
82
82
  ry: toDegrees(rotation.y) % 360,
83
83
  rz: -toDegrees(rotation.z) % 360
84
84
  };
85
- userMesh.setParent(parent);
85
+ userMesh?.setParent?.(parent);
86
86
  return newTracking;
87
87
  });
88
88
  return updatedPositions;
@@ -114,6 +114,31 @@ const performTransform = args => {
114
114
  updateNode();
115
115
  newMetaDataEntry('meshChangeTracking', newTrackingData(newTransforms));
116
116
  };
117
+ const applyUVSettings = args => {
118
+ const {
119
+ texture,
120
+ material
121
+ } = args || {};
122
+ if (!_.isEmpty(texture) && !_.isEmpty(material)) {
123
+ const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === material.id);
124
+ const {
125
+ uvXScale,
126
+ uvYScale,
127
+ uvXRotation,
128
+ uvYRotation,
129
+ uvZRotation,
130
+ uvXOffset,
131
+ uvYOffset
132
+ } = workingMaterial?.uvSettings || {};
133
+ texture.uAng = uvXRotation;
134
+ texture.wAng = uvYRotation;
135
+ texture.vAng = uvZRotation;
136
+ texture.uOffset = uvXOffset;
137
+ texture.vOffset = uvYOffset;
138
+ texture.uScale = uvXScale;
139
+ texture.vScale = uvYScale;
140
+ }
141
+ };
117
142
  export const updateMaterial = inboundData => {
118
143
  const {
119
144
  payload
@@ -151,7 +176,15 @@ export const updateMaterial = inboundData => {
151
176
  opacityTexture,
152
177
  newSelectedMaterial,
153
178
  albedoHasAlpha,
154
- clearTextureChannels
179
+ clearTextureChannels,
180
+ uvXScale,
181
+ uvYScale,
182
+ uvScaleLock,
183
+ uvXRotation,
184
+ uvYRotation,
185
+ uvZRotation,
186
+ uvXOffset,
187
+ uvYOffset
155
188
  } = payload;
156
189
  const material = scene?.getMaterialByName?.(id, true);
157
190
  if (material) {
@@ -219,6 +252,10 @@ export const updateMaterial = inboundData => {
219
252
  if (!_.isEmpty(albedoTexture?.src || albedoTexture?.url)) {
220
253
  material.albedoTexture = newTexture(albedoTexture?.src || albedoTexture?.url);
221
254
  material.albedoTexture.name = albedoTexture.name;
255
+ applyUVSettings({
256
+ texture: material.albedoTexture,
257
+ material
258
+ });
222
259
  }
223
260
 
224
261
  // Metallic
@@ -226,6 +263,10 @@ export const updateMaterial = inboundData => {
226
263
  if (!_.isEmpty(metallicTexture?.src || metallicTexture?.url)) {
227
264
  material.metallicTexture = newTexture(metallicTexture?.src || metallicTexture?.url);
228
265
  material.metallicTexture.name = metallicTexture.name;
266
+ applyUVSettings({
267
+ texture: material.metallicTexture,
268
+ material
269
+ });
229
270
  }
230
271
 
231
272
  // Roughness
@@ -233,6 +274,10 @@ export const updateMaterial = inboundData => {
233
274
  if (!_.isEmpty(microSurfaceTexture?.src || microSurfaceTexture?.url)) {
234
275
  material.microSurfaceTexture = newTexture(microSurfaceTexture?.src || microSurfaceTexture?.url);
235
276
  material.microSurfaceTexture.name = microSurfaceTexture.name;
277
+ applyUVSettings({
278
+ texture: material.microSurfaceTexture,
279
+ material
280
+ });
236
281
  }
237
282
 
238
283
  // Emissive
@@ -241,6 +286,10 @@ export const updateMaterial = inboundData => {
241
286
  if (!_.isEmpty(emissiveTexture?.src || emissiveTexture?.url)) {
242
287
  material.emissiveTexture = newTexture(emissiveTexture?.src || emissiveTexture?.url);
243
288
  material.emissiveTexture.name = emissiveTexture.name;
289
+ applyUVSettings({
290
+ texture: material.emissiveTexture,
291
+ material
292
+ });
244
293
  }
245
294
 
246
295
  // Normal/Bump
@@ -250,6 +299,10 @@ export const updateMaterial = inboundData => {
250
299
  if (!_.isEmpty(bumpTexture?.src || bumpTexture?.url)) {
251
300
  material.bumpTexture = newTexture(bumpTexture?.src || bumpTexture?.url);
252
301
  material.bumpTexture.name = bumpTexture.name;
302
+ applyUVSettings({
303
+ texture: material.bumpTexture,
304
+ material
305
+ });
253
306
  }
254
307
 
255
308
  // Clear coat
@@ -282,6 +335,10 @@ export const updateMaterial = inboundData => {
282
335
  if (!_.isEmpty(opacityTexture?.src || opacityTexture?.url)) {
283
336
  material.opacityTexture = newTexture(opacityTexture?.src || opacityTexture?.url);
284
337
  material.opacityTexture.name = opacityTexture.name;
338
+ applyUVSettings({
339
+ texture: material.opacityTexture,
340
+ material
341
+ });
285
342
  }
286
343
  if (transparencyType) {
287
344
  material.transparencyType = _.toLower(transparencyType);
@@ -294,6 +351,39 @@ export const updateMaterial = inboundData => {
294
351
  if (sssRefractionIntensity !== undefined) material.subSurface.refractionIntensity = sssRefractionIntensity ?? 1;
295
352
  if (sssIOR !== undefined) material.subSurface.indexOfRefraction = sssIOR ?? 1.52;
296
353
  }
354
+
355
+ // UV scale, rotation and offset
356
+ const xScaleReq = uvXScale !== undefined;
357
+ const yScaleReq = uvYScale !== undefined;
358
+ const xRotationReq = uvXRotation !== undefined;
359
+ const yRotationReq = uvYRotation !== undefined;
360
+ const zRotationReq = uvZRotation !== undefined;
361
+ const xOffsetReq = uvXOffset !== undefined;
362
+ const yOffsetReq = uvYOffset !== undefined;
363
+ const uvChangeRequest = xScaleReq || yScaleReq || xRotationReq || yRotationReq || zRotationReq || xOffsetReq || yOffsetReq;
364
+ if (uvChangeRequest) {
365
+ const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === id) || {};
366
+ Object.keys(workingMaterial)?.forEach(key => {
367
+ const texture = material[key];
368
+ const validMaterialWithTexture = _.isObject(texture) && !_.isArray(texture) && key.endsWith('Texture');
369
+ const hasAllKeys = texture && ['uAng', 'wAng', 'vAng', 'uOffset', 'vOffset', 'uScale', 'vScale'].every(key => texture[key] !== undefined);
370
+ if (validMaterialWithTexture && hasAllKeys) {
371
+ if (xRotationReq) texture.uAng = uvXRotation;
372
+ if (yRotationReq) texture.wAng = uvYRotation;
373
+ if (zRotationReq) texture.vAng = uvZRotation;
374
+ if (xOffsetReq) texture.uOffset = uvXOffset;
375
+ if (yOffsetReq) texture.vOffset = uvYOffset;
376
+ if ((xScaleReq || yScaleReq) && uvScaleLock) {
377
+ texture.uScale = uvXScale ?? uvYScale;
378
+ texture.vScale = uvXScale ?? uvYScale;
379
+ return;
380
+ }
381
+ if (xScaleReq) texture.uScale = uvXScale;
382
+ if (yScaleReq) texture.vScale = uvYScale;
383
+ if (uvScaleLock !== undefined) newMetaDataEntry('uvScaleLock', uvScaleLock);
384
+ }
385
+ });
386
+ }
297
387
  newMetaDataEntry('materials', buildMaterialsArray());
298
388
  newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
299
389
  }
@@ -327,6 +327,7 @@ const initMetadataKeyDefaults = () => {
327
327
  newMetaDataEntry('viewportAxes', true);
328
328
  newMetaDataEntry('viewportShadows', mirrorGround.receiveShadows);
329
329
  newMetaDataEntry('fileName', null);
330
+ newMetaDataEntry('uvScaleLock', false);
330
331
  };
331
332
  const checkForRootNode = () => {
332
333
  let rootNode = scene.getNodeById('__root__');
@@ -447,6 +448,7 @@ export const initSceneFromFile = (sceneFromFile, fileName) => {
447
448
  newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
448
449
  newMetaDataEntry('fileName', fileName);
449
450
  newMetaDataEntry('materialVariants', buildMaterialVariantsArray());
451
+ newMetaDataEntry('uvScaleLock', scene.metadata.uvScaleLock || false);
450
452
  updatePublish(fileName ? {
451
453
  payload: {
452
454
  title: fileName
@@ -448,6 +448,46 @@ export const getTextureUrl = args => {
448
448
  asDataUrl
449
449
  });
450
450
  };
451
+ export const getExistingUVSettings = args => {
452
+ const {
453
+ materialId
454
+ } = args || {};
455
+ let settingsFound = false;
456
+ let uvSettings = {
457
+ uvXScale: 1,
458
+ uvYScale: 1,
459
+ uvXRotation: 0,
460
+ uvYRotation: 0,
461
+ uvZRotation: 0,
462
+ uvXOffset: 0,
463
+ uvYOffset: 0
464
+ };
465
+ const userMaterials = getUserMaterials();
466
+ const material = userMaterials.find(mat => mat.id === materialId);
467
+ Object.keys(material)?.find?.(key => {
468
+ const validMaterialWithTexture = _.isObject(material[key]) && !_.isArray(material[key]) && key.endsWith('Texture');
469
+ const texture = material[key];
470
+ const isEnvironment = key.toLowerCase().includes('environment');
471
+ const hasAllKeys = texture && ['uAng', 'wAng', 'vAng', 'uOffset', 'vOffset', 'uScale', 'vScale'].every(key => texture[key] !== undefined);
472
+
473
+ // NOTE: All UV's are adjusted globally, at the same time in canvasUpdateHelpers.js
474
+ // when **any** UV setting is changed. So we only need to find the first one to
475
+ // restore the UV settings.
476
+ if (!isEnvironment && validMaterialWithTexture && hasAllKeys && !settingsFound) {
477
+ settingsFound = true;
478
+ uvSettings = {
479
+ uvXScale: texture.uScale,
480
+ uvYScale: texture.vScale,
481
+ uvXRotation: texture.uAng,
482
+ uvYRotation: texture.wAng,
483
+ uvZRotation: texture.vAng,
484
+ uvXOffset: texture.uOffset,
485
+ uvYOffset: texture.vOffset
486
+ };
487
+ }
488
+ });
489
+ return uvSettings;
490
+ };
451
491
  export const materialData = material => {
452
492
  const meshesWithMaterial = getUserMeshes().map(mesh => {
453
493
  if (mesh.material.name === material.id) return mesh;
@@ -509,6 +549,12 @@ export const materialData = material => {
509
549
  asDataUrl: true
510
550
  })
511
551
  },
552
+ // UV
553
+ uvSettings: {
554
+ ...getExistingUVSettings({
555
+ materialId: material.id
556
+ })
557
+ },
512
558
  // Advanced
513
559
  clearCoatEnabled: material.clearCoat?.isEnabled || false,
514
560
  clearCoatIntensity: material.clearCoat?.intensity,
@@ -573,9 +619,9 @@ export const takePreviewScreenshots = () => {
573
619
  const yAxisMesh = scene.getMeshByName('yAxisMesh');
574
620
  const zAxisMesh = scene.getMeshByName('zAxisMesh');
575
621
  const hdrSkyBox = scene.getMeshByName('hdrSkyBox');
576
- const axesVisible = xAxisMesh.isVisible;
577
- const skyBoxVisible = hdrSkyBox.isVisible;
578
- const frameVisible = outerFrame.isVisible;
622
+ const axesVisible = xAxisMesh?.isVisible;
623
+ const skyBoxVisible = hdrSkyBox?.isVisible;
624
+ const frameVisible = outerFrame?.isVisible;
579
625
  const groundVisible = ground.isVisible;
580
626
  const mirrorGroundVisible = mirrorGround.isVisible;
581
627
  const {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archvisioninc/canvas",
3
- "version": "2.7.6",
3
+ "version": "2.8.1",
4
4
  "private": false,
5
5
  "main": "dist/Canvas.js",
6
6
  "module": "dist/Canvas.js",
@@ -102,9 +102,9 @@ const performTransform = args => {
102
102
  const userMeshes = getUserMeshes();
103
103
  const updatedPositions = scene.metadata.meshChangeTracking.map(mesh => {
104
104
  const userMesh = userMeshes.find(userMesh => userMesh.id === mesh.meshId);
105
- const parent = userMesh.parent;
105
+ const parent = userMesh?.parent;
106
106
 
107
- userMesh.setParent(null);
107
+ userMesh?.setParent?.(null);
108
108
 
109
109
  const boundingBox = getBoundingMeshData([ userMesh ]);
110
110
  const { center, rotation } = boundingBox;
@@ -119,7 +119,7 @@ const performTransform = args => {
119
119
  rz: -toDegrees(rotation.z) % 360,
120
120
  };
121
121
 
122
- userMesh.setParent(parent);
122
+ userMesh?.setParent?.(parent);
123
123
  return newTracking;
124
124
  });
125
125
 
@@ -152,6 +152,31 @@ const performTransform = args => {
152
152
  newMetaDataEntry('meshChangeTracking', newTrackingData(newTransforms));
153
153
  };
154
154
 
155
+ const applyUVSettings = args => {
156
+ const { texture, material } = args || {};
157
+
158
+ if (!_.isEmpty(texture) && !_.isEmpty(material)) {
159
+ const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === material.id);
160
+ const {
161
+ uvXScale,
162
+ uvYScale,
163
+ uvXRotation,
164
+ uvYRotation,
165
+ uvZRotation,
166
+ uvXOffset,
167
+ uvYOffset,
168
+ } = workingMaterial?.uvSettings || {};
169
+
170
+ texture.uAng = uvXRotation;
171
+ texture.wAng = uvYRotation;
172
+ texture.vAng = uvZRotation;
173
+ texture.uOffset = uvXOffset;
174
+ texture.vOffset = uvYOffset;
175
+ texture.uScale = uvXScale;
176
+ texture.vScale = uvYScale;
177
+ }
178
+ };
179
+
155
180
  export const updateMaterial = inboundData => {
156
181
  const { payload } = inboundData;
157
182
  const {
@@ -188,6 +213,14 @@ export const updateMaterial = inboundData => {
188
213
  newSelectedMaterial,
189
214
  albedoHasAlpha,
190
215
  clearTextureChannels,
216
+ uvXScale,
217
+ uvYScale,
218
+ uvScaleLock,
219
+ uvXRotation,
220
+ uvYRotation,
221
+ uvZRotation,
222
+ uvXOffset,
223
+ uvYOffset,
191
224
  } = payload;
192
225
 
193
226
  const material = scene?.getMaterialByName?.(id, true);
@@ -253,6 +286,8 @@ export const updateMaterial = inboundData => {
253
286
  if (!_.isEmpty(albedoTexture?.src || albedoTexture?.url)) {
254
287
  material.albedoTexture = newTexture(albedoTexture?.src || albedoTexture?.url);
255
288
  material.albedoTexture.name = albedoTexture.name;
289
+
290
+ applyUVSettings({ texture: material.albedoTexture, material });
256
291
  }
257
292
 
258
293
  // Metallic
@@ -260,6 +295,8 @@ export const updateMaterial = inboundData => {
260
295
  if (!_.isEmpty(metallicTexture?.src || metallicTexture?.url)) {
261
296
  material.metallicTexture = newTexture(metallicTexture?.src || metallicTexture?.url);
262
297
  material.metallicTexture.name = metallicTexture.name;
298
+
299
+ applyUVSettings({ texture: material.metallicTexture, material });
263
300
  }
264
301
 
265
302
  // Roughness
@@ -267,6 +304,8 @@ export const updateMaterial = inboundData => {
267
304
  if (!_.isEmpty(microSurfaceTexture?.src || microSurfaceTexture?.url)) {
268
305
  material.microSurfaceTexture = newTexture(microSurfaceTexture?.src || microSurfaceTexture?.url);
269
306
  material.microSurfaceTexture.name = microSurfaceTexture.name;
307
+
308
+ applyUVSettings({ texture: material.microSurfaceTexture, material });
270
309
  }
271
310
 
272
311
  // Emissive
@@ -275,6 +314,8 @@ export const updateMaterial = inboundData => {
275
314
  if (!_.isEmpty(emissiveTexture?.src || emissiveTexture?.url)) {
276
315
  material.emissiveTexture = newTexture(emissiveTexture?.src || emissiveTexture?.url);
277
316
  material.emissiveTexture.name = emissiveTexture.name;
317
+
318
+ applyUVSettings({ texture: material.emissiveTexture, material });
278
319
  }
279
320
 
280
321
  // Normal/Bump
@@ -285,6 +326,8 @@ export const updateMaterial = inboundData => {
285
326
  if (!_.isEmpty(bumpTexture?.src || bumpTexture?.url)) {
286
327
  material.bumpTexture = newTexture(bumpTexture?.src || bumpTexture?.url);
287
328
  material.bumpTexture.name = bumpTexture.name;
329
+
330
+ applyUVSettings({ texture: material.bumpTexture, material });
288
331
  }
289
332
 
290
333
  // Clear coat
@@ -322,6 +365,8 @@ export const updateMaterial = inboundData => {
322
365
  if (!_.isEmpty(opacityTexture?.src || opacityTexture?.url)) {
323
366
  material.opacityTexture = newTexture(opacityTexture?.src || opacityTexture?.url);
324
367
  material.opacityTexture.name = opacityTexture.name;
368
+
369
+ applyUVSettings({ texture: material.opacityTexture, material });
325
370
  }
326
371
 
327
372
  if (transparencyType) {
@@ -339,6 +384,48 @@ export const updateMaterial = inboundData => {
339
384
  if (sssIOR !== undefined) material.subSurface.indexOfRefraction = sssIOR ?? 1.52;
340
385
  }
341
386
 
387
+ // UV scale, rotation and offset
388
+ const xScaleReq = uvXScale !== undefined;
389
+ const yScaleReq = uvYScale !== undefined;
390
+ const xRotationReq = uvXRotation !== undefined;
391
+ const yRotationReq = uvYRotation !== undefined;
392
+ const zRotationReq = uvZRotation !== undefined;
393
+ const xOffsetReq = uvXOffset !== undefined;
394
+ const yOffsetReq = uvYOffset !== undefined;
395
+ const uvChangeRequest = xScaleReq || yScaleReq || xRotationReq
396
+ || yRotationReq || zRotationReq || xOffsetReq || yOffsetReq;
397
+
398
+ if (uvChangeRequest) {
399
+ const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === id) || {};
400
+
401
+ Object.keys(workingMaterial)?.forEach(key => {
402
+ const texture = material[key];
403
+ const validMaterialWithTexture = _.isObject(texture) && !_.isArray(texture) && key.endsWith('Texture');
404
+ const hasAllKeys = texture && [ 'uAng', 'wAng', 'vAng', 'uOffset', 'vOffset', 'uScale', 'vScale' ]
405
+ .every(key => texture[key] !== undefined);
406
+
407
+ if (validMaterialWithTexture && hasAllKeys) {
408
+ if (xRotationReq) texture.uAng = uvXRotation;
409
+ if (yRotationReq) texture.wAng = uvYRotation;
410
+ if (zRotationReq) texture.vAng = uvZRotation;
411
+
412
+ if (xOffsetReq) texture.uOffset = uvXOffset;
413
+ if (yOffsetReq) texture.vOffset = uvYOffset;
414
+
415
+ if ((xScaleReq || yScaleReq) && uvScaleLock) {
416
+ texture.uScale = uvXScale ?? uvYScale;
417
+ texture.vScale = uvXScale ?? uvYScale;
418
+ return;
419
+ }
420
+
421
+ if (xScaleReq) texture.uScale = uvXScale;
422
+ if (yScaleReq) texture.vScale = uvYScale;
423
+
424
+ if (uvScaleLock !== undefined) newMetaDataEntry('uvScaleLock', uvScaleLock);
425
+ }
426
+ });
427
+ }
428
+
342
429
  newMetaDataEntry('materials', buildMaterialsArray());
343
430
  newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
344
431
  }
@@ -364,6 +364,7 @@ const initMetadataKeyDefaults = () => {
364
364
  newMetaDataEntry('viewportAxes', true);
365
365
  newMetaDataEntry('viewportShadows', mirrorGround.receiveShadows);
366
366
  newMetaDataEntry('fileName', null);
367
+ newMetaDataEntry('uvScaleLock', false);
367
368
  };
368
369
 
369
370
  const checkForRootNode = () => {
@@ -482,6 +483,7 @@ export const initSceneFromFile = (sceneFromFile, fileName) => {
482
483
  newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
483
484
  newMetaDataEntry('fileName', fileName);
484
485
  newMetaDataEntry('materialVariants', buildMaterialVariantsArray());
486
+ newMetaDataEntry('uvScaleLock', scene.metadata.uvScaleLock || false);
485
487
 
486
488
  updatePublish(fileName ? { payload: { title: fileName } } : {});
487
489
 
@@ -509,6 +509,49 @@ export const getTextureUrl = args => {
509
509
  return getImageFileFromBuffer({ buffer, asDataUrl });
510
510
  };
511
511
 
512
+ export const getExistingUVSettings = args => {
513
+ const { materialId } = args || {};
514
+ let settingsFound = false;
515
+ let uvSettings = {
516
+ uvXScale: 1,
517
+ uvYScale: 1,
518
+ uvXRotation: 0,
519
+ uvYRotation: 0,
520
+ uvZRotation: 0,
521
+ uvXOffset: 0,
522
+ uvYOffset: 0,
523
+ };
524
+
525
+ const userMaterials = getUserMaterials();
526
+ const material = userMaterials.find(mat => mat.id === materialId);
527
+
528
+ Object.keys(material)?.find?.(key => {
529
+ const validMaterialWithTexture = _.isObject(material[key]) && !_.isArray(material[key]) && key.endsWith('Texture');
530
+ const texture = material[key];
531
+ const isEnvironment = key.toLowerCase().includes('environment');
532
+ const hasAllKeys = texture && [ 'uAng', 'wAng', 'vAng', 'uOffset', 'vOffset', 'uScale', 'vScale' ]
533
+ .every(key => texture[key] !== undefined);
534
+
535
+ // NOTE: All UV's are adjusted globally, at the same time in canvasUpdateHelpers.js
536
+ // when **any** UV setting is changed. So we only need to find the first one to
537
+ // restore the UV settings.
538
+ if (!isEnvironment && validMaterialWithTexture && hasAllKeys && !settingsFound) {
539
+ settingsFound = true;
540
+ uvSettings = {
541
+ uvXScale: texture.uScale,
542
+ uvYScale: texture.vScale,
543
+ uvXRotation: texture.uAng,
544
+ uvYRotation: texture.wAng,
545
+ uvZRotation: texture.vAng,
546
+ uvXOffset: texture.uOffset,
547
+ uvYOffset: texture.vOffset,
548
+ };
549
+ }
550
+ });
551
+
552
+ return uvSettings;
553
+ };
554
+
512
555
  export const materialData = material => {
513
556
  const meshesWithMaterial = getUserMeshes().map(mesh => {
514
557
  if (mesh.material.name === material.id) return mesh;
@@ -561,6 +604,11 @@ export const materialData = material => {
561
604
  url: getTextureUrl({ texture: material.bumpTexture, asDataUrl: true }),
562
605
  },
563
606
 
607
+ // UV
608
+ uvSettings: {
609
+ ...getExistingUVSettings({ materialId: material.id }),
610
+ },
611
+
564
612
  // Advanced
565
613
  clearCoatEnabled: material.clearCoat?.isEnabled || false,
566
614
  clearCoatIntensity: material.clearCoat?.intensity,
@@ -635,9 +683,9 @@ export const takePreviewScreenshots = () => {
635
683
  const yAxisMesh = scene.getMeshByName('yAxisMesh');
636
684
  const zAxisMesh = scene.getMeshByName('zAxisMesh');
637
685
  const hdrSkyBox = scene.getMeshByName('hdrSkyBox');
638
- const axesVisible = xAxisMesh.isVisible;
639
- const skyBoxVisible = hdrSkyBox.isVisible;
640
- const frameVisible = outerFrame.isVisible;
686
+ const axesVisible = xAxisMesh?.isVisible;
687
+ const skyBoxVisible = hdrSkyBox?.isVisible;
688
+ const frameVisible = outerFrame?.isVisible;
641
689
  const groundVisible = ground.isVisible;
642
690
  const mirrorGroundVisible = mirrorGround.isVisible;
643
691
  const {
@@ -15,10 +15,18 @@ const demoScene = false;
15
15
  const shaderballGuid = '21-791A-0D8D-F8A0-63F7-5086-471F-69A7-7AB0-00';
16
16
 
17
17
  const serializedResponseData = {
18
- albedoColor: '#004BA6',
18
+ albedoColor: '#FFFFFF',
19
19
  albedoHasAlpha: false,
20
20
  albedoTexture: {
21
21
  name: 'Blender.png',
22
+ uvXScale: 1,
23
+ uvYScale: 1,
24
+ uvScaleLock: false,
25
+ uvXRotation: 0,
26
+ uvYRotation: 0,
27
+ uvZRotation: 0,
28
+ uvXOffset: 0,
29
+ uvYOffset: 0,
22
30
  url: 'data:image/png;base64,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',
23
31
  },
24
32
  alpha: 1,
@@ -44,7 +52,7 @@ const serializedResponseData = {
44
52
  type: 'custom',
45
53
  userTargetTriangles: 76004,
46
54
  },
47
- metallic: 1,
55
+ metallic: 0,
48
56
  metallicTexture: { url: '' },
49
57
  microSurfaceTexture: { url: '' },
50
58
  name: 'material',
@@ -199,6 +207,25 @@ const App = () => {
199
207
  Change material
200
208
  </Button>
201
209
 
210
+ <Button
211
+ theme={theme}
212
+ $selectedTheme={selectedTheme}
213
+ onClick={() => {
214
+ const inboundData = {
215
+ payload: {
216
+ id: 'material',
217
+ uvYOffset: 0.25,
218
+ uvScaleLock: true,
219
+ },
220
+ };
221
+
222
+ updateMaterial(inboundData);
223
+ console.log({ metadata: scene.metadata });
224
+ }}
225
+ >
226
+ UV Y Offset
227
+ </Button>
228
+
202
229
  <Button
203
230
  theme={theme}
204
231
  $selectedTheme={selectedTheme}