@archvisioninc/canvas 3.3.7 → 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.
Files changed (36) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/README_DEV.md +4 -1
  3. package/package.json +1 -1
  4. package/src/package/helpers/canvasUpdateHelpers.js +114 -0
  5. package/dist/Canvas.js +0 -67
  6. package/dist/actions/index.js +0 -1
  7. package/dist/actions/shortcutActions.js +0 -313
  8. package/dist/constants/constants.js +0 -80
  9. package/dist/constants/index.js +0 -1
  10. package/dist/enums/aspectRatios.js +0 -17
  11. package/dist/enums/dimensions.js +0 -20
  12. package/dist/enums/downscaling.js +0 -16
  13. package/dist/enums/exclusions.js +0 -4
  14. package/dist/enums/formats.js +0 -1
  15. package/dist/enums/index.js +0 -8
  16. package/dist/enums/orthoOptions.js +0 -28
  17. package/dist/enums/scaleUnits.js +0 -25
  18. package/dist/enums/shortcuts.js +0 -89
  19. package/dist/helpers/cameraHelpers.js +0 -86
  20. package/dist/helpers/canvasAddHelpers.js +0 -161
  21. package/dist/helpers/canvasCommunicationHelpers.js +0 -52
  22. package/dist/helpers/canvasRemoveHelpers.js +0 -230
  23. package/dist/helpers/canvasUpdateHelpers.js +0 -1274
  24. package/dist/helpers/gizmoHelpers.js +0 -156
  25. package/dist/helpers/guiHelpers.js +0 -46
  26. package/dist/helpers/index.js +0 -16
  27. package/dist/helpers/initHelpers.js +0 -513
  28. package/dist/helpers/lightHelpers.js +0 -17
  29. package/dist/helpers/loadHelpers.js +0 -269
  30. package/dist/helpers/materialHelpers.js +0 -34
  31. package/dist/helpers/meshHelpers.js +0 -169
  32. package/dist/helpers/rayHelpers.js +0 -11
  33. package/dist/helpers/shortcutHelpers.js +0 -35
  34. package/dist/helpers/utilityHelpers.js +0 -710
  35. package/dist/helpers/viewportHelpers.js +0 -364
  36. package/dist/styles.js +0 -25
@@ -1,710 +0,0 @@
1
- import { GLTF2 } from 'babylonjs-loaders';
2
- import { GIZMOS, GUI, TRANSPARENCY_MODES } from '../constants';
3
- import { orthoOptions } from '../enums';
4
- import { downscaling } from '../enums/downscaling';
5
- import { scene, utilLayer, gizmoManager, getUserMeshes, highlightManager, hoverHighlightManager, engine, guiTexture, ground, mirrorGround, getUserMaterials, getUserTextures, selectedMeshes, hiddenMeshes, getBoundingMeshData, singleMeshTNode, updateEnvironment, multiMeshTNode, resetSelectedMeshes, attachToSelectMeshesNode, getUserNodes, canvas } from '../helpers';
6
- import { reactProps as props } from '../Canvas';
7
- import * as BABYLON from 'babylonjs';
8
- import * as SERIALIZERS from 'babylonjs-serializers';
9
- import _ from 'lodash';
10
- export let autoRotation;
11
- export const newVector = (x, y, z) => new BABYLON.Vector3(x || 0, y || 0, z || 0);
12
- export const toRadians = value => BABYLON.Tools.ToRadians(value || 0);
13
- export const toDegrees = value => BABYLON.Tools.ToDegrees(value || 0);
14
- export const addAutoRotation = () => {
15
- autoRotation = new BABYLON.AutoRotationBehavior();
16
- };
17
- export const removeAutoRotation = () => {
18
- autoRotation = null;
19
- };
20
- export const defaultCameraPosition = () => newVector(0, 3, 11);
21
- export const serializeScene = () => {
22
- const serializedData = {
23
- metadata: scene?.metadata
24
- };
25
- return serializedData;
26
- };
27
- export const addHighlightExclusion = mesh => {
28
- if (props.previewMode) return;
29
- highlightManager.addExcludedMesh(mesh);
30
- hoverHighlightManager.addExcludedMesh(mesh);
31
- };
32
- export const newMetaDataEntry = (key, value) => {
33
- const {
34
- metadata
35
- } = scene;
36
- const newMetadata = {
37
- ...metadata,
38
- [key]: value
39
- };
40
- scene.metadata = newMetadata;
41
- };
42
- export const newTexture = (url, isEnvTexture, isSkyBox) => {
43
- const onError = error => console.error(error);
44
- const onLoad = () => {
45
- if (isEnvTexture) {
46
- const rotation = scene.metadata.environmentRotation || 0;
47
- updateEnvironment({
48
- payload: {
49
- url,
50
- transforms: {
51
- rotation: {
52
- ry: rotation
53
- }
54
- }
55
- }
56
- });
57
- }
58
- props.clearNotifications?.();
59
- props.setSerializedData?.(serializeScene());
60
- };
61
- if (isEnvTexture) {
62
- const isUserFile = _.startsWith(url, 'blob');
63
- const extension = `.${url.split('.').pop()}`;
64
- const type = BABYLON.Constants.TEXTUREFORMAT_RGBA;
65
- const options = [url, scene, null, false, null, null, null, type, false, extension];
66
- const hdrOptions = [url, scene, 256, false, true, false, true, onLoad, onError];
67
- const texture = isUserFile ? new BABYLON.HDRCubeTexture(...hdrOptions) : new BABYLON.CubeTexture(...options);
68
- isSkyBox ? texture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE : texture.coordinatesMode = BABYLON.Texture.CUBIC_MODE;
69
- return texture;
70
- }
71
- const deleteBuffer = true;
72
- const buffer = url;
73
- const samplingMode = BABYLON.Texture.NEAREST_SAMPLINGMODE;
74
- const texture = new BABYLON.Texture(url, scene, false, false, samplingMode, onLoad, onError, buffer, deleteBuffer);
75
- return texture;
76
- };
77
- export const newColor = (r, g, b, a) => {
78
- const isHex = _.startsWith(r, '#');
79
- const hexHasAlpha = isHex && r.length === 9;
80
- if (isHex) {
81
- return hexHasAlpha ? new BABYLON.Color4.FromHexString(r) : new BABYLON.Color3.FromHexString(r);
82
- }
83
- return a ? new BABYLON.Color4(r, g, b, a) : new BABYLON.Color3(r, g, b);
84
- };
85
- export const newHighlightManager = name => {
86
- const manager = new BABYLON.HighlightLayer(name, scene);
87
- manager.blurHorizontalSize = 1.5;
88
- manager.blurVerticalSize = 1.5;
89
- manager.innerGlow = false;
90
- return manager;
91
- };
92
- export const newFraming = () => new BABYLON.FramingBehavior();
93
- export const newScreenshot = (isBillboard, callback) => {
94
- let onSuccess = data => callback?.(data);
95
- if (!props.previewMode) {
96
- const outerFrame = guiTexture.getControlByName(GUI.outerSafeFrame);
97
- const xAxisMesh = scene.getMeshByName('xAxisMesh');
98
- const yAxisMesh = scene.getMeshByName('yAxisMesh');
99
- const zAxisMesh = scene.getMeshByName('zAxisMesh');
100
- const hdrSkyBox = scene.getMeshByName('hdrSkyBox');
101
- const axesVisible = xAxisMesh.isVisible;
102
- const skyBoxVisible = hdrSkyBox.isVisible;
103
- const frameVisible = outerFrame.isVisible;
104
- const groundVisible = ground.isVisible;
105
- const mirrorGroundVisible = mirrorGround.isVisible;
106
- const {
107
- positionGizmoEnabled,
108
- rotationGizmoEnabled,
109
- boundingBoxGizmoEnabled,
110
- scaleGizmoEnabled
111
- } = gizmoManager;
112
- const toggleUI = restore => {
113
- if (isBillboard) {
114
- mirrorGround.isVisible = restore ? mirrorGroundVisible : false;
115
- }
116
- ground.isVisible = restore ? groundVisible : false;
117
- xAxisMesh.isVisible = restore ? axesVisible : false;
118
- yAxisMesh.isVisible = restore ? axesVisible : false;
119
- zAxisMesh.isVisible = restore ? axesVisible : false;
120
- hdrSkyBox.isVisible = restore ? skyBoxVisible : false;
121
- outerFrame.isVisible = restore ? frameVisible : false;
122
- gizmoManager.positionGizmoEnabled = restore ? positionGizmoEnabled : false;
123
- gizmoManager.rotationGizmoEnabled = restore ? rotationGizmoEnabled : false;
124
- gizmoManager.boundingBoxGizmoEnabled = restore ? boundingBoxGizmoEnabled : false;
125
- gizmoManager.scaleGizmoEnabled = restore ? scaleGizmoEnabled : false;
126
- };
127
- onSuccess = data => {
128
- toggleUI(true);
129
- callback?.(data);
130
- };
131
- toggleUI();
132
- }
133
- BABYLON.Tools.CreateScreenshot(engine, scene.activeCamera, {
134
- height: canvas.height,
135
- width: canvas.width
136
- }, onSuccess);
137
- };
138
- export const newGizmo = (type, options = {}) => {
139
- if (type === GIZMOS.PositionGizmo || type === GIZMOS.ScaleGizmo) {
140
- const {
141
- thickness
142
- } = options;
143
- return new BABYLON[type](utilLayer, thickness, gizmoManager);
144
- }
145
- if (type === GIZMOS.RotationGizmo) {
146
- const {
147
- tessellation,
148
- useEulerRotation,
149
- thickness,
150
- rotationGizmoOptions
151
- } = options;
152
- return new BABYLON[type](utilLayer, tessellation, useEulerRotation, thickness, gizmoManager, rotationGizmoOptions);
153
- }
154
- if (type === GIZMOS.BoundingBoxGizmo) {
155
- const {
156
- color
157
- } = options;
158
- return new BABYLON[type](utilLayer, color, gizmoManager);
159
- }
160
- };
161
- export const getColor = (props, key) => {
162
- const hasProps = props.theme && props.$selectedTheme;
163
- if (hasProps) {
164
- const themeColors = props.theme.colors[props.$selectedTheme];
165
- return themeColors[key];
166
- }
167
- };
168
- export const blobToGLB = (blob, name) => {
169
- const file = blob;
170
- file.lastModifiedDate = new Date();
171
- file.name = `${name}.glb`;
172
- return file;
173
- };
174
- export const getGLTFData = async (type, download) => {
175
- const shouldExportNode = node => {
176
- const userNodes = getUserNodes();
177
- const userMeshes = getUserMeshes();
178
- const includesNode = userNodes.includes(node);
179
- const includesMesh = userMeshes.includes(node);
180
- return includesNode || includesMesh;
181
- };
182
- try {
183
- const exporterType = type === 'glb' ? 'GLBAsync' : 'GLTFAsync';
184
- resetSelectedMeshes();
185
- attachToSelectMeshesNode();
186
- const gltf = await SERIALIZERS.GLTF2Export[exporterType](scene, 'fileName', {
187
- shouldExportNode
188
- });
189
- if (download) gltf.downloadFiles();
190
- props.setDownloadedModel?.(gltf.glTFFiles);
191
- } catch (e) {
192
- console.error(e);
193
- }
194
- };
195
- export const getMidpoint = (v1, v2) => {
196
- const halfX = 0.5 * (v1._x + v2._x);
197
- const halfY = 0.5 * (v1._y + v2._y);
198
- const halfZ = 0.5 * (v1._z + v2._z);
199
- const midpoint = newVector(halfX, halfY, halfZ);
200
- return midpoint;
201
- };
202
- export const getRadius = (minimum, maximum) => {
203
- const center = getMidpoint(minimum, maximum);
204
- const delta = axis => Math.pow(Math.abs(axis - center[axis]), 2);
205
- const radius = Math.sqrt(delta('_x') + delta('_y') + delta('_z'));
206
- return radius;
207
- };
208
- export const restoreParents = parentList => {
209
- parentList.forEach(item => {
210
- const {
211
- mesh,
212
- parent
213
- } = item;
214
- mesh.setParent(parent);
215
- });
216
- };
217
- export const getMeshesInCameraView = () => {
218
- const camera = scene.activeCamera;
219
- const userMeshes = getUserMeshes();
220
- const possibleMeshes = userMeshes.map(mesh => {
221
- if (mesh.isPickable && camera.isInFrustum(mesh)) {
222
- return mesh;
223
- }
224
- });
225
- return possibleMeshes.filter(mesh => mesh);
226
- };
227
- export const getStatisticsMetadata = props => {
228
- const userMeshes = getUserMeshes();
229
- const userMaterials = getUserMaterials();
230
- const userTextures = getUserTextures();
231
- const meshesVertices = userMeshes.map(mesh => mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind));
232
- const meshesIndices = userMeshes.map(mesh => mesh.getIndices());
233
- const vertices = meshesVertices.reduce((prev, current) => prev + current.length / 3, 0);
234
- const triangles = meshesIndices.reduce((prev, current) => prev + current.length / 3, 0);
235
- const getMeshStatisticsArray = () => {
236
- const meshStats = userMeshes.map(mesh => {
237
- const meshVertices = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind).length / 3;
238
- const meshIndices = mesh.getIndices().length / 3;
239
- return {
240
- id: mesh.id,
241
- vertices: meshVertices,
242
- triangles: meshIndices
243
- };
244
- });
245
- return meshStats;
246
- };
247
- const value = {
248
- materialCount: userMaterials.length?.toLocaleString(),
249
- textures: userTextures.length?.toLocaleString(),
250
- meshes: {
251
- count: userMeshes.length?.toLocaleString(),
252
- meshes: getMeshStatisticsArray()
253
- },
254
- vertices: vertices.toLocaleString(),
255
- triangles: triangles.toLocaleString()
256
- };
257
- newMetaDataEntry('statistics', value);
258
- props?.setSerializedData?.(serializeScene());
259
- };
260
- export const buildMeshIdArray = hidden => {
261
- const arr = hidden ? hiddenMeshes.map(mesh => mesh.id) : selectedMeshes.map(mesh => ({
262
- id: mesh.id,
263
- materialId: mesh.material?.id || 'Default',
264
- textures: mesh.material?.getActiveTextures().map(texture => texture.name)
265
- }));
266
- return arr;
267
- };
268
- export const buildMaterialVariantsArray = () => {
269
- const rootMesh = scene.getMeshByID('__root__');
270
- const khrExtension = GLTF2.KHR_materials_variants;
271
- const variants = khrExtension.GetAvailableVariants(rootMesh);
272
- return variants;
273
- };
274
- export const buildAnimationsArray = () => {
275
- const animationGroups = scene?.animationGroups || [];
276
- return animationGroups.map((group, index) => {
277
- const fallbackLabel = `Animation ${index + 1}`;
278
- const label = group.name || group.id || fallbackLabel;
279
- return {
280
- id: `${index}`,
281
- index,
282
- label,
283
- name: group.name || ''
284
- };
285
- });
286
- };
287
- export const buildSelectedMaterialArray = () => {
288
- if (props.materialMode) {
289
- const mainMaterial = scene.metadata?.materials?.find?.(item => item.materialId === 'material');
290
- return [mainMaterial];
291
- }
292
- const arr = selectedMeshes?.map?.(mesh => {
293
- const material = mesh.material;
294
- if (material) {
295
- const existingMaterial = scene.metadata.materials?.find(mat => mat.materialId === material.id);
296
- return {
297
- ...(existingMaterial || {}),
298
- ...materialData(material)
299
- };
300
- }
301
- }).filter(val => val);
302
- if (_.isEmpty(arr)) {
303
- if (!_.isEmpty(scene.metadata.selectedMaterials)) {
304
- const currentSelectedMaterial = scene.metadata.selectedMaterials[0];
305
- const materialMatch = scene.metadata.materials?.find(material => currentSelectedMaterial.materialId === material.materialId) || {};
306
- return [materialMatch];
307
- }
308
- return [scene.metadata.materials?.[0] || {}];
309
- }
310
- return arr;
311
- };
312
- const updatedMeshTrackingRotation = id => {
313
- const zAxisUp = scene.metadata.selectedAxisCompensation === 'Z-Axis up';
314
- const xRotation = zAxisUp ? 270 : 0;
315
- const meshSelected = selectedMeshes.length > 0;
316
- const workingMesh = scene.metadata.meshChangeTracking?.find(mesh => mesh.meshId === id) || {};
317
- const workingValues = {
318
- rx: xRotation,
319
- ry: 0,
320
- rz: 0
321
- };
322
- if (meshSelected) {
323
- const tNode = selectedMeshes.length > 1 ? multiMeshTNode : singleMeshTNode;
324
- const {
325
- rotation
326
- } = tNode;
327
- const newRotation = Object.values(rotation).map(rad => toDegrees(rad))?.slice(1);
328
- const childMeshes = tNode.getChildMeshes();
329
- const includesMesh = childMeshes.some(mesh => mesh.id === id);
330
- if (includesMesh && newRotation) {
331
- const [rx, ry, rz] = newRotation;
332
- const rotateX = rx <= 0 ? (360 - rx * -1) % 360 : Math.abs(rx) % 360;
333
- const rotateY = ry <= 0 ? (360 - ry * -1) % 360 : Math.abs(ry) % 360;
334
- const rotateZ = rz <= 0 ? (360 - rz * -1) % 360 : Math.abs(rz) % 360;
335
- return {
336
- rx: rotateX,
337
- ry: rotateY,
338
- rz: rotateZ
339
- };
340
- }
341
- if (!_.isEmpty(workingMesh)) {
342
- const {
343
- rx,
344
- ry,
345
- rz
346
- } = workingMesh;
347
- return {
348
- rx,
349
- ry,
350
- rz
351
- };
352
- }
353
- }
354
- return workingValues;
355
- };
356
- const baseMeshScales = [];
357
- const updateMeshTrackingScale = mesh => {
358
- const baseMesh = baseMeshScales.find(bMesh => bMesh.id === mesh.id);
359
- if (_.isEmpty(baseMesh)) {
360
- const parent = mesh.parent;
361
- mesh.setParent(singleMeshTNode);
362
- const meshScaleObj = {
363
- id: mesh.id,
364
- sx: mesh.scaling.x,
365
- sy: mesh.scaling.y,
366
- sz: mesh.scaling.z
367
- };
368
- baseMeshScales.push(meshScaleObj);
369
- mesh.setParent(parent);
370
- return meshScaleObj;
371
- }
372
- return baseMesh;
373
- };
374
- export const buildMeshPositionsArray = args => {
375
- return getUserMeshes().map(mesh => {
376
- const {
377
- rx,
378
- ry,
379
- rz
380
- } = updatedMeshTrackingRotation(mesh.id);
381
- const {
382
- sx,
383
- sy,
384
- sz
385
- } = updateMeshTrackingScale(mesh);
386
- const boundingInfo = getBoundingMeshData([mesh]);
387
- const {
388
- center,
389
- centerWorld
390
- } = boundingInfo;
391
- const pos = center || centerWorld;
392
- const scaleX = mesh?.scaling.x || 1;
393
- const scaleY = mesh?.scaling.y || 1;
394
- const scaleZ = mesh?.scaling.z || 1;
395
- const meshChangeTracking = scene.metadata?.meshChangeTracking;
396
- const prevMeshTransforms = meshChangeTracking?.find(trackedMesh => trackedMesh.meshId === mesh.id) || {};
397
- let defaultMeshPosition = {
398
- ...prevMeshTransforms,
399
- meshId: mesh?.id || '',
400
- tx: pos.x,
401
- ty: pos.y,
402
- tz: pos.z
403
- };
404
- if (!args?.preserveScale) {
405
- defaultMeshPosition = {
406
- ...defaultMeshPosition,
407
- sx: scaleX === 1 ? 1 : scaleX / sx,
408
- sy: scaleY === 1 ? 1 : scaleY / sy,
409
- sz: scaleZ === 1 ? 1 : scaleZ / sz
410
- };
411
- }
412
- if (!args?.preserveRotation) {
413
- defaultMeshPosition = {
414
- ...defaultMeshPosition,
415
- rx,
416
- ry,
417
- rz
418
- };
419
- }
420
- return defaultMeshPosition;
421
- });
422
- };
423
- export const getImageFileFromBuffer = args => {
424
- const {
425
- buffer,
426
- asDataUrl
427
- } = args || {};
428
- if (_.isEmpty(buffer)) return '';
429
- if (!_.isObject(buffer) && !_.isArray(buffer)) return buffer;
430
- if (asDataUrl) {
431
- // Convert buffer to data:image/png;base64 string
432
- const Uint8ToString = u8a => {
433
- if (_.isEmpty(u8a)) return '';
434
- const maxChunkSize = 8192;
435
- const chunks = [];
436
- for (let i = 0; i < u8a.length; i += maxChunkSize) {
437
- const newChunk = String.fromCharCode.apply(null, u8a.subarray(i, i + maxChunkSize));
438
- chunks.push(newChunk);
439
- }
440
- return chunks.join('');
441
- };
442
- const ascii = new Uint8Array(buffer);
443
- const dataString = btoa(Uint8ToString(ascii));
444
- const dataUrl = `data:image/png;base64,${dataString}`;
445
- return dataUrl;
446
- }
447
- const blob = new Blob([buffer], {
448
- type: 'image/png'
449
- });
450
- const blobUrl = URL.createObjectURL(blob);
451
- return blobUrl;
452
- };
453
- export const getTextureUrl = args => {
454
- const {
455
- texture,
456
- asDataUrl
457
- } = args || {};
458
- const buffer = texture?._buffer;
459
- if (_.isEmpty(buffer)) return texture?.url || '';
460
- return getImageFileFromBuffer({
461
- buffer,
462
- asDataUrl
463
- });
464
- };
465
- export const getExistingUVSettings = args => {
466
- const {
467
- materialId
468
- } = args || {};
469
- let settingsFound = false;
470
- let uvSettings = {
471
- uvXScale: 1,
472
- uvYScale: 1,
473
- uvXRotation: 0,
474
- uvYRotation: 0,
475
- uvZRotation: 0,
476
- uvXOffset: 0,
477
- uvYOffset: 0
478
- };
479
- const userMaterials = getUserMaterials();
480
- const material = userMaterials.find(mat => mat.id === materialId);
481
- if (material) {
482
- Object.keys(material)?.find?.(key => {
483
- const validMaterialWithTexture = _.isObject(material[key]) && !_.isArray(material[key]) && key.endsWith('Texture');
484
- const texture = material[key];
485
- const isEnvironment = key.toLowerCase().includes('environment');
486
- const hasAllKeys = texture && ['uAng', 'wAng', 'vAng', 'uOffset', 'vOffset', 'uScale', 'vScale'].every(key => texture[key] !== undefined);
487
-
488
- // NOTE: All UV's are adjusted globally, at the same time in canvasUpdateHelpers.js
489
- // when **any** UV setting is changed. So we only need to find the first one to
490
- // restore the UV settings.
491
- if (!isEnvironment && validMaterialWithTexture && hasAllKeys && !settingsFound) {
492
- settingsFound = true;
493
- uvSettings = {
494
- uvXScale: texture.uScale,
495
- uvYScale: texture.vScale,
496
- uvXRotation: texture.uAng,
497
- uvYRotation: texture.wAng,
498
- uvZRotation: texture.vAng,
499
- uvXOffset: texture.uOffset,
500
- uvYOffset: texture.vOffset
501
- };
502
- }
503
- });
504
- }
505
- return uvSettings;
506
- };
507
- export const materialData = material => {
508
- const meshesWithMaterial = getUserMeshes().map(mesh => {
509
- if (mesh.material.name === material.id) return mesh;
510
- }).filter(mesh => mesh);
511
- const allMeshes = scene.metadata?.statistics.meshes;
512
- const totalTriangles = allMeshes.meshes.reduce((prev, current) => {
513
- const includedMesh = meshesWithMaterial.some(mesh => mesh.id === current.id);
514
- if (includedMesh) return prev + current.triangles;
515
- return prev;
516
- }, 0);
517
- return {
518
- materialId: material.id,
519
- name: material.name,
520
- // General
521
- backfaceCullingEnabled: material.backFaceCulling || false,
522
- wireFrameEnabled: material.wireframe || false,
523
- pointsCloudEnabled: material.pointsCloud || false,
524
- // Basic
525
- albedoColor: material.albedoColor?.toHexString() || props?.theme?.colors?.[props.$selectedTheme]?.black,
526
- albedoTexture: {
527
- name: material.albedoTexture?.name,
528
- url: getTextureUrl({
529
- texture: material.albedoTexture,
530
- asDataUrl: true
531
- })
532
- },
533
- albedoHasAlpha: material.albedoTexture?.hasAlpha || false,
534
- environmentIntensity: material.environmentIntensity,
535
- metallic: material.metallic,
536
- metallicTexture: {
537
- name: material.metallicTexture?.name,
538
- url: getTextureUrl({
539
- texture: material.metallicTexture,
540
- asDataUrl: true
541
- })
542
- },
543
- roughness: material.roughness,
544
- microSurfaceTexture: {
545
- name: material.microSurfaceTexture?.name,
546
- url: getTextureUrl({
547
- texture: material.microSurfaceTexture,
548
- asDataUrl: true
549
- })
550
- },
551
- emissiveColor: material.emissiveColor?.toHexString() || props?.theme?.colors?.[props.$selectedTheme]?.black,
552
- emissiveIntensity: material.emissiveIntensity,
553
- emissiveTexture: {
554
- name: material.emissiveTexture?.name,
555
- url: getTextureUrl({
556
- texture: material.emissiveTexture,
557
- asDataUrl: true
558
- })
559
- },
560
- ambientTextureStength: material.ambientTextureStrength,
561
- ambientTexture: {
562
- name: material.ambientTexture?.name,
563
- url: getTextureUrl({
564
- texture: material.ambientTexture,
565
- asDataUrl: true
566
- })
567
- },
568
- ambientColor: material.ambientColor?.toHexString() || props?.theme?.colors?.[props.$selectedTheme]?.black,
569
- bumpIntensity: material.bumpTexture?.level ?? 1,
570
- bumpTexture: {
571
- name: material.bumpTexture?.name,
572
- url: getTextureUrl({
573
- texture: material.bumpTexture,
574
- asDataUrl: true
575
- })
576
- },
577
- // UV
578
- uvSettings: {
579
- ...getExistingUVSettings({
580
- materialId: material.id
581
- })
582
- },
583
- // Advanced
584
- clearCoatEnabled: material.clearCoat?.isEnabled || false,
585
- clearCoatIntensity: material.clearCoat?.intensity,
586
- clearCoatRoughness: material.clearCoat?.roughness,
587
- clearCoatIOR: material.clearCoat?.indexOfRefraction,
588
- alpha: material.alpha,
589
- opacityTexture: {
590
- name: material.opacityTexture?.name,
591
- url: getTextureUrl({
592
- texture: material.opacityTexture,
593
- asDataUrl: true
594
- })
595
- },
596
- transparencyMode: material.transparencyMode,
597
- sssRefractionEnabled: material.subSurface?.isRefractionEnabled || false,
598
- sssRefractionIntensity: material.subSurface?.refractionIntensity,
599
- sssIOR: material.subSurface?.indexOfRefraction,
600
- transparencyEnabled: material?.transparencyEnabled || false,
601
- transparencyType: material?.albedoTexture?.hasAlpha ? TRANSPARENCY_MODES.stencil : material?.transparencyType || TRANSPARENCY_MODES.simple,
602
- meshSimplification: {
603
- enabled: material.meshSimplification?.enabled || false,
604
- type: material.meshSimplification?.type || 'custom',
605
- // [ 'low, 'medium', 'high', 'custom' ]
606
- optimizationType: material.meshSimplification?.optimizationType || 'estimated triangles',
607
- totalTriangles,
608
- estimatedTriangles: material.meshSimplification?.estimatedTriangles || 99,
609
- // Percent
610
- userTargetTriangles: material.meshSimplification?.userTargetTriangles || totalTriangles || 0,
611
- targetTriangles: Math.round((totalTriangles || +scene.metadata.statistics?.triangles?.replace?.(/,/g, '')) * ((material.meshSimplification?.estimatedTriangles || 99) / 100)) || 0
612
- },
613
- textureDownscaling: {
614
- enabled: material.textureDownscaling?.enabled || false,
615
- size: material.textureDownscaling?.size || downscaling.find(size => size.name === '4K').value
616
- }
617
- };
618
- };
619
- export const buildMaterialsArray = () => {
620
- const materials = getUserMaterials();
621
- const materialsArray = materials.map(material => materialData(material));
622
- return materialsArray;
623
- };
624
- export const buildLightsArray = () => {
625
- const lights = scene.lights;
626
- return lights.map(light => {
627
- const {
628
- position
629
- } = light;
630
- const {
631
- x,
632
- y,
633
- z
634
- } = position;
635
- return {
636
- ...light.serialize(),
637
- rotation: light?.rotation ?? 0,
638
- distance: light?.distance ?? Math.sqrt(Math.pow(Math.abs(x), 2) + Math.pow(Math.abs(y), 2) + Math.pow(Math.abs(z), 2))
639
- };
640
- });
641
- };
642
- const billboardScreenshot = view => {
643
- const camera = scene.activeCamera.clone(view);
644
- if (view !== 'perspective') {
645
- const alpha = orthoOptions[view].alpha;
646
- const beta = orthoOptions[view].beta;
647
- camera.alpha = alpha;
648
- camera.beta = beta;
649
- camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
650
- }
651
- return BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, camera, 2048);
652
- };
653
- export const takePreviewScreenshots = () => {
654
- const baseView = ['perspective'];
655
- const outerFrame = guiTexture.getControlByName(GUI.outerSafeFrame);
656
- const xAxisMesh = scene.getMeshByName('xAxisMesh');
657
- const yAxisMesh = scene.getMeshByName('yAxisMesh');
658
- const zAxisMesh = scene.getMeshByName('zAxisMesh');
659
- const hdrSkyBox = scene.getMeshByName('hdrSkyBox');
660
- const axesVisible = xAxisMesh?.isVisible;
661
- const skyBoxVisible = hdrSkyBox?.isVisible;
662
- const frameVisible = outerFrame?.isVisible;
663
- const groundVisible = ground?.isVisible;
664
- const mirrorGroundVisible = mirrorGround?.isVisible;
665
- const {
666
- positionGizmoEnabled,
667
- rotationGizmoEnabled,
668
- boundingBoxGizmoEnabled,
669
- scaleGizmoEnabled
670
- } = gizmoManager;
671
- const toggleUI = restore => {
672
- const setVisibility = (object, isVisible) => {
673
- if (object !== null && object !== undefined) {
674
- object.isVisible = isVisible;
675
- }
676
- };
677
- setVisibility(mirrorGround, restore ? mirrorGroundVisible : false);
678
- setVisibility(ground, restore ? groundVisible : false);
679
- setVisibility(xAxisMesh, restore ? axesVisible : false);
680
- setVisibility(yAxisMesh, restore ? axesVisible : false);
681
- setVisibility(zAxisMesh, restore ? axesVisible : false);
682
- setVisibility(hdrSkyBox, restore ? skyBoxVisible : false);
683
- setVisibility(outerFrame, restore ? frameVisible : false);
684
- gizmoManager.positionGizmoEnabled = restore ? positionGizmoEnabled : false;
685
- gizmoManager.rotationGizmoEnabled = restore ? rotationGizmoEnabled : false;
686
- gizmoManager.boundingBoxGizmoEnabled = restore ? boundingBoxGizmoEnabled : false;
687
- gizmoManager.scaleGizmoEnabled = restore ? scaleGizmoEnabled : false;
688
- };
689
- toggleUI();
690
- try {
691
- const screenshots = baseView.map(async view => {
692
- const imageData = await billboardScreenshot(view);
693
- return {
694
- name: view,
695
- imageData
696
- };
697
- });
698
- Promise.all(screenshots).then(screenshots => {
699
- const hasAllScreenshots = screenshots.length === baseView.length;
700
- if (hasAllScreenshots) props.setBillboardImages?.(screenshots);
701
- screenshots.forEach(screenshot => {
702
- scene.getCameraByName(screenshot.name)?.dispose();
703
- });
704
- });
705
- } catch (e) {
706
- console.error('Unable to take screenshots.', e);
707
- } finally {
708
- toggleUI(true);
709
- }
710
- };