@combeenation/3d-viewer 17.1.0 → 18.0.0-beta2
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/dist/lib-cjs/buildinfo.json +1 -1
- package/dist/lib-cjs/commonjs.tsconfig.tsbuildinfo +1 -1
- package/dist/lib-cjs/index.d.ts +8 -0
- package/dist/lib-cjs/index.js +8 -0
- package/dist/lib-cjs/index.js.map +1 -1
- package/dist/lib-cjs/internal/asset-helper.d.ts +32 -0
- package/dist/lib-cjs/internal/asset-helper.js +105 -0
- package/dist/lib-cjs/internal/asset-helper.js.map +1 -0
- package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.d.ts +18 -0
- package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.js +22 -3
- package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.js.map +1 -1
- package/dist/lib-cjs/internal/cloning-helper.js +1 -1
- package/dist/lib-cjs/internal/cloning-helper.js.map +1 -1
- package/dist/lib-cjs/internal/texture-parameter-helper.js +26 -7
- package/dist/lib-cjs/internal/texture-parameter-helper.js.map +1 -1
- package/dist/lib-cjs/manager/camera-manager.d.ts +23 -2
- package/dist/lib-cjs/manager/camera-manager.js +91 -24
- package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
- package/dist/lib-cjs/manager/debug-manager.d.ts +1 -1
- package/dist/lib-cjs/manager/debug-manager.js +3 -3
- package/dist/lib-cjs/manager/debug-manager.js.map +1 -1
- package/dist/lib-cjs/manager/dimension-line-manager.d.ts +126 -0
- package/dist/lib-cjs/manager/dimension-line-manager.js +138 -0
- package/dist/lib-cjs/manager/dimension-line-manager.js.map +1 -0
- package/dist/lib-cjs/manager/html-anchor-manager.d.ts +93 -0
- package/dist/lib-cjs/manager/html-anchor-manager.js +228 -0
- package/dist/lib-cjs/manager/html-anchor-manager.js.map +1 -0
- package/dist/lib-cjs/manager/model-manager.d.ts +11 -34
- package/dist/lib-cjs/manager/model-manager.js +47 -107
- package/dist/lib-cjs/manager/model-manager.js.map +1 -1
- package/dist/lib-cjs/manager/parameter-manager.d.ts +17 -12
- package/dist/lib-cjs/manager/parameter-manager.js +78 -69
- package/dist/lib-cjs/manager/parameter-manager.js.map +1 -1
- package/dist/lib-cjs/manager/scene-manager.d.ts +111 -5
- package/dist/lib-cjs/manager/scene-manager.js +276 -10
- package/dist/lib-cjs/manager/scene-manager.js.map +1 -1
- package/dist/lib-cjs/viewer-error.d.ts +1 -0
- package/dist/lib-cjs/viewer-error.js +1 -0
- package/dist/lib-cjs/viewer-error.js.map +1 -1
- package/dist/lib-cjs/viewer.d.ts +9 -14
- package/dist/lib-cjs/viewer.js +16 -38
- package/dist/lib-cjs/viewer.js.map +1 -1
- package/package.json +22 -12
- package/src/index.ts +8 -0
- package/src/internal/asset-helper.ts +115 -0
- package/src/internal/cbn-custom-babylon-loader-plugin.ts +30 -3
- package/src/internal/cloning-helper.ts +1 -1
- package/src/internal/texture-parameter-helper.ts +25 -8
- package/src/manager/camera-manager.ts +153 -39
- package/src/manager/debug-manager.ts +3 -3
- package/src/manager/dimension-line-manager.ts +255 -0
- package/src/manager/html-anchor-manager.ts +332 -0
- package/src/manager/model-manager.ts +55 -138
- package/src/manager/parameter-manager.ts +94 -75
- package/src/manager/scene-manager.ts +375 -10
- package/src/viewer-error.ts +1 -0
- package/src/viewer.ts +30 -56
- package/src/dev.ts +0 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@combeenation/3d-viewer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.0.0-beta2",
|
|
4
4
|
"description": "Combeenation 3D Viewer",
|
|
5
5
|
"homepage": "https://github.com/Combeenation/3d-viewer#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -41,15 +41,20 @@
|
|
|
41
41
|
"pub-beta": "npm run dist-cjs && npm publish --tag beta",
|
|
42
42
|
"pub-final": "npm run dist-cjs && npm publish",
|
|
43
43
|
"pub-rc": "npm run dist-cjs && npm publish --tag rc",
|
|
44
|
-
"replace-version": "cross-env-shell replace \"@VERSION@\" $npm_package_version dist -r"
|
|
44
|
+
"replace-version": "cross-env-shell replace \"@VERSION@\" $npm_package_version dist -r",
|
|
45
|
+
"sleep5s": "node -e \"setTimeout(() => process.exit(0), 5000)\"",
|
|
46
|
+
"start-asset-server": "cross-env NODE_ENV='production' webpack serve --config build/webpack.test.conf.js",
|
|
47
|
+
"test": "vitest",
|
|
48
|
+
"test-delayed": "npm run sleep5s && npm run test"
|
|
45
49
|
},
|
|
46
50
|
"prettier": "@combeenation/prettier-config",
|
|
47
51
|
"dependencies": {
|
|
48
|
-
"@babylonjs/core": "7.
|
|
49
|
-
"@babylonjs/gui": "7.
|
|
50
|
-
"@babylonjs/loaders": "7.
|
|
51
|
-
"@babylonjs/serializers": "7.
|
|
52
|
+
"@babylonjs/core": "7.47.3",
|
|
53
|
+
"@babylonjs/gui": "7.47.3",
|
|
54
|
+
"@babylonjs/loaders": "7.47.3",
|
|
55
|
+
"@babylonjs/serializers": "7.47.3",
|
|
52
56
|
"eventemitter3": "4.0.7",
|
|
57
|
+
"html2canvas": "1.4.1",
|
|
53
58
|
"is-svg": "^5.0.0",
|
|
54
59
|
"lodash-es": "4.17.21"
|
|
55
60
|
},
|
|
@@ -63,6 +68,7 @@
|
|
|
63
68
|
"cross-env": "7.0.3",
|
|
64
69
|
"eslint": "8.25.0",
|
|
65
70
|
"html-webpack-plugin": "5.5.0",
|
|
71
|
+
"jsdom": "^26.0.0",
|
|
66
72
|
"prettier": "2.7.1",
|
|
67
73
|
"replace": "1.2.1",
|
|
68
74
|
"rimraf": "3.0.2",
|
|
@@ -71,6 +77,7 @@
|
|
|
71
77
|
"typedoc": "0.23.15",
|
|
72
78
|
"typedoc-plugin-merge-modules": "4.0.1",
|
|
73
79
|
"typescript": "4.8.4",
|
|
80
|
+
"vitest": "2.1.8",
|
|
74
81
|
"webpack": "5.74.0",
|
|
75
82
|
"webpack-bundle-analyzer": "4.6.1",
|
|
76
83
|
"webpack-cli": "4.10.0",
|
|
@@ -78,16 +85,19 @@
|
|
|
78
85
|
"webpack-merge": "5.8.0"
|
|
79
86
|
},
|
|
80
87
|
"optionalDependencies": {
|
|
81
|
-
"@babylonjs/gui-editor": "7.
|
|
82
|
-
"@babylonjs/inspector": "7.
|
|
83
|
-
"@babylonjs/materials": "7.
|
|
84
|
-
"@babylonjs/node-editor": "7.
|
|
85
|
-
"@babylonjs/node-geometry-editor": "7.
|
|
88
|
+
"@babylonjs/gui-editor": "7.47.3",
|
|
89
|
+
"@babylonjs/inspector": "7.47.3",
|
|
90
|
+
"@babylonjs/materials": "7.47.3",
|
|
91
|
+
"@babylonjs/node-editor": "7.47.3",
|
|
92
|
+
"@babylonjs/node-geometry-editor": "7.47.3"
|
|
86
93
|
},
|
|
87
94
|
"dependenciesComments": {
|
|
88
|
-
"@babylonjs": "Latest version 7.
|
|
95
|
+
"@babylonjs": "Latest version 7.47.3 added lower camera target y limit, as requested from us (https://github.com/BabylonJS/Babylon.js/pull/16125)"
|
|
89
96
|
},
|
|
90
97
|
"optionalDependenciesComments": {
|
|
91
98
|
"@babylonjs": "Defining the inspector package (and it's dependencies) as optional is required to omit these packages in the production build of the CBN react-app (see CB-9269)"
|
|
99
|
+
},
|
|
100
|
+
"scriptsComments": {
|
|
101
|
+
"test-delayed": "Didn't manage to wait 5 seconds for starting up the asset server as an individual VS Code task, therefore it's solved as npm script"
|
|
92
102
|
}
|
|
93
103
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,7 +19,10 @@ export * from '@babylonjs/core/Cameras/camera';
|
|
|
19
19
|
export * from '@babylonjs/core/Culling';
|
|
20
20
|
export * from '@babylonjs/core/Debug/axesViewer';
|
|
21
21
|
export * from '@babylonjs/core/Debug/debugLayer';
|
|
22
|
+
export * from '@babylonjs/core/Engines/abstractEngine';
|
|
22
23
|
export * from '@babylonjs/core/Engines/engine';
|
|
24
|
+
export * from '@babylonjs/core/Engines/Extensions/engine.query';
|
|
25
|
+
export * from '@babylonjs/core/Engines/nullEngine';
|
|
23
26
|
export * from '@babylonjs/core/Engines/thinEngine';
|
|
24
27
|
export * from '@babylonjs/core/Lights';
|
|
25
28
|
export * from '@babylonjs/core/Loading';
|
|
@@ -31,14 +34,17 @@ export * from '@babylonjs/core/Maths';
|
|
|
31
34
|
export * from '@babylonjs/core/Meshes/abstractMesh';
|
|
32
35
|
export * from '@babylonjs/core/Meshes/geometry';
|
|
33
36
|
export * from '@babylonjs/core/Meshes/instancedMesh';
|
|
37
|
+
export * from '@babylonjs/core/Meshes/linesMesh';
|
|
34
38
|
export * from '@babylonjs/core/Meshes/mesh';
|
|
35
39
|
export * from '@babylonjs/core/Meshes/meshBuilder';
|
|
36
40
|
export * from '@babylonjs/core/Meshes/transformNode';
|
|
41
|
+
export * from '@babylonjs/core/Misc/observable';
|
|
37
42
|
export * from '@babylonjs/core/Misc/interfaces/screenshotSize';
|
|
38
43
|
export * from '@babylonjs/core/Misc/screenshotTools';
|
|
39
44
|
export * from '@babylonjs/core/Misc/tags';
|
|
40
45
|
export * from '@babylonjs/core/Morph';
|
|
41
46
|
export * from '@babylonjs/core/node';
|
|
47
|
+
export * from '@babylonjs/core/Rendering/boundingBoxRenderer';
|
|
42
48
|
export * from '@babylonjs/core/Rendering/utilityLayerRenderer';
|
|
43
49
|
export * from '@babylonjs/core/scene';
|
|
44
50
|
export * from '@babylonjs/core/types';
|
|
@@ -64,9 +70,11 @@ export * from '@babylonjs/core/Materials/Node';
|
|
|
64
70
|
export * from './viewer';
|
|
65
71
|
export * from './viewer-error';
|
|
66
72
|
export * from './manager/camera-manager';
|
|
73
|
+
export * from './manager/dimension-line-manager';
|
|
67
74
|
export * from './manager/debug-manager';
|
|
68
75
|
export * from './manager/event-manager';
|
|
69
76
|
export * from './manager/gltf-export-manager';
|
|
77
|
+
export * from './manager/html-anchor-manager';
|
|
70
78
|
export * from './manager/material-manager';
|
|
71
79
|
export * from './manager/model-manager';
|
|
72
80
|
export * from './manager/parameter-manager';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { AssetContainer, SceneAssetSettings, SceneLoader, Viewer, ViewerError, ViewerErrorIds } from '..';
|
|
2
|
+
import { CbnBabylonFileData, ExtendedAssetContainer } from './cbn-custom-babylon-loader-plugin';
|
|
3
|
+
import { getInternalMetadataValue } from './metadata-helper';
|
|
4
|
+
|
|
5
|
+
export type AssetState = 'notLoaded' | 'loading' | 'loaded' | 'inScene';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Common base asset for model and scene asset
|
|
9
|
+
*/
|
|
10
|
+
export type BaseAsset = {
|
|
11
|
+
name: string;
|
|
12
|
+
url: string;
|
|
13
|
+
state: AssetState;
|
|
14
|
+
assetContainer: AssetContainer;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Help function for loading asset as asset container, with some special tweaks and fixes that we need in our CBN
|
|
19
|
+
* environment:
|
|
20
|
+
* - reconstruct current environment texture and intensity, as it get overwritten by Babylon.js basic loading function
|
|
21
|
+
* - explicitely define plugin extension for gzipped babylon files
|
|
22
|
+
* - extract and return cbn custom data from babylon file
|
|
23
|
+
* - crop materials from asset containers and add them to scene directly, as we treat materials as a global entity,
|
|
24
|
+
* which can be used from all models alike
|
|
25
|
+
*
|
|
26
|
+
* This function is used by model and scene asset alike.
|
|
27
|
+
*/
|
|
28
|
+
export async function loadAsset(
|
|
29
|
+
asset: BaseAsset,
|
|
30
|
+
viewer: Viewer
|
|
31
|
+
): Promise<{ cbnData?: CbnBabylonFileData; sceneSettingsData?: SceneAssetSettings }> {
|
|
32
|
+
asset.state = 'loading';
|
|
33
|
+
const curEnvTexture = viewer.scene.environmentTexture;
|
|
34
|
+
const curEnvIntensity = viewer.scene.environmentIntensity;
|
|
35
|
+
|
|
36
|
+
// CB-9240: Babylon.js doesn't recognize gzipped babylon files (`.babylon.gz`) as such, leading to a warning
|
|
37
|
+
// message, therefore we overwrite the plugin extension actively for such files
|
|
38
|
+
let pluginExtension;
|
|
39
|
+
try {
|
|
40
|
+
// URL constructor can throw for "valid" URL, which happened to be the case for the test asset environment where
|
|
41
|
+
// the urls are relative (starting with ".")
|
|
42
|
+
const urlObj = new URL(asset.url);
|
|
43
|
+
if (urlObj.pathname.endsWith('.babylon.gz')) {
|
|
44
|
+
pluginExtension = '.babylon';
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {}
|
|
47
|
+
|
|
48
|
+
let extendedAssetContainer;
|
|
49
|
+
try {
|
|
50
|
+
extendedAssetContainer = (await SceneLoader.LoadAssetContainerAsync(
|
|
51
|
+
'',
|
|
52
|
+
asset.url,
|
|
53
|
+
viewer.scene,
|
|
54
|
+
undefined,
|
|
55
|
+
pluginExtension
|
|
56
|
+
)) as ExtendedAssetContainer;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
throw new ViewerError({
|
|
59
|
+
id: ViewerErrorIds.AssetLoadingFailed,
|
|
60
|
+
message: (e as Error).message,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { cbnData, sceneSettingsData } = extendedAssetContainer;
|
|
65
|
+
delete extendedAssetContainer.cbnData;
|
|
66
|
+
delete extendedAssetContainer.sceneSettingsData;
|
|
67
|
+
|
|
68
|
+
// from here it's a basic asset container again
|
|
69
|
+
const assetContainer = extendedAssetContainer as AssetContainer;
|
|
70
|
+
|
|
71
|
+
// materials should be a "global" thing and not assigned to an asset container
|
|
72
|
+
// this is not relevant for model assets in most of the cases, since materials are cropped from babylon models on the
|
|
73
|
+
// CBN server anyway
|
|
74
|
+
assetContainer.materials.forEach(material => {
|
|
75
|
+
viewer.scene.addMaterial(material);
|
|
76
|
+
material._parentContainer = null;
|
|
77
|
+
viewer.parameterManager.applyParameterValuesToMaterial(material);
|
|
78
|
+
});
|
|
79
|
+
assetContainer.materials = [];
|
|
80
|
+
|
|
81
|
+
// environment texture and intensity has been overwritten by load asset container function
|
|
82
|
+
viewer.scene.environmentTexture = curEnvTexture;
|
|
83
|
+
viewer.scene.environmentIntensity = curEnvIntensity;
|
|
84
|
+
|
|
85
|
+
asset.assetContainer = assetContainer;
|
|
86
|
+
asset.state = 'loaded';
|
|
87
|
+
|
|
88
|
+
return { cbnData, sceneSettingsData };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Help function for applying parameter values and "deferred" material to asset (model or scene asset), before using it
|
|
93
|
+
* in the scene
|
|
94
|
+
*/
|
|
95
|
+
export async function prepareAssetForScene(asset: BaseAsset, viewer: Viewer): Promise<void> {
|
|
96
|
+
await viewer.parameterManager.applyAllParameterValuesToModel(asset.assetContainer);
|
|
97
|
+
// parameter manager did his job, now apply the deferred materials to all meshes that are not explicitely hidden by
|
|
98
|
+
// the parameter manager
|
|
99
|
+
await _applyDeferredMaterialsForAllVisibleMeshes(asset, viewer);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates and assigns each "deferred" material to the corresponding mesh, if the mesh is visible.
|
|
104
|
+
* Model should be ready to use immediately after this function has done it's job.
|
|
105
|
+
*/
|
|
106
|
+
async function _applyDeferredMaterialsForAllVisibleMeshes(asset: BaseAsset, viewer: Viewer): Promise<void> {
|
|
107
|
+
const setMaterialProms = asset.assetContainer.meshes.map(async mesh => {
|
|
108
|
+
const deferredMaterial = getInternalMetadataValue(mesh, 'deferredMaterial');
|
|
109
|
+
const visible = viewer.parameterManager.getNestedVisibilityParameterValueOfNode(mesh);
|
|
110
|
+
if (deferredMaterial && visible) {
|
|
111
|
+
await viewer.materialManager.setMaterialOnMesh(deferredMaterial as string, mesh);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
await Promise.all(setMaterialProms);
|
|
115
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AssetContainer,
|
|
3
|
-
|
|
3
|
+
Color4,
|
|
4
4
|
ISceneLoaderPlugin,
|
|
5
5
|
InstancedMesh,
|
|
6
6
|
ParsedDecalConfiguration,
|
|
7
|
+
SceneAssetSettings,
|
|
7
8
|
SceneLoader,
|
|
8
9
|
ViewerError,
|
|
9
10
|
ViewerErrorIds,
|
|
@@ -13,6 +14,23 @@ import { setInternalMetadataValue } from './metadata-helper';
|
|
|
13
14
|
import { deleteAllTags, getTagsAsStrArr, setTagsAsString } from './tags-helper';
|
|
14
15
|
import { isArray, isString } from 'lodash-es';
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Contains cbn custom data, like decals or scene settings.
|
|
19
|
+
* This is just a temporary type, as the `loadAssetContainer` function only returns an asset container, which can be
|
|
20
|
+
* altered by our file loader plugin.
|
|
21
|
+
* After loading the model, `cbnData` is cropped and a pure asset container is available for further processing.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export class ExtendedAssetContainer extends AssetContainer {
|
|
25
|
+
cbnData?: CbnBabylonFileData;
|
|
26
|
+
sceneSettingsData?: SceneAssetSettings;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
export type CbnBabylonFileData = { decals?: ParsedDecalConfiguration[] };
|
|
33
|
+
|
|
16
34
|
type DataWithMeshes = { meshes: unknown[] };
|
|
17
35
|
type DataWithDecalConfigurations = { cbnData: { decals: unknown[] } };
|
|
18
36
|
|
|
@@ -59,7 +77,11 @@ export function registerCustomCbnBabylonLoaderPlugin(): void {
|
|
|
59
77
|
load: previousLoaderPlugin.load,
|
|
60
78
|
loadAssetContainer: (scene, data, rootUrl, onError): ExtendedAssetContainer => {
|
|
61
79
|
const dataParsed = JSON.parse(data as string);
|
|
62
|
-
|
|
80
|
+
// root url parameter is left empty for the following reasons:
|
|
81
|
+
// - cube texture creation (for environment) breaks, as Babylon.js tries to prefix absolute paths (e.g. from
|
|
82
|
+
// Babylon.js server) with the root url
|
|
83
|
+
// - we always have absolute paths anyway => links to 3d model, material & textures assets
|
|
84
|
+
const importedContainer = previousLoaderPlugin.loadAssetContainer(scene, data, '');
|
|
63
85
|
|
|
64
86
|
_addMissingMaterialMetadata(dataParsed, importedContainer);
|
|
65
87
|
_reconstructTagsForInstancedMeshes(dataParsed, importedContainer);
|
|
@@ -73,9 +95,14 @@ export function registerCustomCbnBabylonLoaderPlugin(): void {
|
|
|
73
95
|
onError?.((e as Error).message);
|
|
74
96
|
}
|
|
75
97
|
|
|
76
|
-
// add `cbnData` to output asset container, so that this information can be
|
|
98
|
+
// add `cbnData` to output asset container, so that this information can be stored as metadata for the model
|
|
99
|
+
// same with `sceneSettingsData`, which is used in the scene asset
|
|
77
100
|
const extendedContainer = importedContainer as ExtendedAssetContainer;
|
|
78
101
|
extendedContainer.cbnData = dataParsed.cbnData;
|
|
102
|
+
extendedContainer.sceneSettingsData = {
|
|
103
|
+
environmentIntensity: dataParsed.environmentIntensity,
|
|
104
|
+
clearColor: dataParsed.clearColor && Color4.FromArray(dataParsed.clearColor),
|
|
105
|
+
};
|
|
79
106
|
|
|
80
107
|
return extendedContainer;
|
|
81
108
|
},
|
|
@@ -186,7 +186,7 @@ function _cloneNode(
|
|
|
186
186
|
// making deep copies of the node metadata might be an optional flag in the cloning functions in the future
|
|
187
187
|
|
|
188
188
|
// ATM the assignment from clone to source is needed for reassigning instanced meshes after cloning
|
|
189
|
-
// may be
|
|
189
|
+
// may be useful for other node types in the future as well
|
|
190
190
|
setInternalMetadataValue(clone, 'cloneSource', node.uniqueId);
|
|
191
191
|
setInternalMetadataValue(node, 'cloneTarget', clone.uniqueId);
|
|
192
192
|
|
|
@@ -73,17 +73,21 @@ export function createBuiltInTextureParameter(parameterManager: ParameterManager
|
|
|
73
73
|
const texture = _getTextureFromParameterChannel(pbrMaterial, channel);
|
|
74
74
|
|
|
75
75
|
if (texture) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
if (url) {
|
|
77
|
+
// create a clone of the texture, in this way we can load the image before assigning it to the material
|
|
78
|
+
// channel
|
|
79
|
+
const clonedTexture = texture.clone();
|
|
80
|
+
// update texture and await loading time right away
|
|
81
|
+
await new Promise<void>(resolve => clonedTexture.updateURL(url, undefined, resolve));
|
|
82
|
+
|
|
83
|
+
_assignTextureParameterChannel(clonedTexture, pbrMaterial, channel);
|
|
84
|
+
} else {
|
|
85
|
+
_removeFromTextureParameterChannel(pbrMaterial, channel);
|
|
86
|
+
}
|
|
83
87
|
|
|
84
88
|
// dispose old texture
|
|
85
89
|
texture.dispose();
|
|
86
|
-
} else {
|
|
90
|
+
} else if (url) {
|
|
87
91
|
// no texture, or wrong type => create texture from scratch
|
|
88
92
|
// first we check if some settings were provided in the material definition
|
|
89
93
|
const addMatSettings = await window.Cbn?.Assets.getMaterial(pbrMaterial.id);
|
|
@@ -341,6 +345,19 @@ function _assignTextureParameterChannel(
|
|
|
341
345
|
}
|
|
342
346
|
}
|
|
343
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Remove runtime texture from dedicated channel in the PBR material
|
|
350
|
+
*/
|
|
351
|
+
function _removeFromTextureParameterChannel(pbrMaterial: PBRMaterial, channel: ParameterTextureChannelsKeys): void {
|
|
352
|
+
if (channel === 'metallicRoughnessTexture') {
|
|
353
|
+
pbrMaterial.metallicTexture = null;
|
|
354
|
+
} else if (channel === 'detailmapTexture') {
|
|
355
|
+
pbrMaterial.detailMap.texture = null;
|
|
356
|
+
} else {
|
|
357
|
+
pbrMaterial[channel] = null;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
344
361
|
/**
|
|
345
362
|
* Similar to `_getTextureFromParameterChannel`, whereas the input is a plain JSON object instead of a runtime material
|
|
346
363
|
*/
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
ArcRotateCamera,
|
|
5
5
|
BoundingSphere,
|
|
6
6
|
ExcludedGeometryList,
|
|
7
|
-
IScreenshotSize,
|
|
8
7
|
ScreenshotTools,
|
|
9
8
|
Vector3,
|
|
10
9
|
Viewer,
|
|
@@ -64,6 +63,11 @@ export type ScreenshotSettings = {
|
|
|
64
63
|
autofocusScene?: boolean;
|
|
65
64
|
/** Optional list of geometry to be excluded from consideration */
|
|
66
65
|
exclude?: ExcludedGeometryList;
|
|
66
|
+
/**
|
|
67
|
+
* Optional list of html anchor groups (see {@link HtmlAnchorManager}), that should be excluded from the screenshot.\
|
|
68
|
+
* Excludes ALL html anchor groups if set to `true`.
|
|
69
|
+
*/
|
|
70
|
+
excludeHtmlAnchorGroups?: string[] | true;
|
|
67
71
|
/**
|
|
68
72
|
* "MIME type" of the returned screenshot image, defaults to `image/png`
|
|
69
73
|
*
|
|
@@ -89,11 +93,27 @@ export type ScreenshotSettings = {
|
|
|
89
93
|
quality?: number;
|
|
90
94
|
};
|
|
91
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Internal data type for screenshot sizing.
|
|
98
|
+
* `imageWidth/Height` defines the size of the final image.
|
|
99
|
+
* `canvasWidth/Height` is the size of the canvas from which the screenshot is taken.
|
|
100
|
+
* Canvas size always has the same aspect ratio as the viewport and is larger than image size, as the final image may be
|
|
101
|
+
* cropped.
|
|
102
|
+
*
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
export type ScreenshotSize = {
|
|
106
|
+
imageWidth: number;
|
|
107
|
+
imageHeight: number;
|
|
108
|
+
canvasWidth: number;
|
|
109
|
+
canvasHeight: number;
|
|
110
|
+
};
|
|
111
|
+
|
|
92
112
|
/**
|
|
93
113
|
* Manager for camera related tasks
|
|
94
114
|
*/
|
|
95
115
|
export class CameraManager {
|
|
96
|
-
public static readonly CAMERA_ANIMATION_NAME = '
|
|
116
|
+
public static readonly CAMERA_ANIMATION_NAME = '$cameraAnimation';
|
|
97
117
|
|
|
98
118
|
/** @internal */
|
|
99
119
|
public static readonly DEFAULT_CAMERA_POSITION: CameraPosition = {
|
|
@@ -112,7 +132,7 @@ export class CameraManager {
|
|
|
112
132
|
|
|
113
133
|
protected static readonly _DEFAULT_AUTOFOCUS_RADIUS_FACTOR = 1;
|
|
114
134
|
protected static readonly _DEFAULT_CAM_SPEED_MS = 250;
|
|
115
|
-
protected static readonly _SCREENSHOT_CAMERA_NAME = '
|
|
135
|
+
protected static readonly _SCREENSHOT_CAMERA_NAME = '$screenshotCamera';
|
|
116
136
|
|
|
117
137
|
/** @internal */
|
|
118
138
|
public constructor(protected viewer: Viewer) {}
|
|
@@ -131,7 +151,7 @@ export class CameraManager {
|
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
// get bounding box of all visible meshes, this is the base for the autofocus algorithm
|
|
134
|
-
const boundingInfo = this.viewer.calculateBoundingInfo(settings?.exclude);
|
|
154
|
+
const boundingInfo = this.viewer.sceneManager.calculateBoundingInfo(settings?.exclude);
|
|
135
155
|
// optionally show bounding sphere for debugging purpose
|
|
136
156
|
this.viewer.eventManager.fireEvent(ViewerEvent.AutofocusStart, boundingInfo.boundingSphere);
|
|
137
157
|
|
|
@@ -269,19 +289,38 @@ export class CameraManager {
|
|
|
269
289
|
* The result is a string containing a base64 encoded image.
|
|
270
290
|
*/
|
|
271
291
|
public async createScreenshot(settings?: ScreenshotSettings): Promise<string> {
|
|
292
|
+
const {
|
|
293
|
+
width,
|
|
294
|
+
height,
|
|
295
|
+
alpha,
|
|
296
|
+
beta,
|
|
297
|
+
radius,
|
|
298
|
+
target,
|
|
299
|
+
autofocusScene,
|
|
300
|
+
exclude,
|
|
301
|
+
excludeHtmlAnchorGroups,
|
|
302
|
+
fileName,
|
|
303
|
+
samples,
|
|
304
|
+
antialiasing,
|
|
305
|
+
renderSprites,
|
|
306
|
+
enableStencilBuffer,
|
|
307
|
+
useLayerMask,
|
|
308
|
+
quality,
|
|
309
|
+
} = settings ?? {};
|
|
310
|
+
|
|
272
311
|
const screenshotCam = this.viewer.scene.activeCamera?.clone(
|
|
273
312
|
CameraManager._SCREENSHOT_CAMERA_NAME
|
|
274
313
|
) as ArcRotateCamera;
|
|
275
|
-
const boundingInfo =
|
|
314
|
+
const boundingInfo = autofocusScene ? this.viewer.sceneManager.calculateBoundingInfo(exclude) : undefined;
|
|
276
315
|
|
|
277
|
-
if (
|
|
278
|
-
screenshotCam.alpha =
|
|
316
|
+
if (alpha !== undefined) {
|
|
317
|
+
screenshotCam.alpha = alpha;
|
|
279
318
|
}
|
|
280
|
-
if (
|
|
281
|
-
screenshotCam.beta =
|
|
319
|
+
if (beta !== undefined) {
|
|
320
|
+
screenshotCam.beta = beta;
|
|
282
321
|
}
|
|
283
|
-
if (
|
|
284
|
-
screenshotCam.radius =
|
|
322
|
+
if (radius !== undefined) {
|
|
323
|
+
screenshotCam.radius = radius;
|
|
285
324
|
} else if (boundingInfo) {
|
|
286
325
|
// zoom out to have full scene in view if requested by `autofocusScene` flag
|
|
287
326
|
const distance = this._getAutofocusZoomingDistance(boundingInfo.boundingSphere, screenshotCam);
|
|
@@ -290,44 +329,90 @@ export class CameraManager {
|
|
|
290
329
|
}
|
|
291
330
|
// `cloneAlphaBetaRadius` has to be set to true, otherwise these values get adapted by setting the new target
|
|
292
331
|
// this would also be the case when setting `screenshotCam.target` directly
|
|
293
|
-
if (
|
|
294
|
-
screenshotCam.setTarget(
|
|
332
|
+
if (target !== undefined) {
|
|
333
|
+
screenshotCam.setTarget(target, undefined, undefined, true);
|
|
295
334
|
} else if (boundingInfo) {
|
|
296
335
|
screenshotCam.setTarget(boundingInfo.boundingSphere.center, undefined, undefined, true);
|
|
297
336
|
}
|
|
298
337
|
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
338
|
+
// calculate screenshot size manually as it is used for html anchor screenshot as well
|
|
339
|
+
const screenshotSize = this._getScreenshotSize(width, height);
|
|
340
|
+
const mimeType = settings?.mimeType ?? 'image/png';
|
|
341
|
+
|
|
342
|
+
const imageStr3d = await ScreenshotTools.CreateScreenshotUsingRenderTargetAsync(
|
|
343
|
+
this.viewer.engine,
|
|
344
|
+
screenshotCam,
|
|
345
|
+
{ width: screenshotSize.imageWidth, height: screenshotSize.imageHeight },
|
|
346
|
+
mimeType,
|
|
347
|
+
samples,
|
|
348
|
+
antialiasing,
|
|
349
|
+
undefined,
|
|
350
|
+
renderSprites,
|
|
351
|
+
enableStencilBuffer,
|
|
352
|
+
useLayerMask,
|
|
353
|
+
quality,
|
|
354
|
+
texture => {
|
|
355
|
+
// NOTE: this doesn't work ATM, see https://github.com/BabylonJS/Babylon.js/pull/16081#issuecomment-2652861176
|
|
356
|
+
// we'll have to wait for a fix or solve the visibility in a different way (e.g. camera layer mask)
|
|
357
|
+
texture.renderList = this.viewer.scene.meshes.filter(
|
|
358
|
+
mesh => !settings?.exclude || !isNodeExcluded(mesh, settings.exclude)
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
);
|
|
303
362
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
this.viewer.
|
|
363
|
+
let imageStr = '';
|
|
364
|
+
const htmlAnchorKeys =
|
|
365
|
+
excludeHtmlAnchorGroups === true
|
|
366
|
+
? []
|
|
367
|
+
: this.viewer.htmlAnchorManager.getHtmlAnchorKeys(undefined, excludeHtmlAnchorGroups, true);
|
|
368
|
+
if (htmlAnchorKeys.length) {
|
|
369
|
+
// html anchors are not included in the main screenshot, as the html elements are located outside of the canvas
|
|
370
|
+
// the idea is to create a dedicated canvas for these elements and merge the result with the main screenshot into
|
|
371
|
+
// a combined canvas
|
|
372
|
+
const screenshotHtmlCanvas = await this.viewer.htmlAnchorManager.createScreenshotCanvas(
|
|
373
|
+
screenshotSize,
|
|
309
374
|
screenshotCam,
|
|
310
|
-
|
|
311
|
-
(data: string) => resolve(data),
|
|
312
|
-
settings?.mimeType,
|
|
313
|
-
settings?.samples,
|
|
314
|
-
settings?.antialiasing,
|
|
315
|
-
settings?.fileName,
|
|
316
|
-
settings?.renderSprites,
|
|
317
|
-
settings?.enableStencilBuffer,
|
|
318
|
-
settings?.useLayerMask,
|
|
319
|
-
settings?.quality,
|
|
320
|
-
texture => {
|
|
321
|
-
texture.renderList = this.viewer.scene.meshes.filter(
|
|
322
|
-
mesh => !settings?.exclude || !isNodeExcluded(mesh, settings.exclude)
|
|
323
|
-
);
|
|
324
|
-
}
|
|
375
|
+
htmlAnchorKeys
|
|
325
376
|
);
|
|
326
|
-
|
|
377
|
+
|
|
378
|
+
// convert the main screenshot into an image, so that it can be drawn onto a canvas as well
|
|
379
|
+
const screenshot3dImg = new Image();
|
|
380
|
+
screenshot3dImg.src = imageStr3d;
|
|
381
|
+
await new Promise(resolve => {
|
|
382
|
+
screenshot3dImg.onload = resolve;
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const screenshotCombinedCanvas = document.createElement('canvas');
|
|
386
|
+
screenshotCombinedCanvas.width = screenshotSize.imageWidth;
|
|
387
|
+
screenshotCombinedCanvas.height = screenshotSize.imageHeight;
|
|
388
|
+
|
|
389
|
+
// draw main and html screenshot on a new canvas and get the base64 string from it
|
|
390
|
+
const context = screenshotCombinedCanvas.getContext('2d')!;
|
|
391
|
+
context.drawImage(screenshot3dImg, 0, 0, screenshotSize.imageWidth, screenshotSize.imageHeight);
|
|
392
|
+
context.drawImage(screenshotHtmlCanvas, 0, 0, screenshotSize.imageWidth, screenshotSize.imageHeight);
|
|
393
|
+
imageStr = screenshotCombinedCanvas.toDataURL(mimeType);
|
|
394
|
+
|
|
395
|
+
screenshotCombinedCanvas.remove();
|
|
396
|
+
} else {
|
|
397
|
+
imageStr = imageStr3d;
|
|
398
|
+
}
|
|
327
399
|
|
|
328
400
|
screenshotCam.dispose();
|
|
329
401
|
|
|
330
|
-
|
|
402
|
+
if (fileName) {
|
|
403
|
+
// rebuild Babylon.js default behaviour: if a file name is given, download the screenshot instead of returning the
|
|
404
|
+
// data string
|
|
405
|
+
const link = document.createElement('a');
|
|
406
|
+
link.href = imageStr;
|
|
407
|
+
link.download = fileName;
|
|
408
|
+
document.body.appendChild(link);
|
|
409
|
+
link.click();
|
|
410
|
+
document.body.removeChild(link);
|
|
411
|
+
|
|
412
|
+
return '';
|
|
413
|
+
} else {
|
|
414
|
+
return imageStr;
|
|
415
|
+
}
|
|
331
416
|
}
|
|
332
417
|
|
|
333
418
|
protected static _addCameraAnimationToGroup(
|
|
@@ -373,4 +458,33 @@ export class CameraManager {
|
|
|
373
458
|
|
|
374
459
|
return distance;
|
|
375
460
|
}
|
|
461
|
+
|
|
462
|
+
protected _getScreenshotSize(width?: number, height?: number): ScreenshotSize {
|
|
463
|
+
const canvas = this.viewer.canvas!;
|
|
464
|
+
const aspectRatio = canvas.width / canvas.height;
|
|
465
|
+
|
|
466
|
+
let imageWidth = 0;
|
|
467
|
+
let imageHeight = 0;
|
|
468
|
+
if (width && height) {
|
|
469
|
+
imageWidth = width;
|
|
470
|
+
imageHeight = height;
|
|
471
|
+
} else if (width && !height) {
|
|
472
|
+
imageWidth = width;
|
|
473
|
+
imageHeight = width / aspectRatio;
|
|
474
|
+
} else if (!width && height) {
|
|
475
|
+
imageWidth = height * aspectRatio;
|
|
476
|
+
imageHeight = height;
|
|
477
|
+
} else {
|
|
478
|
+
imageWidth = canvas.width;
|
|
479
|
+
imageHeight = canvas.height;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// canvas size only differs from image size if both width and height are set
|
|
483
|
+
// in this case the aspect ratio differs from the canvas aspect ratio and some parts of the canvas have to be
|
|
484
|
+
// cropped for the screenshot
|
|
485
|
+
const canvasWidth = width && height ? height * aspectRatio : imageWidth;
|
|
486
|
+
const canvasHeight = imageHeight;
|
|
487
|
+
|
|
488
|
+
return { imageWidth, imageHeight, canvasWidth, canvasHeight };
|
|
489
|
+
}
|
|
376
490
|
}
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
* Manager for debugging functionalities
|
|
21
21
|
*/
|
|
22
22
|
export class DebugManager {
|
|
23
|
-
protected static _BOUNDING_SPHERE_KEY = '
|
|
23
|
+
protected static _BOUNDING_SPHERE_KEY = '$boundingSphere';
|
|
24
24
|
|
|
25
25
|
protected _axesViewer: AxesViewer | null = null;
|
|
26
26
|
protected _showBoundingSphereForAutofocus: boolean = false;
|
|
@@ -91,7 +91,7 @@ The inspector can only be used in development builds.`);
|
|
|
91
91
|
public showCoordinateSystem(node?: TransformNode, size?: number): void {
|
|
92
92
|
// calculate the default size of not provided
|
|
93
93
|
if (!size) {
|
|
94
|
-
const sceneBoundingInfo = this.viewer.calculateBoundingInfo();
|
|
94
|
+
const sceneBoundingInfo = this.viewer.sceneManager.calculateBoundingInfo();
|
|
95
95
|
const radius = sceneBoundingInfo.boundingSphere.radius;
|
|
96
96
|
|
|
97
97
|
// takes a third of the radius from scene boundingsphere
|
|
@@ -145,7 +145,7 @@ The inspector can only be used in development builds.`);
|
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
147
|
* Draws a wireframe bounding sphere on the next call of {@link CameraManager.autofocusActiveCamera}.\
|
|
148
|
-
* This is
|
|
148
|
+
* This is useful for checking which parts of the scene get centered.
|
|
149
149
|
*/
|
|
150
150
|
public showBoundingSphereForAutofocus(): void {
|
|
151
151
|
this._showBoundingSphereForAutofocus = true;
|