@needle-tools/engine 4.11.0-next.c7baa24 → 4.11.0-next.cc37c71
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/{needle-engine.bundle-tE15q9uM.js → needle-engine.bundle-BPZ6emFK.js} +2635 -2570
- package/dist/{needle-engine.bundle-DBDKvYj5.umd.cjs → needle-engine.bundle-CTY0RgBZ.umd.cjs} +97 -97
- package/dist/{needle-engine.bundle-qNZSuWhD.min.js → needle-engine.bundle-JV2ghuCa.min.js} +100 -100
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine-components/OrbitControls.js +2 -0
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/splines/SplineWalker.d.ts +43 -4
- package/lib/engine-components/splines/SplineWalker.js +88 -12
- package/lib/engine-components/splines/SplineWalker.js.map +1 -1
- package/lib/engine-components/web/Clickthrough.d.ts +2 -0
- package/lib/engine-components/web/Clickthrough.js +23 -1
- package/lib/engine-components/web/Clickthrough.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +1 -9
- package/lib/engine-components/web/ScrollFollow.js +12 -28
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +16 -0
- package/lib/engine-components/web/ViewBox.js +35 -3
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/package.json +1 -1
- package/src/engine-components/OrbitControls.ts +5 -2
- package/src/engine-components/splines/SplineWalker.ts +99 -14
- package/src/engine-components/web/Clickthrough.ts +28 -1
- package/src/engine-components/web/ScrollFollow.ts +15 -33
- package/src/engine-components/web/ViewBox.ts +35 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "4.11.0-next.
|
|
3
|
+
"version": "4.11.0-next.cc37c71",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
|
|
5
5
|
"main": "dist/needle-engine.min.js",
|
|
6
6
|
"exports": {
|
|
@@ -711,8 +711,10 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
711
711
|
|
|
712
712
|
|
|
713
713
|
if (this._controls) {
|
|
714
|
-
if (this.debugLog)
|
|
715
|
-
|
|
714
|
+
if (this.debugLog) this._controls.domElement = this.context.renderer.domElement;
|
|
715
|
+
|
|
716
|
+
const viewZoomFactor = 1 / (this.context.focusRectSettings?.zoom || 1);
|
|
717
|
+
|
|
716
718
|
this._controls.enabled = !this._shouldDisable && this._camera === this.context.mainCameraComponent && !this.context.isInXR && !this._activePointerEvents.some(e => e.used);
|
|
717
719
|
this._controls.keys = this.enableKeys ? defaultKeys : disabledKeys;
|
|
718
720
|
this._controls.autoRotate = this.autoRotate;
|
|
@@ -723,6 +725,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
723
725
|
this._controls.enableDamping = this.enableDamping;
|
|
724
726
|
this._controls.dampingFactor = this.dampingFactor;
|
|
725
727
|
this._controls.enablePan = this.enablePan;
|
|
728
|
+
this._controls.panSpeed = viewZoomFactor;
|
|
726
729
|
this._controls.enableRotate = this.enableRotate;
|
|
727
730
|
this._controls.minAzimuthAngle = this.minAzimuthAngle;
|
|
728
731
|
this._controls.maxAzimuthAngle = this.maxAzimuthAngle;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { Object3D } from "three"
|
|
2
|
+
import { Object3D, Vector3 } from "three"
|
|
3
3
|
|
|
4
4
|
import { Mathf } from "../../engine/engine_math.js";
|
|
5
5
|
import { serializeable } from "../../engine/engine_serialization_decorator.js";
|
|
@@ -7,7 +7,10 @@ import { Behaviour } from "../Component.js";
|
|
|
7
7
|
import { SplineContainer } from "./Spline.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Moves an object along a spline.
|
|
10
|
+
* Moves an object along a spline.
|
|
11
|
+
* Use this with a SplineContainer component.
|
|
12
|
+
*
|
|
13
|
+
* - Example http://samples.needle.tools/splines
|
|
11
14
|
*/
|
|
12
15
|
export class SplineWalker extends Behaviour {
|
|
13
16
|
|
|
@@ -17,18 +20,35 @@ export class SplineWalker extends Behaviour {
|
|
|
17
20
|
@serializeable(SplineContainer)
|
|
18
21
|
spline: SplineContainer | null = null;
|
|
19
22
|
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The object to move along the spline.
|
|
25
|
+
* If object is undefined then the spline walker will use it's own object (gameObject).
|
|
26
|
+
* If object is null the spline walker will not move any object.
|
|
21
27
|
* @default undefined
|
|
22
28
|
*/
|
|
23
29
|
@serializeable(Object3D)
|
|
24
30
|
object?: Object3D | null = undefined;
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* If true the object will rotate to look in the direction of the spline while moving along it.
|
|
36
|
+
* @default true
|
|
37
|
+
*/
|
|
38
|
+
@serializeable()
|
|
39
|
+
useLookAt = true;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The object to look at while moving along the spline.
|
|
43
|
+
* If null the object will look in the direction of the spline.
|
|
44
|
+
* This can be disabled by setting useLookAt to false.
|
|
27
45
|
* @default null
|
|
28
46
|
*/
|
|
29
47
|
@serializeable(Object3D)
|
|
30
48
|
lookAt: Object3D | null = null;
|
|
31
|
-
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
32
52
|
/**
|
|
33
53
|
* When clamp is set to true, the position01 value will be clamped between 0 and 1 and the object will not loop the spline.
|
|
34
54
|
* @default false
|
|
@@ -36,7 +56,10 @@ export class SplineWalker extends Behaviour {
|
|
|
36
56
|
@serializeable()
|
|
37
57
|
clamp: boolean = false;
|
|
38
58
|
|
|
39
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* The current position on the spline. The value ranges from 0 (start of the spline curve) to 1 (end of the spline curve)
|
|
61
|
+
*
|
|
62
|
+
* When setting this value, the position will be updated in the next frame.
|
|
40
63
|
* @default 0
|
|
41
64
|
*/
|
|
42
65
|
// @type float
|
|
@@ -46,7 +69,7 @@ export class SplineWalker extends Behaviour {
|
|
|
46
69
|
}
|
|
47
70
|
set position01(v: number) {
|
|
48
71
|
this._position01 = v;
|
|
49
|
-
this.
|
|
72
|
+
this._needsUpdate = true;
|
|
50
73
|
}
|
|
51
74
|
|
|
52
75
|
/** Resets the position to 0 */
|
|
@@ -68,25 +91,60 @@ export class SplineWalker extends Behaviour {
|
|
|
68
91
|
@serializeable()
|
|
69
92
|
duration: number = 10;
|
|
70
93
|
|
|
94
|
+
/**
|
|
95
|
+
* The strength with which the object is pulled to the spline.
|
|
96
|
+
* This can be used to create a "rubber band" effect when the object is moved away from the spline by other forces.
|
|
97
|
+
* A value of 0 means no pull, a value of 1 means the object is always on the spline.
|
|
98
|
+
* @default 1
|
|
99
|
+
*/
|
|
100
|
+
pullStrength: number = 1;
|
|
101
|
+
|
|
71
102
|
|
|
72
103
|
// #region internal
|
|
73
104
|
|
|
74
105
|
private _position01: number = 0;
|
|
106
|
+
private _needsUpdate = false;
|
|
75
107
|
|
|
76
108
|
/** @internal */
|
|
77
109
|
start() {
|
|
78
|
-
if(this.object === undefined) this.object = this.gameObject;
|
|
110
|
+
if (this.object === undefined) this.object = this.gameObject;
|
|
79
111
|
this.updateFromPosition();
|
|
80
112
|
}
|
|
81
113
|
|
|
114
|
+
/** @internal */
|
|
115
|
+
onEnable(): void {
|
|
116
|
+
window.addEventListener("pointerdown", this.onUserInput, { passive: true });
|
|
117
|
+
window.addEventListener("wheel", this.onUserInput, { passive: true });
|
|
118
|
+
}
|
|
119
|
+
/** @internal */
|
|
120
|
+
onDisable(): void {
|
|
121
|
+
window.removeEventListener("pointerdown", this.onUserInput);
|
|
122
|
+
window.removeEventListener("wheel", this.onUserInput);
|
|
123
|
+
}
|
|
124
|
+
private onUserInput = () => {
|
|
125
|
+
if (this.object?.contains(this.context.mainCamera)) {
|
|
126
|
+
this._needsUpdate = false;
|
|
127
|
+
this._performedUpdates += 999;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @internal */
|
|
82
132
|
update() {
|
|
83
133
|
if (this.autoRun) {
|
|
134
|
+
this._needsUpdate = true;
|
|
84
135
|
this._position01 += this.context.time.deltaTime / this.duration;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (this._needsUpdate) {
|
|
139
|
+
this._needsUpdate = false;
|
|
85
140
|
this.updateFromPosition();
|
|
86
141
|
}
|
|
87
142
|
}
|
|
88
143
|
|
|
89
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Updates the position of the object based on the current position01 value.
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
90
148
|
private updateFromPosition() {
|
|
91
149
|
if (!this.spline || !this.spline.curve) return;
|
|
92
150
|
if (!this.object) return;
|
|
@@ -96,11 +154,38 @@ export class SplineWalker extends Behaviour {
|
|
|
96
154
|
|
|
97
155
|
const t = this._position01 >= 1 ? 1 : this._position01 % 1;
|
|
98
156
|
const pt = this.spline.getPointAt(t);
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
157
|
+
|
|
158
|
+
if (this.pullStrength >= 1) {
|
|
159
|
+
this.object.worldPosition = pt;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
if (this._position01 !== this._lastPosition01) {
|
|
163
|
+
this._performedUpdates = 0;
|
|
164
|
+
}
|
|
165
|
+
this._requiredUpdates = Math.round(100 / this.pullStrength);
|
|
166
|
+
if (this._performedUpdates < this._requiredUpdates) {
|
|
167
|
+
const wp = this.object.worldPosition;
|
|
168
|
+
this._performedUpdates++;
|
|
169
|
+
const pull = Mathf.clamp01(this.pullStrength);
|
|
170
|
+
const newPosition = this.object.worldPosition = wp.lerp(pt, pull * (this.context.time.deltaTime / .3));
|
|
171
|
+
this._lastPositionVector.copy(newPosition);
|
|
172
|
+
this._needsUpdate = true;
|
|
173
|
+
}
|
|
103
174
|
}
|
|
104
|
-
|
|
175
|
+
|
|
176
|
+
if (this.useLookAt) {
|
|
177
|
+
if (!this.lookAt) {
|
|
178
|
+
const tan = this.spline.getTangentAt(t);
|
|
179
|
+
this.object.lookAt(pt.add(tan));
|
|
180
|
+
}
|
|
181
|
+
else this.object.lookAt(this.lookAt.worldPosition);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this._lastPosition01 = this._position01;
|
|
105
185
|
}
|
|
186
|
+
|
|
187
|
+
private _lastPosition01 = 0;
|
|
188
|
+
private _requiredUpdates: number = 0;
|
|
189
|
+
private _performedUpdates: number = 0;
|
|
190
|
+
private _lastPositionVector = new Vector3();
|
|
106
191
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Vector2 } from "three";
|
|
1
2
|
import { NEPointerEvent } from "../../engine/engine_input.js";
|
|
2
3
|
import { onStart } from "../../engine/engine_lifecycle_api.js";
|
|
3
4
|
import { addAttributeChangeCallback } from "../../engine/engine_utils.js";
|
|
@@ -45,12 +46,14 @@ export class ClickThrough extends Behaviour {
|
|
|
45
46
|
this.context.input.addEventListener('pointermove', this.onPointerEvent, {
|
|
46
47
|
queue: 100,
|
|
47
48
|
});
|
|
49
|
+
window.addEventListener("touchstart", this.onTouchStart, { passive: true });
|
|
48
50
|
window.addEventListener("touchend", this.onTouchEnd, { passive: true });
|
|
49
51
|
this._previousPointerEvents = this.context.domElement.style.pointerEvents;
|
|
50
52
|
}
|
|
51
53
|
onDisable() {
|
|
52
54
|
this.context.input.removeEventListener('pointerdown', this.onPointerEvent);
|
|
53
55
|
this.context.input.removeEventListener('pointermove', this.onPointerEvent);
|
|
56
|
+
window.removeEventListener("touchstart", this.onTouchStart);
|
|
54
57
|
window.removeEventListener("touchend", this.onTouchEnd);
|
|
55
58
|
this.context.domElement.style.pointerEvents = this._previousPointerEvents;
|
|
56
59
|
}
|
|
@@ -69,9 +72,33 @@ export class ClickThrough extends Behaviour {
|
|
|
69
72
|
}
|
|
70
73
|
};
|
|
71
74
|
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// #region Touch hack
|
|
78
|
+
|
|
79
|
+
private _touchDidHitAnything = false;
|
|
80
|
+
|
|
81
|
+
private onTouchStart = (_evt: TouchEvent) => {
|
|
82
|
+
|
|
83
|
+
const touch = _evt.touches[0];
|
|
84
|
+
if (!touch) return;
|
|
85
|
+
|
|
86
|
+
const ndx = touch.clientX / window.innerWidth * 2 - 1;
|
|
87
|
+
const ndy = -(touch.clientY / window.innerHeight) * 2 + 1;
|
|
88
|
+
// console.log(ndx, ndy);
|
|
89
|
+
const hits = this.context.physics.raycast({
|
|
90
|
+
screenPoint: new Vector2(ndx, ndy),
|
|
91
|
+
})
|
|
92
|
+
if (hits.length > 0) {
|
|
93
|
+
this._touchDidHitAnything = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
72
97
|
private onTouchEnd = (_evt: TouchEvent) => {
|
|
98
|
+
const _didHit = this._touchDidHitAnything;
|
|
99
|
+
this._touchDidHitAnything = false;
|
|
73
100
|
setTimeout(() => {
|
|
74
|
-
this.context.domElement.style.pointerEvents = 'all';
|
|
101
|
+
if (_didHit) this.context.domElement.style.pointerEvents = 'all';
|
|
75
102
|
}, 100);
|
|
76
103
|
}
|
|
77
104
|
}
|
|
@@ -96,6 +96,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
96
96
|
@serializable()
|
|
97
97
|
invert: boolean = false;
|
|
98
98
|
|
|
99
|
+
|
|
99
100
|
/**
|
|
100
101
|
* **Experimental - might change in future updates**
|
|
101
102
|
* If set, the scroll position will be read from the specified element instead of the window.
|
|
@@ -106,7 +107,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
106
107
|
htmlSelector: string | null = null;
|
|
107
108
|
|
|
108
109
|
@serializable()
|
|
109
|
-
|
|
110
|
+
mode: "window" = "window";
|
|
110
111
|
|
|
111
112
|
/**
|
|
112
113
|
* Event fired when the scroll position changes
|
|
@@ -126,11 +127,6 @@ export class ScrollFollow extends Behaviour {
|
|
|
126
127
|
private _appliedValue: number = -1;
|
|
127
128
|
|
|
128
129
|
|
|
129
|
-
private _scrollStart: number = 0;
|
|
130
|
-
private _scrollEnd: number = 0;
|
|
131
|
-
private _scrollValue: number = 0;
|
|
132
|
-
private _scrollContainerHeight: number = 0;
|
|
133
|
-
|
|
134
130
|
/** @internal */
|
|
135
131
|
onEnable() {
|
|
136
132
|
window.addEventListener("wheel", this.updateCurrentScrollValue, { passive: true });
|
|
@@ -159,8 +155,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
159
155
|
}
|
|
160
156
|
}
|
|
161
157
|
|
|
162
|
-
|
|
163
|
-
{
|
|
158
|
+
if (this._current_value !== this._appliedValue) {
|
|
164
159
|
this._appliedValue = this._current_value;
|
|
165
160
|
|
|
166
161
|
let defaultPrevented = false;
|
|
@@ -182,9 +177,6 @@ export class ScrollFollow extends Behaviour {
|
|
|
182
177
|
|
|
183
178
|
const value = this.invert ? 1 - this._current_value : this._current_value;
|
|
184
179
|
|
|
185
|
-
// const height = this._rangeEndValue - this._rangeStartValue;
|
|
186
|
-
// const pixelValue = this._rangeStartValue + value * height;
|
|
187
|
-
|
|
188
180
|
// apply scroll to target(s)
|
|
189
181
|
if (Array.isArray(this.target)) {
|
|
190
182
|
this.target.forEach(t => t && this.applyScroll(t, value));
|
|
@@ -202,16 +194,11 @@ export class ScrollFollow extends Behaviour {
|
|
|
202
194
|
|
|
203
195
|
private _lastSelectorValue: string | null = null;
|
|
204
196
|
private _lastSelectorElement: Element | null = null;
|
|
205
|
-
/** Top y */
|
|
206
|
-
private _rangeStartValue: number = 0;
|
|
207
|
-
/** Bottom y */
|
|
208
|
-
private _rangeEndValue: number = 0;
|
|
209
197
|
|
|
210
198
|
private updateCurrentScrollValue = () => {
|
|
211
199
|
|
|
212
200
|
switch (this.mode) {
|
|
213
201
|
case "window":
|
|
214
|
-
|
|
215
202
|
if (this.htmlSelector?.length) {
|
|
216
203
|
if (this.htmlSelector !== this._lastSelectorValue) {
|
|
217
204
|
this._lastSelectorElement = document.querySelector(this.htmlSelector);
|
|
@@ -219,26 +206,20 @@ export class ScrollFollow extends Behaviour {
|
|
|
219
206
|
}
|
|
220
207
|
if (this._lastSelectorElement) {
|
|
221
208
|
const rect = this._lastSelectorElement.getBoundingClientRect();
|
|
222
|
-
|
|
223
|
-
this._scrollStart = rect.top + window.scrollY;
|
|
224
|
-
this._scrollEnd = rect.height - window.innerHeight;
|
|
225
|
-
this._scrollValue = -rect.top;
|
|
226
209
|
this._target_value = -rect.top / (rect.height - window.innerHeight);
|
|
227
|
-
this._rangeStartValue = rect.top + window.scrollY;
|
|
228
|
-
this._rangeEndValue = this._rangeStartValue + rect.height - window.innerHeight;
|
|
229
|
-
this._scrollContainerHeight = rect.height;
|
|
230
210
|
break;
|
|
231
211
|
}
|
|
232
212
|
}
|
|
233
213
|
else {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
214
|
+
if (window.document.body.scrollHeight <= window.innerHeight) {
|
|
215
|
+
// If the page is not scrollable we can still increment the scroll value to allow triggering timelines etc.
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const diff = window.document.body.scrollHeight - window.innerHeight;
|
|
219
|
+
this._target_value = window.scrollY / (diff || 1);
|
|
220
|
+
}
|
|
241
221
|
}
|
|
222
|
+
|
|
242
223
|
break;
|
|
243
224
|
}
|
|
244
225
|
|
|
@@ -272,11 +253,12 @@ export class ScrollFollow extends Behaviour {
|
|
|
272
253
|
target.intensity = value;
|
|
273
254
|
}
|
|
274
255
|
else if (target instanceof Object3D) {
|
|
256
|
+
const t = target as any;
|
|
275
257
|
// When objects are assigned they're expected to move vertically based on scroll
|
|
276
|
-
if (
|
|
277
|
-
|
|
258
|
+
if (t["needle:scrollbounds"] === undefined) {
|
|
259
|
+
t["needle:scrollbounds"] = getBoundingBox(target) || null;
|
|
278
260
|
}
|
|
279
|
-
const bounds =
|
|
261
|
+
const bounds = t["needle:scrollbounds"] as Box3;
|
|
280
262
|
if (bounds) {
|
|
281
263
|
// TODO: remap position to use upper screen edge and lower edge instead of center
|
|
282
264
|
target.position.y = -bounds.min.y - value * (bounds.max.y - bounds.min.y);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Camera, Matrix4, PerspectiveCamera,
|
|
1
|
+
import { Camera, Matrix4, PerspectiveCamera, Vector3 } from "three";
|
|
2
2
|
|
|
3
3
|
import { isDevEnvironment } from "../../engine/debug/debug.js";
|
|
4
4
|
import { Gizmos } from "../../engine/engine_gizmos.js";
|
|
@@ -13,6 +13,8 @@ import { Behaviour } from "../Component.js";
|
|
|
13
13
|
const debugParam = getParam("debugviewbox");
|
|
14
14
|
const disabledGizmoColor = new RGBAColor(.5, .5, .5, .5);
|
|
15
15
|
|
|
16
|
+
export type ViewBoxMode = "continuous" | "once";
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* This component can be used to automatically fit a certain box area into the camera view - no matter your screen size or aspect ratio.
|
|
18
20
|
* This is useful for example to frame a character or object in the center of the screen and ensure it is always fully visible. You can also animate or scale the viewbox to create zoom or framing effects.
|
|
@@ -36,6 +38,9 @@ const disabledGizmoColor = new RGBAColor(.5, .5, .5, .5);
|
|
|
36
38
|
@registerType
|
|
37
39
|
export class ViewBox extends Behaviour {
|
|
38
40
|
|
|
41
|
+
/**
|
|
42
|
+
* All active ViewBox instances. The last one in the array is the currently active one.
|
|
43
|
+
*/
|
|
39
44
|
static readonly instances: ViewBox[] = [];
|
|
40
45
|
|
|
41
46
|
/**
|
|
@@ -45,21 +50,39 @@ export class ViewBox extends Behaviour {
|
|
|
45
50
|
@serializable()
|
|
46
51
|
referenceFieldOfView: number = -1;
|
|
47
52
|
|
|
53
|
+
/**
|
|
54
|
+
* The mode determines if the viewbox should be applied once or continuously while it is the active viewbox.
|
|
55
|
+
* Options:
|
|
56
|
+
* - "once": The viewbox will be applied once when it becomes the active viewbox. This is useful if you want to fit the view once and then allow the user to zoom or pan freely.
|
|
57
|
+
* - "continuous": The viewbox will be applied continuously while it is the active viewbox. This is useful if you animate or scale the viewbox over time.
|
|
58
|
+
*/
|
|
59
|
+
@serializable()
|
|
60
|
+
get mode() { return this._mode; }
|
|
61
|
+
set mode(v: ViewBoxMode) {
|
|
62
|
+
if (v === this._mode) return;
|
|
63
|
+
this._mode = v;
|
|
64
|
+
if (v === "once") this._applyCount = 0;
|
|
65
|
+
if (debugParam || this.debug) console.debug("[ViewBox] Set mode:", v);
|
|
66
|
+
}
|
|
67
|
+
private _mode: ViewBoxMode = "continuous";
|
|
68
|
+
|
|
48
69
|
/**
|
|
49
70
|
* Enable debug logs and rendering for this component instance
|
|
50
71
|
*/
|
|
51
72
|
@serializable()
|
|
52
73
|
debug: boolean = false;
|
|
53
74
|
|
|
75
|
+
/** @internal */
|
|
54
76
|
onEnable(): void {
|
|
55
77
|
if (debugParam || this.debug || isDevEnvironment()) console.debug("[ViewBox] Using camera fov:", this.referenceFieldOfView);
|
|
56
78
|
// register instance
|
|
57
79
|
ViewBox.instances.push(this);
|
|
58
|
-
|
|
80
|
+
this._applyCount = 0;
|
|
59
81
|
this.removeUpdateCallback();
|
|
60
82
|
this.context.pre_render_callbacks.push(this.internalUpdate);
|
|
61
83
|
}
|
|
62
84
|
|
|
85
|
+
/** @internal */
|
|
63
86
|
onDisable(): void {
|
|
64
87
|
if (debugParam || this.debug) console.debug("[ViewBox] Disabled");
|
|
65
88
|
// unregister instance
|
|
@@ -77,6 +100,7 @@ export class ViewBox extends Behaviour {
|
|
|
77
100
|
|
|
78
101
|
private static readonly _tempProjectionMatrix: Matrix4 = new Matrix4();
|
|
79
102
|
private static readonly _tempProjectionMatrixInverse: Matrix4 = new Matrix4();
|
|
103
|
+
private _applyCount = 0;
|
|
80
104
|
|
|
81
105
|
private internalUpdate = () => {
|
|
82
106
|
if (this.context.isInXR) return;
|
|
@@ -108,6 +132,11 @@ export class ViewBox extends Behaviour {
|
|
|
108
132
|
return;
|
|
109
133
|
}
|
|
110
134
|
|
|
135
|
+
if (this._applyCount >= 1 && this.mode === "once") {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
this._applyCount++;
|
|
139
|
+
|
|
111
140
|
const domWidth = this.context.domWidth;
|
|
112
141
|
const domHeight = this.context.domHeight;
|
|
113
142
|
|
|
@@ -129,7 +158,7 @@ export class ViewBox extends Behaviour {
|
|
|
129
158
|
ViewBox._tempProjectionMatrix.copy(camera.projectionMatrix);
|
|
130
159
|
ViewBox._tempProjectionMatrixInverse.copy(camera.projectionMatrixInverse);
|
|
131
160
|
const view = camera.view;
|
|
132
|
-
const
|
|
161
|
+
const cameraZoom = camera.zoom;
|
|
133
162
|
const aspect = camera.aspect;
|
|
134
163
|
const fov = camera.fov;
|
|
135
164
|
// Set values to default so we can calculate the box size correctly
|
|
@@ -194,6 +223,7 @@ export class ViewBox extends Behaviour {
|
|
|
194
223
|
width / diffWidth,
|
|
195
224
|
height / diffHeight
|
|
196
225
|
);
|
|
226
|
+
const zoom = scale / (height * .5);
|
|
197
227
|
// console.log({ scale, width, height, boxWidth: boxWidth * camera.aspect, boxHeight, diffWidth, diffHeight, aspect: camera.aspect, distance })
|
|
198
228
|
// this.context.focusRectSettings.zoom = 1.39;
|
|
199
229
|
// if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
|
|
@@ -202,13 +232,13 @@ export class ViewBox extends Behaviour {
|
|
|
202
232
|
vec.project(camera);
|
|
203
233
|
this.context.focusRectSettings.offsetX = vec.x;
|
|
204
234
|
this.context.focusRectSettings.offsetY = vec.y;
|
|
205
|
-
this.context.focusRectSettings.zoom =
|
|
235
|
+
this.context.focusRectSettings.zoom = zoom;
|
|
206
236
|
// if we don't have a focus rect yet, set it to the dom element
|
|
207
237
|
if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
|
|
208
238
|
|
|
209
239
|
// Reset values
|
|
210
240
|
camera.view = view;
|
|
211
|
-
camera.zoom =
|
|
241
|
+
camera.zoom = cameraZoom;
|
|
212
242
|
camera.aspect = aspect;
|
|
213
243
|
camera.fov = fov;
|
|
214
244
|
camera.projectionMatrix.copy(ViewBox._tempProjectionMatrix);
|