@archvisioninc/canvas 3.0.0 → 3.1.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.
@@ -4,6 +4,62 @@ import { deleteSelected } from '../actions';
4
4
  import { TRANSPARENCY_MODES } from '../constants';
5
5
  import * as BABYLON from 'babylonjs';
6
6
  import _ from 'lodash';
7
+ const removeChannelFromTexture = (image, channel) => {
8
+ const imageHeight = image?.naturalHeight ?? 0;
9
+ const imageWidth = image?.naturalWidth ?? 0;
10
+ let imageData;
11
+ const maxHeight = Math.max(...[imageHeight, 1024]);
12
+ const maxWidth = Math.max(...[imageWidth, 1024]);
13
+ const canvas = document.createElement('canvas');
14
+ const ctx = canvas.getContext('2d');
15
+ canvas.width = maxWidth;
16
+ canvas.height = maxHeight;
17
+ if (image) {
18
+ ctx.drawImage(image, 0, 0, maxWidth, maxHeight);
19
+ imageData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
20
+ }
21
+ const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
22
+ const combinedData = combinedImageData.data;
23
+ for (let i = 0; i < combinedData.length; i += 4) {
24
+ combinedData[i] = channel === 'R' ? 255 : imageData[i];
25
+ combinedData[i + 1] = channel === 'G' ? 255 : imageData[i + 1];
26
+ combinedData[i + 2] = channel === 'B' ? 255 : imageData[i + 2];
27
+ combinedData[i + 3] = channel === 'A' ? 255 : imageData[i + 3];
28
+ }
29
+ ctx.putImageData(combinedImageData, 0, 0);
30
+ return canvas.toDataURL('image/png');
31
+ };
32
+ const textureToImage = texture => {
33
+ return new Promise(resolve => {
34
+ if (!texture.readPixels()) {
35
+ resolve(null);
36
+ return;
37
+ }
38
+ texture.readPixels().then(pixels => {
39
+ const canvas = document.createElement('canvas');
40
+ const ctx = canvas.getContext('2d');
41
+ const {
42
+ width,
43
+ height
44
+ } = texture.getSize();
45
+ canvas.width = width;
46
+ canvas.height = height;
47
+ const img = new Image();
48
+ const imageData = ctx.createImageData(width, height);
49
+ imageData.data.set(new Uint8ClampedArray(pixels));
50
+ ctx.putImageData(imageData, 0, 0);
51
+ img.onload = () => {
52
+ resolve(img);
53
+ };
54
+ img.onerror = () => {
55
+ resolve(null);
56
+ };
57
+ img.src = canvas.toDataURL();
58
+ }).catch(() => {
59
+ resolve(null);
60
+ });
61
+ });
62
+ };
7
63
  export const removeFromMaterial = inboundData => {
8
64
  const {
9
65
  payload
@@ -15,7 +71,9 @@ export const removeFromMaterial = inboundData => {
15
71
  removeMicroSurfaceTexture,
16
72
  removeEmissiveTexture,
17
73
  removeBumpTexture,
18
- removeOpacityTexture
74
+ removeOpacityTexture,
75
+ removeRoughnessTexture,
76
+ removeAmbientTexture
19
77
  } = payload;
20
78
  const material = scene.getMaterialByName(id, true);
21
79
  if (material) {
@@ -32,8 +90,16 @@ export const removeFromMaterial = inboundData => {
32
90
  material.albedoTexture = null;
33
91
  }
34
92
  if (removeMetallicTexture) {
35
- material.metallicTexture?.dispose();
36
- material.metallicTexture = null;
93
+ if (material.metallicTexture) {
94
+ textureToImage(material.metallicTexture).then(data => {
95
+ const updatedTexture = removeChannelFromTexture(data, 'B');
96
+ material.metallicTexture.updateURL(updatedTexture);
97
+ newMetaDataEntry('materials', buildMaterialsArray());
98
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
99
+ }).catch(() => {
100
+ console.log('Failed to remove metallic texture');
101
+ });
102
+ }
37
103
  }
38
104
  if (removeMicroSurfaceTexture) {
39
105
  material.microSurfaceTexture?.dispose();
@@ -51,6 +117,22 @@ export const removeFromMaterial = inboundData => {
51
117
  material.opacityTexture?.dispose();
52
118
  material.opacityTexture = null;
53
119
  }
120
+ if (removeAmbientTexture) {
121
+ material.ambientTexture?.dispose();
122
+ material.opacityTexture = null;
123
+ }
124
+ if (removeRoughnessTexture) {
125
+ if (material.metallicTexture) {
126
+ textureToImage(material.metallicTexture).then(data => {
127
+ const updatedTexture = removeChannelFromTexture(data, 'G');
128
+ material.metallicTexture.updateURL(updatedTexture);
129
+ newMetaDataEntry('materials', buildMaterialsArray());
130
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
131
+ }).catch(() => {
132
+ console.log('Failed to remove roughness texture');
133
+ });
134
+ }
135
+ }
54
136
  newMetaDataEntry('materials', buildMaterialsArray());
55
137
  newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
56
138
  }
@@ -141,28 +141,42 @@ const applyUVSettings = args => {
141
141
  };
142
142
 
143
143
  // TODO: Future uses should support red, green, blue, and alpha channels with defaults for each
144
- const combineMetallicRoughnessTextures = (metallicImage, roughnessImage) => {
145
- const roughnessHeight = roughnessImage.naturalHeight;
146
- const roughnessWidth = roughnessImage.naturalWidth;
147
- const metallicHeight = metallicImage.naturalHeight;
148
- const metallicWidth = metallicImage.naturalWidth;
149
- const maxHeight = Math.max(roughnessHeight, metallicHeight);
150
- const maxWidth = Math.max(roughnessWidth, metallicWidth);
144
+ const updateTextureChannel = (imageToMaintain, newImage, channelToUpdate) => {
145
+ const maintainHeight = imageToMaintain?.naturalHeight ?? 0;
146
+ const maintainWidth = imageToMaintain?.naturalWidth ?? 0;
147
+ const newHeight = newImage?.naturalHeight ?? 0;
148
+ const newWidth = newImage?.naturalWidth ?? 0;
149
+ let maintainData;
150
+ let newData;
151
+ const maxHeight = Math.max(...[maintainHeight, newHeight, 1024]);
152
+ const maxWidth = Math.max(...[maintainWidth, newWidth, 1024]);
151
153
  const canvas = document.createElement('canvas');
152
154
  const ctx = canvas.getContext('2d');
153
155
  canvas.width = maxWidth;
154
156
  canvas.height = maxHeight;
155
- ctx.drawImage(roughnessImage, 0, 0, maxWidth, maxHeight);
156
- const roughnessData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
157
- ctx.drawImage(metallicImage, 0, 0, maxWidth, maxHeight);
158
- const metallicData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
157
+ if (newImage) {
158
+ ctx.drawImage(newImage, 0, 0, maxWidth, maxHeight);
159
+ newData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
160
+ }
161
+ if (imageToMaintain) {
162
+ ctx.drawImage(imageToMaintain, 0, 0, maxWidth, maxHeight);
163
+ maintainData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
164
+ }
159
165
  const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
160
166
  const combinedData = combinedImageData.data;
161
167
  for (let i = 0; i < combinedData.length; i += 4) {
162
- combinedData[i] = 255;
163
- combinedData[i + 1] = roughnessData[i + 1];
164
- combinedData[i + 2] = metallicData[i + 2];
165
- combinedData[i + 3] = 255;
168
+ const maintainRed = maintainData ? maintainData[i] : 255;
169
+ const maintainGreen = maintainData ? maintainData[i + 1] : 255;
170
+ const maintainBlue = maintainData ? maintainData[i + 2] : 255;
171
+ const maintainAlpha = maintainData ? maintainData[i + 3] : 255;
172
+ const newRed = newData ? newData[i] : 255;
173
+ const newGreen = newData ? newData[i + 1] : 255;
174
+ const newBlue = newData ? newData[i + 2] : 255;
175
+ const newAlpha = newData ? newData[i + 3] : 255;
176
+ combinedData[i] = channelToUpdate === 'R' ? newRed : maintainRed;
177
+ combinedData[i + 1] = channelToUpdate === 'G' ? newGreen : maintainGreen;
178
+ combinedData[i + 2] = channelToUpdate === 'B' ? newBlue : maintainBlue;
179
+ combinedData[i + 3] = channelToUpdate === 'A' ? newAlpha : maintainAlpha;
166
180
  }
167
181
  ctx.putImageData(combinedImageData, 0, 0);
168
182
  return canvas.toDataURL('image/png');
@@ -170,22 +184,28 @@ const combineMetallicRoughnessTextures = (metallicImage, roughnessImage) => {
170
184
 
171
185
  // eslint-disable-next-line
172
186
  const loadImage = async file => {
173
- return new Promise((resolve, reject) => {
187
+ return new Promise(resolve => {
174
188
  const reader = new FileReader();
175
189
  reader.onload = event => {
176
190
  const img = new Image();
177
- img.onload = () => {
178
- resolve(img);
191
+ img.onload = () => resolve(img);
192
+ img.onerror = () => {
193
+ resolve(null);
179
194
  };
180
- img.onerror = reject;
181
195
  img.src = event.target.result;
182
196
  };
183
- reader.onerror = reject;
197
+ reader.onerror = () => {
198
+ resolve(null);
199
+ };
184
200
  reader.readAsDataURL(file);
185
201
  });
186
202
  };
187
- const textureToImage = (texture, textureName) => {
188
- return new Promise((resolve, reject) => {
203
+ const textureToImage = texture => {
204
+ return new Promise(resolve => {
205
+ if (!texture.readPixels()) {
206
+ resolve(null);
207
+ return;
208
+ }
189
209
  texture.readPixels().then(pixels => {
190
210
  const canvas = document.createElement('canvas');
191
211
  const ctx = canvas.getContext('2d');
@@ -202,11 +222,12 @@ const textureToImage = (texture, textureName) => {
202
222
  img.onload = () => {
203
223
  resolve(img);
204
224
  };
205
- img.onerror = event => {
206
- console.log(`Error loading image: ${textureName}`);
207
- reject(event);
225
+ img.onerror = () => {
226
+ resolve(null);
208
227
  };
209
228
  img.src = canvas.toDataURL();
229
+ }).catch(() => {
230
+ resolve(null);
210
231
  });
211
232
  });
212
233
  };
@@ -255,6 +276,9 @@ export const updateMaterial = inboundData => {
255
276
  metallicTexture,
256
277
  roughness,
257
278
  roughnessTexture,
279
+ ambientColor,
280
+ ambientTextureStrength,
281
+ ambientTexture,
258
282
  microSurfaceTexture,
259
283
  emissiveColor,
260
284
  emissiveIntensity,
@@ -363,19 +387,27 @@ export const updateMaterial = inboundData => {
363
387
  let currentTexture = material.metallicTexture;
364
388
  if (!currentTexture) {
365
389
  material.metallicTexture = newTexture();
390
+ material.metallicTexture.name = `${material.name} (Metallic-Roughness)`;
366
391
  currentTexture = material.metallicTexture;
367
392
  }
368
393
  const metallicBlob = dataUrlToBlob(texture.url);
369
- Promise.all([loadImage(metallicBlob), textureToImage(currentTexture, 'metallic')]).then(data => {
394
+ Promise.all([loadImage(metallicBlob), textureToImage(currentTexture)]).then(data => {
370
395
  const [metallicImage, currentImage] = data;
371
- const combinedTexture = combineMetallicRoughnessTextures(metallicImage, currentImage);
396
+ const combinedTexture = updateTextureChannel(currentImage, metallicImage, 'B');
372
397
  currentTexture.updateURL(combinedTexture);
373
398
  texture.dispose();
374
399
  applyUVSettings({
375
400
  texture: material.metallicTexture,
376
401
  material
377
402
  });
378
- }).catch(() => console.log(`Error updating the metallic texture of material: ${material.name}`));
403
+ newMetaDataEntry('materials', buildMaterialsArray());
404
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
405
+ }).catch(err => {
406
+ console.log(`Error updating the roughness texture of material: ${material.name}`);
407
+ console.log({
408
+ err
409
+ });
410
+ });
379
411
  }
380
412
 
381
413
  // Roughness
@@ -394,19 +426,39 @@ export const updateMaterial = inboundData => {
394
426
  let currentTexture = material.metallicTexture;
395
427
  if (!currentTexture) {
396
428
  material.metallicTexture = newTexture();
429
+ material.metallicTexture.name = `${material.name} (Metallic-Roughness)`;
397
430
  currentTexture = material.metallicTexture;
398
431
  }
399
432
  const roughnessBlob = dataUrlToBlob(texture.url);
400
- Promise.all([loadImage(roughnessBlob), textureToImage(currentTexture, 'metallic')]).then(data => {
433
+ Promise.all([loadImage(roughnessBlob), textureToImage(currentTexture)]).then(data => {
401
434
  const [roughnessImage, metallicImage] = data;
402
- const combinedTexture = combineMetallicRoughnessTextures(metallicImage, roughnessImage);
435
+ const combinedTexture = updateTextureChannel(metallicImage, roughnessImage, 'G');
403
436
  currentTexture.updateURL(combinedTexture);
404
437
  texture.dispose();
405
438
  applyUVSettings({
406
439
  texture: material.metallicTexture,
407
440
  material
408
441
  });
409
- }).catch(() => console.log(`Error updating the roughness texture of material: ${material.name}`));
442
+ newMetaDataEntry('materials', buildMaterialsArray());
443
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
444
+ }).catch(err => {
445
+ console.log(`Error updating the roughness texture of material: ${material.name}`);
446
+ console.log({
447
+ err
448
+ });
449
+ });
450
+ }
451
+
452
+ // Ambient
453
+ if (ambientColor) material.ambientColor = newColor(emissiveColor);
454
+ if (ambientTextureStrength !== undefined) material.ambientTextureStrength = ambientTextureStrength;
455
+ if (!_.isEmpty(ambientTexture?.src || ambientTexture?.url)) {
456
+ material.ambientTexture = newTexture(ambientTexture?.src || ambientTexture?.url);
457
+ material.ambientTexture.name = ambientTexture.name;
458
+ applyUVSettings({
459
+ texture: material.ambientTexture,
460
+ material
461
+ });
410
462
  }
411
463
 
412
464
  // Emissive
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archvisioninc/canvas",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "private": false,
5
5
  "main": "dist/Canvas.js",
6
6
  "module": "dist/Canvas.js",
@@ -13,6 +13,82 @@ import { TRANSPARENCY_MODES } from '../constants';
13
13
  import * as BABYLON from 'babylonjs';
14
14
  import _ from 'lodash';
15
15
 
16
+ const removeChannelFromTexture = (image, channel) => {
17
+ const imageHeight = image?.naturalHeight ?? 0;
18
+ const imageWidth = image?.naturalWidth ?? 0;
19
+
20
+ let imageData;
21
+
22
+ const maxHeight = Math.max(...[ imageHeight, 1024 ]);
23
+ const maxWidth = Math.max(...[ imageWidth, 1024 ]);
24
+
25
+ const canvas = document.createElement('canvas');
26
+
27
+ const ctx = canvas.getContext('2d');
28
+
29
+ canvas.width = maxWidth;
30
+ canvas.height = maxHeight;
31
+
32
+ if (image) {
33
+ ctx.drawImage(image, 0, 0, maxWidth, maxHeight);
34
+ imageData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
35
+ }
36
+
37
+ const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
38
+ const combinedData = combinedImageData.data;
39
+
40
+ for (let i = 0; i < combinedData.length; i += 4) {
41
+ combinedData[i] = channel === 'R' ? 255 : imageData[i];
42
+ combinedData[i + 1] = channel === 'G' ? 255 : imageData[i + 1];
43
+ combinedData[i + 2] = channel === 'B' ? 255 : imageData[i + 2];
44
+ combinedData[i + 3] = channel === 'A' ? 255 : imageData[i + 3];
45
+ }
46
+
47
+ ctx.putImageData(combinedImageData, 0, 0);
48
+
49
+ return canvas.toDataURL('image/png');
50
+
51
+ };
52
+
53
+ const textureToImage = texture => {
54
+ return new Promise(resolve => {
55
+ if (!texture.readPixels()) {
56
+ resolve(null);
57
+ return;
58
+ }
59
+ texture.readPixels()
60
+ .then(pixels => {
61
+ const canvas = document.createElement('canvas');
62
+ const ctx = canvas.getContext('2d');
63
+ const { width, height } = texture.getSize();
64
+
65
+ canvas.width = width;
66
+ canvas.height = height;
67
+
68
+ const img = new Image();
69
+ const imageData = ctx.createImageData(width, height);
70
+ imageData.data.set(new Uint8ClampedArray(pixels));
71
+
72
+ ctx.putImageData(imageData, 0, 0);
73
+
74
+ img.onload = () => {
75
+ resolve(img);
76
+ };
77
+
78
+ img.onerror = () => {
79
+ resolve(null);
80
+ };
81
+
82
+
83
+ img.src = canvas.toDataURL();
84
+ })
85
+ .catch(() => {
86
+ resolve(null);
87
+ });
88
+
89
+ });
90
+ };
91
+
16
92
  export const removeFromMaterial = inboundData => {
17
93
  const { payload } = inboundData;
18
94
  const {
@@ -23,6 +99,8 @@ export const removeFromMaterial = inboundData => {
23
99
  removeEmissiveTexture,
24
100
  removeBumpTexture,
25
101
  removeOpacityTexture,
102
+ removeRoughnessTexture,
103
+ removeAmbientTexture,
26
104
  } = payload;
27
105
 
28
106
  const material = scene.getMaterialByName(id, true);
@@ -44,8 +122,18 @@ export const removeFromMaterial = inboundData => {
44
122
  }
45
123
 
46
124
  if (removeMetallicTexture) {
47
- material.metallicTexture?.dispose();
48
- material.metallicTexture = null;
125
+ if (material.metallicTexture) {
126
+ textureToImage(material.metallicTexture)
127
+ .then(data => {
128
+ const updatedTexture = removeChannelFromTexture(data, 'B');
129
+ material.metallicTexture.updateURL(updatedTexture);
130
+ newMetaDataEntry('materials', buildMaterialsArray());
131
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
132
+ })
133
+ .catch(() => {
134
+ console.log('Failed to remove metallic texture');
135
+ });
136
+ }
49
137
  }
50
138
 
51
139
  if (removeMicroSurfaceTexture) {
@@ -67,6 +155,24 @@ export const removeFromMaterial = inboundData => {
67
155
  material.opacityTexture?.dispose();
68
156
  material.opacityTexture = null;
69
157
  }
158
+ if (removeAmbientTexture) {
159
+ material.ambientTexture?.dispose();
160
+ material.opacityTexture = null;
161
+ }
162
+ if (removeRoughnessTexture) {
163
+ if (material.metallicTexture) {
164
+ textureToImage(material.metallicTexture)
165
+ .then(data => {
166
+ const updatedTexture = removeChannelFromTexture(data, 'G');
167
+ material.metallicTexture.updateURL(updatedTexture);
168
+ newMetaDataEntry('materials', buildMaterialsArray());
169
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
170
+ })
171
+ .catch(() => {
172
+ console.log('Failed to remove roughness texture');
173
+ });
174
+ }
175
+ }
70
176
 
71
177
  newMetaDataEntry('materials', buildMaterialsArray());
72
178
  newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
@@ -161,4 +267,4 @@ export const removeFromViewport = inboundData => {
161
267
 
162
268
  props.setConfirmMessage?.(confirmConfig);
163
269
  }
164
- };
270
+ };
@@ -178,14 +178,17 @@ const applyUVSettings = args => {
178
178
  };
179
179
 
180
180
  // TODO: Future uses should support red, green, blue, and alpha channels with defaults for each
181
- const combineMetallicRoughnessTextures = (metallicImage, roughnessImage) => {
182
- const roughnessHeight = roughnessImage.naturalHeight;
183
- const roughnessWidth = roughnessImage.naturalWidth;
184
- const metallicHeight = metallicImage.naturalHeight;
185
- const metallicWidth = metallicImage.naturalWidth;
181
+ const updateTextureChannel = (imageToMaintain, newImage, channelToUpdate) => {
182
+ const maintainHeight = imageToMaintain?.naturalHeight ?? 0;
183
+ const maintainWidth = imageToMaintain?.naturalWidth ?? 0;
184
+ const newHeight = newImage?.naturalHeight ?? 0;
185
+ const newWidth = newImage?.naturalWidth ?? 0;
186
186
 
187
- const maxHeight = Math.max(roughnessHeight, metallicHeight);
188
- const maxWidth = Math.max(roughnessWidth, metallicWidth);
187
+ let maintainData;
188
+ let newData;
189
+
190
+ const maxHeight = Math.max(...[ maintainHeight, newHeight, 1024 ]);
191
+ const maxWidth = Math.max(...[ maintainWidth, newWidth, 1024 ]);
189
192
 
190
193
  const canvas = document.createElement('canvas');
191
194
 
@@ -194,20 +197,34 @@ const combineMetallicRoughnessTextures = (metallicImage, roughnessImage) => {
194
197
  canvas.width = maxWidth;
195
198
  canvas.height = maxHeight;
196
199
 
197
- ctx.drawImage(roughnessImage, 0, 0, maxWidth, maxHeight);
198
- const roughnessData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
200
+ if (newImage) {
201
+ ctx.drawImage(newImage, 0, 0, maxWidth, maxHeight);
202
+ newData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
203
+ }
199
204
 
200
- ctx.drawImage(metallicImage, 0, 0, maxWidth, maxHeight);
201
- const metallicData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
205
+ if (imageToMaintain) {
206
+ ctx.drawImage(imageToMaintain, 0, 0, maxWidth, maxHeight);
207
+ maintainData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
208
+ }
202
209
 
203
210
  const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
204
211
  const combinedData = combinedImageData.data;
205
212
 
206
213
  for (let i = 0; i < combinedData.length; i += 4) {
207
- combinedData[i] = 255;
208
- combinedData[i + 1] = roughnessData[i + 1];
209
- combinedData[i + 2] = metallicData[i + 2];
210
- combinedData[i + 3] = 255;
214
+ const maintainRed = maintainData ? maintainData[i] : 255;
215
+ const maintainGreen = maintainData ? maintainData[i + 1] : 255;
216
+ const maintainBlue = maintainData ? maintainData[i + 2] : 255;
217
+ const maintainAlpha = maintainData ? maintainData[i + 3] : 255;
218
+
219
+ const newRed = newData ? newData[i] : 255;
220
+ const newGreen = newData ? newData[i + 1] : 255;
221
+ const newBlue = newData ? newData[i + 2] : 255;
222
+ const newAlpha = newData ? newData[i + 3] : 255;
223
+
224
+ combinedData[i] = channelToUpdate === 'R' ? newRed : maintainRed;
225
+ combinedData[i + 1] = channelToUpdate === 'G' ? newGreen : maintainGreen;
226
+ combinedData[i + 2] = channelToUpdate === 'B' ? newBlue : maintainBlue;
227
+ combinedData[i + 3] = channelToUpdate === 'A' ? newAlpha : maintainAlpha;
211
228
  }
212
229
 
213
230
  ctx.putImageData(combinedImageData, 0, 0);
@@ -218,23 +235,29 @@ const combineMetallicRoughnessTextures = (metallicImage, roughnessImage) => {
218
235
 
219
236
  // eslint-disable-next-line
220
237
  const loadImage = async file => {
221
- return new Promise((resolve, reject) => {
238
+ return new Promise(resolve => {
222
239
  const reader = new FileReader();
223
240
  reader.onload = event => {
224
241
  const img = new Image();
225
- img.onload = () => {
226
- resolve(img);
242
+ img.onload = () => resolve(img);
243
+ img.onerror = () => {
244
+ resolve(null);
227
245
  };
228
- img.onerror = reject;
229
246
  img.src = event.target.result;
230
247
  };
231
- reader.onerror = reject;
248
+ reader.onerror = () => {
249
+ resolve(null);
250
+ };
232
251
  reader.readAsDataURL(file);
233
252
  });
234
253
  };
235
254
 
236
- const textureToImage = (texture, textureName) => {
237
- return new Promise((resolve, reject) => {
255
+ const textureToImage = texture => {
256
+ return new Promise(resolve => {
257
+ if (!texture.readPixels()) {
258
+ resolve(null);
259
+ return;
260
+ }
238
261
  texture.readPixels()
239
262
  .then(pixels => {
240
263
  const canvas = document.createElement('canvas');
@@ -254,13 +277,15 @@ const textureToImage = (texture, textureName) => {
254
277
  resolve(img);
255
278
  };
256
279
 
257
- img.onerror = event => {
258
- console.log(`Error loading image: ${textureName}`);
259
- reject(event);
280
+ img.onerror = () => {
281
+ resolve(null);
260
282
  };
261
283
 
262
284
 
263
285
  img.src = canvas.toDataURL();
286
+ })
287
+ .catch(() => {
288
+ resolve(null);
264
289
  });
265
290
 
266
291
  });
@@ -308,6 +333,9 @@ export const updateMaterial = inboundData => {
308
333
  metallicTexture,
309
334
  roughness,
310
335
  roughnessTexture,
336
+ ambientColor,
337
+ ambientTextureStrength,
338
+ ambientTexture,
311
339
  microSurfaceTexture,
312
340
  emissiveColor,
313
341
  emissiveIntensity,
@@ -412,21 +440,30 @@ export const updateMaterial = inboundData => {
412
440
  let currentTexture = material.metallicTexture;
413
441
  if (!currentTexture) {
414
442
  material.metallicTexture = newTexture();
443
+ material.metallicTexture.name = `${material.name} (Metallic-Roughness)`;
415
444
  currentTexture = material.metallicTexture;
416
445
  }
417
446
 
418
447
  const metallicBlob = dataUrlToBlob(texture.url);
419
448
 
420
- Promise.all([ loadImage(metallicBlob), textureToImage(currentTexture, 'metallic') ])
449
+ Promise.all([
450
+ loadImage(metallicBlob),
451
+ textureToImage(currentTexture),
452
+ ])
421
453
  .then(data => {
422
454
  const [ metallicImage, currentImage ] = data;
423
- const combinedTexture = combineMetallicRoughnessTextures(metallicImage, currentImage);
455
+ const combinedTexture = updateTextureChannel(currentImage, metallicImage, 'B');
424
456
  currentTexture.updateURL(combinedTexture);
425
457
 
426
458
  texture.dispose();
427
459
  applyUVSettings({ texture: material.metallicTexture, material });
460
+ newMetaDataEntry('materials', buildMaterialsArray());
461
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
428
462
  })
429
- .catch(() => console.log(`Error updating the metallic texture of material: ${material.name}`));
463
+ .catch(err => {
464
+ console.log(`Error updating the roughness texture of material: ${material.name}`);
465
+ console.log({ err });
466
+ });
430
467
  }
431
468
 
432
469
  // Roughness
@@ -444,21 +481,38 @@ export const updateMaterial = inboundData => {
444
481
  let currentTexture = material.metallicTexture;
445
482
  if (!currentTexture) {
446
483
  material.metallicTexture = newTexture();
484
+ material.metallicTexture.name = `${material.name} (Metallic-Roughness)`;
447
485
  currentTexture = material.metallicTexture;
448
486
  }
449
487
 
450
488
  const roughnessBlob = dataUrlToBlob(texture.url);
451
489
 
452
- Promise.all([ loadImage(roughnessBlob), textureToImage(currentTexture, 'metallic') ])
490
+ Promise.all([ loadImage(roughnessBlob), textureToImage(currentTexture) ])
453
491
  .then(data => {
454
492
  const [ roughnessImage, metallicImage ] = data;
455
- const combinedTexture = combineMetallicRoughnessTextures(metallicImage, roughnessImage);
493
+ const combinedTexture = updateTextureChannel(metallicImage, roughnessImage, 'G');
456
494
  currentTexture.updateURL(combinedTexture);
457
495
 
458
496
  texture.dispose();
459
497
  applyUVSettings({ texture: material.metallicTexture, material });
498
+ newMetaDataEntry('materials', buildMaterialsArray());
499
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
460
500
  })
461
- .catch(() => console.log(`Error updating the roughness texture of material: ${material.name}`));
501
+ .catch(err => {
502
+ console.log(`Error updating the roughness texture of material: ${material.name}`);
503
+ console.log({ err });
504
+ });
505
+
506
+ }
507
+
508
+ // Ambient
509
+ if (ambientColor) material.ambientColor = newColor(emissiveColor);
510
+ if (ambientTextureStrength !== undefined) material.ambientTextureStrength = ambientTextureStrength;
511
+ if (!_.isEmpty(ambientTexture?.src || ambientTexture?.url)) {
512
+ material.ambientTexture = newTexture(ambientTexture?.src || ambientTexture?.url);
513
+ material.ambientTexture.name = ambientTexture.name;
514
+
515
+ applyUVSettings({ texture: material.ambientTexture, material });
462
516
 
463
517
  }
464
518
 
@@ -5,7 +5,7 @@ import { AppContainer, DebugButtons, Button } from './styles';
5
5
  import { theme } from 'static/theme';
6
6
  import { fetchDownloadURL } from 'helpers/fetchHelpers';
7
7
  import { ENVIRONMENTS } from 'constants';
8
- import { updateMaterial, scene } from 'package/helpers';
8
+ import { updateMaterial, scene, removeFromMaterial } from 'package/helpers';
9
9
  import Canvas from 'package/Canvas';
10
10
  import _ from 'lodash';
11
11
 
@@ -143,7 +143,10 @@ const App = () => {
143
143
  const file = e.currentTarget.files[0];
144
144
  const reader = new FileReader();
145
145
  reader.readAsDataURL(file);
146
- reader.onload = () => onImageLoad({ name: file.name, src: reader.result });
146
+ reader.onload = () => onImageLoad({
147
+ name: file.name,
148
+ src: reader.result,
149
+ });
147
150
  };
148
151
 
149
152
  useEffect(() => {
@@ -195,19 +198,22 @@ const App = () => {
195
198
  onClick={() => {
196
199
  const inboundData = {
197
200
  payload: {
198
- id: 'material',
199
- ...serializedResponseData,
201
+ id: 'Material Metal PBR Rich',
202
+ removeRoughnessTexture: true,
200
203
  },
201
204
  };
202
205
 
203
- updateMaterial(inboundData);
204
- console.log({ metadata: scene.metadata });
206
+ removeFromMaterial(inboundData);
205
207
  }}
206
208
  >
207
- Import material
209
+ Remove Roughness Texture
208
210
  </Button>
209
211
 
210
- <input type={'file'} onChange={handleUpload} accept={'image/*'} />
212
+ <input
213
+ type={'file'}
214
+ onChange={handleUpload}
215
+ accept={'image/*'}
216
+ />
211
217
 
212
218
  <Button
213
219
  theme={theme}