@needle-tools/engine 4.10.0-beta.3 → 4.10.0-beta.5
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/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-CLzMgxkO.min.js → needle-engine.bundle-BGVvfSfQ.min.js} +132 -130
- package/dist/{needle-engine.bundle-Ddybtee9.js → needle-engine.bundle-DcvMQ1Nq.js} +6115 -6083
- package/dist/{needle-engine.bundle-Ckr5KE6m.umd.cjs → needle-engine.bundle-DzVytSSo.umd.cjs} +133 -131
- package/dist/needle-engine.js +332 -331
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/codegen/register_types.js +1 -1
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_camera.js +5 -5
- package/lib/engine/engine_camera.js.map +1 -1
- package/lib/engine/engine_gizmos.d.ts +11 -10
- package/lib/engine/engine_gizmos.js +24 -10
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/extensions/extension_utils.js +1 -1
- package/lib/engine/extensions/extension_utils.js.map +1 -1
- package/lib/engine/xr/NeedleXRController.d.ts +3 -3
- package/lib/engine/xr/NeedleXRController.js +28 -0
- package/lib/engine/xr/NeedleXRController.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -1
- package/lib/engine-components/codegen/components.js +1 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/debug/LogStats.d.ts +1 -0
- package/lib/engine-components/debug/LogStats.js +1 -0
- package/lib/engine-components/debug/LogStats.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +2 -1
- package/lib/engine-components/timeline/TimelineTracks.js +24 -19
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.js +36 -34
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +14 -2
- package/lib/engine-components/web/ViewBox.js +39 -23
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/lib/engine-components-experimental/Presentation.d.ts +1 -0
- package/lib/engine-components-experimental/Presentation.js +1 -0
- package/lib/engine-components-experimental/Presentation.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/codegen/register_types.ts +1 -1
- package/src/engine/engine_camera.ts +5 -7
- package/src/engine/engine_gizmos.ts +37 -23
- package/src/engine/extensions/extension_utils.ts +1 -1
- package/src/engine/xr/NeedleXRController.ts +36 -4
- package/src/engine-components/codegen/components.ts +1 -1
- package/src/engine-components/debug/LogStats.ts +1 -0
- package/src/engine-components/timeline/PlayableDirector.ts +1 -1
- package/src/engine-components/timeline/TimelineTracks.ts +24 -19
- package/src/engine-components/web/ScrollFollow.ts +40 -36
- package/src/engine-components/web/ViewBox.ts +45 -27
- package/src/engine-components-experimental/Presentation.ts +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Box3, Object3D } from "three";
|
|
2
2
|
import { element } from "three/src/nodes/TSL.js";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { isDevEnvironment } from "../../engine/debug/debug.js";
|
|
5
5
|
import { Mathf } from "../../engine/engine_math.js";
|
|
6
6
|
import { serializable } from "../../engine/engine_serialization.js";
|
|
7
7
|
import { getBoundingBox, setVisibleInCustomShadowRendering } from "../../engine/engine_three_utils.js";
|
|
@@ -174,8 +174,8 @@ export class ScrollFollow extends Behaviour {
|
|
|
174
174
|
|
|
175
175
|
const value = this.invert ? 1 - this._current_value : this._current_value;
|
|
176
176
|
|
|
177
|
-
const height = this._rangeEndValue - this._rangeStartValue;
|
|
178
|
-
const pixelValue = this._rangeStartValue + value * height;
|
|
177
|
+
// const height = this._rangeEndValue - this._rangeStartValue;
|
|
178
|
+
// const pixelValue = this._rangeStartValue + value * height;
|
|
179
179
|
|
|
180
180
|
// apply scroll to target(s)
|
|
181
181
|
if (Array.isArray(this.target)) {
|
|
@@ -186,7 +186,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
if (debug && this.context.time.frame % 30 === 0) {
|
|
189
|
-
console.debug(`[ScrollFollow] ${this._current_value.toFixed(5)} — ${(this._target_value * 100).toFixed(0)}
|
|
189
|
+
console.debug(`[ScrollFollow] ${this._current_value.toFixed(5)} — ${(this._target_value * 100).toFixed(0)}%, targets [${Array.isArray(this.target) ? this.target.length : 1}]`);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -304,13 +304,15 @@ export class ScrollFollow extends Behaviour {
|
|
|
304
304
|
const index = markerIndex++;
|
|
305
305
|
|
|
306
306
|
// Get marker elements from DOM
|
|
307
|
-
if ((marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (!marker.element?.parentNode))) {
|
|
307
|
+
if ((marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (marker.element && !marker.element?.parentNode))) {
|
|
308
308
|
marker.needsUpdate = false;
|
|
309
309
|
try {
|
|
310
|
+
// TODO: with this it's currently not possible to remap markers from HTML. For example if I have two sections and I want to now use the marker["center"] multiple times to stay at that marker for a longer time
|
|
310
311
|
marker.element = tryGetElementsForSelector(index, marker.name) as HTMLElement | null;
|
|
311
|
-
if (debug) console.debug(
|
|
312
|
+
if (debug) console.debug(`ScrollMarker #${index} "${marker.name}" (${marker.time.toFixed(2)}) found`, marker.element);
|
|
312
313
|
if (!marker.element) {
|
|
313
314
|
marker.timeline = undefined;
|
|
315
|
+
if (debug || isDevEnvironment()) console.warn(`No HTML element found for ScrollMarker: ${marker.name} (index ${index})`);
|
|
314
316
|
continue;
|
|
315
317
|
}
|
|
316
318
|
else {
|
|
@@ -370,19 +372,20 @@ export class ScrollFollow extends Behaviour {
|
|
|
370
372
|
const time01 = calculateTimelinePositionNormalized(timeline);
|
|
371
373
|
// remap 0-1 to 0 - 1 - 0 (full weight at center)
|
|
372
374
|
const weight = 1 - Math.abs(time01 - 0.5) * 2;
|
|
375
|
+
const name = marker.name || `marker${i}`;
|
|
373
376
|
if (time01 > 0 && time01 <= 1) {
|
|
374
377
|
const lerpTime = marker.time + (nextTime - marker.time) * time01;
|
|
375
|
-
weightsArray.push({ time: lerpTime, weight: weight });
|
|
378
|
+
weightsArray.push({ name, time: lerpTime, weight: weight });
|
|
376
379
|
sum += weight;
|
|
377
380
|
}
|
|
378
381
|
// Before the first marker is reached
|
|
379
382
|
else if (i === 0 && time01 <= 0) {
|
|
380
|
-
weightsArray.push({ time: 0, weight: 1 });
|
|
383
|
+
weightsArray.push({ name, time: 0, weight: 1 });
|
|
381
384
|
sum += 1;
|
|
382
385
|
}
|
|
383
386
|
// After the last marker is reached
|
|
384
387
|
else if (i === markersArray.length - 1 && time01 >= 1) {
|
|
385
|
-
weightsArray.push({ time: duration, weight: 1 });
|
|
388
|
+
weightsArray.push({ name, time: duration, weight: 1 });
|
|
386
389
|
sum += 1;
|
|
387
390
|
}
|
|
388
391
|
}
|
|
@@ -435,13 +438,16 @@ export class ScrollFollow extends Behaviour {
|
|
|
435
438
|
time += diff * weight;
|
|
436
439
|
}
|
|
437
440
|
}
|
|
438
|
-
if (debug && this.context.time.frame % 20 === 0) console.log(time.toFixed(3), [...weightsArray])
|
|
439
441
|
if (this.damping <= 0) {
|
|
440
442
|
director.time = time;
|
|
441
443
|
}
|
|
442
444
|
else {
|
|
443
445
|
director.time = Mathf.lerp(director.time, time, this.context.time.deltaTime / this.damping);
|
|
444
446
|
}
|
|
447
|
+
|
|
448
|
+
if (debug && this.context.time.frame % 30 === 0) {
|
|
449
|
+
console.log(`[ScrollFollow ] Timeline ${director.name}: ${time.toFixed(3)}`, weightsArray.map(w => `[${w.name} ${(w.weight * 100).toFixed(0)}%]`).join(", "));
|
|
450
|
+
}
|
|
445
451
|
}
|
|
446
452
|
}
|
|
447
453
|
|
|
@@ -450,9 +456,13 @@ export class ScrollFollow extends Behaviour {
|
|
|
450
456
|
|
|
451
457
|
|
|
452
458
|
const weightsArray: OverlapInfo[] = [];
|
|
453
|
-
const markersArray:
|
|
459
|
+
const markersArray: Array<ScrollMarkerModel & {
|
|
460
|
+
element?: HTMLElement | null,
|
|
461
|
+
timeline?: ViewTimeline,
|
|
462
|
+
}> = [];
|
|
454
463
|
|
|
455
464
|
type OverlapInfo = {
|
|
465
|
+
name: string,
|
|
456
466
|
/** Marker time */
|
|
457
467
|
time: number,
|
|
458
468
|
/** Overlap in pixels */
|
|
@@ -468,17 +478,29 @@ type OverlapInfo = {
|
|
|
468
478
|
// }
|
|
469
479
|
// const querySelectorResults: Array<SelectorCache> = [];
|
|
470
480
|
|
|
471
|
-
const needleScrollMarkerCacheKey = "data-timeline-marker";
|
|
472
481
|
const needleScrollMarkerIndexCache = new Map<number, Element | null>();
|
|
473
482
|
const needleScrollMarkerNameCache = new Map<string, Element | null>();
|
|
474
483
|
let needsScrollMarkerRefresh = true;
|
|
475
484
|
|
|
476
|
-
function tryGetElementsForSelector(index: number, name: string): Element | null {
|
|
485
|
+
function tryGetElementsForSelector(index: number, name: string, _cycle: number = 0): Element | null {
|
|
477
486
|
|
|
478
487
|
if (!needsScrollMarkerRefresh) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
488
|
+
if (name?.length) {
|
|
489
|
+
const element = needleScrollMarkerNameCache.get(name) || null;
|
|
490
|
+
if (element) return element;
|
|
491
|
+
// const isNumber = !isNaN(Number(name));
|
|
492
|
+
// if (!isNumber) {
|
|
493
|
+
// }
|
|
494
|
+
}
|
|
495
|
+
const element = needleScrollMarkerIndexCache.get(index) || null;
|
|
496
|
+
const value = element?.getAttribute("data-timeline-marker");
|
|
497
|
+
// if (value?.length) {
|
|
498
|
+
// if (cycle === 0) {
|
|
499
|
+
// // if the HTML marker we found by index does define a different marker name we try to find the correct HTML element by name
|
|
500
|
+
// return tryGetElementsForSelector(index, value, 1);
|
|
501
|
+
// }
|
|
502
|
+
// if (isDevEnvironment()) console.warn(`ScrollMarker name mismatch: expected "${name}", got "${value}"`);
|
|
503
|
+
// }
|
|
482
504
|
return element;
|
|
483
505
|
}
|
|
484
506
|
needsScrollMarkerRefresh = false;
|
|
@@ -489,26 +511,8 @@ function tryGetElementsForSelector(index: number, name: string): Element | null
|
|
|
489
511
|
const name = m.getAttribute("data-timeline-marker");
|
|
490
512
|
if (name?.length) needleScrollMarkerNameCache.set(name, m);
|
|
491
513
|
});
|
|
492
|
-
|
|
493
|
-
return
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
/* e.g.
|
|
497
|
-
<div class="section behind start" data-needle-scroll-marker>
|
|
498
|
-
*/
|
|
499
|
-
// console.log(index, element)
|
|
500
|
-
if (element) return element;
|
|
501
|
-
|
|
502
|
-
// for (const entry of querySelectorResults) {
|
|
503
|
-
// if (entry.selector === selector) {
|
|
504
|
-
// const index = entry.usedElementCount++;
|
|
505
|
-
// return entry.elements && index < entry.elements.length ? entry.elements[index] : null;
|
|
506
|
-
// }
|
|
507
|
-
// }
|
|
508
|
-
// const elements = document.querySelectorAll(selector);
|
|
509
|
-
// querySelectorResults.push({ selector, elements: Array.from(elements), usedElementCount: 1 });
|
|
510
|
-
// if (elements.length > 0) return elements[0];
|
|
511
|
-
return null;
|
|
514
|
+
needsScrollMarkerRefresh = false;
|
|
515
|
+
return tryGetElementsForSelector(index, name);
|
|
512
516
|
}
|
|
513
517
|
|
|
514
518
|
|
|
@@ -1,54 +1,66 @@
|
|
|
1
|
-
import { Camera, PerspectiveCamera, Vector2, Vector3 } from "three";
|
|
1
|
+
import { Camera, PerspectiveCamera, Quaternion, Scene, Vector2, Vector3 } from "three";
|
|
2
2
|
|
|
3
3
|
import { isDevEnvironment } from "../../engine/debug/debug.js";
|
|
4
4
|
import { Gizmos } from "../../engine/engine_gizmos.js";
|
|
5
5
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getTempVector } from "../../engine/engine_three_utils.js";
|
|
7
7
|
import { registerType } from "../../engine/engine_typestore.js";
|
|
8
8
|
import { getParam } from "../../engine/engine_utils.js";
|
|
9
|
+
import { RGBAColor } from "../../engine/js-extensions/RGBAColor.js";
|
|
9
10
|
import { Behaviour } from "../Component.js";
|
|
10
11
|
|
|
12
|
+
|
|
11
13
|
const debugParam = getParam("debugviewbox");
|
|
14
|
+
const disabledGizmoColor = new RGBAColor(.5, .5, .5, .5);
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* 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
|
+
*
|
|
19
|
+
* Add the ViewBox to an object into your scene
|
|
20
|
+
*/
|
|
13
21
|
@registerType
|
|
14
22
|
export class ViewBox extends Behaviour {
|
|
15
23
|
|
|
16
|
-
static instances: ViewBox[] = [];
|
|
24
|
+
static readonly instances: ViewBox[] = [];
|
|
17
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The reference field of view is used to calculate the box size. This should usually be the same as your camera's fov.
|
|
28
|
+
* @default undefined (meaning it will use the camera fov on the first frame)
|
|
29
|
+
*/
|
|
18
30
|
@serializable()
|
|
19
|
-
referenceFieldOfView: number =
|
|
31
|
+
referenceFieldOfView: number | undefined = undefined;
|
|
20
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Enable debug logs and rendering for this component instance
|
|
35
|
+
*/
|
|
21
36
|
@serializable()
|
|
22
37
|
debug: boolean = false;
|
|
23
38
|
|
|
24
|
-
// awake() {
|
|
25
|
-
// // this.referenceFieldOfView = (this.context.mainCamera as PerspectiveCamera)?.fov || 60;
|
|
26
|
-
// setInterval(()=>{
|
|
27
|
-
// if(Math.random() > .5)
|
|
28
|
-
// this.enabled = !this.enabled
|
|
29
|
-
// }, 1000)
|
|
30
|
-
// }
|
|
31
|
-
|
|
32
39
|
onEnable(): void {
|
|
33
40
|
if (debugParam || this.debug || isDevEnvironment()) console.debug("[ViewBox] Using camera fov:", this.referenceFieldOfView);
|
|
41
|
+
// register instance
|
|
34
42
|
ViewBox.instances.push(this);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
onDisable(): void {
|
|
38
46
|
if (debugParam || this.debug) console.debug("[ViewBox] Disabled");
|
|
47
|
+
// unregister instance
|
|
39
48
|
const idx = ViewBox.instances.indexOf(this);
|
|
40
49
|
if (idx !== -1) ViewBox.instances.splice(idx, 1);
|
|
41
50
|
this._projectedBoxElement?.remove();
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
onBeforeRender() {
|
|
53
|
+
onBeforeRender(): void {
|
|
45
54
|
if (this.context.isInXR) return;
|
|
55
|
+
if (this.destroyed) return;
|
|
46
56
|
const isActive = ViewBox.instances[ViewBox.instances.length - 1] === this;
|
|
47
57
|
if (!isActive) {
|
|
48
|
-
if (debugParam || this.debug)
|
|
58
|
+
if (debugParam || this.debug) {
|
|
59
|
+
Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, disabledGizmoColor);
|
|
60
|
+
}
|
|
49
61
|
return;
|
|
50
62
|
}
|
|
51
|
-
if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00);
|
|
63
|
+
if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00, 0, true, this.gameObject.worldQuaternion);
|
|
52
64
|
|
|
53
65
|
// calculate box size to fit the camera frustrum size at the current position (just scale)
|
|
54
66
|
const camera = this.context.mainCamera;
|
|
@@ -58,6 +70,10 @@ export class ViewBox extends Behaviour {
|
|
|
58
70
|
return;
|
|
59
71
|
}
|
|
60
72
|
|
|
73
|
+
if (this.referenceFieldOfView === undefined) {
|
|
74
|
+
this.referenceFieldOfView = camera.fov;
|
|
75
|
+
}
|
|
76
|
+
|
|
61
77
|
if (this.referenceFieldOfView === undefined || this.referenceFieldOfView <= 0) {
|
|
62
78
|
if (debugParam || this.debug) console.warn("[ViewBox] No valid referenceFieldOfView set, cannot adjust box size:", this.referenceFieldOfView);
|
|
63
79
|
return;
|
|
@@ -94,6 +110,7 @@ export class ViewBox extends Behaviour {
|
|
|
94
110
|
camera.fov = this.referenceFieldOfView;
|
|
95
111
|
camera.updateProjectionMatrix();
|
|
96
112
|
|
|
113
|
+
|
|
97
114
|
const boxPosition = this.gameObject.worldPosition;
|
|
98
115
|
const boxScale = this.gameObject.worldScale;
|
|
99
116
|
|
|
@@ -138,7 +155,7 @@ export class ViewBox extends Behaviour {
|
|
|
138
155
|
const height = 2 * Math.tan(vFOV / 2) * distance; // visible height
|
|
139
156
|
const width = height * camera.aspect; // visible width
|
|
140
157
|
|
|
141
|
-
const projectedBox = this.projectBoxIntoCamera(
|
|
158
|
+
const projectedBox = this.projectBoxIntoCamera(camera, 1);
|
|
142
159
|
// return
|
|
143
160
|
const boxWidth = (projectedBox.maxX - projectedBox.minX);
|
|
144
161
|
const boxHeight = (projectedBox.maxY - projectedBox.minY);
|
|
@@ -189,18 +206,18 @@ export class ViewBox extends Behaviour {
|
|
|
189
206
|
|
|
190
207
|
|
|
191
208
|
|
|
192
|
-
private projectBoxIntoCamera(
|
|
209
|
+
private projectBoxIntoCamera(camera: Camera, _factor: number) {
|
|
193
210
|
const factor = .5 * _factor;
|
|
194
211
|
|
|
195
212
|
const corners = [
|
|
196
|
-
getTempVector(-
|
|
197
|
-
getTempVector(
|
|
198
|
-
getTempVector(-
|
|
199
|
-
getTempVector(
|
|
200
|
-
getTempVector(-
|
|
201
|
-
getTempVector(
|
|
202
|
-
getTempVector(-
|
|
203
|
-
getTempVector(
|
|
213
|
+
getTempVector(-factor, -factor, -factor),
|
|
214
|
+
getTempVector(factor, -factor, -factor),
|
|
215
|
+
getTempVector(-factor, factor, -factor),
|
|
216
|
+
getTempVector(factor, factor, -factor),
|
|
217
|
+
getTempVector(-factor, -factor, factor),
|
|
218
|
+
getTempVector(factor, -factor, factor),
|
|
219
|
+
getTempVector(-factor, factor, factor),
|
|
220
|
+
getTempVector(factor, factor, factor),
|
|
204
221
|
];
|
|
205
222
|
let minX = Number.POSITIVE_INFINITY;
|
|
206
223
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -208,7 +225,7 @@ export class ViewBox extends Behaviour {
|
|
|
208
225
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
209
226
|
for (let i = 0; i < corners.length; i++) {
|
|
210
227
|
const c = corners[i];
|
|
211
|
-
c.
|
|
228
|
+
c.applyMatrix4(this.gameObject.matrixWorld);
|
|
212
229
|
c.project(camera);
|
|
213
230
|
if (c.x < minX) minX = c.x;
|
|
214
231
|
if (c.x > maxX) maxX = c.x;
|
|
@@ -223,7 +240,8 @@ export class ViewBox extends Behaviour {
|
|
|
223
240
|
if (this._projectedBoxElement.parentElement !== this.context.domElement)
|
|
224
241
|
this.context.domElement.appendChild(this._projectedBoxElement);
|
|
225
242
|
this._projectedBoxElement.style.position = "fixed";
|
|
226
|
-
|
|
243
|
+
// dotted but with larger gaps
|
|
244
|
+
this._projectedBoxElement.style.outline = "2px dashed rgba(255,0,0,.5)";
|
|
227
245
|
this._projectedBoxElement.style.left = ((minX * .5 + .5) * this.context.domWidth) + "px";
|
|
228
246
|
this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * this.context.domHeight) + "px";
|
|
229
247
|
this._projectedBoxElement.style.width = ((maxX - minX) * .5 * this.context.domWidth) + "px";
|