@needle-tools/engine 4.10.0-next.4f9d92a → 4.10.0-next.f0ec242

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.
Files changed (68) hide show
  1. package/components.needle.json +1 -1
  2. package/dist/{needle-engine.bundle-BeZ_xmJa.js → needle-engine.bundle-BC-0Ex9m.js} +5301 -5485
  3. package/dist/{needle-engine.bundle-C3bpSNYu.min.js → needle-engine.bundle-BSh7dSEx.min.js} +154 -165
  4. package/dist/{needle-engine.bundle-D4dO0t5I.umd.cjs → needle-engine.bundle-dgNq9Vsa.umd.cjs} +131 -142
  5. package/dist/needle-engine.d.ts +15 -15
  6. package/dist/needle-engine.js +257 -258
  7. package/dist/needle-engine.min.js +1 -1
  8. package/dist/needle-engine.umd.cjs +1 -1
  9. package/lib/engine/codegen/register_types.js +0 -2
  10. package/lib/engine/codegen/register_types.js.map +1 -1
  11. package/lib/engine/engine_camera.d.ts +1 -7
  12. package/lib/engine/engine_camera.js +6 -46
  13. package/lib/engine/engine_camera.js.map +1 -1
  14. package/lib/engine/engine_context.d.ts +0 -6
  15. package/lib/engine/engine_context.js +9 -48
  16. package/lib/engine/engine_context.js.map +1 -1
  17. package/lib/engine/extensions/NEEDLE_lightmaps.js +1 -1
  18. package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
  19. package/lib/engine/webcomponents/logo-element.d.ts +1 -1
  20. package/lib/engine/webcomponents/logo-element.js +5 -29
  21. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  22. package/lib/engine/webcomponents/needle menu/needle-menu.js +3 -4
  23. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  24. package/lib/engine/webcomponents/needle-engine.loading.d.ts +1 -0
  25. package/lib/engine/webcomponents/needle-engine.loading.js +36 -3
  26. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  27. package/lib/engine-components/Renderer.js +1 -6
  28. package/lib/engine-components/Renderer.js.map +1 -1
  29. package/lib/engine-components/codegen/components.d.ts +0 -1
  30. package/lib/engine-components/codegen/components.js +0 -1
  31. package/lib/engine-components/codegen/components.js.map +1 -1
  32. package/lib/engine-components/timeline/PlayableDirector.d.ts +0 -7
  33. package/lib/engine-components/timeline/PlayableDirector.js +0 -7
  34. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  35. package/lib/engine-components/timeline/TimelineModels.d.ts +1 -9
  36. package/lib/engine-components/timeline/TimelineTracks.js +2 -4
  37. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  38. package/lib/engine-components/utils/LookAt.js +1 -5
  39. package/lib/engine-components/utils/LookAt.js.map +1 -1
  40. package/lib/engine-components/web/Clickthrough.js +2 -10
  41. package/lib/engine-components/web/Clickthrough.js.map +1 -1
  42. package/lib/engine-components/web/ScrollFollow.d.ts +0 -22
  43. package/lib/engine-components/web/ScrollFollow.js +14 -126
  44. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  45. package/lib/engine-components/web/index.d.ts +0 -1
  46. package/lib/engine-components/web/index.js +0 -1
  47. package/lib/engine-components/web/index.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/engine/codegen/register_types.ts +0 -2
  50. package/src/engine/engine_camera.ts +8 -62
  51. package/src/engine/engine_context.ts +10 -50
  52. package/src/engine/extensions/NEEDLE_lightmaps.ts +1 -1
  53. package/src/engine/webcomponents/logo-element.ts +4 -29
  54. package/src/engine/webcomponents/needle menu/needle-menu.ts +3 -4
  55. package/src/engine/webcomponents/needle-engine.loading.ts +32 -32
  56. package/src/engine-components/Renderer.ts +1 -6
  57. package/src/engine-components/codegen/components.ts +0 -1
  58. package/src/engine-components/timeline/PlayableDirector.ts +0 -9
  59. package/src/engine-components/timeline/TimelineModels.ts +1 -9
  60. package/src/engine-components/timeline/TimelineTracks.ts +2 -4
  61. package/src/engine-components/utils/LookAt.ts +1 -5
  62. package/src/engine-components/web/Clickthrough.ts +2 -11
  63. package/src/engine-components/web/ScrollFollow.ts +15 -151
  64. package/src/engine-components/web/index.ts +1 -2
  65. package/lib/engine-components/web/ViewBox.d.ts +0 -16
  66. package/lib/engine-components/web/ViewBox.js +0 -186
  67. package/lib/engine-components/web/ViewBox.js.map +0 -1
  68. package/src/engine-components/web/ViewBox.ts +0 -202
@@ -374,40 +374,40 @@ export class EngineLoadingView implements ILoadingViewHandler {
374
374
  // }
375
375
  // }
376
376
 
377
- // this.handleRuntimeLicense(this._loadingElement);
377
+ this.handleRuntimeLicense(this._loadingElement);
378
378
 
379
379
  return this._loadingElement;
380
380
  }
381
381
 
382
- // private async handleRuntimeLicense(loadingElement: HTMLElement) {
383
- // // First check if we have a commercial license
384
- // let commercialLicense = hasCommercialLicense();
385
- // // if it's the case then we don't need to perform a runtime check
386
- // if (commercialLicense) return;
387
-
388
- // // If we don't have a commercial license, then we need to display our message
389
- // if (debugLicense) console.log("Loading UI has commercial license?", commercialLicense);
390
- // const nonCommercialContainer = document.createElement("div");
391
- // nonCommercialContainer.style.paddingTop = ".6em";
392
- // nonCommercialContainer.style.fontSize = ".8em";
393
- // nonCommercialContainer.style.textTransform = "uppercase";
394
- // nonCommercialContainer.innerText = "NEEDLE ENGINE NON COMMERCIAL VERSION\nCLICK HERE TO GET A LICENSE";
395
- // nonCommercialContainer.style.cursor = "pointer";
396
- // nonCommercialContainer.style.userSelect = "none";
397
- // nonCommercialContainer.style.textAlign = "center";
398
- // nonCommercialContainer.style.pointerEvents = "all";
399
- // nonCommercialContainer.addEventListener("click", () => window.open("https://needle.tools/pricing", "_self"));
400
- // nonCommercialContainer.style.opacity = "0";
401
- // loadingElement.appendChild(nonCommercialContainer);
402
-
403
- // // Use the runtime license check
404
- // if (!isDevEnvironment() && runtimeLicenseCheckPromise) {
405
- // if (debugLicense) console.log("Waiting for runtime license check");
406
- // await runtimeLicenseCheckPromise;
407
- // commercialLicense = hasCommercialLicense();
408
- // }
409
- // if (commercialLicense) return;
410
- // nonCommercialContainer.style.transition = "opacity .5s ease-in-out";
411
- // nonCommercialContainer.style.opacity = "1";
412
- // }
382
+ private async handleRuntimeLicense(loadingElement: HTMLElement) {
383
+ // First check if we have a commercial license
384
+ let commercialLicense = hasCommercialLicense();
385
+ // if it's the case then we don't need to perform a runtime check
386
+ if (commercialLicense) return;
387
+
388
+ // If we don't have a commercial license, then we need to display our message
389
+ if (debugLicense) console.log("Loading UI has commercial license?", commercialLicense);
390
+ const nonCommercialContainer = document.createElement("div");
391
+ nonCommercialContainer.style.paddingTop = ".6em";
392
+ nonCommercialContainer.style.fontSize = ".8em";
393
+ nonCommercialContainer.style.textTransform = "uppercase";
394
+ nonCommercialContainer.innerText = "NEEDLE ENGINE NON COMMERCIAL VERSION\nCLICK HERE TO GET A LICENSE";
395
+ nonCommercialContainer.style.cursor = "pointer";
396
+ nonCommercialContainer.style.userSelect = "none";
397
+ nonCommercialContainer.style.textAlign = "center";
398
+ nonCommercialContainer.style.pointerEvents = "all";
399
+ nonCommercialContainer.addEventListener("click", () => window.open("https://needle.tools/pricing", "_self"));
400
+ nonCommercialContainer.style.opacity = "0";
401
+ loadingElement.appendChild(nonCommercialContainer);
402
+
403
+ // Use the runtime license check
404
+ if (!isDevEnvironment() && runtimeLicenseCheckPromise) {
405
+ if (debugLicense) console.log("Waiting for runtime license check");
406
+ await runtimeLicenseCheckPromise;
407
+ commercialLicense = hasCommercialLicense();
408
+ }
409
+ if (commercialLicense) return;
410
+ nonCommercialContainer.style.transition = "opacity .5s ease-in-out";
411
+ nonCommercialContainer.style.opacity = "1";
412
+ }
413
413
  }
@@ -358,11 +358,8 @@ export class Renderer extends Behaviour implements IRenderer {
358
358
  //@ts-ignore
359
359
  get sharedMaterials(): SharedMaterialArray {
360
360
 
361
- // @ts-ignore (original materials will be set during deserialization)
362
- if(this._originalMaterials === undefined) return null;
363
-
364
361
  // @ts-ignore during deserialization code might access this property *before* the setter and then create an empty array
365
- if (this.__isDeserializing === true) return null;
362
+ if (!this.__didAwake) return null;
366
363
 
367
364
  if (!this._sharedMaterials || !this._sharedMaterials.is(this)) {
368
365
  if (!this._originalMaterials) this._originalMaterials = [];
@@ -728,8 +725,6 @@ export class Renderer extends Behaviour implements IRenderer {
728
725
  // If the material has a envMap and is NOT using a reflection probe we set the envMap to the scene environment
729
726
  if (mat && "envMap" in mat && "envMapIntensity" in mat && !ReflectionProbe.isUsingReflectionProbe(mat)) {
730
727
  mat.envMap = this.context.scene.environment;
731
- mat.envMapIntensity = this.context.scene.environmentIntensity;
732
- mat.envMapRotation = this.context.scene.environmentRotation;
733
728
  }
734
729
  }
735
730
  }
@@ -208,7 +208,6 @@ export { ClickThrough } from "../web/Clickthrough.js";
208
208
  export { CursorFollow } from "../web/CursorFollow.js";
209
209
  export { HoverAnimation } from "../web/HoverAnimation.js";
210
210
  export { ScrollFollow } from "../web/ScrollFollow.js";
211
- export { ViewBox } from "../web/ViewBox.js";
212
211
  export { Avatar } from "../webxr/Avatar.js";
213
212
  export { XRControllerFollow } from "../webxr/controllers/XRControllerFollow.js";
214
213
  export { XRControllerModel } from "../webxr/controllers/XRControllerModel.js";
@@ -66,19 +66,10 @@ export class PlayableDirector extends Behaviour {
66
66
  this.createTrackFunctions[type] = fn;
67
67
  }
68
68
 
69
- /**
70
- * The timeline asset that is played by this director.
71
- */
72
69
  playableAsset?: Models.TimelineAssetModel;
73
-
74
70
  /** Set to true to start playing the timeline when the scene starts */
75
71
  @serializable()
76
72
  playOnAwake?: boolean;
77
-
78
- /**
79
- * Determines how the timeline behaves when it reaches the end of its duration.
80
- * @default DirectorWrapMode.Loop
81
- */
82
73
  @serializable()
83
74
  extrapolationMode: DirectorWrapMode = DirectorWrapMode.Loop;
84
75
 
@@ -92,12 +92,4 @@ export declare class SignalMarkerModel extends MarkerModel {
92
92
  asset: string;
93
93
  }
94
94
 
95
- /**
96
- * Marker with a name, used for scroll-driven timelines. It is used together with elements in your HTML to define what time in the timeline should be active when the element is in the scroll view.
97
- *
98
- * @example Mark html elements to define scroll positions
99
- * ```html
100
- * <div data-timeline-marker>...</div>
101
- * ```
102
- */
103
- export type ScrollMarkerModel = MarkerModel & { name: string };
95
+ export type ScrollMarkerModel = MarkerModel & { selector: string };
@@ -175,10 +175,8 @@ export class AnimationTrackHandler extends TrackHandler {
175
175
  // which means we want to notify the object that it's not animated anymore
176
176
  // and the animator can then take over
177
177
  onStateChanged() {
178
- if (this._animator) {
179
- // We can not check the *isPlaying* state here because the timeline might be paused and evaluated by e.g. ScrollFollow
180
- setObjectAnimated(this._animator.gameObject, this, this.director.enabled && this.director.weight > 0);
181
- }
178
+ if (this._animator)
179
+ setObjectAnimated(this._animator.gameObject, this, this.director.isPlaying);
182
180
  }
183
181
 
184
182
  createHooks(clipModel: Models.AnimationClipModel, clip) {
@@ -1,6 +1,5 @@
1
1
  import { Matrix4, Object3D, Quaternion, Vector3 } from "three";
2
2
 
3
- import { isDevEnvironment } from "../../engine/debug/index.js";
4
3
  import { serializable } from "../../engine/engine_serialization.js";
5
4
  import { lookAtObject } from "../../engine/engine_three_utils.js";
6
5
  import { type UsdzBehaviour } from "../../engine-components/export/usdz/extensions/behavior/Behaviour.js";
@@ -43,10 +42,7 @@ export class LookAt extends Behaviour implements UsdzBehaviour {
43
42
  /** @internal */
44
43
  onBeforeRender(): void {
45
44
  let target: Object3D | null | undefined = this.target;
46
- if (!target) {
47
- target = this.context.mainCamera;
48
- if (isDevEnvironment()) console.warn(`[LookAt] No target set on ${this.name}, using main camera as target.`);
49
- }
45
+ if (!target) target = this.context.mainCamera;
50
46
  if (!target) return;
51
47
 
52
48
  let copyTargetRotation = this.copyTargetRotation;
@@ -1,21 +1,12 @@
1
1
  import { NEPointerEvent } from "../../engine/engine_input.js";
2
2
  import { onStart } from "../../engine/engine_lifecycle_api.js";
3
- import { addAttributeChangeCallback } from "../../engine/engine_utils.js";
4
3
  import { Behaviour } from "../Component.js";
5
4
 
6
5
  // Automatically add ClickThrough component if "clickthrough" attribute is present on the needle-engine element
7
6
  onStart(ctx => {
8
7
  const attribute = ctx.domElement.getAttribute("clickthrough");
9
- if (clickthroughEnabled(attribute)) {
10
- const comp = ctx.scene.addComponent(ClickThrough);
11
- addAttributeChangeCallback(ctx.domElement, "clickthrough", () => {
12
- const attribute = ctx.domElement.getAttribute("clickthrough");
13
- comp.enabled = clickthroughEnabled(attribute);
14
- });
15
- }
16
-
17
- function clickthroughEnabled(val: string | null) {
18
- return val !== null && val !== "0" && val !== "false";
8
+ if (attribute !== null && attribute !== "0" && attribute !== "false") {
9
+ ctx.scene.addComponent(ClickThrough);
19
10
  }
20
11
  });
21
12
 
@@ -150,8 +150,7 @@ export class ScrollFollow extends Behaviour {
150
150
  }
151
151
  }
152
152
 
153
- // if (this._current_value !== this._appliedValue)
154
- {
153
+ if (this._current_value !== this._appliedValue) {
155
154
  this._appliedValue = this._current_value;
156
155
 
157
156
  let defaultPrevented = false;
@@ -243,8 +242,7 @@ export class ScrollFollow extends Behaviour {
243
242
 
244
243
  if (target instanceof PlayableDirector) {
245
244
  this.handleTimelineTarget(target, value);
246
- if (target.isPlaying) target.pause();
247
- target.evaluate();
245
+ if (!target.isPlaying) target.evaluate();
248
246
  }
249
247
  else if (target instanceof Animator) {
250
248
  target.setFloat("scroll", value);
@@ -294,35 +292,18 @@ export class ScrollFollow extends Behaviour {
294
292
  let scrollRegionEnd = 0;
295
293
  markersArray.length = 0;
296
294
 
297
- // querySelectorResults.length = 0;
298
- let markerIndex = 0;
299
-
300
- // https://scroll-driven-animations.style/tools/view-timeline/ranges
301
- for (const marker of director.foreachMarker<ScrollMarkerModel & { element?: HTMLElement | null, needsUpdate?: boolean, timeline?: ViewTimeline }>("ScrollMarker")) {
302
-
303
- const index = markerIndex++;
295
+ for (const marker of director.foreachMarker<ScrollMarkerModel & { element?: HTMLElement | null, needsUpdate?: boolean }>("ScrollMarker")) {
304
296
 
305
297
  // Get marker elements from DOM
306
- if ((marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (!marker.element?.parentNode))) {
298
+ if (marker.selector?.length && (marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (!marker.element?.parentNode))) {
307
299
  marker.needsUpdate = false;
308
300
  try {
309
- marker.element = tryGetElementsForSelector(index, marker.name) as HTMLElement | null;
310
- if (debug) console.debug("ScrollMarker found on page", marker.element, marker.name);
311
- // if (!marker.element) {
312
- // marker.timeline = undefined;
313
- // continue;
314
- // }
315
- // else {
316
- // /** @ts-ignore */
317
- // marker.timeline = new ViewTimeline({
318
- // subject: marker.element,
319
- // axis: 'block', // https://drafts.csswg.org/scroll-animations/#scroll-notation
320
- // });
321
- // }
301
+ marker.element = document.querySelector<HTMLElement>(marker.selector) || null;
302
+ if (debug) console.debug("ScrollMarker found on page", marker.element, marker.selector);
322
303
  }
323
304
  catch (error) {
324
305
  marker.element = null;
325
- console.error("ScrollMarker selector is not valid: " + marker.name + "\n", error);
306
+ console.error("ScrollMarker selector is not valid: " + marker.selector + "\n", error);
326
307
  }
327
308
  }
328
309
 
@@ -350,28 +331,11 @@ export class ScrollFollow extends Behaviour {
350
331
  weightsArray.length = 0;
351
332
  let sum = 0;
352
333
 
353
- // We keep a separate count here in case there are some markers that could not be resolved so point to *invalid* elements - the timeline should fallback to 0-1 scroll behaviour then
354
334
  let markerCount = 0;
355
335
  for (const marker of markersArray) {
356
336
 
357
337
  if (!marker.element) continue;
358
338
 
359
- markerCount += 1;
360
-
361
- // const timeline = marker.timeline;
362
- // if (timeline) {
363
- // const time01 = calculateTimelinePositionNormalized(timeline);
364
- // if (time01 > 0 && time01 <= 1) {
365
- // const overlap = calculateTimelinePositionNormalized(timeline!);
366
- // const weight = overlap;
367
- // // console.log(marker.element.className, time01)
368
- // weightsArray.push({ time: marker.time, weight: weight });
369
- // sum += weight;
370
- // }
371
- // }
372
- // continue;
373
- // if(this.context.time.frame % 10 === 0) console.log(marker.element?.className, timeline, calculateTimelinePositionNormalized(timeline!));
374
-
375
339
  const top = marker.element.offsetTop;
376
340
  const height = marker.element.offsetHeight;
377
341
  const bottom = top + height;
@@ -392,139 +356,39 @@ export class ScrollFollow extends Behaviour {
392
356
  const overlapTop = Math.max(top, currentTop);
393
357
  const overlapBottom = Math.min(bottom, currentBottom);
394
358
  overlap = Math.max(0, overlapBottom - overlapTop);
359
+ // console.log(marker.element.className, overlap)
395
360
  }
396
361
 
397
- markerCount += 1;
398
-
399
362
  if (overlap > 0) {
400
363
  weightsArray.push({ time: marker.time, weight: overlap });
401
364
  sum += overlap;
402
365
  }
403
366
  }
404
367
 
405
- if (weightsArray.length <= 0 && markerCount <= 0) {
368
+ if (weightsArray.length <= 0 && markersArray.length <= 0) {
406
369
  director.time = value * duration;
407
370
  }
408
371
  else if (weightsArray.length > 0) {
409
372
  // normalize and calculate weighted time
410
- let time = 0;
411
- for (const entry of weightsArray) {
412
- const weight = entry.weight / Math.max(0.00001, sum);
413
- // console.log(weight.toFixed(2))
373
+ let time = weightsArray[0].time;
374
+ for (const o of weightsArray) {
375
+ const weight = o.weight / Math.max(0.00001, sum);
414
376
  // lerp time based on weight
415
- const diff = Math.abs(entry.time - time);
377
+ const diff = Math.abs(o.time - time);
416
378
  time += diff * weight;
417
379
  }
418
- // console.log(time.toFixed(2), [...weightsArray])
419
- if (this.damping <= 0)
420
- director.time = time;
421
- else
422
- director.time = Mathf.lerp(director.time, time, this.context.time.deltaTime / this.damping);
380
+ director.time = time;
423
381
  }
424
382
  }
425
383
 
426
384
  }
427
385
 
428
-
429
-
430
386
  const weightsArray: OverlapInfo[] = [];
431
- const markersArray: (ScrollMarkerModel & { element?: HTMLElement | null, timeline?: ViewTimeline })[] = [];
387
+ const markersArray: (ScrollMarkerModel & { element?: HTMLElement | null })[] = [];
432
388
 
433
389
  type OverlapInfo = {
434
390
  /** Marker time */
435
391
  time: number,
436
392
  /** Overlap in pixels */
437
393
  weight: number,
438
- }
439
-
440
-
441
- // type SelectorCache = {
442
- // /** The selector used to query the *elements */
443
- // selector: string,
444
- // elements: Element[] | null,
445
- // usedElementCount: number,
446
- // }
447
- // const querySelectorResults: Array<SelectorCache> = [];
448
-
449
- const needleScrollMarkerCacheKey = "data-timeline-marker";
450
- const needleScrollMarkerIndexCache = new Map<number, Element | null>();
451
- const needleScrollMarkerNameCache = new Map<string, Element | null>();
452
- let needsScrollMarkerRefresh = true;
453
-
454
- function tryGetElementsForSelector(index: number, name: string): Element | null {
455
-
456
- if (!needsScrollMarkerRefresh) {
457
- let element = name?.length ? needleScrollMarkerNameCache.get(name) : null;
458
- if (element) return element;
459
- element = needleScrollMarkerIndexCache.get(index) || null;
460
- return element;
461
- }
462
- needsScrollMarkerRefresh = false;
463
- needleScrollMarkerIndexCache.clear();
464
- const markers = document.querySelectorAll(`[data-timeline-marker]`);
465
- markers.forEach((m, i) => {
466
- needleScrollMarkerIndexCache.set(i, m);
467
- const name = m.getAttribute("data-timeline-marker");
468
- if (name?.length) needleScrollMarkerNameCache.set(name, m);
469
- });
470
- const element = needleScrollMarkerIndexCache.get(index) || null;
471
- return element;
472
-
473
-
474
- /* e.g.
475
- <div class="section behind start" data-needle-scroll-marker>
476
- */
477
- // console.log(index, element)
478
- if (element) return element;
479
-
480
- // for (const entry of querySelectorResults) {
481
- // if (entry.selector === selector) {
482
- // const index = entry.usedElementCount++;
483
- // return entry.elements && index < entry.elements.length ? entry.elements[index] : null;
484
- // }
485
- // }
486
- // const elements = document.querySelectorAll(selector);
487
- // querySelectorResults.push({ selector, elements: Array.from(elements), usedElementCount: 1 });
488
- // if (elements.length > 0) return elements[0];
489
- return null;
490
- }
491
-
492
-
493
- // #region ScrollTimeline
494
-
495
- function calculateTimelinePositionNormalized(timeline: ViewTimeline) {
496
- if (!timeline.source) return 0;
497
- const currentTime = timeline.currentTime;
498
- const duration = timeline.duration;
499
- let durationValue = 1;
500
- if (duration.unit === "seconds") {
501
- durationValue = duration.value;
502
- }
503
- else if (duration.unit === "percent") {
504
- durationValue = duration.value;
505
- }
506
- const t01 = currentTime.unit === "seconds" ? (currentTime.value / durationValue) : (currentTime.value / 100);
507
- return t01;
508
- }
509
- function calculateNormalizedOverlap(timeline: ViewTimeline) {
510
- if (!timeline.source) return 0;
511
- const start = timeline.startOffset;
512
- const end = timeline.endOffset;
513
- const total = start.value + end.value;
514
- if (total <= 0) return 1;
515
- const startNorm = start.value / total;
516
- const endNorm = end.value / total;
517
- return 1 - (startNorm + endNorm);
518
- }
519
-
520
-
521
- declare global {
522
- interface ViewTimeline {
523
- axis: 'block' | 'inline';
524
- currentTime: { unit: 'seconds' | 'percent', value: number };
525
- duration: { unit: 'seconds' | 'percent', value: number };
526
- source: Element | null;
527
- startOffset: { unit: 'px', value: number };
528
- endOffset: { unit: 'px', value: number };
529
- }
530
394
  }
@@ -1,5 +1,4 @@
1
1
  export * from "./Clickthrough.js";
2
2
  export * from "./CursorFollow.js";
3
3
  export * from "./HoverAnimation.js";
4
- export * from "./ScrollFollow.js";
5
- export * from "./ViewBox.js";
4
+ export * from "./ScrollFollow.js";
@@ -1,16 +0,0 @@
1
- import { Behaviour } from "../Component.js";
2
- export declare class ViewBox extends Behaviour {
3
- static instances: ViewBox[];
4
- referenceFieldOfView: number;
5
- debug: boolean;
6
- awake(): void;
7
- onEnable(): void;
8
- onDisable(): void;
9
- onBeforeRender(): void;
10
- /**
11
- * Cover fit
12
- */
13
- private fit;
14
- private projectBoxIntoCamera;
15
- private _projectedBoxElement;
16
- }
@@ -1,186 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- var ViewBox_1;
8
- import { PerspectiveCamera } from "three";
9
- import { isDevEnvironment } from "../../engine/debug/debug.js";
10
- import { Gizmos } from "../../engine/engine_gizmos.js";
11
- import { serializable } from "../../engine/engine_serialization_decorator.js";
12
- import { getTempVector } from "../../engine/engine_three_utils.js";
13
- import { registerType } from "../../engine/engine_typestore.js";
14
- import { getParam } from "../../engine/engine_utils.js";
15
- import { Behaviour } from "../Component.js";
16
- const debugParam = getParam("debugviewbox");
17
- let ViewBox = ViewBox_1 = class ViewBox extends Behaviour {
18
- static instances = [];
19
- referenceFieldOfView = 60;
20
- debug = false;
21
- awake() {
22
- // this.referenceFieldOfView = (this.context.mainCamera as PerspectiveCamera)?.fov || 60;
23
- // setInterval(()=>{
24
- // this.enabled = !this.enabled
25
- // }, 1000)
26
- }
27
- onEnable() {
28
- if (debugParam || this.debug || isDevEnvironment())
29
- console.debug("[ViewBox] Using camera fov:", this.referenceFieldOfView);
30
- ViewBox_1.instances.push(this);
31
- }
32
- onDisable() {
33
- const idx = ViewBox_1.instances.indexOf(this);
34
- if (idx !== -1)
35
- ViewBox_1.instances.splice(idx, 1);
36
- }
37
- onBeforeRender() {
38
- if (this.context.isInXR)
39
- return;
40
- const isActive = ViewBox_1.instances[ViewBox_1.instances.length - 1] === this;
41
- if (!isActive) {
42
- if (debugParam || this.debug)
43
- Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0x333333);
44
- return;
45
- }
46
- if (debugParam || this.debug)
47
- Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00);
48
- // calculate box size to fit the camera frustrum size at the current position (just scale)
49
- const camera = this.context.mainCamera;
50
- if (!camera)
51
- return;
52
- if (!(camera instanceof PerspectiveCamera)) {
53
- // TODO: support orthographic camera
54
- return;
55
- }
56
- if (this.referenceFieldOfView === undefined || this.referenceFieldOfView <= 0) {
57
- if (debugParam || this.debug)
58
- console.warn("[ViewBox] No valid referenceFieldOfView set, cannot adjust box size:", this.referenceFieldOfView);
59
- return;
60
- }
61
- const domWidth = this.context.domWidth;
62
- const domHeight = this.context.domHeight;
63
- let rectPosX = 0;
64
- let rectPosY = 0;
65
- let rectWidth = domWidth;
66
- let rectHeight = domHeight;
67
- let diffWidth = 1;
68
- let diffHeight = 1;
69
- // use focus rect if available
70
- const focusRectSize = this.context.focusRectSize;
71
- if (focusRectSize) {
72
- // console.log(focusRectSize)
73
- rectPosX = focusRectSize.x;
74
- rectPosY = focusRectSize.y;
75
- rectWidth = focusRectSize.width;
76
- rectHeight = focusRectSize.height;
77
- diffWidth = domWidth / rectWidth;
78
- diffHeight = domHeight / rectHeight;
79
- }
80
- // const view = camera.view;
81
- const view = camera.view;
82
- const zoom = camera.zoom;
83
- const aspect = camera.aspect;
84
- const fov = camera.fov;
85
- camera.view = null;
86
- camera.zoom = 1;
87
- // camera.aspect = rectWidth / rectHeight;
88
- camera.fov = this.referenceFieldOfView;
89
- camera.updateProjectionMatrix();
90
- const boxPosition = this.gameObject.worldPosition;
91
- const boxScale = this.gameObject.worldScale;
92
- // const fov = this.referenceFieldOfView
93
- const distance = camera.worldPosition.distanceTo(boxPosition);
94
- const vFOV = this.referenceFieldOfView * Math.PI / 180; // convert vertical fov to radians
95
- const height = 2 * Math.tan(vFOV / 2) * distance; // visible height
96
- const width = height * camera.aspect; // visible width
97
- const projectedBox = this.projectBoxIntoCamera(boxPosition, boxScale, camera, height * .5);
98
- const boxWidth = (projectedBox.maxX - projectedBox.minX);
99
- const boxHeight = (projectedBox.maxY - projectedBox.minY);
100
- // TODO: take the rect size different into account
101
- const scale = this.fit(boxWidth * camera.aspect, boxHeight, width / diffWidth, height / diffHeight);
102
- const vec = getTempVector(boxPosition);
103
- vec.project(camera);
104
- this.context.focusRectSettings.offsetX = vec.x;
105
- this.context.focusRectSettings.offsetY = vec.y;
106
- this.context.focusRectSettings.zoom = scale;
107
- // if we don't have a focus rect yet, set it to the dom element
108
- if (!this.context.focusRect)
109
- this.context.setCameraFocusRect(this.context.domElement);
110
- // Reset values
111
- camera.view = view;
112
- camera.zoom = zoom;
113
- camera.aspect = aspect;
114
- camera.fov = fov;
115
- // camera.updateProjectionMatrix();
116
- // BACKLOG: some code for box scale of an object (different component)
117
- // this.gameObject.worldScale = getTempVector(width, height, worldscale.z);
118
- // this.gameObject.scale.multiplyScalar(.98)
119
- // const minscale = Math.min(width, height);
120
- // console.log(width, height);
121
- // this.gameObject.worldScale = getTempVector(scale, scale, scale);
122
- }
123
- /**
124
- * Cover fit
125
- */
126
- fit(width1, height1, width2, height2) {
127
- const scaleX = width2 / width1;
128
- const scaleY = height2 / height1;
129
- return Math.min(scaleX, scaleY);
130
- }
131
- projectBoxIntoCamera(position, scale, camera, diff) {
132
- const factor = .5 * diff;
133
- const corners = [
134
- getTempVector(-scale.x * factor, -scale.y * factor, -scale.z * factor),
135
- getTempVector(scale.x * factor, -scale.y * factor, -scale.z * factor),
136
- getTempVector(-scale.x * factor, scale.y * factor, -scale.z * factor),
137
- getTempVector(scale.x * factor, scale.y * factor, -scale.z * factor),
138
- getTempVector(-scale.x * factor, -scale.y * factor, scale.z * factor),
139
- getTempVector(scale.x * factor, -scale.y * factor, scale.z * factor),
140
- getTempVector(-scale.x * factor, scale.y * factor, scale.z * factor),
141
- getTempVector(scale.x * factor, scale.y * factor, scale.z * factor),
142
- ];
143
- let minX = Number.POSITIVE_INFINITY;
144
- let maxX = Number.NEGATIVE_INFINITY;
145
- let minY = Number.POSITIVE_INFINITY;
146
- let maxY = Number.NEGATIVE_INFINITY;
147
- for (let i = 0; i < corners.length; i++) {
148
- const c = corners[i];
149
- c.add(position);
150
- c.project(camera);
151
- if (c.x < minX)
152
- minX = c.x;
153
- if (c.x > maxX)
154
- maxX = c.x;
155
- if (c.y < minY)
156
- minY = c.y;
157
- if (c.y > maxY)
158
- maxY = c.y;
159
- }
160
- // if(!this._projectedBoxElement) {
161
- // this._projectedBoxElement = document.createElement("div");
162
- // this.context.domElement.appendChild(this._projectedBoxElement);
163
- // }
164
- // this._projectedBoxElement.style.position = "fixed";
165
- // this._projectedBoxElement.style.outline = "10px solid red";
166
- // this._projectedBoxElement.style.left = ((minX * .5 + .5) * this.context.domWidth) + "px";
167
- // this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * this.context.domHeight) + "px";
168
- // this._projectedBoxElement.style.width = ((maxX - minX) * .5 * this.context.domWidth) + "px";
169
- // this._projectedBoxElement.style.height = ((maxY - minY) * .5 * this.context.domHeight) + "px";
170
- // this._projectedBoxElement.style.pointerEvents = "none";
171
- // this._projectedBoxElement.style.zIndex = "1000";
172
- return { minX, maxX, minY, maxY };
173
- }
174
- _projectedBoxElement = null;
175
- };
176
- __decorate([
177
- serializable()
178
- ], ViewBox.prototype, "referenceFieldOfView", void 0);
179
- __decorate([
180
- serializable()
181
- ], ViewBox.prototype, "debug", void 0);
182
- ViewBox = ViewBox_1 = __decorate([
183
- registerType
184
- ], ViewBox);
185
- export { ViewBox };
186
- //# sourceMappingURL=ViewBox.js.map