@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
|
|
36
|
-
|
|
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
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
197
|
+
reader.onerror = () => {
|
|
198
|
+
resolve(null);
|
|
199
|
+
};
|
|
184
200
|
reader.readAsDataURL(file);
|
|
185
201
|
});
|
|
186
202
|
};
|
|
187
|
-
const textureToImage =
|
|
188
|
-
return new Promise(
|
|
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 =
|
|
206
|
-
|
|
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
|
|
394
|
+
Promise.all([loadImage(metallicBlob), textureToImage(currentTexture)]).then(data => {
|
|
370
395
|
const [metallicImage, currentImage] = data;
|
|
371
|
-
const combinedTexture =
|
|
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
|
-
|
|
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
|
|
433
|
+
Promise.all([loadImage(roughnessBlob), textureToImage(currentTexture)]).then(data => {
|
|
401
434
|
const [roughnessImage, metallicImage] = data;
|
|
402
|
-
const combinedTexture =
|
|
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
|
-
|
|
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
|
@@ -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
|
-
|
|
48
|
-
|
|
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
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const
|
|
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
|
-
|
|
188
|
-
|
|
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
|
-
|
|
198
|
-
|
|
200
|
+
if (newImage) {
|
|
201
|
+
ctx.drawImage(newImage, 0, 0, maxWidth, maxHeight);
|
|
202
|
+
newData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
|
|
203
|
+
}
|
|
199
204
|
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
248
|
+
reader.onerror = () => {
|
|
249
|
+
resolve(null);
|
|
250
|
+
};
|
|
232
251
|
reader.readAsDataURL(file);
|
|
233
252
|
});
|
|
234
253
|
};
|
|
235
254
|
|
|
236
|
-
const textureToImage =
|
|
237
|
-
return new Promise(
|
|
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 =
|
|
258
|
-
|
|
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([
|
|
449
|
+
Promise.all([
|
|
450
|
+
loadImage(metallicBlob),
|
|
451
|
+
textureToImage(currentTexture),
|
|
452
|
+
])
|
|
421
453
|
.then(data => {
|
|
422
454
|
const [ metallicImage, currentImage ] = data;
|
|
423
|
-
const combinedTexture =
|
|
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
|
-
|
|
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
|
|
490
|
+
Promise.all([ loadImage(roughnessBlob), textureToImage(currentTexture) ])
|
|
453
491
|
.then(data => {
|
|
454
492
|
const [ roughnessImage, metallicImage ] = data;
|
|
455
|
-
const combinedTexture =
|
|
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(
|
|
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
|
|
package/src/scenes/App/App.js
CHANGED
|
@@ -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({
|
|
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: '
|
|
199
|
-
|
|
201
|
+
id: 'Material Metal PBR Rich',
|
|
202
|
+
removeRoughnessTexture: true,
|
|
200
203
|
},
|
|
201
204
|
};
|
|
202
205
|
|
|
203
|
-
|
|
204
|
-
console.log({ metadata: scene.metadata });
|
|
206
|
+
removeFromMaterial(inboundData);
|
|
205
207
|
}}
|
|
206
208
|
>
|
|
207
|
-
|
|
209
|
+
Remove Roughness Texture
|
|
208
210
|
</Button>
|
|
209
211
|
|
|
210
|
-
<input
|
|
212
|
+
<input
|
|
213
|
+
type={'file'}
|
|
214
|
+
onChange={handleUpload}
|
|
215
|
+
accept={'image/*'}
|
|
216
|
+
/>
|
|
211
217
|
|
|
212
218
|
<Button
|
|
213
219
|
theme={theme}
|