@xeokit/xeokit-sdk 2.6.73 → 2.6.75
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/xeokit-sdk.cjs.js +221 -108
- package/dist/xeokit-sdk.es.js +221 -108
- package/dist/xeokit-sdk.es5.js +311 -301
- package/dist/xeokit-sdk.min.cjs.js +6 -6
- package/dist/xeokit-sdk.min.es.js +7 -7
- package/dist/xeokit-sdk.min.es5.js +6 -6
- package/package.json +1 -1
- package/src/extras/ContextMenu/ContextMenu.js +25 -16
- package/src/plugins/FastNavPlugin/FastNavPlugin.js +10 -0
- package/src/plugins/SkyboxesPlugin/SkyboxesPlugin.js +24 -49
- package/src/plugins/TreeViewPlugin/RenderService.js +13 -4
- package/src/plugins/TreeViewPlugin/TreeViewPlugin.js +1 -1
- package/src/viewer/Viewer.js +0 -16
- package/src/viewer/scene/CameraControl/lib/controllers/PivotController.js +29 -8
- package/src/viewer/scene/LineSet/LineSet.js +7 -2
- package/src/viewer/scene/skybox/Skybox.js +110 -9
- package/types/plugins/SkyboxesPlugin/SkyboxesPlugin.d.ts +18 -4
- package/types/viewer/scene/CameraControl/CameraControl.d.ts +168 -0
- package/types/viewer/scene/LineSet/LineSet.d.ts +1 -1
- package/types/viewer/scene/camera/CameraPath.d.ts +86 -6
- package/types/viewer/scene/camera/CameraPathAnimation.d.ts +2 -0
- package/types/viewer/scene/geometry/ReadableGeometry.d.ts +14 -0
- package/types/viewer/scene/geometry/VBOGeometry.d.ts +2 -0
- package/types/viewer/scene/geometry/index.d.ts +2 -1
- package/types/viewer/scene/geometry/loaders/index.d.ts +2 -0
- package/types/viewer/scene/geometry/loaders/load3DSGeometry.d.ts +17 -0
- package/types/viewer/scene/geometry/loaders/loadOBJGeometry.d.ts +18 -0
- package/types/viewer/scene/index.d.ts +2 -0
- package/types/viewer/scene/input/input.d.ts +117 -0
- package/types/viewer/scene/lights/AmbientLight.d.ts +41 -0
- package/types/viewer/scene/lights/CubeTexture.d.ts +27 -0
- package/types/viewer/scene/lights/DirLight.d.ts +77 -0
- package/types/viewer/scene/lights/LightMap.d.ts +27 -0
- package/types/viewer/scene/lights/PointLight.d.ts +135 -0
- package/types/viewer/scene/lights/ReflectionMap.d.ts +26 -0
- package/types/viewer/scene/lights/index.d.ts +4 -1
- package/types/viewer/scene/marker/Marker.d.ts +5 -0
- package/types/viewer/scene/materials/EmphasisMaterial.d.ts +5 -0
- package/types/viewer/scene/materials/Fresnel.d.ts +25 -62
- package/types/viewer/scene/materials/LambertMaterial.d.ts +192 -0
- package/types/viewer/scene/materials/MetallicMaterial.d.ts +359 -0
- package/types/viewer/scene/materials/PhongMaterial.d.ts +5 -0
- package/types/viewer/scene/materials/PointsMaterial.d.ts +5 -0
- package/types/viewer/scene/materials/SpecularMaterial.d.ts +342 -0
- package/types/viewer/scene/materials/Texture.d.ts +36 -78
- package/types/viewer/scene/materials/index.d.ts +2 -0
- package/types/viewer/scene/models/SceneModel.d.ts +134 -7
- package/types/viewer/scene/models/SceneModelEntity.d.ts +65 -0
- package/types/viewer/scene/models/SceneModelMesh.d.ts +12 -0
- package/types/viewer/scene/paths/CubicBezierCurve.d.ts +146 -0
- package/types/viewer/scene/paths/Curve.d.ts +75 -0
- package/types/viewer/scene/paths/Path.d.ts +119 -0
- package/types/viewer/scene/paths/QuadraticBezierCurve.d.ts +124 -0
- package/types/viewer/scene/paths/SplineCurve.d.ts +84 -0
- package/types/viewer/scene/paths/index.d.ts +5 -0
- package/types/viewer/scene/scene/Scene.d.ts +102 -0
- package/types/viewer/scene/sectionPlane/SectionPlane.d.ts +41 -0
- package/types/viewer/scene/skybox/Skybox.d.ts +59 -0
- package/types/viewer/scene/skybox/index.d.ts +1 -0
- package/types/viewer/scene/webgl/PickResult.d.ts +19 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xeokit/xeokit-sdk",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.75",
|
|
4
4
|
"description": "3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision",
|
|
5
5
|
"module": "./dist/xeokit-sdk.es.js",
|
|
6
6
|
"main": "./dist/xeokit-sdk.cjs.js",
|
|
@@ -298,7 +298,8 @@ class ContextMenu {
|
|
|
298
298
|
this._shown = false; // True when the ContextMenu is visible
|
|
299
299
|
this._nextId = 0;
|
|
300
300
|
this._parentNode = cfg.parentNode || document.body;
|
|
301
|
-
|
|
301
|
+
this._offsetParent = (this._parentNode instanceof ShadowRoot) ? this._parentNode.host : this._parentNode;
|
|
302
|
+
|
|
302
303
|
/**
|
|
303
304
|
* Subscriptions to events fired at this ContextMenu.
|
|
304
305
|
* @private
|
|
@@ -510,7 +511,7 @@ class ContextMenu {
|
|
|
510
511
|
for (let i = 0, len = this._menuList.length; i < len; i++) {
|
|
511
512
|
const menu = this._menuList[i];
|
|
512
513
|
const menuElement = menu.menuElement;
|
|
513
|
-
menuElement.
|
|
514
|
+
menuElement.remove();
|
|
514
515
|
}
|
|
515
516
|
this._itemsCfg = [];
|
|
516
517
|
this._rootMenu = null;
|
|
@@ -738,21 +739,22 @@ class ContextMenu {
|
|
|
738
739
|
|
|
739
740
|
const itemRect = itemElement.getBoundingClientRect();
|
|
740
741
|
const menuRect = subMenuElement.getBoundingClientRect();
|
|
742
|
+
const offsetRect = self._offsetParent.getBoundingClientRect();
|
|
741
743
|
|
|
742
744
|
const subMenuWidth = 200; // TODO
|
|
743
|
-
const showOnRight = (itemRect.right + subMenuWidth) <
|
|
744
|
-
const showOnLeft = (itemRect.left - subMenuWidth) >
|
|
745
|
-
|
|
745
|
+
const showOnRight = (itemRect.right + subMenuWidth) < offsetRect.right;
|
|
746
|
+
const showOnLeft = (itemRect.left - subMenuWidth) > offsetRect.left;
|
|
747
|
+
|
|
746
748
|
if(showOnRight)
|
|
747
|
-
self._showMenu(subMenu.id, itemRect.right - 5, itemRect.top - 1);
|
|
749
|
+
self._showMenu(subMenu.id, itemRect.right + window.scrollX - 5, itemRect.top + window.scrollY - 1);
|
|
748
750
|
else if (showOnLeft)
|
|
749
|
-
self._showMenu(subMenu.id, itemRect.left - subMenuWidth, itemRect.top - 1);
|
|
751
|
+
self._showMenu(subMenu.id, itemRect.left - subMenuWidth + window.scrollX, itemRect.top + window.scrollY - 1);
|
|
750
752
|
else {
|
|
751
|
-
const spaceOnLeft = itemRect.left, spaceOnRight =
|
|
753
|
+
const spaceOnLeft = itemRect.left - offsetRect.left, spaceOnRight = offsetRect.right - itemRect.right;
|
|
752
754
|
if(spaceOnRight > spaceOnLeft)
|
|
753
|
-
self._showMenu(subMenu.id, itemRect.right - 5 - (subMenuWidth - spaceOnRight), itemRect.top - 1);
|
|
755
|
+
self._showMenu(subMenu.id, itemRect.right - 5 - (subMenuWidth - spaceOnRight), itemRect.top + window.scrollY - 1);
|
|
754
756
|
else
|
|
755
|
-
self._showMenu(subMenu.id, itemRect.left - spaceOnLeft, itemRect.top - 1);
|
|
757
|
+
self._showMenu(subMenu.id, itemRect.left - spaceOnLeft, itemRect.top + window.scrollY - 1);
|
|
756
758
|
}
|
|
757
759
|
|
|
758
760
|
lastSubMenu = subMenu;
|
|
@@ -945,14 +947,21 @@ class ContextMenu {
|
|
|
945
947
|
menuElement.style.display = 'block';
|
|
946
948
|
const menuHeight = menuElement.offsetHeight;
|
|
947
949
|
const menuWidth = menuElement.offsetWidth;
|
|
948
|
-
|
|
949
|
-
|
|
950
|
+
|
|
951
|
+
const offsetRect = this._offsetParent.getBoundingClientRect();
|
|
952
|
+
|
|
953
|
+
const bottomContainerBorder = offsetRect.bottom + window.scrollY;
|
|
954
|
+
const rightContainerBorder = offsetRect.right + window.scrollX;
|
|
955
|
+
if ((pageY + menuHeight) > bottomContainerBorder) {
|
|
956
|
+
pageY = bottomContainerBorder- menuHeight;
|
|
950
957
|
}
|
|
951
|
-
if ((pageX + menuWidth) >
|
|
952
|
-
pageX =
|
|
958
|
+
if ((pageX + menuWidth) > rightContainerBorder) {
|
|
959
|
+
pageX = rightContainerBorder - menuWidth;
|
|
953
960
|
}
|
|
954
|
-
|
|
955
|
-
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
menuElement.style.left = pageX - offsetRect.left - window.scrollX + 'px';
|
|
964
|
+
menuElement.style.top = pageY - offsetRect.top - window.scrollY + 'px';
|
|
956
965
|
}
|
|
957
966
|
|
|
958
967
|
_hideMenuElement(menuElement) {
|
|
@@ -108,6 +108,7 @@ class FastNavPlugin extends Plugin {
|
|
|
108
108
|
* @param {Number} [cfg.scaleCanvasResolutionFactor=0.6] The factor by which we downscale the canvas resolution whenever we interact with the Viewer.
|
|
109
109
|
* @param {Boolean} [cfg.delayBeforeRestore=true] Whether to temporarily have a delay before restoring normal rendering after we stop interacting with the Viewer.
|
|
110
110
|
* @param {Number} [cfg.delayBeforeRestoreSeconds=0.5] Delay in seconds before restoring normal rendering after we stop interacting with the Viewer.
|
|
111
|
+
* @param {Function} [cfg.onMoved] Optional callback function fired during moving mode, should return the callback function that will be fired when the interaction stops.
|
|
111
112
|
*/
|
|
112
113
|
constructor(viewer, cfg = {}) {
|
|
113
114
|
|
|
@@ -123,6 +124,8 @@ class FastNavPlugin extends Plugin {
|
|
|
123
124
|
this._scaleCanvasResolutionFactor = cfg.scaleCanvasResolutionFactor || 0.6;
|
|
124
125
|
this._delayBeforeRestore = (cfg.delayBeforeRestore !== false);
|
|
125
126
|
this._delayBeforeRestoreSeconds = cfg.delayBeforeRestoreSeconds || 0.5;
|
|
127
|
+
this._onMoved = cfg.onMoved;
|
|
128
|
+
this._onStopped = null;
|
|
126
129
|
|
|
127
130
|
let timer = this._delayBeforeRestoreSeconds * 1000;
|
|
128
131
|
let fastMode = false;
|
|
@@ -141,6 +144,10 @@ class FastNavPlugin extends Plugin {
|
|
|
141
144
|
} else {
|
|
142
145
|
viewer.scene.canvas.resolutionScale = this._defaultScaleCanvasResolutionFactor;
|
|
143
146
|
}
|
|
147
|
+
if (this._onMoved) {
|
|
148
|
+
this._onStopped = this._onMoved();
|
|
149
|
+
}
|
|
150
|
+
|
|
144
151
|
fastMode = true;
|
|
145
152
|
}
|
|
146
153
|
};
|
|
@@ -152,6 +159,9 @@ class FastNavPlugin extends Plugin {
|
|
|
152
159
|
viewer.scene._renderer.setPBREnabled(true);
|
|
153
160
|
viewer.scene._renderer.setSAOEnabled(true);
|
|
154
161
|
viewer.scene._renderer.setTransparentEnabled(true);
|
|
162
|
+
if (this._onStopped) {
|
|
163
|
+
this._onStopped();
|
|
164
|
+
}
|
|
155
165
|
fastMode = false;
|
|
156
166
|
};
|
|
157
167
|
|
|
@@ -12,46 +12,27 @@ import {Skybox} from "../../viewer/scene/skybox/Skybox.js"
|
|
|
12
12
|
* });
|
|
13
13
|
*
|
|
14
14
|
* // Add a GLTFModelsPlugin
|
|
15
|
-
* var
|
|
16
|
-
* id: "GLTFModels" // Default value
|
|
17
|
-
* });
|
|
15
|
+
* var xktLoaderPlugin = new XKTLoaderPlugin(viewer);
|
|
18
16
|
*
|
|
19
17
|
* // Add a SkyboxesPlugin
|
|
20
|
-
* var skyboxesPlugin = new SkyboxesPlugin(viewer
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* var skyboxesPlugin = new SkyboxesPlugin(viewer);
|
|
19
|
+
*
|
|
20
|
+
* // Create a skybox
|
|
21
|
+
* skyBoxesPlugin.createSkybox({
|
|
22
|
+
* src: [
|
|
23
|
+
* "./assets/images/posx.jpg",
|
|
24
|
+
* "./assets/images/negx.jpg",
|
|
25
|
+
* "./assets/images/posy.jpg",
|
|
26
|
+
* "./assets/images/negy.jpg",
|
|
27
|
+
* "./assets/images/posz.jpg",
|
|
28
|
+
* "./assets/images/negz.jpg"
|
|
29
|
+
* ]
|
|
30
|
+
* })
|
|
23
31
|
*
|
|
24
|
-
* // Load
|
|
25
|
-
* const model =
|
|
32
|
+
* // Load an XKT model
|
|
33
|
+
* const model = xktLoaderPlugin.load({
|
|
26
34
|
* id: "myModel",
|
|
27
|
-
* src: "./models/
|
|
28
|
-
* });
|
|
29
|
-
*
|
|
30
|
-
* // Create three directional World-space lights. "World" means that they will appear as if part
|
|
31
|
-
* // of the world, instead of "View", where they move with the user's head.
|
|
32
|
-
*
|
|
33
|
-
* skyboxesPlugin.createLight({
|
|
34
|
-
* id: "keyLight",
|
|
35
|
-
* dir: [0.8, -0.6, -0.8],
|
|
36
|
-
* color: [1.0, 0.3, 0.3],
|
|
37
|
-
* intensity: 1.0,
|
|
38
|
-
* space: "world"
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* skyboxesPlugin.createLight({
|
|
42
|
-
* id: "fillLight",
|
|
43
|
-
* dir: [-0.8, -0.4, -0.4],
|
|
44
|
-
* color: [0.3, 1.0, 0.3],
|
|
45
|
-
* intensity: 1.0,
|
|
46
|
-
* space: "world"
|
|
47
|
-
* });
|
|
48
|
-
*
|
|
49
|
-
* skyboxesPlugin.createDirLight({
|
|
50
|
-
* id: "rimLight",
|
|
51
|
-
* dir: [0.2, -0.8, 0.8],
|
|
52
|
-
* color: [0.6, 0.6, 0.6],
|
|
53
|
-
* intensity: 1.0,
|
|
54
|
-
* space: "world"
|
|
35
|
+
* src: "./models/xkt/myModel.xkt"
|
|
55
36
|
* });
|
|
56
37
|
*
|
|
57
38
|
* @class SkyboxesPlugin
|
|
@@ -77,23 +58,17 @@ class SkyboxesPlugin extends Plugin {
|
|
|
77
58
|
/**
|
|
78
59
|
Creates a skybox.
|
|
79
60
|
|
|
80
|
-
@param {String} id Unique ID to assign to the skybox.
|
|
81
61
|
@param {Object} params Skybox configuration.
|
|
62
|
+
@param {String} [params.id] Optional ID, unique among all components in the parent {Scene}, generated automatically when omitted.
|
|
63
|
+
@param {String | String[]} [params.src=null] Path to skybox texture
|
|
64
|
+
@param {Number} [params.encoding=LinearEncoding] Texture encoding format. See the {@link Texture#encoding} property for more info.
|
|
65
|
+
@param {Number} [params.size=1000] Size of this Skybox, given as the distance from the center at ````[0,0,0]```` to each face.
|
|
82
66
|
@param {Boolean} [params.active=true] Whether the skybox plane is initially active. Only skyboxes while this is true.
|
|
83
67
|
@returns {Skybox} The new skybox.
|
|
84
68
|
*/
|
|
85
|
-
createSkybox(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
var skybox = new Skybox(this.viewer.scene, {
|
|
91
|
-
id: id,
|
|
92
|
-
pos: params.pos,
|
|
93
|
-
dir: params.dir,
|
|
94
|
-
active: true || params.active
|
|
95
|
-
});
|
|
96
|
-
this.skyboxes[id] = skybox;
|
|
69
|
+
createSkybox(params) {
|
|
70
|
+
const skybox = new Skybox(this.viewer.scene, params);
|
|
71
|
+
this.skyboxes[skybox.id] = skybox;
|
|
97
72
|
return skybox;
|
|
98
73
|
}
|
|
99
74
|
|
|
@@ -5,6 +5,15 @@ import { addContextMenuListener } from "../lib/html/MenuEvent.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export class RenderService {
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @constructor
|
|
10
|
+
*
|
|
11
|
+
* @param {HTMLElement} treeViewContainer Container element for the tree which uses this render service.
|
|
12
|
+
*/
|
|
13
|
+
constructor(treeViewContainer) {
|
|
14
|
+
this._treeViewContainer = treeViewContainer;
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
/*
|
|
9
18
|
* Creates the root node of the tree.
|
|
10
19
|
* @return {HTMLElement} The root node of the tree.
|
|
@@ -129,7 +138,7 @@ export class RenderService {
|
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
getSwitchElement(nodeId) {
|
|
132
|
-
return
|
|
141
|
+
return this._treeViewContainer.querySelector(`[id="switch-${nodeId}"]`);
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
isChecked(element) {
|
|
@@ -137,7 +146,7 @@ export class RenderService {
|
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
setCheckbox(nodeId, checked, indeterminate = false) {
|
|
140
|
-
const checkbox =
|
|
149
|
+
const checkbox = this._treeViewContainer.querySelector(`[id="checkbox-${nodeId}"]`);
|
|
141
150
|
if (checkbox) {
|
|
142
151
|
if (checked !== checkbox.checked) {
|
|
143
152
|
checkbox.checked = checked;
|
|
@@ -152,7 +161,7 @@ export class RenderService {
|
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
setXRayed(nodeId, xrayed) {
|
|
155
|
-
const treeNode =
|
|
164
|
+
const treeNode = this._treeViewContainer.querySelector(`[id="${nodeId}"]`);
|
|
156
165
|
if (treeNode) {
|
|
157
166
|
if (xrayed) {
|
|
158
167
|
treeNode.classList.add('xrayed-node');
|
|
@@ -163,7 +172,7 @@ export class RenderService {
|
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
setHighlighted(nodeId, highlighted) {
|
|
166
|
-
const treeNode =
|
|
175
|
+
const treeNode = this._treeViewContainer.querySelector(`[id="${nodeId}"]`);
|
|
167
176
|
if (treeNode) {
|
|
168
177
|
if (highlighted) {
|
|
169
178
|
treeNode.scrollIntoView({block: "center"});
|
|
@@ -415,7 +415,7 @@ export class TreeViewPlugin extends Plugin {
|
|
|
415
415
|
this._sortNodes = cfg.sortNodes;
|
|
416
416
|
this._pruneEmptyNodes = cfg.pruneEmptyNodes;
|
|
417
417
|
this._showListItemElementId = null;
|
|
418
|
-
this._renderService = cfg.renderService || new RenderService();
|
|
418
|
+
this._renderService = cfg.renderService || new RenderService(this._containerElement);
|
|
419
419
|
this._showIndeterminate = cfg.showIndeterminate ?? false;
|
|
420
420
|
this._showProjectNode = cfg.showProjectNode ?? false;
|
|
421
421
|
this._elevationSortFunction = cfg.elevationSortFunction ?? undefined;
|
package/src/viewer/Viewer.js
CHANGED
|
@@ -363,22 +363,6 @@ class Viewer {
|
|
|
363
363
|
this.sendToPlugins("snapshotStarting"); // Tells plugins to hide things that shouldn't be in snapshot
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
const captured = {};
|
|
367
|
-
for (let i = 0, len = this._plugins.length; i < len; i++) {
|
|
368
|
-
const plugin = this._plugins[i];
|
|
369
|
-
if (plugin.getContainerElement) {
|
|
370
|
-
const container = plugin.getContainerElement();
|
|
371
|
-
if (container !== document.body) {
|
|
372
|
-
if (!captured[container.id]) {
|
|
373
|
-
captured[container.id] = true;
|
|
374
|
-
html2canvas(container).then(function (canvas) {
|
|
375
|
-
document.body.appendChild(canvas);
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
366
|
// firing "rendering" is necessary to trigger DTX{Lines,Triangles}Layer::_uploadDeferredFlags
|
|
383
367
|
this.scene.fire("rendering", { }, true);
|
|
384
368
|
|
|
@@ -13,6 +13,9 @@ const tempVec4a = math.vec4();
|
|
|
13
13
|
const tempVec4b = math.vec4();
|
|
14
14
|
const tempVec4c = math.vec4();
|
|
15
15
|
|
|
16
|
+
const TOP_LIMIT = 0.001;
|
|
17
|
+
const BOTTOM_LIMIT = Math.PI - 0.001;
|
|
18
|
+
|
|
16
19
|
|
|
17
20
|
/** @private */
|
|
18
21
|
class PivotController {
|
|
@@ -188,14 +191,9 @@ class PivotController {
|
|
|
188
191
|
*/
|
|
189
192
|
startPivot() {
|
|
190
193
|
|
|
191
|
-
if (this._cameraLookingDownwards()) {
|
|
192
|
-
this._pivoting = false;
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
194
|
const camera = this._scene.camera;
|
|
197
195
|
|
|
198
|
-
let lookat = math.lookAtMat4v(camera.eye, camera.look, camera.
|
|
196
|
+
let lookat = math.lookAtMat4v(camera.eye, camera.look, camera.up);
|
|
199
197
|
math.transformPoint3(lookat, this.getPivotPos(), this._cameraOffset);
|
|
200
198
|
|
|
201
199
|
const pivotPos = this.getPivotPos();
|
|
@@ -298,8 +296,31 @@ class PivotController {
|
|
|
298
296
|
dx = -dx;
|
|
299
297
|
}
|
|
300
298
|
this._azimuth += -dx * .01;
|
|
301
|
-
|
|
302
|
-
|
|
299
|
+
|
|
300
|
+
const isMovingUp = dy < 0;
|
|
301
|
+
const isMovingDown = dy > 0;
|
|
302
|
+
|
|
303
|
+
// Track if we're at limits - only check if we're very close to the limit
|
|
304
|
+
const atTopLimit = Math.abs(this._polar - TOP_LIMIT) < 0.005;
|
|
305
|
+
const atBottomLimit = Math.abs(this._polar - BOTTOM_LIMIT) < 0.005;
|
|
306
|
+
|
|
307
|
+
let newPolar = this._polar + dy * .01;
|
|
308
|
+
|
|
309
|
+
// Case 1: At top limit and trying to go beyond
|
|
310
|
+
if (atTopLimit && isMovingUp) {
|
|
311
|
+
newPolar = TOP_LIMIT;
|
|
312
|
+
}
|
|
313
|
+
// Case 2: At bottom limit and trying to go beyond
|
|
314
|
+
else if (atBottomLimit && isMovingDown) {
|
|
315
|
+
newPolar = BOTTOM_LIMIT;
|
|
316
|
+
}
|
|
317
|
+
// Case 3: Normal rotation or moving away from a limit
|
|
318
|
+
else {
|
|
319
|
+
newPolar = math.clamp(newPolar, TOP_LIMIT, BOTTOM_LIMIT);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this._polar = newPolar;
|
|
323
|
+
|
|
303
324
|
const pos = [
|
|
304
325
|
this._radius * Math.sin(this._polar) * Math.sin(this._azimuth),
|
|
305
326
|
this._radius * Math.cos(this._polar),
|
|
@@ -66,7 +66,7 @@ class LineSet extends Component {
|
|
|
66
66
|
* @param {String} [cfg.id] Optional ID, unique among all components in the parent {@link Scene}, generated automatically when omitted.
|
|
67
67
|
* @param {Number[]} cfg.positions World-space 3D vertex positions.
|
|
68
68
|
* @param {Number[]} [cfg.indices] Indices to connect ````positions```` into line segments. Note that these are separate line segments, not a polyline.
|
|
69
|
-
* @param {Number[]} [cfg.color=[
|
|
69
|
+
* @param {Number[]} [cfg.color=[1,1,1]] The color of this ````LineSet````. This is both emissive and diffuse.
|
|
70
70
|
* @param {Boolean} [cfg.visible=true] Indicates whether or not this ````LineSet```` is visible.
|
|
71
71
|
* @param {Number} [cfg.opacity=1.0] ````LineSet````'s initial opacity factor.
|
|
72
72
|
*/
|
|
@@ -86,6 +86,9 @@ class LineSet extends Component {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
this._color = cfg.color || [1, 1, 1];
|
|
90
|
+
this._opacity = cfg.opacity || 1.0;
|
|
91
|
+
|
|
89
92
|
this._sceneModel = new SceneModel(this, {
|
|
90
93
|
isModel: false // Don't register in Scene.models
|
|
91
94
|
});
|
|
@@ -94,7 +97,9 @@ class LineSet extends Component {
|
|
|
94
97
|
id: "linesMesh",
|
|
95
98
|
primitive: "lines",
|
|
96
99
|
positions: this._positions,
|
|
97
|
-
indices: this._indices
|
|
100
|
+
indices: this._indices,
|
|
101
|
+
color: this._color,
|
|
102
|
+
opacity: this._opacity,
|
|
98
103
|
})
|
|
99
104
|
|
|
100
105
|
this._sceneModel.createEntity({
|
|
@@ -3,6 +3,7 @@ import {Mesh} from "../mesh/Mesh.js";
|
|
|
3
3
|
import {ReadableGeometry} from "../geometry/ReadableGeometry.js";
|
|
4
4
|
import {PhongMaterial} from "../materials/PhongMaterial.js";
|
|
5
5
|
import {Texture} from "../materials/Texture.js";
|
|
6
|
+
import { ClampToEdgeWrapping, LinearEncoding } from "../constants/constants.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @desc A Skybox.
|
|
@@ -14,8 +15,8 @@ class Skybox extends Component {
|
|
|
14
15
|
* @param {Component} owner Owner component. When destroyed, the owner will destroy this PointLight as well.
|
|
15
16
|
* @param {*} [cfg] Skybox configuration
|
|
16
17
|
* @param {String} [cfg.id] Optional ID, unique among all components in the parent {Scene}, generated automatically when omitted.
|
|
17
|
-
* @param {String} [cfg.src=null] Path to skybox texture
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {String | String[]} [cfg.src=null] Path to skybox texture
|
|
19
|
+
* @param {Number} [cfg.encoding=LinearEncoding] Texture encoding format. See the {@link Texture#encoding} property for more info.
|
|
19
20
|
* @param {Number} [cfg.size=1000] Size of this Skybox, given as the distance from the center at ````[0,0,0]```` to each face.
|
|
20
21
|
* @param {Boolean} [cfg.active=true] True when this Skybox is visible.
|
|
21
22
|
*/
|
|
@@ -23,6 +24,32 @@ class Skybox extends Component {
|
|
|
23
24
|
|
|
24
25
|
super(owner, cfg);
|
|
25
26
|
|
|
27
|
+
const useMultipleTexture = typeof cfg.src === "object" && cfg.src !== null;
|
|
28
|
+
|
|
29
|
+
if (useMultipleTexture) {
|
|
30
|
+
this._createCombinedTexture(cfg).then(combinedTexture => {
|
|
31
|
+
this._createSkyboxMesh(cfg, combinedTexture);
|
|
32
|
+
}).catch(e => this.error(e));
|
|
33
|
+
} else {
|
|
34
|
+
this._createSkyboxMesh(cfg);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @private
|
|
41
|
+
* @param {*} cfg
|
|
42
|
+
* @param {Texture} combinedTexture
|
|
43
|
+
*/
|
|
44
|
+
_createSkyboxMesh(cfg, combinedTexture = null) {
|
|
45
|
+
const texture = combinedTexture || new Texture(this, {
|
|
46
|
+
src: cfg.src,
|
|
47
|
+
flipY: true,
|
|
48
|
+
wrapS: ClampToEdgeWrapping,
|
|
49
|
+
wrapT: ClampToEdgeWrapping,
|
|
50
|
+
encoding: cfg.encoding || LinearEncoding
|
|
51
|
+
});
|
|
52
|
+
|
|
26
53
|
this._skyboxMesh = new Mesh(this, {
|
|
27
54
|
|
|
28
55
|
geometry: new ReadableGeometry(this, { // Box-shaped geometry
|
|
@@ -53,13 +80,7 @@ class Skybox extends Component {
|
|
|
53
80
|
diffuse: [0, 0, 0],
|
|
54
81
|
specular: [0, 0, 0],
|
|
55
82
|
emissive: [1, 1, 1],
|
|
56
|
-
emissiveMap:
|
|
57
|
-
src: cfg.src,
|
|
58
|
-
flipY: true,
|
|
59
|
-
wrapS: "clampToEdge",
|
|
60
|
-
wrapT: "clampToEdge",
|
|
61
|
-
encoding: cfg.encoding || "sRGB"
|
|
62
|
-
}),
|
|
83
|
+
emissiveMap: texture,
|
|
63
84
|
backfaces: true // Show interior faces of our skybox geometry
|
|
64
85
|
}),
|
|
65
86
|
// stationary: true,
|
|
@@ -73,6 +94,86 @@ class Skybox extends Component {
|
|
|
73
94
|
this.active = cfg.active;
|
|
74
95
|
}
|
|
75
96
|
|
|
97
|
+
/**
|
|
98
|
+
*
|
|
99
|
+
* @private
|
|
100
|
+
* @param {*} cfg
|
|
101
|
+
* @returns {Texture}
|
|
102
|
+
*/
|
|
103
|
+
async _createCombinedTexture(cfg) {
|
|
104
|
+
const [
|
|
105
|
+
posX,
|
|
106
|
+
negX,
|
|
107
|
+
posY,
|
|
108
|
+
negY,
|
|
109
|
+
posZ,
|
|
110
|
+
negZ
|
|
111
|
+
] = cfg.src;
|
|
112
|
+
|
|
113
|
+
if (!posX || !negX || !posY || !negY || !posZ || !negZ) {
|
|
114
|
+
throw new Error("All six skybox textures must be provided");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const canvas = document.createElement('canvas');
|
|
118
|
+
const ctx = canvas.getContext('2d');
|
|
119
|
+
|
|
120
|
+
const loadImage = src => {
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
const img = new Image();
|
|
123
|
+
img.crossOrigin = "anonymous";
|
|
124
|
+
img.onload = () => resolve(img);
|
|
125
|
+
img.onerror = () => reject(new Error(`Failed to load image: ${src}`));
|
|
126
|
+
img.src = src;
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const [imgPosX, imgNegX, imgPosY, imgNegY, imgPosZ, imgNegZ] = await Promise.all([
|
|
132
|
+
loadImage(posX),
|
|
133
|
+
loadImage(negX),
|
|
134
|
+
loadImage(posY),
|
|
135
|
+
loadImage(negY),
|
|
136
|
+
loadImage(posZ),
|
|
137
|
+
loadImage(negZ)
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
const imageSize = imgPosX.width;
|
|
141
|
+
if (
|
|
142
|
+
imgNegX.width !== imageSize || imgPosY.width !== imageSize ||
|
|
143
|
+
imgNegY.width !== imageSize || imgPosZ.width !== imageSize ||
|
|
144
|
+
imgNegZ.width !== imageSize
|
|
145
|
+
) {
|
|
146
|
+
throw new Error("All skybox textures must have the same dimensions");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Set canvas size for the Christ cross layout (3×4 grid)
|
|
150
|
+
canvas.width = imageSize * 4;
|
|
151
|
+
canvas.height = imageSize * 3;
|
|
152
|
+
|
|
153
|
+
ctx.fillStyle = 'black';
|
|
154
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
155
|
+
|
|
156
|
+
ctx.drawImage(imgNegX, imageSize * 0, imageSize * 1, imageSize, imageSize); // -X (left)
|
|
157
|
+
ctx.drawImage(imgPosX, imageSize * 2, imageSize * 1, imageSize, imageSize); // +X (right)
|
|
158
|
+
ctx.drawImage(imgPosY, imageSize * 1, imageSize * 0, imageSize, imageSize); // +Y (top)
|
|
159
|
+
ctx.drawImage(imgNegY, imageSize * 1, imageSize * 2, imageSize, imageSize); // -Y (bottom)
|
|
160
|
+
ctx.drawImage(imgPosZ, imageSize * 1, imageSize * 1, imageSize, imageSize); // +Z (front)
|
|
161
|
+
ctx.drawImage(imgNegZ, imageSize * 3, imageSize * 1, imageSize, imageSize); // -Z (back)
|
|
162
|
+
|
|
163
|
+
const combinedTexture = new Texture(this, {
|
|
164
|
+
image: canvas,
|
|
165
|
+
flipY: true,
|
|
166
|
+
wrapS: ClampToEdgeWrapping,
|
|
167
|
+
wrapT: ClampToEdgeWrapping,
|
|
168
|
+
encoding: cfg.encoding || LinearEncoding
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return combinedTexture;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error("Error creating combined skybox texture:", error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
76
177
|
|
|
77
178
|
/**
|
|
78
179
|
* Sets the size of this Skybox, given as the distance from the center at [0,0,0] to each face.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plugin, Viewer } from "../../viewer";
|
|
1
|
+
import { Plugin, Viewer, Skybox } from "../../viewer";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* {@link Viewer} plugin that manages skyboxes
|
|
@@ -12,14 +12,21 @@ export declare class SkyboxesPlugin extends Plugin {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Creates a skybox.
|
|
15
|
-
* @param {String} id Unique ID to assign to the skybox.
|
|
16
15
|
* @param {Object} params Skybox configuration.
|
|
16
|
+
* @param {String} [params.id] Optional ID, unique among all components in the parent {Scene}, generated automatically when omitted.
|
|
17
|
+
* @param {String | String[]} [params.src=null] Path to skybox texture
|
|
18
|
+
* @param {Number} [params.encoding=LinearEncoding] Texture encoding format. See the {@link Texture#encoding} property for more info.
|
|
19
|
+
* @param {Number} [params.size=1000] Size of this Skybox, given as the distance from the center at ````[0,0,0]```` to each face.
|
|
17
20
|
* @param {Boolean} [params.active=true] Whether the skybox plane is initially active. Only skyboxes while this is true.
|
|
18
21
|
* @returns {Skybox} The new skybox.
|
|
19
22
|
*/
|
|
20
|
-
createSkybox(
|
|
23
|
+
createSkybox(params: {
|
|
24
|
+
id?: string,
|
|
25
|
+
src: string | string[],
|
|
26
|
+
encoding?: number,
|
|
27
|
+
size?: number,
|
|
21
28
|
active?: boolean;
|
|
22
|
-
}):
|
|
29
|
+
}): Skybox; //Skybox;
|
|
23
30
|
|
|
24
31
|
/**
|
|
25
32
|
* Destroys a skybox.
|
|
@@ -31,4 +38,11 @@ export declare class SkyboxesPlugin extends Plugin {
|
|
|
31
38
|
Destroys all skyboxes.
|
|
32
39
|
*/
|
|
33
40
|
clear(): void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Destroys this plugin.
|
|
44
|
+
*
|
|
45
|
+
* Clears skyboxes from the Viewer first.
|
|
46
|
+
*/
|
|
47
|
+
destroy(): void;
|
|
34
48
|
}
|