@archvisioninc/canvas 2.8.7 → 3.0.0
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.md +1 -1
- package/dist/helpers/canvasUpdateHelpers.js +135 -6
- package/jsconfig.json +5 -5
- package/package.json +1 -1
- package/public/index.html +21 -21
- package/public/manifest.json +25 -25
- package/public/robots.txt +3 -3
- package/src/helpers/fetchHelpers.js +2 -2
- package/src/package/helpers/canvasUpdateHelpers.js +158 -4
- package/src/scenes/App/App.js +22 -2
- package/src/scenes/App/App.test.js +8 -8
- package/src/setupTests.js +5 -5
package/README.md
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
## GET STARTED
|
|
1
|
+
## GET STARTED
|
|
2
2
|
Please see [developer documentation](https://github.com/ArchvisionInc/archvision-canvas/blob/main/README_DEV.md), in the repository.
|
|
@@ -139,6 +139,102 @@ const applyUVSettings = args => {
|
|
|
139
139
|
texture.vScale = uvYScale;
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
|
+
|
|
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);
|
|
151
|
+
const canvas = document.createElement('canvas');
|
|
152
|
+
const ctx = canvas.getContext('2d');
|
|
153
|
+
canvas.width = maxWidth;
|
|
154
|
+
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;
|
|
159
|
+
const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
|
|
160
|
+
const combinedData = combinedImageData.data;
|
|
161
|
+
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;
|
|
166
|
+
}
|
|
167
|
+
ctx.putImageData(combinedImageData, 0, 0);
|
|
168
|
+
return canvas.toDataURL('image/png');
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// eslint-disable-next-line
|
|
172
|
+
const loadImage = async file => {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const reader = new FileReader();
|
|
175
|
+
reader.onload = event => {
|
|
176
|
+
const img = new Image();
|
|
177
|
+
img.onload = () => {
|
|
178
|
+
resolve(img);
|
|
179
|
+
};
|
|
180
|
+
img.onerror = reject;
|
|
181
|
+
img.src = event.target.result;
|
|
182
|
+
};
|
|
183
|
+
reader.onerror = reject;
|
|
184
|
+
reader.readAsDataURL(file);
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
const textureToImage = (texture, textureName) => {
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
texture.readPixels().then(pixels => {
|
|
190
|
+
const canvas = document.createElement('canvas');
|
|
191
|
+
const ctx = canvas.getContext('2d');
|
|
192
|
+
const {
|
|
193
|
+
width,
|
|
194
|
+
height
|
|
195
|
+
} = texture.getSize();
|
|
196
|
+
canvas.width = width;
|
|
197
|
+
canvas.height = height;
|
|
198
|
+
const img = new Image();
|
|
199
|
+
const imageData = ctx.createImageData(width, height);
|
|
200
|
+
imageData.data.set(new Uint8ClampedArray(pixels));
|
|
201
|
+
ctx.putImageData(imageData, 0, 0);
|
|
202
|
+
img.onload = () => {
|
|
203
|
+
resolve(img);
|
|
204
|
+
};
|
|
205
|
+
img.onerror = event => {
|
|
206
|
+
console.log(`Error loading image: ${textureName}`);
|
|
207
|
+
reject(event);
|
|
208
|
+
};
|
|
209
|
+
img.src = canvas.toDataURL();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
const dataUrlToBlob = dataURI => {
|
|
214
|
+
// convert base64 to raw binary data held in a string
|
|
215
|
+
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
|
|
216
|
+
const byteString = atob(dataURI.split(',')[1]);
|
|
217
|
+
|
|
218
|
+
// separate out the mime component
|
|
219
|
+
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
|
|
220
|
+
|
|
221
|
+
// write the bytes of the string to an ArrayBuffer
|
|
222
|
+
const ab = new ArrayBuffer(byteString.length);
|
|
223
|
+
|
|
224
|
+
// create a view into the buffer
|
|
225
|
+
const ia = new Uint8Array(ab);
|
|
226
|
+
|
|
227
|
+
// set the bytes of the buffer to the correct values
|
|
228
|
+
for (let i = 0; i < byteString.length; i++) {
|
|
229
|
+
ia[i] = byteString.charCodeAt(i);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// write the ArrayBuffer to a blob, and you're done
|
|
233
|
+
const blob = new Blob([ab], {
|
|
234
|
+
type: mimeString
|
|
235
|
+
});
|
|
236
|
+
return blob;
|
|
237
|
+
};
|
|
142
238
|
export const updateMaterial = inboundData => {
|
|
143
239
|
const {
|
|
144
240
|
payload
|
|
@@ -158,6 +254,7 @@ export const updateMaterial = inboundData => {
|
|
|
158
254
|
metallic,
|
|
159
255
|
metallicTexture,
|
|
160
256
|
roughness,
|
|
257
|
+
roughnessTexture,
|
|
161
258
|
microSurfaceTexture,
|
|
162
259
|
emissiveColor,
|
|
163
260
|
emissiveIntensity,
|
|
@@ -261,12 +358,24 @@ export const updateMaterial = inboundData => {
|
|
|
261
358
|
// Metallic
|
|
262
359
|
if (metallic !== undefined) material.metallic = metallic;
|
|
263
360
|
if (!_.isEmpty(metallicTexture?.src || metallicTexture?.url)) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
material
|
|
269
|
-
|
|
361
|
+
// Metallic Texture uses the B channel of the metallicTexture for a PBRMaterial
|
|
362
|
+
const texture = newTexture(metallicTexture?.src || metallicTexture?.url);
|
|
363
|
+
let currentTexture = material.metallicTexture;
|
|
364
|
+
if (!currentTexture) {
|
|
365
|
+
material.metallicTexture = newTexture();
|
|
366
|
+
currentTexture = material.metallicTexture;
|
|
367
|
+
}
|
|
368
|
+
const metallicBlob = dataUrlToBlob(texture.url);
|
|
369
|
+
Promise.all([loadImage(metallicBlob), textureToImage(currentTexture, 'metallic')]).then(data => {
|
|
370
|
+
const [metallicImage, currentImage] = data;
|
|
371
|
+
const combinedTexture = combineMetallicRoughnessTextures(metallicImage, currentImage);
|
|
372
|
+
currentTexture.updateURL(combinedTexture);
|
|
373
|
+
texture.dispose();
|
|
374
|
+
applyUVSettings({
|
|
375
|
+
texture: material.metallicTexture,
|
|
376
|
+
material
|
|
377
|
+
});
|
|
378
|
+
}).catch(() => console.log(`Error updating the metallic texture of material: ${material.name}`));
|
|
270
379
|
}
|
|
271
380
|
|
|
272
381
|
// Roughness
|
|
@@ -279,6 +388,26 @@ export const updateMaterial = inboundData => {
|
|
|
279
388
|
material
|
|
280
389
|
});
|
|
281
390
|
}
|
|
391
|
+
if (!_.isEmpty(roughnessTexture?.src || roughnessTexture?.url)) {
|
|
392
|
+
// Roughness Texture uses the G channel of the metallicTexture for a PBRMaterial
|
|
393
|
+
const texture = newTexture(roughnessTexture?.src || roughnessTexture?.url);
|
|
394
|
+
let currentTexture = material.metallicTexture;
|
|
395
|
+
if (!currentTexture) {
|
|
396
|
+
material.metallicTexture = newTexture();
|
|
397
|
+
currentTexture = material.metallicTexture;
|
|
398
|
+
}
|
|
399
|
+
const roughnessBlob = dataUrlToBlob(texture.url);
|
|
400
|
+
Promise.all([loadImage(roughnessBlob), textureToImage(currentTexture, 'metallic')]).then(data => {
|
|
401
|
+
const [roughnessImage, metallicImage] = data;
|
|
402
|
+
const combinedTexture = combineMetallicRoughnessTextures(metallicImage, roughnessImage);
|
|
403
|
+
currentTexture.updateURL(combinedTexture);
|
|
404
|
+
texture.dispose();
|
|
405
|
+
applyUVSettings({
|
|
406
|
+
texture: material.metallicTexture,
|
|
407
|
+
material
|
|
408
|
+
});
|
|
409
|
+
}).catch(() => console.log(`Error updating the roughness texture of material: ${material.name}`));
|
|
410
|
+
}
|
|
282
411
|
|
|
283
412
|
// Emissive
|
|
284
413
|
if (emissiveColor) material.emissiveColor = newColor(emissiveColor);
|
package/jsconfig.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"baseUrl": "src"
|
|
4
|
-
},
|
|
5
|
-
"include": ["src"]
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": "src"
|
|
4
|
+
},
|
|
5
|
+
"include": ["src"]
|
|
6
6
|
}
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
-
<meta name="theme-color" content="#000000" />
|
|
8
|
-
<meta
|
|
9
|
-
name="description"
|
|
10
|
-
content="Web site created using create-react-app"
|
|
11
|
-
/>
|
|
12
|
-
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
13
|
-
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
14
|
-
<title>React App</title>
|
|
15
|
-
</head>
|
|
16
|
-
|
|
17
|
-
<body>
|
|
18
|
-
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
19
|
-
<div id="root"></div>
|
|
20
|
-
</body>
|
|
21
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta name="theme-color" content="#000000" />
|
|
8
|
+
<meta
|
|
9
|
+
name="description"
|
|
10
|
+
content="Web site created using create-react-app"
|
|
11
|
+
/>
|
|
12
|
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
13
|
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
14
|
+
<title>React App</title>
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<body>
|
|
18
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
19
|
+
<div id="root"></div>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
package/public/manifest.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
{
|
|
2
|
-
"short_name": "React App",
|
|
3
|
-
"name": "Create React App Sample",
|
|
4
|
-
"icons": [
|
|
5
|
-
{
|
|
6
|
-
"src": "favicon.ico",
|
|
7
|
-
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
-
"type": "image/x-icon"
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"src": "logo192.png",
|
|
12
|
-
"type": "image/png",
|
|
13
|
-
"sizes": "192x192"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"src": "logo512.png",
|
|
17
|
-
"type": "image/png",
|
|
18
|
-
"sizes": "512x512"
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"start_url": ".",
|
|
22
|
-
"display": "standalone",
|
|
23
|
-
"theme_color": "#000000",
|
|
24
|
-
"background_color": "#ffffff"
|
|
25
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"short_name": "React App",
|
|
3
|
+
"name": "Create React App Sample",
|
|
4
|
+
"icons": [
|
|
5
|
+
{
|
|
6
|
+
"src": "favicon.ico",
|
|
7
|
+
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
+
"type": "image/x-icon"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"src": "logo192.png",
|
|
12
|
+
"type": "image/png",
|
|
13
|
+
"sizes": "192x192"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "logo512.png",
|
|
17
|
+
"type": "image/png",
|
|
18
|
+
"sizes": "512x512"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"start_url": ".",
|
|
22
|
+
"display": "standalone",
|
|
23
|
+
"theme_color": "#000000",
|
|
24
|
+
"background_color": "#ffffff"
|
|
25
|
+
}
|
package/public/robots.txt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
# https://www.robotstxt.org/robotstxt.html
|
|
2
|
-
User-agent: *
|
|
3
|
-
Disallow:
|
|
1
|
+
# https://www.robotstxt.org/robotstxt.html
|
|
2
|
+
User-agent: *
|
|
3
|
+
Disallow:
|
|
@@ -14,7 +14,7 @@ export const fetchDownloadURL = async args => {
|
|
|
14
14
|
if (!_.isEmpty(rpcGuid)) {
|
|
15
15
|
const servicesURL = `https://api.archvision.services/rpc/v1/integration/${rpcGuid}/download/${format}`;
|
|
16
16
|
// eslint-disable-next-line
|
|
17
|
-
const token = '
|
|
17
|
+
const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjE2MDAyMDc3ODQifQ.eyJpZCI6ImowbzVobDJkdXc5ZXZ4bXJubGIxZWh6a3lhYmJmN3d6ZzN3Z2g3c2kiLCJqdGkiOiJqMG81aGwyZHV3OWV2eG1ybmxiMWVoemt5YWJiZjd3emczd2doN3NpIiwiaXNzIjoiaHR0cHM6XC9cL2FyY2h2aXNpb24uY29tIiwiYXVkIjoiNDVFUnF3SEV5SWRqbXhkaUU4MUxMbDlFYTN2SzZZQ3dLNWE2R2xEMyIsInN1YiI6IjM5MDQxOSIsImV4cCI6MTc0ODI3ODEwNiwiaWF0IjoxNzE3MTc4MTA2LCJ0b2tlbl90eXBlIjoiYmVhcmVyIiwic2NvcGUiOiJiYXNpYyJ9.xKpX0xCEdoVpn-GGdmRWcMPp3pEa7PUbMxlhuAsNjL1ZoFIbxwTrAoXDnvozxjdxomxM7nFw05I1caGtfJARPipOSLFgzD1ANYvogmAImg1zXUV8RsHEVaApxhdwUS7IPO13K3QJMGmn-NgqpYly9ZF9XYxZ7twT4JneGmc_WzeairT9qqGLhKXuU_tkJ49tKAXfCSHAFFBkb93nnsrpd-RlsIeQnc7puTqxlKTREzvmW3_RdqO_1X9ohlnn3wWBEFo_a0W770lfWoF1JlJIqd9NIPrgVRG5inRmYNEz6gw5yLHcDDG-Z5DrQExVUJkYV81wBK2krr0AgX45CWTEdQ';
|
|
18
18
|
const options = {
|
|
19
19
|
method: 'GET',
|
|
20
20
|
headers: {
|
|
@@ -42,4 +42,4 @@ export const fetchDownloadURL = async args => {
|
|
|
42
42
|
clearExisting,
|
|
43
43
|
);
|
|
44
44
|
}
|
|
45
|
-
};
|
|
45
|
+
};
|
|
@@ -177,6 +177,119 @@ const applyUVSettings = args => {
|
|
|
177
177
|
}
|
|
178
178
|
};
|
|
179
179
|
|
|
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;
|
|
186
|
+
|
|
187
|
+
const maxHeight = Math.max(roughnessHeight, metallicHeight);
|
|
188
|
+
const maxWidth = Math.max(roughnessWidth, metallicWidth);
|
|
189
|
+
|
|
190
|
+
const canvas = document.createElement('canvas');
|
|
191
|
+
|
|
192
|
+
const ctx = canvas.getContext('2d');
|
|
193
|
+
|
|
194
|
+
canvas.width = maxWidth;
|
|
195
|
+
canvas.height = maxHeight;
|
|
196
|
+
|
|
197
|
+
ctx.drawImage(roughnessImage, 0, 0, maxWidth, maxHeight);
|
|
198
|
+
const roughnessData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
|
|
199
|
+
|
|
200
|
+
ctx.drawImage(metallicImage, 0, 0, maxWidth, maxHeight);
|
|
201
|
+
const metallicData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
|
|
202
|
+
|
|
203
|
+
const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
|
|
204
|
+
const combinedData = combinedImageData.data;
|
|
205
|
+
|
|
206
|
+
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;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
ctx.putImageData(combinedImageData, 0, 0);
|
|
214
|
+
|
|
215
|
+
return canvas.toDataURL('image/png');
|
|
216
|
+
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// eslint-disable-next-line
|
|
220
|
+
const loadImage = async file => {
|
|
221
|
+
return new Promise((resolve, reject) => {
|
|
222
|
+
const reader = new FileReader();
|
|
223
|
+
reader.onload = event => {
|
|
224
|
+
const img = new Image();
|
|
225
|
+
img.onload = () => {
|
|
226
|
+
resolve(img);
|
|
227
|
+
};
|
|
228
|
+
img.onerror = reject;
|
|
229
|
+
img.src = event.target.result;
|
|
230
|
+
};
|
|
231
|
+
reader.onerror = reject;
|
|
232
|
+
reader.readAsDataURL(file);
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const textureToImage = (texture, textureName) => {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
texture.readPixels()
|
|
239
|
+
.then(pixels => {
|
|
240
|
+
const canvas = document.createElement('canvas');
|
|
241
|
+
const ctx = canvas.getContext('2d');
|
|
242
|
+
const { width, height } = texture.getSize();
|
|
243
|
+
|
|
244
|
+
canvas.width = width;
|
|
245
|
+
canvas.height = height;
|
|
246
|
+
|
|
247
|
+
const img = new Image();
|
|
248
|
+
const imageData = ctx.createImageData(width, height);
|
|
249
|
+
imageData.data.set(new Uint8ClampedArray(pixels));
|
|
250
|
+
|
|
251
|
+
ctx.putImageData(imageData, 0, 0);
|
|
252
|
+
|
|
253
|
+
img.onload = () => {
|
|
254
|
+
resolve(img);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
img.onerror = event => {
|
|
258
|
+
console.log(`Error loading image: ${textureName}`);
|
|
259
|
+
reject(event);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
img.src = canvas.toDataURL();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const dataUrlToBlob = dataURI => {
|
|
270
|
+
// convert base64 to raw binary data held in a string
|
|
271
|
+
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
|
|
272
|
+
const byteString = atob(dataURI.split(',')[1]);
|
|
273
|
+
|
|
274
|
+
// separate out the mime component
|
|
275
|
+
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
|
|
276
|
+
|
|
277
|
+
// write the bytes of the string to an ArrayBuffer
|
|
278
|
+
const ab = new ArrayBuffer(byteString.length);
|
|
279
|
+
|
|
280
|
+
// create a view into the buffer
|
|
281
|
+
const ia = new Uint8Array(ab);
|
|
282
|
+
|
|
283
|
+
// set the bytes of the buffer to the correct values
|
|
284
|
+
for (let i = 0; i < byteString.length; i++) {
|
|
285
|
+
ia[i] = byteString.charCodeAt(i);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// write the ArrayBuffer to a blob, and you're done
|
|
289
|
+
const blob = new Blob([ ab ], { type: mimeString });
|
|
290
|
+
return blob;
|
|
291
|
+
};
|
|
292
|
+
|
|
180
293
|
export const updateMaterial = inboundData => {
|
|
181
294
|
const { payload } = inboundData;
|
|
182
295
|
const {
|
|
@@ -194,6 +307,7 @@ export const updateMaterial = inboundData => {
|
|
|
194
307
|
metallic,
|
|
195
308
|
metallicTexture,
|
|
196
309
|
roughness,
|
|
310
|
+
roughnessTexture,
|
|
197
311
|
microSurfaceTexture,
|
|
198
312
|
emissiveColor,
|
|
199
313
|
emissiveIntensity,
|
|
@@ -293,10 +407,26 @@ export const updateMaterial = inboundData => {
|
|
|
293
407
|
// Metallic
|
|
294
408
|
if (metallic !== undefined) material.metallic = metallic;
|
|
295
409
|
if (!_.isEmpty(metallicTexture?.src || metallicTexture?.url)) {
|
|
296
|
-
|
|
297
|
-
|
|
410
|
+
// Metallic Texture uses the B channel of the metallicTexture for a PBRMaterial
|
|
411
|
+
const texture = newTexture(metallicTexture?.src || metallicTexture?.url);
|
|
412
|
+
let currentTexture = material.metallicTexture;
|
|
413
|
+
if (!currentTexture) {
|
|
414
|
+
material.metallicTexture = newTexture();
|
|
415
|
+
currentTexture = material.metallicTexture;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const metallicBlob = dataUrlToBlob(texture.url);
|
|
298
419
|
|
|
299
|
-
|
|
420
|
+
Promise.all([ loadImage(metallicBlob), textureToImage(currentTexture, 'metallic') ])
|
|
421
|
+
.then(data => {
|
|
422
|
+
const [ metallicImage, currentImage ] = data;
|
|
423
|
+
const combinedTexture = combineMetallicRoughnessTextures(metallicImage, currentImage);
|
|
424
|
+
currentTexture.updateURL(combinedTexture);
|
|
425
|
+
|
|
426
|
+
texture.dispose();
|
|
427
|
+
applyUVSettings({ texture: material.metallicTexture, material });
|
|
428
|
+
})
|
|
429
|
+
.catch(() => console.log(`Error updating the metallic texture of material: ${material.name}`));
|
|
300
430
|
}
|
|
301
431
|
|
|
302
432
|
// Roughness
|
|
@@ -308,6 +438,30 @@ export const updateMaterial = inboundData => {
|
|
|
308
438
|
applyUVSettings({ texture: material.microSurfaceTexture, material });
|
|
309
439
|
}
|
|
310
440
|
|
|
441
|
+
if (!_.isEmpty(roughnessTexture?.src || roughnessTexture?.url)) {
|
|
442
|
+
// Roughness Texture uses the G channel of the metallicTexture for a PBRMaterial
|
|
443
|
+
const texture = newTexture(roughnessTexture?.src || roughnessTexture?.url);
|
|
444
|
+
let currentTexture = material.metallicTexture;
|
|
445
|
+
if (!currentTexture) {
|
|
446
|
+
material.metallicTexture = newTexture();
|
|
447
|
+
currentTexture = material.metallicTexture;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const roughnessBlob = dataUrlToBlob(texture.url);
|
|
451
|
+
|
|
452
|
+
Promise.all([ loadImage(roughnessBlob), textureToImage(currentTexture, 'metallic') ])
|
|
453
|
+
.then(data => {
|
|
454
|
+
const [ roughnessImage, metallicImage ] = data;
|
|
455
|
+
const combinedTexture = combineMetallicRoughnessTextures(metallicImage, roughnessImage);
|
|
456
|
+
currentTexture.updateURL(combinedTexture);
|
|
457
|
+
|
|
458
|
+
texture.dispose();
|
|
459
|
+
applyUVSettings({ texture: material.metallicTexture, material });
|
|
460
|
+
})
|
|
461
|
+
.catch(() => console.log(`Error updating the roughness texture of material: ${material.name}`));
|
|
462
|
+
|
|
463
|
+
}
|
|
464
|
+
|
|
311
465
|
// Emissive
|
|
312
466
|
if (emissiveColor) material.emissiveColor = newColor(emissiveColor);
|
|
313
467
|
if (emissiveIntensity !== undefined) material.emissiveIntensity = emissiveIntensity;
|
|
@@ -948,4 +1102,4 @@ export const updatePublish = inboundData => {
|
|
|
948
1102
|
};
|
|
949
1103
|
|
|
950
1104
|
newMetaDataEntry('publish', withOptimizationValues);
|
|
951
|
-
};
|
|
1105
|
+
};
|
package/src/scenes/App/App.js
CHANGED
|
@@ -9,7 +9,7 @@ import { updateMaterial, scene } from 'package/helpers';
|
|
|
9
9
|
import Canvas from 'package/Canvas';
|
|
10
10
|
import _ from 'lodash';
|
|
11
11
|
|
|
12
|
-
const materialMode =
|
|
12
|
+
const materialMode = false;
|
|
13
13
|
const previewMode = false;
|
|
14
14
|
const demoScene = false;
|
|
15
15
|
const shaderballGuid = '21-791A-0D8D-F8A0-63F7-5086-471F-69A7-7AB0-00';
|
|
@@ -127,6 +127,24 @@ const App = () => {
|
|
|
127
127
|
const updatedArray = notifications.filter((item, i) => index !== i);
|
|
128
128
|
setNotifications(updatedArray);
|
|
129
129
|
};
|
|
130
|
+
const onImageLoad = args => {
|
|
131
|
+
const inboundData = {
|
|
132
|
+
payload: {
|
|
133
|
+
id: 'Material Metal PBR Rich',
|
|
134
|
+
metallicTexture: args,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
updateMaterial(inboundData);
|
|
139
|
+
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const handleUpload = e => {
|
|
143
|
+
const file = e.currentTarget.files[0];
|
|
144
|
+
const reader = new FileReader();
|
|
145
|
+
reader.readAsDataURL(file);
|
|
146
|
+
reader.onload = () => onImageLoad({ name: file.name, src: reader.result });
|
|
147
|
+
};
|
|
130
148
|
|
|
131
149
|
useEffect(() => {
|
|
132
150
|
getPreviewURL();
|
|
@@ -189,6 +207,8 @@ const App = () => {
|
|
|
189
207
|
Import material
|
|
190
208
|
</Button>
|
|
191
209
|
|
|
210
|
+
<input type={'file'} onChange={handleUpload} accept={'image/*'} />
|
|
211
|
+
|
|
192
212
|
<Button
|
|
193
213
|
theme={theme}
|
|
194
214
|
$selectedTheme={selectedTheme}
|
|
@@ -249,4 +269,4 @@ const App = () => {
|
|
|
249
269
|
);
|
|
250
270
|
};
|
|
251
271
|
|
|
252
|
-
export default App;
|
|
272
|
+
export default App;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import App from './App';
|
|
3
|
-
|
|
4
|
-
test('renders learn react link', () => {
|
|
5
|
-
render(<App />);
|
|
6
|
-
const linkElement = screen.getByText(/learn react/i);
|
|
7
|
-
expect(linkElement).toBeInTheDocument();
|
|
8
|
-
});
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import App from './App';
|
|
3
|
+
|
|
4
|
+
test('renders learn react link', () => {
|
|
5
|
+
render(<App />);
|
|
6
|
+
const linkElement = screen.getByText(/learn react/i);
|
|
7
|
+
expect(linkElement).toBeInTheDocument();
|
|
8
|
+
});
|
package/src/setupTests.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
|
2
|
-
// allows you to do things like:
|
|
3
|
-
// expect(element).toHaveTextContent(/react/i)
|
|
4
|
-
// learn more: https://github.com/testing-library/jest-dom
|
|
5
|
-
import '@testing-library/jest-dom';
|
|
1
|
+
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
|
2
|
+
// allows you to do things like:
|
|
3
|
+
// expect(element).toHaveTextContent(/react/i)
|
|
4
|
+
// learn more: https://github.com/testing-library/jest-dom
|
|
5
|
+
import '@testing-library/jest-dom';
|