@archvisioninc/canvas 3.3.6 → 3.3.8
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/.claude/settings.local.json +8 -0
- package/README_DEV.md +4 -1
- package/package.json +1 -1
- package/src/package/helpers/canvasUpdateHelpers.js +145 -0
- package/src/package/helpers/initHelpers.js +10 -0
- package/src/package/helpers/utilityHelpers.js +17 -0
- package/dist/Canvas.js +0 -67
- package/dist/actions/index.js +0 -1
- package/dist/actions/shortcutActions.js +0 -313
- package/dist/constants/constants.js +0 -80
- package/dist/constants/index.js +0 -1
- package/dist/enums/aspectRatios.js +0 -17
- package/dist/enums/dimensions.js +0 -20
- package/dist/enums/downscaling.js +0 -16
- package/dist/enums/exclusions.js +0 -4
- package/dist/enums/formats.js +0 -1
- package/dist/enums/index.js +0 -8
- package/dist/enums/orthoOptions.js +0 -28
- package/dist/enums/scaleUnits.js +0 -25
- package/dist/enums/shortcuts.js +0 -89
- package/dist/helpers/cameraHelpers.js +0 -86
- package/dist/helpers/canvasAddHelpers.js +0 -161
- package/dist/helpers/canvasCommunicationHelpers.js +0 -52
- package/dist/helpers/canvasRemoveHelpers.js +0 -230
- package/dist/helpers/canvasUpdateHelpers.js +0 -1247
- package/dist/helpers/gizmoHelpers.js +0 -156
- package/dist/helpers/guiHelpers.js +0 -46
- package/dist/helpers/index.js +0 -16
- package/dist/helpers/initHelpers.js +0 -507
- package/dist/helpers/lightHelpers.js +0 -17
- package/dist/helpers/loadHelpers.js +0 -269
- package/dist/helpers/materialHelpers.js +0 -34
- package/dist/helpers/meshHelpers.js +0 -169
- package/dist/helpers/rayHelpers.js +0 -11
- package/dist/helpers/shortcutHelpers.js +0 -35
- package/dist/helpers/utilityHelpers.js +0 -697
- package/dist/helpers/viewportHelpers.js +0 -364
- package/dist/styles.js +0 -25
|
@@ -1,1247 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
import { getUserMeshes, resetSelectedMeshes, addNewMesh, scene, mirrorGround, ground, newVector, newColor, newScreenshot, serializeScene, newMetaDataEntry, selectedMeshes, singleMeshTNode, toRadians, toDegrees, multiMeshTNode, handleMousePointerTap, prepareCamera, buildMaterialsArray, buildMeshPositionsArray, createBoundingMesh, attachToSelectMeshesNode, newTexture, getUserMaterials, buildSelectedMaterialArray, buildMeshIdArray, refreshHighlight, guiTexture, getBoundingMeshData, getScaleFactor, gizmoManager, iblShadowPipeline, buildLightsArray } from '../helpers';
|
|
3
|
-
import { toggleSafeFrame, toggleOrthographicViews, toggleBoundingBoxWidget, resetManagerGizmos, modelToOrigin, focusCamera } from '../actions';
|
|
4
|
-
import { GLTF2 } from 'babylonjs-loaders';
|
|
5
|
-
import { GIZMOS, TRANSPARENCY_MODES, GUI } from '../constants';
|
|
6
|
-
import { reactProps as props } from '../Canvas';
|
|
7
|
-
import { ratios, scaleUnits } from '../enums';
|
|
8
|
-
import * as BABYLON from 'babylonjs';
|
|
9
|
-
import _ from 'lodash';
|
|
10
|
-
export let hiddenMeshes = [];
|
|
11
|
-
const unitless = 'unitless';
|
|
12
|
-
const performTransform = args => {
|
|
13
|
-
const {
|
|
14
|
-
type,
|
|
15
|
-
vector,
|
|
16
|
-
globalTransform,
|
|
17
|
-
newTrackingData,
|
|
18
|
-
tx,
|
|
19
|
-
ty,
|
|
20
|
-
tz,
|
|
21
|
-
rx,
|
|
22
|
-
ry,
|
|
23
|
-
rz,
|
|
24
|
-
sx,
|
|
25
|
-
sy,
|
|
26
|
-
sz
|
|
27
|
-
} = args;
|
|
28
|
-
const globalTransforms = scene.metadata.globalTransforms;
|
|
29
|
-
let newTransforms;
|
|
30
|
-
const updateNode = () => {
|
|
31
|
-
const fallback = type === 'scale' ? 1 : 0;
|
|
32
|
-
const node0 = scene.getNodeByName('node0');
|
|
33
|
-
const node = globalTransform ? node0 : singleMeshTNode;
|
|
34
|
-
if (node) {
|
|
35
|
-
switch (type) {
|
|
36
|
-
case 'move':
|
|
37
|
-
node.position = globalTransform ? vector.multiplyByFloats(-1, 1, 1) : vector;
|
|
38
|
-
newTransforms = {
|
|
39
|
-
tx: tx || fallback,
|
|
40
|
-
ty: ty || fallback,
|
|
41
|
-
tz: tz || fallback
|
|
42
|
-
};
|
|
43
|
-
break;
|
|
44
|
-
case 'rotate':
|
|
45
|
-
if (globalTransform) {
|
|
46
|
-
vector.applyRotationQuaternionInPlace(node.absoluteRotationQuaternion);
|
|
47
|
-
}
|
|
48
|
-
node.rotation = globalTransform ? vector.multiplyByFloats(-1, 1, -1) : vector;
|
|
49
|
-
newTransforms = {
|
|
50
|
-
rx: (rx || fallback) % 360,
|
|
51
|
-
ry: (ry || fallback) % 360,
|
|
52
|
-
rz: (rz || fallback) % 360
|
|
53
|
-
};
|
|
54
|
-
break;
|
|
55
|
-
default:
|
|
56
|
-
node.scaling = vector;
|
|
57
|
-
newTransforms = {
|
|
58
|
-
sx: sx || fallback,
|
|
59
|
-
sy: sy || fallback,
|
|
60
|
-
sz: sz || fallback
|
|
61
|
-
};
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
const getPositionsChangeAfterGlobal = () => {
|
|
67
|
-
const userMeshes = getUserMeshes();
|
|
68
|
-
const updatedPositions = scene.metadata.meshChangeTracking?.map(mesh => {
|
|
69
|
-
const userMesh = userMeshes.find(userMesh => userMesh.id === mesh.meshId);
|
|
70
|
-
const parent = userMesh?.parent;
|
|
71
|
-
userMesh?.setParent?.(null);
|
|
72
|
-
const boundingBox = getBoundingMeshData([userMesh]);
|
|
73
|
-
const {
|
|
74
|
-
center,
|
|
75
|
-
rotation
|
|
76
|
-
} = boundingBox;
|
|
77
|
-
const newTracking = {
|
|
78
|
-
...mesh,
|
|
79
|
-
tx: center.x,
|
|
80
|
-
ty: center.y,
|
|
81
|
-
tz: center.z,
|
|
82
|
-
rx: -toDegrees(rotation.x) % 360,
|
|
83
|
-
ry: toDegrees(rotation.y) % 360,
|
|
84
|
-
rz: -toDegrees(rotation.z) % 360
|
|
85
|
-
};
|
|
86
|
-
userMesh?.setParent?.(parent);
|
|
87
|
-
return newTracking;
|
|
88
|
-
});
|
|
89
|
-
return updatedPositions;
|
|
90
|
-
};
|
|
91
|
-
if (globalTransform) {
|
|
92
|
-
/*
|
|
93
|
-
TODO: Changing any transform input box value when multiple meshes are selected does nothing.
|
|
94
|
-
TODO: Look into this rotation bug:
|
|
95
|
-
Rotation:
|
|
96
|
-
- Global rotate 90 on x.
|
|
97
|
-
- Select balloon.
|
|
98
|
-
- Local reset rotation Y to 0.
|
|
99
|
-
- Local reset rotation X to 0.
|
|
100
|
-
- Global reset rotation X to 0.
|
|
101
|
-
- Select balloon.
|
|
102
|
-
- First issue: Local rotation X value should be 90, reads -90.
|
|
103
|
-
- Local reset rotation X to 0.
|
|
104
|
-
- Result: Mesh is upside down.
|
|
105
|
-
*/
|
|
106
|
-
|
|
107
|
-
updateNode();
|
|
108
|
-
newMetaDataEntry('globalTransforms', {
|
|
109
|
-
...globalTransforms,
|
|
110
|
-
...newTransforms
|
|
111
|
-
});
|
|
112
|
-
newMetaDataEntry('meshChangeTracking', getPositionsChangeAfterGlobal());
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
updateNode();
|
|
116
|
-
newMetaDataEntry('meshChangeTracking', newTrackingData(newTransforms));
|
|
117
|
-
};
|
|
118
|
-
const applyUVSettings = args => {
|
|
119
|
-
const {
|
|
120
|
-
texture,
|
|
121
|
-
material
|
|
122
|
-
} = args || {};
|
|
123
|
-
if (!_.isEmpty(texture) && !_.isEmpty(material)) {
|
|
124
|
-
const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === material.id);
|
|
125
|
-
const {
|
|
126
|
-
uvXScale,
|
|
127
|
-
uvYScale,
|
|
128
|
-
uvXRotation,
|
|
129
|
-
uvYRotation,
|
|
130
|
-
uvZRotation,
|
|
131
|
-
uvXOffset,
|
|
132
|
-
uvYOffset
|
|
133
|
-
} = workingMaterial?.uvSettings || {};
|
|
134
|
-
texture.uAng = uvXRotation;
|
|
135
|
-
texture.wAng = uvYRotation;
|
|
136
|
-
texture.vAng = uvZRotation;
|
|
137
|
-
texture.uOffset = uvXOffset;
|
|
138
|
-
texture.vOffset = uvYOffset;
|
|
139
|
-
texture.uScale = uvXScale;
|
|
140
|
-
texture.vScale = uvYScale;
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
const updateTextureChannel = (imageToMaintain, newImage, channelToUpdate) => {
|
|
144
|
-
const maintainHeight = imageToMaintain?.naturalHeight ?? 0;
|
|
145
|
-
const maintainWidth = imageToMaintain?.naturalWidth ?? 0;
|
|
146
|
-
const newHeight = newImage?.naturalHeight ?? 0;
|
|
147
|
-
const newWidth = newImage?.naturalWidth ?? 0;
|
|
148
|
-
let maintainData;
|
|
149
|
-
let newData;
|
|
150
|
-
const maxHeight = Math.max(...[maintainHeight, newHeight, 1024]);
|
|
151
|
-
const maxWidth = Math.max(...[maintainWidth, newWidth, 1024]);
|
|
152
|
-
const canvas = document.createElement('canvas');
|
|
153
|
-
const ctx = canvas.getContext('2d', {
|
|
154
|
-
willReadFrequently: true
|
|
155
|
-
});
|
|
156
|
-
canvas.width = maxWidth;
|
|
157
|
-
canvas.height = maxHeight;
|
|
158
|
-
if (newImage) {
|
|
159
|
-
ctx.drawImage(newImage, 0, 0, maxWidth, maxHeight);
|
|
160
|
-
newData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
|
|
161
|
-
}
|
|
162
|
-
if (imageToMaintain) {
|
|
163
|
-
ctx.drawImage(imageToMaintain, 0, 0, maxWidth, maxHeight);
|
|
164
|
-
maintainData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
|
|
165
|
-
}
|
|
166
|
-
const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
|
|
167
|
-
const combinedData = combinedImageData.data;
|
|
168
|
-
for (let i = 0; i < combinedData.length; i += 4) {
|
|
169
|
-
const maintainRed = maintainData ? maintainData[i] : 1;
|
|
170
|
-
const maintainGreen = maintainData ? maintainData[i + 1] : 0;
|
|
171
|
-
const maintainBlue = maintainData ? maintainData[i + 2] : 0;
|
|
172
|
-
const maintainAlpha = maintainData ? maintainData[i + 3] : 255;
|
|
173
|
-
const newRed = newData ? newData[i] : 1;
|
|
174
|
-
const newGreen = newData ? newData[i + 1] : 0;
|
|
175
|
-
const newBlue = newData ? newData[i + 2] : 0;
|
|
176
|
-
const newAlpha = newData ? newData[i + 3] : 255;
|
|
177
|
-
combinedData[i] = channelToUpdate === 'R' ? newRed : maintainRed;
|
|
178
|
-
combinedData[i + 1] = channelToUpdate === 'G' ? newGreen : maintainGreen;
|
|
179
|
-
combinedData[i + 2] = channelToUpdate === 'B' ? newBlue : maintainBlue;
|
|
180
|
-
combinedData[i + 3] = channelToUpdate === 'A' ? newAlpha : maintainAlpha;
|
|
181
|
-
}
|
|
182
|
-
ctx.putImageData(combinedImageData, 0, 0);
|
|
183
|
-
return canvas.toDataURL('image/png');
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
// eslint-disable-next-line
|
|
187
|
-
const loadImage = async file => {
|
|
188
|
-
return new Promise(resolve => {
|
|
189
|
-
const reader = new FileReader();
|
|
190
|
-
reader.onload = event => {
|
|
191
|
-
const img = new Image();
|
|
192
|
-
img.onload = () => resolve(img);
|
|
193
|
-
img.onerror = () => {
|
|
194
|
-
resolve(null);
|
|
195
|
-
};
|
|
196
|
-
img.src = event.target.result;
|
|
197
|
-
};
|
|
198
|
-
reader.onerror = () => {
|
|
199
|
-
resolve(null);
|
|
200
|
-
};
|
|
201
|
-
reader.readAsDataURL(file);
|
|
202
|
-
});
|
|
203
|
-
};
|
|
204
|
-
const metallicTextureToImage = texture => {
|
|
205
|
-
return new Promise(resolve => {
|
|
206
|
-
if (!texture.readPixels()) {
|
|
207
|
-
resolve(null);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
texture.readPixels().then(pixels => {
|
|
211
|
-
const canvas = document.createElement('canvas');
|
|
212
|
-
const ctx = canvas.getContext('2d');
|
|
213
|
-
const {
|
|
214
|
-
width,
|
|
215
|
-
height
|
|
216
|
-
} = texture.getSize();
|
|
217
|
-
canvas.width = width;
|
|
218
|
-
canvas.height = height;
|
|
219
|
-
const img = new Image();
|
|
220
|
-
const imageData = ctx.createImageData(width, height);
|
|
221
|
-
imageData.data.set(new Uint8ClampedArray(pixels));
|
|
222
|
-
ctx.putImageData(imageData, 0, 0);
|
|
223
|
-
img.onload = () => {
|
|
224
|
-
resolve(img);
|
|
225
|
-
};
|
|
226
|
-
img.onerror = () => {
|
|
227
|
-
resolve(null);
|
|
228
|
-
};
|
|
229
|
-
img.src = canvas.toDataURL();
|
|
230
|
-
}).catch(() => {
|
|
231
|
-
resolve(null);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
};
|
|
235
|
-
const dataUrlToBlob = dataURI => {
|
|
236
|
-
// convert base64 to raw binary data held in a string
|
|
237
|
-
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
|
|
238
|
-
const byteString = atob(dataURI.split(',')[1]);
|
|
239
|
-
|
|
240
|
-
// separate out the mime component
|
|
241
|
-
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
|
|
242
|
-
|
|
243
|
-
// write the bytes of the string to an ArrayBuffer
|
|
244
|
-
const ab = new ArrayBuffer(byteString.length);
|
|
245
|
-
|
|
246
|
-
// create a view into the buffer
|
|
247
|
-
const ia = new Uint8Array(ab);
|
|
248
|
-
|
|
249
|
-
// set the bytes of the buffer to the correct values
|
|
250
|
-
for (let i = 0; i < byteString.length; i++) {
|
|
251
|
-
ia[i] = byteString.charCodeAt(i);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// write the ArrayBuffer to a blob, and you're done
|
|
255
|
-
const blob = new Blob([ab], {
|
|
256
|
-
type: mimeString
|
|
257
|
-
});
|
|
258
|
-
return blob;
|
|
259
|
-
};
|
|
260
|
-
export const updateMaterial = inboundData => {
|
|
261
|
-
const {
|
|
262
|
-
payload
|
|
263
|
-
} = inboundData;
|
|
264
|
-
const {
|
|
265
|
-
id,
|
|
266
|
-
meshSimplification,
|
|
267
|
-
textureDownscaling,
|
|
268
|
-
newId,
|
|
269
|
-
selectMaterial,
|
|
270
|
-
backfaceCullingEnabled,
|
|
271
|
-
wireFrameEnabled,
|
|
272
|
-
pointsCloudEnabled,
|
|
273
|
-
albedoColor,
|
|
274
|
-
albedoTexture,
|
|
275
|
-
environmentIntensity,
|
|
276
|
-
metallic,
|
|
277
|
-
metallicTexture,
|
|
278
|
-
roughness,
|
|
279
|
-
roughnessTexture,
|
|
280
|
-
ambientColor,
|
|
281
|
-
ambientTextureStrength,
|
|
282
|
-
ambientTexture,
|
|
283
|
-
microSurfaceTexture,
|
|
284
|
-
emissiveColor,
|
|
285
|
-
emissiveIntensity,
|
|
286
|
-
emissiveTexture,
|
|
287
|
-
bumpIntensity,
|
|
288
|
-
bumpTexture,
|
|
289
|
-
clearCoatEnabled,
|
|
290
|
-
clearCoatIntensity,
|
|
291
|
-
clearCoatRoughness,
|
|
292
|
-
clearCoatIOR,
|
|
293
|
-
alpha,
|
|
294
|
-
transparencyType,
|
|
295
|
-
sssIOR,
|
|
296
|
-
sssRefractionIntensity,
|
|
297
|
-
transparencyEnabled,
|
|
298
|
-
opacityTexture,
|
|
299
|
-
newSelectedMaterial,
|
|
300
|
-
albedoHasAlpha,
|
|
301
|
-
clearTextureChannels,
|
|
302
|
-
uvXScale,
|
|
303
|
-
uvYScale,
|
|
304
|
-
uvScaleLock,
|
|
305
|
-
uvXRotation,
|
|
306
|
-
uvYRotation,
|
|
307
|
-
uvZRotation,
|
|
308
|
-
uvXOffset,
|
|
309
|
-
uvYOffset
|
|
310
|
-
} = payload;
|
|
311
|
-
const material = scene?.getMaterialByName?.(id, true);
|
|
312
|
-
if (material) {
|
|
313
|
-
// Renaming material.
|
|
314
|
-
if (newId) {
|
|
315
|
-
material.id = newId;
|
|
316
|
-
material.name = newId;
|
|
317
|
-
if (Array.isArray(scene.metadata.selectedMaterials) && scene.metadata.selectedMaterials.length > 0) {
|
|
318
|
-
// We currently only support one selectedMaterial at a time so we should set the changed material to selected
|
|
319
|
-
newMetaDataEntry('selectedMaterials', [material]);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Select all meshes with current material.
|
|
324
|
-
if (selectMaterial) {
|
|
325
|
-
resetSelectedMeshes();
|
|
326
|
-
const userMeshes = getUserMeshes();
|
|
327
|
-
userMeshes.forEach(mesh => mesh.material?.name === id && addNewMesh(mesh));
|
|
328
|
-
newMetaDataEntry('selectedMeshes', buildMeshIdArray());
|
|
329
|
-
refreshHighlight();
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Selecting single material.
|
|
334
|
-
if (newSelectedMaterial) {
|
|
335
|
-
resetSelectedMeshes();
|
|
336
|
-
const newMaterial = scene.metadata.materials.find(mat => mat.materialId === id);
|
|
337
|
-
newMetaDataEntry('selectedMaterials', [newMaterial]);
|
|
338
|
-
refreshHighlight();
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Reset supported texture channels, useful when manually importing a new material.
|
|
343
|
-
if (clearTextureChannels) {
|
|
344
|
-
const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === id) || {};
|
|
345
|
-
Object.keys(workingMaterial)?.forEach(key => {
|
|
346
|
-
if (key.endsWith('Texture')) {
|
|
347
|
-
material[key] = null;
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Back-face culling
|
|
353
|
-
if (backfaceCullingEnabled !== undefined) material.backFaceCulling = backfaceCullingEnabled;
|
|
354
|
-
|
|
355
|
-
// Wireframe
|
|
356
|
-
if (wireFrameEnabled !== undefined) material.wireframe = wireFrameEnabled;
|
|
357
|
-
|
|
358
|
-
// Point cloud
|
|
359
|
-
if (pointsCloudEnabled !== undefined) material.pointsCloud = pointsCloudEnabled;
|
|
360
|
-
|
|
361
|
-
// Mesh simplification and texture downscaling
|
|
362
|
-
if (!_.isEmpty(meshSimplification)) {
|
|
363
|
-
material.meshSimplification = {
|
|
364
|
-
...material.meshSimplification,
|
|
365
|
-
...meshSimplification
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
if (!_.isEmpty(textureDownscaling)) {
|
|
369
|
-
material.textureDownscaling = {
|
|
370
|
-
...material.textureDownscaling,
|
|
371
|
-
...textureDownscaling
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Diffuse
|
|
376
|
-
if (albedoColor) material.albedoColor = newColor(albedoColor);
|
|
377
|
-
if (environmentIntensity !== undefined) material.environmentIntensity = environmentIntensity;
|
|
378
|
-
if (!_.isEmpty(albedoTexture?.src || albedoTexture?.url)) {
|
|
379
|
-
material.albedoTexture = newTexture(albedoTexture?.src || albedoTexture?.url);
|
|
380
|
-
material.albedoTexture.name = albedoTexture.name;
|
|
381
|
-
applyUVSettings({
|
|
382
|
-
texture: material.albedoTexture,
|
|
383
|
-
material
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Metallic
|
|
388
|
-
if (metallic !== undefined) material.metallic = metallic;
|
|
389
|
-
if (!_.isEmpty(metallicTexture?.src || metallicTexture?.url)) {
|
|
390
|
-
// Metallic Texture uses the B channel of the metallicTexture for a PBRMaterial
|
|
391
|
-
const texture = newTexture(metallicTexture?.src || metallicTexture?.url);
|
|
392
|
-
let currentTexture = material.metallicTexture;
|
|
393
|
-
if (!currentTexture) {
|
|
394
|
-
material.metallicTexture = newTexture();
|
|
395
|
-
material.metallicTexture.name = `${material.name} (Metallic-Roughness)`;
|
|
396
|
-
currentTexture = material.metallicTexture;
|
|
397
|
-
}
|
|
398
|
-
const metallicBlob = dataUrlToBlob(texture.url);
|
|
399
|
-
Promise.all([loadImage(metallicBlob), metallicTextureToImage(currentTexture)]).then(data => {
|
|
400
|
-
const [metallicImage, currentImage] = data;
|
|
401
|
-
const combinedTexture = updateTextureChannel(currentImage, metallicImage, 'B');
|
|
402
|
-
currentTexture.updateURL(combinedTexture);
|
|
403
|
-
material.useRoughnessFromMetallicTextureAlpha = false;
|
|
404
|
-
material.useMetalnessFromMetallicTextureBlue = true;
|
|
405
|
-
texture.dispose();
|
|
406
|
-
applyUVSettings({
|
|
407
|
-
texture: material.metallicTexture,
|
|
408
|
-
material
|
|
409
|
-
});
|
|
410
|
-
newMetaDataEntry('materials', buildMaterialsArray());
|
|
411
|
-
newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
|
|
412
|
-
props.clearNotifications?.();
|
|
413
|
-
props.setSerializedData?.(serializeScene());
|
|
414
|
-
}).catch(err => {
|
|
415
|
-
console.log(`Error updating the roughness texture of material: ${material.name}`);
|
|
416
|
-
console.log({
|
|
417
|
-
err
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Roughness
|
|
423
|
-
if (roughness !== undefined) material.roughness = roughness;
|
|
424
|
-
if (!_.isEmpty(microSurfaceTexture?.src || microSurfaceTexture?.url)) {
|
|
425
|
-
material.microSurfaceTexture = newTexture(microSurfaceTexture?.src || microSurfaceTexture?.url);
|
|
426
|
-
material.microSurfaceTexture.name = microSurfaceTexture.name;
|
|
427
|
-
applyUVSettings({
|
|
428
|
-
texture: material.microSurfaceTexture,
|
|
429
|
-
material
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
if (!_.isEmpty(roughnessTexture?.src || roughnessTexture?.url)) {
|
|
433
|
-
// Roughness Texture uses the G channel of the metallicTexture for a PBRMaterial
|
|
434
|
-
const texture = newTexture(roughnessTexture?.src || roughnessTexture?.url);
|
|
435
|
-
let currentTexture = material.metallicTexture;
|
|
436
|
-
if (!currentTexture) {
|
|
437
|
-
material.metallicTexture = newTexture();
|
|
438
|
-
material.metallicTexture.name = `${material.name} (Metallic-Roughness)`;
|
|
439
|
-
currentTexture = material.metallicTexture;
|
|
440
|
-
}
|
|
441
|
-
const roughnessBlob = dataUrlToBlob(texture.url);
|
|
442
|
-
Promise.all([loadImage(roughnessBlob), metallicTextureToImage(currentTexture)]).then(data => {
|
|
443
|
-
const [roughnessImage, metallicImage] = data;
|
|
444
|
-
const combinedTexture = updateTextureChannel(metallicImage, roughnessImage, 'G');
|
|
445
|
-
currentTexture.updateURL(combinedTexture);
|
|
446
|
-
material.useRoughnessFromMetallicTextureAlpha = false;
|
|
447
|
-
material.useRoughnessFromMetallicTextureGreen = true;
|
|
448
|
-
texture.dispose();
|
|
449
|
-
applyUVSettings({
|
|
450
|
-
texture: material.metallicTexture,
|
|
451
|
-
material
|
|
452
|
-
});
|
|
453
|
-
newMetaDataEntry('materials', buildMaterialsArray());
|
|
454
|
-
newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
|
|
455
|
-
props.clearNotifications?.();
|
|
456
|
-
props.setSerializedData?.(serializeScene());
|
|
457
|
-
}).catch(err => {
|
|
458
|
-
console.log(`Error updating the roughness texture of material: ${material.name}`);
|
|
459
|
-
console.log({
|
|
460
|
-
err
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Ambient
|
|
466
|
-
if (ambientColor) material.ambientColor = newColor(ambientColor);
|
|
467
|
-
if (ambientTextureStrength !== undefined) material.ambientTextureStrength = ambientTextureStrength;
|
|
468
|
-
if (!_.isEmpty(ambientTexture?.src || ambientTexture?.url)) {
|
|
469
|
-
material.ambientTexture = newTexture(ambientTexture?.src || ambientTexture?.url);
|
|
470
|
-
material.ambientTexture.name = ambientTexture.name;
|
|
471
|
-
applyUVSettings({
|
|
472
|
-
texture: material.ambientTexture,
|
|
473
|
-
material
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Emissive
|
|
478
|
-
if (emissiveColor) material.emissiveColor = newColor(emissiveColor);
|
|
479
|
-
if (emissiveIntensity !== undefined) material.emissiveIntensity = emissiveIntensity;
|
|
480
|
-
if (!_.isEmpty(emissiveTexture?.src || emissiveTexture?.url)) {
|
|
481
|
-
material.emissiveTexture = newTexture(emissiveTexture?.src || emissiveTexture?.url);
|
|
482
|
-
material.emissiveTexture.name = emissiveTexture.name;
|
|
483
|
-
applyUVSettings({
|
|
484
|
-
texture: material.emissiveTexture,
|
|
485
|
-
material
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// Normal/Bump
|
|
490
|
-
if (bumpIntensity !== undefined && !_.isEmpty(material.bumpTexture?.url)) {
|
|
491
|
-
material.bumpTexture.level = bumpIntensity;
|
|
492
|
-
}
|
|
493
|
-
if (!_.isEmpty(bumpTexture?.src || bumpTexture?.url)) {
|
|
494
|
-
material.bumpTexture = newTexture(bumpTexture?.src || bumpTexture?.url);
|
|
495
|
-
material.bumpTexture.name = bumpTexture.name;
|
|
496
|
-
applyUVSettings({
|
|
497
|
-
texture: material.bumpTexture,
|
|
498
|
-
material
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Clear coat
|
|
503
|
-
if (clearCoatEnabled !== undefined) material.clearCoat.isEnabled = clearCoatEnabled;
|
|
504
|
-
if (material.clearCoat?.isEnabled) {
|
|
505
|
-
if (clearCoatIntensity !== undefined) material.clearCoat.intensity = clearCoatIntensity ?? 1;
|
|
506
|
-
if (clearCoatRoughness !== undefined) material.clearCoat.roughness = clearCoatRoughness ?? 0.1;
|
|
507
|
-
if (clearCoatIOR !== undefined) material.clearCoat.indexOfRefraction = clearCoatIOR ?? 1.52;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Transparency
|
|
511
|
-
if (!_.isEmpty(id) && material && transparencyEnabled !== undefined) {
|
|
512
|
-
if (!albedoHasAlpha) {
|
|
513
|
-
material.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
|
514
|
-
}
|
|
515
|
-
material.transparencyEnabled = transparencyEnabled;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// Albedo alpha
|
|
519
|
-
if (!_.isEmpty(id) && material && albedoHasAlpha !== undefined) {
|
|
520
|
-
const hasAlbedoTexture = material?.albedoTexture;
|
|
521
|
-
const alphaMode = albedoHasAlpha ? 'PBRMATERIAL_ALPHATEST' : 'PBRMATERIAL_ALPHABLEND';
|
|
522
|
-
if (hasAlbedoTexture) {
|
|
523
|
-
material.albedoTexture.hasAlpha = albedoHasAlpha;
|
|
524
|
-
material.albedoTexture.useAlphaFromAlbedoTexture = albedoHasAlpha;
|
|
525
|
-
material.transparencyMode = BABYLON.PBRMaterial[alphaMode];
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
if (alpha !== undefined) material.alpha = alpha ?? 1;
|
|
529
|
-
if (!_.isEmpty(opacityTexture?.src || opacityTexture?.url)) {
|
|
530
|
-
material.opacityTexture = newTexture(opacityTexture?.src || opacityTexture?.url);
|
|
531
|
-
material.opacityTexture.name = opacityTexture.name;
|
|
532
|
-
applyUVSettings({
|
|
533
|
-
texture: material.opacityTexture,
|
|
534
|
-
material
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
if (transparencyType) {
|
|
538
|
-
material.transparencyType = _.toLower(transparencyType);
|
|
539
|
-
}
|
|
540
|
-
const isComplex = _.toLower(material.transparencyType) === TRANSPARENCY_MODES.complex;
|
|
541
|
-
if (material.subSurface) {
|
|
542
|
-
material.subSurface.isRefractionEnabled = isComplex;
|
|
543
|
-
}
|
|
544
|
-
if (isComplex) {
|
|
545
|
-
if (sssRefractionIntensity !== undefined) material.subSurface.refractionIntensity = sssRefractionIntensity ?? 1;
|
|
546
|
-
if (sssIOR !== undefined) material.subSurface.indexOfRefraction = sssIOR ?? 1.52;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// UV scale, rotation and offset
|
|
550
|
-
const xScaleReq = uvXScale !== undefined;
|
|
551
|
-
const yScaleReq = uvYScale !== undefined;
|
|
552
|
-
const xRotationReq = uvXRotation !== undefined;
|
|
553
|
-
const yRotationReq = uvYRotation !== undefined;
|
|
554
|
-
const zRotationReq = uvZRotation !== undefined;
|
|
555
|
-
const xOffsetReq = uvXOffset !== undefined;
|
|
556
|
-
const yOffsetReq = uvYOffset !== undefined;
|
|
557
|
-
const uvChangeRequest = xScaleReq || yScaleReq || xRotationReq || yRotationReq || zRotationReq || xOffsetReq || yOffsetReq;
|
|
558
|
-
if (uvChangeRequest) {
|
|
559
|
-
const workingMaterial = scene.metadata.materials.find(mat => mat.materialId === id) || {};
|
|
560
|
-
Object.keys(workingMaterial)?.forEach(key => {
|
|
561
|
-
const texture = material[key];
|
|
562
|
-
const validMaterialWithTexture = _.isObject(texture) && !_.isArray(texture) && key.endsWith('Texture');
|
|
563
|
-
const hasAllKeys = texture && ['uAng', 'wAng', 'vAng', 'uOffset', 'vOffset', 'uScale', 'vScale'].every(key => texture[key] !== undefined);
|
|
564
|
-
if (validMaterialWithTexture && hasAllKeys) {
|
|
565
|
-
if (xRotationReq) texture.uAng = uvXRotation;
|
|
566
|
-
if (yRotationReq) texture.wAng = uvYRotation;
|
|
567
|
-
if (zRotationReq) texture.vAng = uvZRotation;
|
|
568
|
-
if (xOffsetReq) texture.uOffset = uvXOffset;
|
|
569
|
-
if (yOffsetReq) texture.vOffset = uvYOffset;
|
|
570
|
-
if ((xScaleReq || yScaleReq) && uvScaleLock) {
|
|
571
|
-
texture.uScale = uvXScale ?? uvYScale;
|
|
572
|
-
texture.vScale = uvXScale ?? uvYScale;
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
if (xScaleReq) texture.uScale = uvXScale;
|
|
576
|
-
if (yScaleReq) texture.vScale = uvYScale;
|
|
577
|
-
if (uvScaleLock !== undefined) newMetaDataEntry('uvScaleLock', uvScaleLock);
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
newMetaDataEntry('materials', buildMaterialsArray());
|
|
582
|
-
newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
export const updateMesh = inboundData => {
|
|
586
|
-
const {
|
|
587
|
-
payload
|
|
588
|
-
} = inboundData;
|
|
589
|
-
const {
|
|
590
|
-
id,
|
|
591
|
-
useSavedPosition,
|
|
592
|
-
axisCompensation,
|
|
593
|
-
boundingBoxEnabled,
|
|
594
|
-
transforms,
|
|
595
|
-
flipHorizontally,
|
|
596
|
-
globalTransform,
|
|
597
|
-
variantName,
|
|
598
|
-
resetVariant,
|
|
599
|
-
sourceUnit,
|
|
600
|
-
displayUnit
|
|
601
|
-
} = payload;
|
|
602
|
-
const khrExtension = GLTF2.KHR_materials_variants;
|
|
603
|
-
const selectedMesh = scene.metadata.selectedMeshes?.[0];
|
|
604
|
-
const meshChangeTracking = scene.metadata?.meshChangeTracking || [];
|
|
605
|
-
const selectedSourceUnit = scene.metadata.selectedSourceUnit;
|
|
606
|
-
const selectedDisplayUnit = scene.metadata?.selectedDisplayUnit;
|
|
607
|
-
const isBoundingBoxEnabled = gizmoManager.boundingBoxGizmoEnabled;
|
|
608
|
-
const userMeshes = getUserMeshes();
|
|
609
|
-
const rootMesh = scene.getMeshByID('__root__');
|
|
610
|
-
const newTrackingData = args => meshChangeTracking?.map(mesh => {
|
|
611
|
-
return mesh.meshId === selectedMesh?.id ? {
|
|
612
|
-
...mesh,
|
|
613
|
-
...args
|
|
614
|
-
} : mesh;
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
// Change display units
|
|
618
|
-
if (!_.isEmpty(displayUnit) && selectedSourceUnit && selectedSourceUnit !== unitless) {
|
|
619
|
-
const newDisplayUnit = displayUnit === unitless ? selectedSourceUnit : displayUnit;
|
|
620
|
-
newMetaDataEntry('selectedDisplayUnit', newDisplayUnit);
|
|
621
|
-
if (isBoundingBoxEnabled) {
|
|
622
|
-
toggleBoundingBoxWidget();
|
|
623
|
-
toggleBoundingBoxWidget();
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Change mesh scale based on source units
|
|
628
|
-
if (!_.isEmpty(sourceUnit) && !_.isEmpty(userMeshes)) {
|
|
629
|
-
const globalTransforms = scene.metadata.globalTransforms;
|
|
630
|
-
const {
|
|
631
|
-
tx,
|
|
632
|
-
ty,
|
|
633
|
-
tz,
|
|
634
|
-
sx,
|
|
635
|
-
sy,
|
|
636
|
-
sz
|
|
637
|
-
} = globalTransforms;
|
|
638
|
-
const scaleFactor = getScaleFactor(sourceUnit);
|
|
639
|
-
const resetScaleFactor = getScaleFactor(selectedSourceUnit) / scaleFactor;
|
|
640
|
-
const isDifferent = !_.isEqual(sourceUnit, selectedSourceUnit);
|
|
641
|
-
const isMeter = _.isEqual(sourceUnit, 'm') || _.isEqual(sourceUnit, 'meter');
|
|
642
|
-
const newScale = scaleValue => {
|
|
643
|
-
if (!selectedSourceUnit && !isMeter) return scaleValue * scaleFactor;
|
|
644
|
-
if (selectedSourceUnit && isMeter) return scaleValue / resetScaleFactor;
|
|
645
|
-
if (isMeter) return scaleValue / scaleFactor;
|
|
646
|
-
if (selectedSourceUnit === 'm' || selectedSourceUnit === 'meter') {
|
|
647
|
-
return scaleValue / resetScaleFactor;
|
|
648
|
-
}
|
|
649
|
-
if (selectedSourceUnit && !isMeter || selectedSourceUnit && sourceUnit === unitless) {
|
|
650
|
-
// NOTE: If switching to unit other than meter, or user sends source unit reset,
|
|
651
|
-
// reset back to original mystery unit size/scale first.
|
|
652
|
-
const lastScaleFactor = getScaleFactor(selectedSourceUnit);
|
|
653
|
-
const originalScale = scaleValue / lastScaleFactor;
|
|
654
|
-
return originalScale * scaleFactor;
|
|
655
|
-
}
|
|
656
|
-
return scaleValue / resetScaleFactor * scaleFactor;
|
|
657
|
-
};
|
|
658
|
-
const convertedScales = {
|
|
659
|
-
tx: newScale(tx),
|
|
660
|
-
ty: newScale(ty),
|
|
661
|
-
tz: newScale(tz),
|
|
662
|
-
sx: newScale(sx),
|
|
663
|
-
sy: newScale(sy),
|
|
664
|
-
sz: newScale(sz)
|
|
665
|
-
};
|
|
666
|
-
if (isDifferent) {
|
|
667
|
-
const unit = scaleUnits.find(unit => unit.abbreviation === sourceUnit || unit.name === sourceUnit);
|
|
668
|
-
const {
|
|
669
|
-
tx,
|
|
670
|
-
ty,
|
|
671
|
-
tz,
|
|
672
|
-
sx,
|
|
673
|
-
sy,
|
|
674
|
-
sz
|
|
675
|
-
} = convertedScales;
|
|
676
|
-
const scalingVector = newVector(sx, sy, sz);
|
|
677
|
-
const positionVector = newVector(tx, ty, tz);
|
|
678
|
-
const sourceUnitScaleOperation = true;
|
|
679
|
-
performTransform({
|
|
680
|
-
type: 'scale',
|
|
681
|
-
vector: scalingVector,
|
|
682
|
-
newTrackingData,
|
|
683
|
-
globalTransform: true,
|
|
684
|
-
sx,
|
|
685
|
-
sy,
|
|
686
|
-
sz
|
|
687
|
-
});
|
|
688
|
-
performTransform({
|
|
689
|
-
type: 'move',
|
|
690
|
-
vector: positionVector,
|
|
691
|
-
newTrackingData,
|
|
692
|
-
globalTransform: true,
|
|
693
|
-
tx,
|
|
694
|
-
ty,
|
|
695
|
-
tz
|
|
696
|
-
});
|
|
697
|
-
if (sourceUnit !== unitless) {
|
|
698
|
-
newMetaDataEntry('selectedSourceUnit', sourceUnit);
|
|
699
|
-
!selectedDisplayUnit && newMetaDataEntry('selectedDisplayUnit', unit?.abbreviation);
|
|
700
|
-
}
|
|
701
|
-
if (sourceUnit === unitless) {
|
|
702
|
-
newMetaDataEntry('selectedSourceUnit', null);
|
|
703
|
-
newMetaDataEntry('selectedDisplayUnit', null);
|
|
704
|
-
}
|
|
705
|
-
newMetaDataEntry('globalTransforms', {
|
|
706
|
-
...globalTransforms,
|
|
707
|
-
...convertedScales
|
|
708
|
-
});
|
|
709
|
-
modelToOrigin(sourceUnitScaleOperation);
|
|
710
|
-
isBoundingBoxEnabled && toggleBoundingBoxWidget();
|
|
711
|
-
focusCamera();
|
|
712
|
-
isBoundingBoxEnabled && toggleBoundingBoxWidget();
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// Change mesh variant data
|
|
718
|
-
if (!_.isEmpty(variantName) && rootMesh) {
|
|
719
|
-
khrExtension.SelectVariant(rootMesh, variantName);
|
|
720
|
-
newMetaDataEntry('selectedMaterialVariant', variantName);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// Reset mesh variant data
|
|
724
|
-
if (_.isEmpty(variantName) && resetVariant && rootMesh) {
|
|
725
|
-
khrExtension.Reset(rootMesh);
|
|
726
|
-
newMetaDataEntry('selectedMaterialVariant', null);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// Bounding Box Toggle
|
|
730
|
-
if (boundingBoxEnabled !== undefined) {
|
|
731
|
-
toggleBoundingBoxWidget();
|
|
732
|
-
}
|
|
733
|
-
if (flipHorizontally) {
|
|
734
|
-
const multiMeshOperation = userMeshes.length > 1;
|
|
735
|
-
userMeshes.forEach(mesh => addNewMesh(mesh));
|
|
736
|
-
multiMeshOperation ? multiMeshTNode.scaling = newVector(-1, 1, 1) : singleMeshTNode.scaling = newVector(-1, 1, 1);
|
|
737
|
-
handleMousePointerTap();
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
const meshPosition = useSavedPosition ? scene.metadata?.userMeshPositions?.find(mesh => mesh.positionId === id && selectedMesh.id === mesh.meshId) || {} : transforms;
|
|
741
|
-
if (_.isEmpty(meshPosition)) return;
|
|
742
|
-
const {
|
|
743
|
-
tx,
|
|
744
|
-
ty,
|
|
745
|
-
tz,
|
|
746
|
-
rx,
|
|
747
|
-
ry,
|
|
748
|
-
rz,
|
|
749
|
-
sx,
|
|
750
|
-
sy,
|
|
751
|
-
sz
|
|
752
|
-
} = meshPosition;
|
|
753
|
-
if (useSavedPosition) {
|
|
754
|
-
const filteredPositions = scene.metadata.selectedUserMeshPositions.filter(pos => pos.meshId !== meshPosition.meshId);
|
|
755
|
-
filteredPositions.push(meshPosition);
|
|
756
|
-
newMetaDataEntry('selectedUserMeshPositions', filteredPositions);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Axis Compensation
|
|
760
|
-
if (_.isString(axisCompensation) && transforms && scene.metadata.selectedAxisCompensation !== axisCompensation) {
|
|
761
|
-
const userMaterials = getUserMaterials();
|
|
762
|
-
const firstMaterial = userMaterials[0] || {};
|
|
763
|
-
const multiMeshOperation = userMeshes.length > 1;
|
|
764
|
-
resetSelectedMeshes();
|
|
765
|
-
userMeshes.forEach(mesh => addNewMesh(mesh));
|
|
766
|
-
multiMeshOperation ? multiMeshTNode.rotation = newVector(toRadians(rx), toRadians(ry), toRadians(rz)) : singleMeshTNode.rotation = newVector(toRadians(rx), toRadians(ry), toRadians(rz));
|
|
767
|
-
handleMousePointerTap();
|
|
768
|
-
newMetaDataEntry('selectedAxisCompensation', axisCompensation);
|
|
769
|
-
newMetaDataEntry('meshChangeTracking', buildMeshPositionsArray());
|
|
770
|
-
newMetaDataEntry('selectedMaterials', []);
|
|
771
|
-
|
|
772
|
-
// Reset scaling to previous
|
|
773
|
-
meshChangeTracking.forEach(mesh => {
|
|
774
|
-
const foundMesh = meshChangeTracking.find(prevMesh => prevMesh.id === mesh.id);
|
|
775
|
-
if (foundMesh) {
|
|
776
|
-
mesh.sx = foundMesh.sx;
|
|
777
|
-
mesh.sy = foundMesh.sy;
|
|
778
|
-
mesh.sz = foundMesh.sz;
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
if (!_.isEmpty(firstMaterial)) {
|
|
782
|
-
const existingMaterial = scene.metadata.materials.find(mat => mat.materialId === firstMaterial.id);
|
|
783
|
-
newMetaDataEntry('selectedMaterials', [existingMaterial]);
|
|
784
|
-
}
|
|
785
|
-
newMetaDataEntry('selectedMeshes', []);
|
|
786
|
-
modelToOrigin();
|
|
787
|
-
focusCamera();
|
|
788
|
-
multiMeshOperation ? multiMeshTNode.rotation = newVector(toRadians(0), toRadians(0), toRadians(0)) : singleMeshTNode.rotation = newVector(toRadians(0), toRadians(0), toRadians(0));
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
// Transform inputs and Custom user position restoring.
|
|
792
|
-
if (meshPosition) {
|
|
793
|
-
const doPosition = [tx, ty, tz].every(val => val !== undefined);
|
|
794
|
-
const doRotation = [rx, ry, rz].every(val => val !== undefined);
|
|
795
|
-
const doScale = [sx, sy, sz].every(val => val !== undefined);
|
|
796
|
-
if (doPosition) {
|
|
797
|
-
const positionVector = newVector(tx, ty, tz);
|
|
798
|
-
performTransform({
|
|
799
|
-
type: 'move',
|
|
800
|
-
vector: positionVector,
|
|
801
|
-
newTrackingData,
|
|
802
|
-
globalTransform,
|
|
803
|
-
tx,
|
|
804
|
-
ty,
|
|
805
|
-
tz
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
if (doRotation) {
|
|
809
|
-
const rotationVector = newVector(toRadians(rx), toRadians(ry), toRadians(rz));
|
|
810
|
-
performTransform({
|
|
811
|
-
type: 'rotate',
|
|
812
|
-
vector: rotationVector,
|
|
813
|
-
newTrackingData,
|
|
814
|
-
globalTransform,
|
|
815
|
-
rx,
|
|
816
|
-
ry,
|
|
817
|
-
rz
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
if (doScale) {
|
|
821
|
-
// TODO: Handle zeroes
|
|
822
|
-
const currentMeshTracking = globalTransform ? scene.metadata.globalTransforms : scene.metadata.meshChangeTracking.find(mesh => mesh.meshId === selectedMesh.id);
|
|
823
|
-
const {
|
|
824
|
-
sx: currentSx,
|
|
825
|
-
sy: currentSy,
|
|
826
|
-
sz: currentSz
|
|
827
|
-
} = currentMeshTracking;
|
|
828
|
-
const newScaleX = globalTransform ? sx : sx / currentSx || 1;
|
|
829
|
-
const newScaleY = globalTransform ? sy : sy / currentSy || 1;
|
|
830
|
-
const newScaleZ = globalTransform ? sz : sz / currentSz || 1;
|
|
831
|
-
const isValid = [newScaleX, newScaleY, newScaleZ].every(val => val !== 0);
|
|
832
|
-
if (isValid) {
|
|
833
|
-
const scalingVector = newVector(newScaleX, newScaleY, newScaleZ);
|
|
834
|
-
performTransform({
|
|
835
|
-
type: 'scale',
|
|
836
|
-
vector: scalingVector,
|
|
837
|
-
newTrackingData,
|
|
838
|
-
globalTransform,
|
|
839
|
-
sx,
|
|
840
|
-
sy,
|
|
841
|
-
sz
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
attachToSelectMeshesNode();
|
|
847
|
-
createBoundingMesh();
|
|
848
|
-
resetManagerGizmos(GIZMOS.BoundingBoxGizmo);
|
|
849
|
-
};
|
|
850
|
-
export const updateCamera = inboundData => {
|
|
851
|
-
const {
|
|
852
|
-
payload
|
|
853
|
-
} = inboundData;
|
|
854
|
-
const {
|
|
855
|
-
id,
|
|
856
|
-
transforms,
|
|
857
|
-
enableSafeFrame,
|
|
858
|
-
aspectRatio,
|
|
859
|
-
orthoCamera,
|
|
860
|
-
resetCamera,
|
|
861
|
-
takeScreenshot,
|
|
862
|
-
isBillboard,
|
|
863
|
-
imageDataOnly
|
|
864
|
-
} = payload;
|
|
865
|
-
const camera = scene.activeCamera;
|
|
866
|
-
if (camera) {
|
|
867
|
-
// Orthographic view selections.
|
|
868
|
-
if (orthoCamera !== undefined) toggleOrthographicViews(null, orthoCamera);
|
|
869
|
-
|
|
870
|
-
// Update camera position and rotation.
|
|
871
|
-
if (_.isString(id) && !_.isEmpty(transforms)) {
|
|
872
|
-
const {
|
|
873
|
-
tx,
|
|
874
|
-
ty,
|
|
875
|
-
tz,
|
|
876
|
-
rx,
|
|
877
|
-
ry,
|
|
878
|
-
rz,
|
|
879
|
-
tarX,
|
|
880
|
-
tarY,
|
|
881
|
-
tarZ
|
|
882
|
-
} = transforms;
|
|
883
|
-
if (tx && ty && tz) camera.position = newVector(tx, ty, tz);
|
|
884
|
-
if (rx && ry && rz) camera.rotation = newVector(rx, ry, rz);
|
|
885
|
-
if (tarX && tarY && tarZ) camera.target = newVector(tarX, tarY, tarZ);
|
|
886
|
-
if (camera.mode === BABYLON.Camera.ORTHOGRAPHIC_CAMERA) {
|
|
887
|
-
camera.mode = BABYLON.Camera.PERSPECTIVE_CAMERA;
|
|
888
|
-
}
|
|
889
|
-
newMetaDataEntry('selectedUserCameraView', {
|
|
890
|
-
id,
|
|
891
|
-
tx,
|
|
892
|
-
ty,
|
|
893
|
-
tz,
|
|
894
|
-
rx,
|
|
895
|
-
ry,
|
|
896
|
-
rz,
|
|
897
|
-
tarX,
|
|
898
|
-
tarY,
|
|
899
|
-
tarZ
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Reset camera to default position and rotation.
|
|
904
|
-
if (resetCamera !== undefined) {
|
|
905
|
-
prepareCamera();
|
|
906
|
-
const {
|
|
907
|
-
position,
|
|
908
|
-
rotation,
|
|
909
|
-
target
|
|
910
|
-
} = scene.activeCamera;
|
|
911
|
-
const [tx, ty, tz] = [position.x, position.y, position.z];
|
|
912
|
-
const [rx, ry, rz] = [rotation.x, rotation.y, rotation.z];
|
|
913
|
-
const [tarX, tarY, tarZ] = [target.x, target.y, target.z];
|
|
914
|
-
newMetaDataEntry('selectedOrthoView', '');
|
|
915
|
-
newMetaDataEntry('selectedUserCameraView', {
|
|
916
|
-
id: 'Default',
|
|
917
|
-
tx,
|
|
918
|
-
ty,
|
|
919
|
-
tz,
|
|
920
|
-
rx,
|
|
921
|
-
ry,
|
|
922
|
-
rz,
|
|
923
|
-
tarX,
|
|
924
|
-
tarY,
|
|
925
|
-
tarZ
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
// Toggle safe frame.
|
|
930
|
-
if (enableSafeFrame !== undefined && _.isNumber(aspectRatio)) {
|
|
931
|
-
const newMetaValue = enableSafeFrame ? Object.values(ratios).find(ratio => ratio.value === aspectRatio) : {};
|
|
932
|
-
toggleSafeFrame(null, aspectRatio, enableSafeFrame);
|
|
933
|
-
newMetaDataEntry('selectedRatio', newMetaValue);
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// Take screenshot.
|
|
937
|
-
if (takeScreenshot !== undefined && _.isNumber(aspectRatio)) {
|
|
938
|
-
const callback = screenshotData => {
|
|
939
|
-
newMetaDataEntry('screenshotData', screenshotData);
|
|
940
|
-
const outerFrame = guiTexture.getControlByName(GUI.outerSafeFrame);
|
|
941
|
-
const {
|
|
942
|
-
widthInPixels,
|
|
943
|
-
heightInPixels,
|
|
944
|
-
centerX,
|
|
945
|
-
centerY
|
|
946
|
-
} = outerFrame;
|
|
947
|
-
const baseResolution = 2048;
|
|
948
|
-
const ratio = widthInPixels > heightInPixels ? widthInPixels / baseResolution : heightInPixels / baseResolution;
|
|
949
|
-
const highResHeight = heightInPixels / ratio;
|
|
950
|
-
const highResWidth = widthInPixels / ratio;
|
|
951
|
-
|
|
952
|
-
// NOTE: Serializes scene for billboard screenshots exactly onSuccess.
|
|
953
|
-
props.setSerializedData?.(serializeScene());
|
|
954
|
-
const canvas = document.createElement('canvas');
|
|
955
|
-
const context = canvas.getContext('2d');
|
|
956
|
-
canvas.width = highResWidth;
|
|
957
|
-
canvas.height = highResHeight;
|
|
958
|
-
const image = new Image();
|
|
959
|
-
image.src = screenshotData;
|
|
960
|
-
image.onload = () => {
|
|
961
|
-
context.drawImage(image, outerFrame.isVisible ? centerX - widthInPixels / 2 : centerX - canvas.width / 2,
|
|
962
|
-
// Moves image right
|
|
963
|
-
outerFrame.isVisible ? centerY - heightInPixels / 2 : centerY - canvas.height / 2,
|
|
964
|
-
// Moves image down
|
|
965
|
-
outerFrame.isVisible ? widthInPixels : canvas.width, outerFrame.isVisible ? heightInPixels : canvas.height, 0,
|
|
966
|
-
// Moves image right
|
|
967
|
-
0,
|
|
968
|
-
// Moves image down
|
|
969
|
-
highResWidth, highResHeight);
|
|
970
|
-
const imageData = canvas.toDataURL('image/png', 1.0);
|
|
971
|
-
|
|
972
|
-
// Download
|
|
973
|
-
if (!imageDataOnly) {
|
|
974
|
-
const link = document.createElement('a');
|
|
975
|
-
link.download = 'screenshot.png';
|
|
976
|
-
link.href = imageData;
|
|
977
|
-
document.body.appendChild(link);
|
|
978
|
-
link.click();
|
|
979
|
-
document.body.removeChild(link);
|
|
980
|
-
}
|
|
981
|
-
};
|
|
982
|
-
};
|
|
983
|
-
newScreenshot(isBillboard, callback);
|
|
984
|
-
newMetaDataEntry('selectedRatio', Object.values(ratios).find(ratio => ratio.value === aspectRatio));
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
export const updateEnvironment = inboundData => {
|
|
989
|
-
const {
|
|
990
|
-
payload
|
|
991
|
-
} = inboundData;
|
|
992
|
-
const {
|
|
993
|
-
url,
|
|
994
|
-
envIntensity,
|
|
995
|
-
envBlur,
|
|
996
|
-
transforms
|
|
997
|
-
} = payload;
|
|
998
|
-
const skyBox = scene.getMaterialById('skyBox', true);
|
|
999
|
-
if (skyBox) {
|
|
1000
|
-
// Intensity
|
|
1001
|
-
if (envIntensity !== undefined) {
|
|
1002
|
-
scene.environmentIntensity = envIntensity;
|
|
1003
|
-
newMetaDataEntry('environmentIntensity', envIntensity);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
// Blur
|
|
1007
|
-
if (envBlur !== undefined) {
|
|
1008
|
-
skyBox.microSurface = 1 - envBlur;
|
|
1009
|
-
newMetaDataEntry('environmentBlur', envBlur);
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
// Update environment texture.
|
|
1013
|
-
if (_.isString(url)) {
|
|
1014
|
-
const selectedUserEnvironment = scene.metadata?.userEnvironments?.find(env => env.url === url) || props.defaultEnvironments.find(item => item.envURL === url) || {
|
|
1015
|
-
id: 'Default',
|
|
1016
|
-
url: scene.metadata.defaultEnvironment
|
|
1017
|
-
};
|
|
1018
|
-
const {
|
|
1019
|
-
id,
|
|
1020
|
-
name,
|
|
1021
|
-
envURL,
|
|
1022
|
-
url: userURL
|
|
1023
|
-
} = selectedUserEnvironment;
|
|
1024
|
-
const metaName = id || name;
|
|
1025
|
-
const metaURL = userURL || envURL;
|
|
1026
|
-
scene.environmentTexture.url = metaURL;
|
|
1027
|
-
scene.environmentTexture.name = metaURL;
|
|
1028
|
-
skyBox.reflectionTexture.url = metaURL;
|
|
1029
|
-
skyBox.reflectionTexture.name = metaURL;
|
|
1030
|
-
const newEnv = scene.environmentTexture.clone();
|
|
1031
|
-
const newSkyBox = skyBox.reflectionTexture.clone();
|
|
1032
|
-
scene.environmentTexture.dispose();
|
|
1033
|
-
skyBox.reflectionTexture.dispose();
|
|
1034
|
-
scene.environmentTexture = newEnv;
|
|
1035
|
-
skyBox.reflectionTexture = newSkyBox;
|
|
1036
|
-
newMetaDataEntry('selectedUserEnvironment', {
|
|
1037
|
-
id: metaName,
|
|
1038
|
-
url: metaURL
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
// Rotation
|
|
1043
|
-
if (transforms?.rotation?.ry !== undefined) {
|
|
1044
|
-
const envRotation = toRadians(transforms.rotation.ry);
|
|
1045
|
-
skyBox.reflectionTexture.rotationY = envRotation;
|
|
1046
|
-
scene.environmentTexture.rotationY = envRotation;
|
|
1047
|
-
newMetaDataEntry('environmentRotation', toDegrees(envRotation));
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
};
|
|
1051
|
-
export const updateViewport = inboundData => {
|
|
1052
|
-
const {
|
|
1053
|
-
payload
|
|
1054
|
-
} = inboundData;
|
|
1055
|
-
const {
|
|
1056
|
-
envVisible,
|
|
1057
|
-
shadows,
|
|
1058
|
-
iblPipeline,
|
|
1059
|
-
axes,
|
|
1060
|
-
grid,
|
|
1061
|
-
hideSelected,
|
|
1062
|
-
unhideAll,
|
|
1063
|
-
unhideLast,
|
|
1064
|
-
deselectAll
|
|
1065
|
-
} = payload;
|
|
1066
|
-
const hdrSkyBox = scene.getMeshByName('hdrSkyBox');
|
|
1067
|
-
const xAxisMesh = scene.getMeshByName('xAxisMesh');
|
|
1068
|
-
const yAxisMesh = scene.getMeshByName('yAxisMesh');
|
|
1069
|
-
const zAxisMesh = scene.getMeshByName('zAxisMesh');
|
|
1070
|
-
|
|
1071
|
-
// Hide selected meshes.
|
|
1072
|
-
if (hideSelected) {
|
|
1073
|
-
selectedMeshes.forEach(mesh => {
|
|
1074
|
-
mesh.isVisible = false;
|
|
1075
|
-
hiddenMeshes.push(mesh);
|
|
1076
|
-
});
|
|
1077
|
-
resetSelectedMeshes();
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// Unhide all meshes.
|
|
1081
|
-
if (unhideAll) {
|
|
1082
|
-
const userMeshes = getUserMeshes();
|
|
1083
|
-
userMeshes.forEach(mesh => mesh.isVisible = true);
|
|
1084
|
-
hiddenMeshes = [];
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
// Unhide last hidden mesh.
|
|
1088
|
-
if (unhideLast) {
|
|
1089
|
-
if (!_.isEmpty(hiddenMeshes)) {
|
|
1090
|
-
const mesh = hiddenMeshes.pop();
|
|
1091
|
-
mesh.isVisible = true;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// Deselect all meshes.
|
|
1096
|
-
if (deselectAll) resetSelectedMeshes();
|
|
1097
|
-
|
|
1098
|
-
// Environment visibility.
|
|
1099
|
-
if (envVisible !== undefined) {
|
|
1100
|
-
hdrSkyBox.setEnabled(envVisible);
|
|
1101
|
-
newMetaDataEntry('viewportEnvironment', envVisible);
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
// Shadow visbility
|
|
1105
|
-
if (shadows !== undefined) {
|
|
1106
|
-
mirrorGround.receiveShadows = shadows;
|
|
1107
|
-
newMetaDataEntry('viewportShadows', shadows);
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
// Grid visibility
|
|
1111
|
-
if (grid !== undefined) {
|
|
1112
|
-
ground.setEnabled(grid);
|
|
1113
|
-
newMetaDataEntry('viewportGround', grid);
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// Axis visibility
|
|
1117
|
-
if (axes !== undefined) {
|
|
1118
|
-
xAxisMesh.setEnabled(axes);
|
|
1119
|
-
yAxisMesh.setEnabled(axes);
|
|
1120
|
-
zAxisMesh.setEnabled(axes);
|
|
1121
|
-
newMetaDataEntry('viewportAxes', axes);
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// IBL Shadow Rendering Pipeline
|
|
1125
|
-
if (iblPipeline !== undefined) {
|
|
1126
|
-
iblShadowPipeline.toggleShadow(iblPipeline);
|
|
1127
|
-
newMetaDataEntry('iblShadowPipelineEnabled', iblPipeline);
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
export const updatePublish = inboundData => {
|
|
1131
|
-
const {
|
|
1132
|
-
payload
|
|
1133
|
-
} = inboundData;
|
|
1134
|
-
const existingPublish = scene.metadata?.publish || {};
|
|
1135
|
-
const totalTriangles = +(scene.metadata.statistics?.triangles?.replace?.(/,/g, '') || 0);
|
|
1136
|
-
const incomingTargetTriangles = payload?.meshSimplification?.userTargetTriangles || existingPublish?.meshSimplification?.userTargetTriangles || totalTriangles;
|
|
1137
|
-
const estimatedTriangles = payload?.meshSimplification?.estimatedTriangles || existingPublish?.meshSimplification?.estimatedTriangles || 99; // Percent
|
|
1138
|
-
|
|
1139
|
-
const category = payload?.category || existingPublish?.category;
|
|
1140
|
-
const tags = payload?.tags || existingPublish?.tags;
|
|
1141
|
-
const proxy = payload?.proxy || existingPublish?.proxy;
|
|
1142
|
-
const title = payload?.title || existingPublish?.title;
|
|
1143
|
-
const withOptimizationValues = {
|
|
1144
|
-
...existingPublish,
|
|
1145
|
-
category,
|
|
1146
|
-
tags,
|
|
1147
|
-
proxy,
|
|
1148
|
-
title,
|
|
1149
|
-
textureDownscaling: {
|
|
1150
|
-
...(existingPublish?.textureDownscaling || {}),
|
|
1151
|
-
...(payload?.textureDownscaling || {})
|
|
1152
|
-
},
|
|
1153
|
-
meshSimplification: {
|
|
1154
|
-
...(existingPublish?.meshSimplification || {}),
|
|
1155
|
-
...(payload?.meshSimplification || {}),
|
|
1156
|
-
totalTriangles: totalTriangles,
|
|
1157
|
-
estimatedTriangles: estimatedTriangles,
|
|
1158
|
-
userTargetTriangles: incomingTargetTriangles,
|
|
1159
|
-
targetTriangles: Math.round(totalTriangles * (estimatedTriangles / 100))
|
|
1160
|
-
}
|
|
1161
|
-
};
|
|
1162
|
-
newMetaDataEntry('publish', withOptimizationValues);
|
|
1163
|
-
};
|
|
1164
|
-
export const updateLighting = inboundData => {
|
|
1165
|
-
const {
|
|
1166
|
-
payload
|
|
1167
|
-
} = inboundData;
|
|
1168
|
-
const {
|
|
1169
|
-
id,
|
|
1170
|
-
transforms,
|
|
1171
|
-
target,
|
|
1172
|
-
enable,
|
|
1173
|
-
diffuse,
|
|
1174
|
-
specular,
|
|
1175
|
-
radius,
|
|
1176
|
-
intensity,
|
|
1177
|
-
distance,
|
|
1178
|
-
rotation
|
|
1179
|
-
} = payload;
|
|
1180
|
-
const light = scene.getLightByName(id);
|
|
1181
|
-
if (!_.isEmpty(transforms)) {
|
|
1182
|
-
const {
|
|
1183
|
-
tx,
|
|
1184
|
-
ty,
|
|
1185
|
-
tz,
|
|
1186
|
-
dirX,
|
|
1187
|
-
dirY,
|
|
1188
|
-
dirZ
|
|
1189
|
-
} = transforms;
|
|
1190
|
-
const isPosition = tx !== undefined && ty !== undefined && tz !== undefined;
|
|
1191
|
-
const isDirection = dirX !== undefined && dirY !== undefined && dirZ !== undefined;
|
|
1192
|
-
if (isPosition) light.position = newVector(tx, ty, tz);
|
|
1193
|
-
if (isDirection) light.direction = newVector(dirX, dirY, dirZ);
|
|
1194
|
-
}
|
|
1195
|
-
if (target) {
|
|
1196
|
-
light.setDirectionToTarget(target);
|
|
1197
|
-
}
|
|
1198
|
-
if (enable !== undefined) {
|
|
1199
|
-
light.setEnabled(enable);
|
|
1200
|
-
}
|
|
1201
|
-
if (radius !== undefined) {
|
|
1202
|
-
light.radius = radius;
|
|
1203
|
-
}
|
|
1204
|
-
if (diffuse) {
|
|
1205
|
-
const {
|
|
1206
|
-
r,
|
|
1207
|
-
g,
|
|
1208
|
-
b
|
|
1209
|
-
} = diffuse;
|
|
1210
|
-
light.diffuse = newColor(r, g, b);
|
|
1211
|
-
}
|
|
1212
|
-
if (specular) {
|
|
1213
|
-
const {
|
|
1214
|
-
r,
|
|
1215
|
-
g,
|
|
1216
|
-
b
|
|
1217
|
-
} = specular;
|
|
1218
|
-
light.specular = newColor(r, g, b);
|
|
1219
|
-
}
|
|
1220
|
-
if (intensity !== undefined) {
|
|
1221
|
-
light.intensity = intensity;
|
|
1222
|
-
}
|
|
1223
|
-
if (distance !== undefined) {
|
|
1224
|
-
const rotation = light?.rotation || 0;
|
|
1225
|
-
const angleRad = rotation * Math.PI / 180;
|
|
1226
|
-
light.position = newVector(distance * Math.cos(angleRad), distance * Math.tanh(angleRad), distance * Math.sin(angleRad));
|
|
1227
|
-
light.setDirectionToTarget(newVector(0, 0, 0));
|
|
1228
|
-
light.distance = distance;
|
|
1229
|
-
}
|
|
1230
|
-
if (rotation !== undefined) {
|
|
1231
|
-
const {
|
|
1232
|
-
position
|
|
1233
|
-
} = light;
|
|
1234
|
-
const {
|
|
1235
|
-
x,
|
|
1236
|
-
y,
|
|
1237
|
-
z
|
|
1238
|
-
} = position;
|
|
1239
|
-
const angleRad = rotation * Math.PI / 180;
|
|
1240
|
-
let distance = light?.distance || Math.sqrt(Math.pow(Math.abs(x), 2) + Math.pow(Math.abs(y), 2) + Math.pow(Math.abs(z), 2));
|
|
1241
|
-
distance = distance > 200 ? 200 : distance;
|
|
1242
|
-
light.position = newVector(distance * Math.cos(angleRad), distance * Math.tanh(angleRad), distance * Math.sin(angleRad));
|
|
1243
|
-
light.setDirectionToTarget(newVector(0, 0, 0));
|
|
1244
|
-
light.rotation = rotation;
|
|
1245
|
-
}
|
|
1246
|
-
newMetaDataEntry('lights', buildLightsArray());
|
|
1247
|
-
};
|