@needle-tools/engine 4.11.0 → 4.11.1-next.0bef517
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/CHANGELOG.md +6 -0
- package/README.md +3 -1
- package/dist/{needle-engine.bundle-IPerDSpg.min.js → needle-engine.bundle-CETlDRXF.min.js} +100 -100
- package/dist/{needle-engine.bundle-DhRclTK5.umd.cjs → needle-engine.bundle-DgECSnSa.umd.cjs} +97 -97
- package/dist/{needle-engine.bundle-Dh4X0Qy5.js → needle-engine.bundle-GfmZcJ_E.js} +2646 -2575
- 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/engine_physics.js +2 -1
- package/lib/engine/engine_physics.js.map +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 +4 -9
- package/lib/engine-components/web/ScrollFollow.js +26 -30
- 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 +2 -2
- package/plugins/types/userconfig.d.ts +7 -0
- package/plugins/vite/build-pipeline.js +14 -6
- package/src/engine/engine_physics.ts +3 -3
- package/src/engine-components/OrbitControls.ts +5 -2
- package/src/engine-components/splines/SplineWalker.ts +99 -14
- package/src/engine-components/web/Clickthrough.ts +29 -1
- package/src/engine-components/web/ScrollFollow.ts +31 -33
- package/src/engine-components/web/ViewBox.ts +35 -5
|
@@ -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
|
|
@@ -124,17 +125,19 @@ export class ScrollFollow extends Behaviour {
|
|
|
124
125
|
private _current_value: number = 0;
|
|
125
126
|
private _target_value: number = 0;
|
|
126
127
|
private _appliedValue: number = -1;
|
|
128
|
+
private _needsUpdate = false;
|
|
129
|
+
private _firstUpdate = false;
|
|
127
130
|
|
|
131
|
+
awake() {
|
|
132
|
+
this._firstUpdate = true;
|
|
133
|
+
}
|
|
128
134
|
|
|
129
|
-
private _scrollStart: number = 0;
|
|
130
|
-
private _scrollEnd: number = 0;
|
|
131
|
-
private _scrollValue: number = 0;
|
|
132
|
-
private _scrollContainerHeight: number = 0;
|
|
133
135
|
|
|
134
136
|
/** @internal */
|
|
135
137
|
onEnable() {
|
|
136
138
|
window.addEventListener("wheel", this.updateCurrentScrollValue, { passive: true });
|
|
137
139
|
this._appliedValue = -1;
|
|
140
|
+
this._needsUpdate = true;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
/** @internal */
|
|
@@ -148,7 +151,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
148
151
|
this.updateCurrentScrollValue();
|
|
149
152
|
|
|
150
153
|
if (this._target_value >= 0) {
|
|
151
|
-
if (this.damping > 0) { // apply damping
|
|
154
|
+
if (this.damping > 0 && !this._firstUpdate) { // apply damping
|
|
152
155
|
this._current_value = Mathf.lerp(this._current_value, this._target_value, this.context.time.deltaTime / this.damping);
|
|
153
156
|
if (Math.abs(this._current_value - this._target_value) < 0.001) {
|
|
154
157
|
this._current_value = this._target_value;
|
|
@@ -159,9 +162,10 @@ export class ScrollFollow extends Behaviour {
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
|
|
165
|
+
if (this._needsUpdate || this._current_value !== this._appliedValue)
|
|
163
166
|
{
|
|
164
167
|
this._appliedValue = this._current_value;
|
|
168
|
+
this._needsUpdate = false;
|
|
165
169
|
|
|
166
170
|
let defaultPrevented = false;
|
|
167
171
|
if (this.changed.listenerCount > 0) {
|
|
@@ -182,9 +186,6 @@ export class ScrollFollow extends Behaviour {
|
|
|
182
186
|
|
|
183
187
|
const value = this.invert ? 1 - this._current_value : this._current_value;
|
|
184
188
|
|
|
185
|
-
// const height = this._rangeEndValue - this._rangeStartValue;
|
|
186
|
-
// const pixelValue = this._rangeStartValue + value * height;
|
|
187
|
-
|
|
188
189
|
// apply scroll to target(s)
|
|
189
190
|
if (Array.isArray(this.target)) {
|
|
190
191
|
this.target.forEach(t => t && this.applyScroll(t, value));
|
|
@@ -197,21 +198,18 @@ export class ScrollFollow extends Behaviour {
|
|
|
197
198
|
console.debug(`[ScrollFollow] ${this._current_value.toFixed(5)} — ${(this._target_value * 100).toFixed(0)}%, targets [${Array.isArray(this.target) ? this.target.length : 1}]`);
|
|
198
199
|
}
|
|
199
200
|
}
|
|
201
|
+
|
|
202
|
+
this._firstUpdate = false;
|
|
200
203
|
}
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
private _lastSelectorValue: string | null = null;
|
|
204
207
|
private _lastSelectorElement: Element | null = null;
|
|
205
|
-
/** Top y */
|
|
206
|
-
private _rangeStartValue: number = 0;
|
|
207
|
-
/** Bottom y */
|
|
208
|
-
private _rangeEndValue: number = 0;
|
|
209
208
|
|
|
210
209
|
private updateCurrentScrollValue = () => {
|
|
211
210
|
|
|
212
211
|
switch (this.mode) {
|
|
213
212
|
case "window":
|
|
214
|
-
|
|
215
213
|
if (this.htmlSelector?.length) {
|
|
216
214
|
if (this.htmlSelector !== this._lastSelectorValue) {
|
|
217
215
|
this._lastSelectorElement = document.querySelector(this.htmlSelector);
|
|
@@ -219,26 +217,20 @@ export class ScrollFollow extends Behaviour {
|
|
|
219
217
|
}
|
|
220
218
|
if (this._lastSelectorElement) {
|
|
221
219
|
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
220
|
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
221
|
break;
|
|
231
222
|
}
|
|
232
223
|
}
|
|
233
224
|
else {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
225
|
+
if (window.document.body.scrollHeight <= window.innerHeight) {
|
|
226
|
+
// If the page is not scrollable we can still increment the scroll value to allow triggering timelines etc.
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const diff = window.document.body.scrollHeight - window.innerHeight;
|
|
230
|
+
this._target_value = window.scrollY / (diff || 1);
|
|
231
|
+
}
|
|
241
232
|
}
|
|
233
|
+
|
|
242
234
|
break;
|
|
243
235
|
}
|
|
244
236
|
|
|
@@ -272,11 +264,12 @@ export class ScrollFollow extends Behaviour {
|
|
|
272
264
|
target.intensity = value;
|
|
273
265
|
}
|
|
274
266
|
else if (target instanceof Object3D) {
|
|
267
|
+
const t = target as any;
|
|
275
268
|
// When objects are assigned they're expected to move vertically based on scroll
|
|
276
|
-
if (
|
|
277
|
-
|
|
269
|
+
if (t["needle:scrollbounds"] === undefined) {
|
|
270
|
+
t["needle:scrollbounds"] = getBoundingBox(target) || null;
|
|
278
271
|
}
|
|
279
|
-
const bounds =
|
|
272
|
+
const bounds = t["needle:scrollbounds"] as Box3;
|
|
280
273
|
if (bounds) {
|
|
281
274
|
// TODO: remap position to use upper screen edge and lower edge instead of center
|
|
282
275
|
target.position.y = -bounds.min.y - value * (bounds.max.y - bounds.min.y);
|
|
@@ -422,13 +415,18 @@ export class ScrollFollow extends Behaviour {
|
|
|
422
415
|
time += diff * weight;
|
|
423
416
|
}
|
|
424
417
|
}
|
|
425
|
-
if (this.damping <= 0) {
|
|
418
|
+
if (this.damping <= 0 || this._firstUpdate) {
|
|
426
419
|
director.time = time;
|
|
427
420
|
}
|
|
428
421
|
else {
|
|
429
422
|
director.time = Mathf.lerp(director.time, time, this.context.time.deltaTime / this.damping);
|
|
430
423
|
}
|
|
431
424
|
|
|
425
|
+
const delta = Math.abs(director.time - time);
|
|
426
|
+
if (delta > .001) { // if the time is > 1/100th of a second off we need another update
|
|
427
|
+
this._needsUpdate = true;
|
|
428
|
+
}
|
|
429
|
+
|
|
432
430
|
if (debug && this.context.time.frame % 30 === 0) {
|
|
433
431
|
console.log(`[ScrollFollow ] Timeline ${director.name}: ${time.toFixed(3)}`, weightsArray.map(w => `[${w.name} ${(w.weight * 100).toFixed(0)}%]`).join(", "));
|
|
434
432
|
}
|
|
@@ -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);
|