@needle-tools/engine 4.10.0-beta.1 → 4.10.0-beta.3
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-CLzMgxkO.min.js +1650 -0
- package/dist/{needle-engine.bundle-6so_os_w.umd.cjs → needle-engine.bundle-Ckr5KE6m.umd.cjs} +132 -132
- package/dist/{needle-engine.bundle-Dj2DYdMY.js → needle-engine.bundle-Ddybtee9.js} +7082 -7021
- package/dist/needle-engine.js +15 -14
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_camera.js +2 -2
- package/lib/engine/engine_camera.js.map +1 -1
- package/lib/engine/engine_lightdata.d.ts +3 -3
- package/lib/engine/engine_lightdata.js +10 -10
- package/lib/engine/engine_lightdata.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +4 -0
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_scenelighting.d.ts +1 -1
- package/lib/engine/engine_scenelighting.js +4 -5
- package/lib/engine/engine_scenelighting.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +3 -1
- package/lib/engine/engine_utils.js +11 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_lightmaps.js +1 -1
- package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +22 -0
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +2 -1
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/Renderer.js +2 -0
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/Skybox.js +22 -4
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/utils/LookAt.js +1 -1
- package/lib/engine-components/utils/LookAt.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.js +80 -70
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +0 -1
- package/lib/engine-components/web/ViewBox.js +79 -36
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_camera.ts +2 -2
- package/src/engine/engine_lightdata.ts +11 -11
- package/src/engine/engine_physics_rapier.ts +3 -0
- package/src/engine/engine_scenelighting.ts +5 -6
- package/src/engine/engine_utils.ts +12 -0
- package/src/engine/extensions/NEEDLE_lightmaps.ts +1 -1
- package/src/engine/webcomponents/needle-engine.ts +33 -6
- package/src/engine-components/CameraUtils.ts +1 -1
- package/src/engine-components/Renderer.ts +2 -0
- package/src/engine-components/Skybox.ts +26 -7
- package/src/engine-components/utils/LookAt.ts +1 -1
- package/src/engine-components/web/ScrollFollow.ts +82 -70
- package/src/engine-components/web/ViewBox.ts +82 -37
- package/dist/needle-engine.bundle-Djy6H4lx.min.js +0 -1650
|
@@ -8,7 +8,7 @@ import { syncField } from "../engine/engine_networking_auto.js";
|
|
|
8
8
|
import { loadPMREM } from "../engine/engine_pmrem.js";
|
|
9
9
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
10
10
|
import { type IContext } from "../engine/engine_types.js";
|
|
11
|
-
import { addAttributeChangeCallback, getParam, PromiseAllWithErrors, removeAttributeChangeCallback } from "../engine/engine_utils.js";
|
|
11
|
+
import { addAttributeChangeCallback, getParam, PromiseAllWithErrors, removeAttributeChangeCallback, toSourceId } from "../engine/engine_utils.js";
|
|
12
12
|
import { registerObservableAttribute } from "../engine/webcomponents/needle-engine.extras.js";
|
|
13
13
|
import { Camera, ClearFlags } from "./Camera.js";
|
|
14
14
|
import { Behaviour, GameObject } from "./Component.js";
|
|
@@ -27,15 +27,34 @@ function createRemoteSkyboxComponent(context: IContext, url: string, skybox: boo
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const remote = new RemoteSkybox();
|
|
30
|
+
remote.sourceId = toSourceId(url);
|
|
30
31
|
remote.allowDrop = false;
|
|
31
32
|
remote.allowNetworking = false;
|
|
32
33
|
remote.background = skybox;
|
|
33
34
|
remote.environment = environment;
|
|
34
35
|
GameObject.addComponent(context.scene, remote);
|
|
35
36
|
const urlChanged = newValue => {
|
|
36
|
-
if (
|
|
37
|
-
if (
|
|
38
|
-
|
|
37
|
+
if (debug) console.log(attribute, "CHANGED TO", newValue);
|
|
38
|
+
if (newValue) {
|
|
39
|
+
if (typeof newValue !== "string") {
|
|
40
|
+
console.warn("Invalid attribute value for " + attribute);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
remote.setSkybox(newValue);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (remote.sourceId) {
|
|
47
|
+
if (environment) {
|
|
48
|
+
if (!context.sceneLighting.internalEnableReflection(remote.sourceId)) {
|
|
49
|
+
context.scene.environment = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (skybox) {
|
|
53
|
+
const skybox = context.lightmaps.tryGetSkybox(remote.sourceId);
|
|
54
|
+
context.scene.background = skybox;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
39
58
|
};
|
|
40
59
|
addAttributeChangeCallback(context.domElement, attribute, urlChanged);
|
|
41
60
|
remote.addEventListener("destroy", () => {
|
|
@@ -50,7 +69,7 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, (args) => {
|
|
|
50
69
|
const context = args.context;
|
|
51
70
|
const backgroundImage = context.domElement.getAttribute("background-image");
|
|
52
71
|
const environmentImage = context.domElement.getAttribute("environment-image");
|
|
53
|
-
|
|
72
|
+
|
|
54
73
|
if (backgroundImage) {
|
|
55
74
|
if (debug) console.log("Creating RemoteSkybox to load background " + backgroundImage);
|
|
56
75
|
// if the user is loading a GLB without a camera then the CameraUtils (which creates the default camera)
|
|
@@ -246,8 +265,8 @@ export class RemoteSkybox extends Behaviour {
|
|
|
246
265
|
envMap.needsUpdate = true;
|
|
247
266
|
}
|
|
248
267
|
|
|
249
|
-
if(this.destroyed) return;
|
|
250
|
-
if(!this.context) {
|
|
268
|
+
if (this.destroyed) return;
|
|
269
|
+
if (!this.context) {
|
|
251
270
|
console.warn("RemoteSkybox: Context is not available - can not apply skybox.");
|
|
252
271
|
return;
|
|
253
272
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Matrix4, Object3D, Quaternion, Vector3 } from "three";
|
|
2
2
|
|
|
3
|
+
import { isDevEnvironment } from "../../engine/debug/index.js";
|
|
3
4
|
import { serializable } from "../../engine/engine_serialization.js";
|
|
4
5
|
import { lookAtObject } from "../../engine/engine_three_utils.js";
|
|
5
6
|
import { type UsdzBehaviour } from "../../engine-components/export/usdz/extensions/behavior/Behaviour.js";
|
|
6
7
|
import { ActionBuilder, BehaviorModel, TriggerBuilder, USDVec3 } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js";
|
|
7
8
|
import { USDObject } from "../../engine-components/export/usdz/ThreeUSDZExporter.js";
|
|
8
9
|
import { Behaviour } from "../Component.js";
|
|
9
|
-
import { isDevEnvironment } from "../../engine/debug/index.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* LookAt behaviour makes the object look at a target object or the camera.
|
|
@@ -4,10 +4,11 @@ import { element } from "three/src/nodes/TSL.js";
|
|
|
4
4
|
import { Context } from "../../engine/engine_context.js";
|
|
5
5
|
import { Mathf } from "../../engine/engine_math.js";
|
|
6
6
|
import { serializable } from "../../engine/engine_serialization.js";
|
|
7
|
-
import { getBoundingBox } from "../../engine/engine_three_utils.js";
|
|
7
|
+
import { getBoundingBox, setVisibleInCustomShadowRendering } from "../../engine/engine_three_utils.js";
|
|
8
8
|
import { getParam } from "../../engine/engine_utils.js";
|
|
9
9
|
import { Animation } from "../Animation.js";
|
|
10
10
|
import { Animator } from "../Animator.js";
|
|
11
|
+
import { MarkerTrackHandler } from "../api.js";
|
|
11
12
|
import { AudioSource } from "../AudioSource.js";
|
|
12
13
|
import { Behaviour } from "../Component.js";
|
|
13
14
|
import { EventList } from "../EventList.js";
|
|
@@ -308,17 +309,17 @@ export class ScrollFollow extends Behaviour {
|
|
|
308
309
|
try {
|
|
309
310
|
marker.element = tryGetElementsForSelector(index, marker.name) as HTMLElement | null;
|
|
310
311
|
if (debug) console.debug("ScrollMarker found on page", marker.element, marker.name);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
312
|
+
if (!marker.element) {
|
|
313
|
+
marker.timeline = undefined;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
/** @ts-ignore */
|
|
318
|
+
marker.timeline = new ViewTimeline({
|
|
319
|
+
subject: marker.element,
|
|
320
|
+
axis: 'block', // https://drafts.csswg.org/scroll-animations/#scroll-notation
|
|
321
|
+
});
|
|
322
|
+
}
|
|
322
323
|
}
|
|
323
324
|
catch (error) {
|
|
324
325
|
marker.element = null;
|
|
@@ -349,57 +350,74 @@ export class ScrollFollow extends Behaviour {
|
|
|
349
350
|
|
|
350
351
|
weightsArray.length = 0;
|
|
351
352
|
let sum = 0;
|
|
353
|
+
const oneFrameTime = 1 / 60;
|
|
352
354
|
|
|
353
355
|
// 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
356
|
let markerCount = 0;
|
|
355
|
-
for (
|
|
356
|
-
|
|
357
|
+
for (let i = 0; i < markersArray.length; i++) {
|
|
358
|
+
const marker = markersArray[i];
|
|
357
359
|
if (!marker.element) continue;
|
|
360
|
+
const nextMarker = markersArray[i + 1];
|
|
361
|
+
|
|
362
|
+
const nextTime = nextMarker
|
|
363
|
+
? (nextMarker.time - oneFrameTime)
|
|
364
|
+
: duration;
|
|
358
365
|
|
|
359
366
|
markerCount += 1;
|
|
360
367
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
368
|
+
const timeline = marker.timeline;
|
|
369
|
+
if (timeline) {
|
|
370
|
+
const time01 = calculateTimelinePositionNormalized(timeline);
|
|
371
|
+
// remap 0-1 to 0 - 1 - 0 (full weight at center)
|
|
372
|
+
const weight = 1 - Math.abs(time01 - 0.5) * 2;
|
|
373
|
+
if (time01 > 0 && time01 <= 1) {
|
|
374
|
+
const lerpTime = marker.time + (nextTime - marker.time) * time01;
|
|
375
|
+
weightsArray.push({ time: lerpTime, weight: weight });
|
|
376
|
+
sum += weight;
|
|
377
|
+
}
|
|
378
|
+
// Before the first marker is reached
|
|
379
|
+
else if (i === 0 && time01 <= 0) {
|
|
380
|
+
weightsArray.push({ time: 0, weight: 1 });
|
|
381
|
+
sum += 1;
|
|
382
|
+
}
|
|
383
|
+
// After the last marker is reached
|
|
384
|
+
else if (i === markersArray.length - 1 && time01 >= 1) {
|
|
385
|
+
weightsArray.push({ time: duration, weight: 1 });
|
|
386
|
+
sum += 1;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
continue;
|
|
373
390
|
// if(this.context.time.frame % 10 === 0) console.log(marker.element?.className, timeline, calculateTimelinePositionNormalized(timeline!));
|
|
374
391
|
|
|
375
|
-
const top = marker.element.offsetTop;
|
|
376
|
-
const height = marker.element.offsetHeight;
|
|
377
|
-
const bottom = top + height;
|
|
378
|
-
let overlap = 0;
|
|
392
|
+
// const top = marker.element.offsetTop - this._scrollContainerHeight;
|
|
393
|
+
// const height = marker.element.offsetHeight + this._scrollContainerHeight;
|
|
394
|
+
// const bottom = top + height;
|
|
395
|
+
// let overlap = 0;
|
|
379
396
|
|
|
380
|
-
// TODO: if we have two marker sections where no HTML overlaps (vor example because some large section is between them) we probably want to still virtually interpolate between them slowly in that region
|
|
397
|
+
// // TODO: if we have two marker sections where no HTML overlaps (vor example because some large section is between them) we probably want to still virtually interpolate between them slowly in that region
|
|
381
398
|
|
|
382
|
-
if (bottom < currentTop) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
else if (top > currentBottom) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
399
|
+
// if (bottom < currentTop) {
|
|
400
|
+
// // marker is above scroll region
|
|
401
|
+
// overlap = 0;
|
|
402
|
+
// }
|
|
403
|
+
// else if (top > currentBottom) {
|
|
404
|
+
// // marker is below scroll region
|
|
405
|
+
// overlap = 0;
|
|
406
|
+
// }
|
|
407
|
+
// else {
|
|
408
|
+
// // calculate overlap in pixels
|
|
409
|
+
// const overlapTop = Math.max(top, currentTop);
|
|
410
|
+
// const overlapBottom = Math.min(bottom, currentBottom);
|
|
411
|
+
// const height = Math.max(1, currentBottom - currentTop);
|
|
412
|
+
// overlap = Math.max(0, overlapBottom - overlapTop);
|
|
413
|
+
// }
|
|
396
414
|
|
|
397
|
-
|
|
415
|
+
// // if(this.context.time.frame % 20 === 0) console.log(overlap)
|
|
398
416
|
|
|
399
|
-
if (overlap > 0) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
417
|
+
// if (overlap > 0) {
|
|
418
|
+
// weightsArray.push({ time: marker.time, weight: overlap });
|
|
419
|
+
// sum += overlap;
|
|
420
|
+
// }
|
|
403
421
|
}
|
|
404
422
|
|
|
405
423
|
if (weightsArray.length <= 0 && markerCount <= 0) {
|
|
@@ -407,19 +425,23 @@ export class ScrollFollow extends Behaviour {
|
|
|
407
425
|
}
|
|
408
426
|
else if (weightsArray.length > 0) {
|
|
409
427
|
// normalize and calculate weighted time
|
|
410
|
-
let time = 0;
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
428
|
+
let time = weightsArray[0].time; // fallback to first time
|
|
429
|
+
if (weightsArray.length > 1) {
|
|
430
|
+
for (const entry of weightsArray) {
|
|
431
|
+
const weight = entry.weight / Math.max(0.00001, sum);
|
|
432
|
+
// console.log(weight.toFixed(2))
|
|
433
|
+
// lerp time based on weight
|
|
434
|
+
const diff = Math.abs(entry.time - time);
|
|
435
|
+
time += diff * weight;
|
|
436
|
+
}
|
|
417
437
|
}
|
|
418
|
-
|
|
419
|
-
if (this.damping <= 0)
|
|
438
|
+
if (debug && this.context.time.frame % 20 === 0) console.log(time.toFixed(3), [...weightsArray])
|
|
439
|
+
if (this.damping <= 0) {
|
|
420
440
|
director.time = time;
|
|
421
|
-
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
422
443
|
director.time = Mathf.lerp(director.time, time, this.context.time.deltaTime / this.damping);
|
|
444
|
+
}
|
|
423
445
|
}
|
|
424
446
|
}
|
|
425
447
|
|
|
@@ -506,16 +528,6 @@ function calculateTimelinePositionNormalized(timeline: ViewTimeline) {
|
|
|
506
528
|
const t01 = currentTime.unit === "seconds" ? (currentTime.value / durationValue) : (currentTime.value / 100);
|
|
507
529
|
return t01;
|
|
508
530
|
}
|
|
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
531
|
|
|
520
532
|
|
|
521
533
|
declare global {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Camera, PerspectiveCamera, Vector2, Vector3 } from "three";
|
|
2
|
+
|
|
3
|
+
import { isDevEnvironment } from "../../engine/debug/debug.js";
|
|
4
|
+
import { Gizmos } from "../../engine/engine_gizmos.js";
|
|
2
5
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
6
|
+
import { getTempQuaternion, getTempVector } from "../../engine/engine_three_utils.js";
|
|
3
7
|
import { registerType } from "../../engine/engine_typestore.js";
|
|
4
|
-
import { getTempVector } from "../../engine/engine_three_utils.js";
|
|
5
|
-
import { Behaviour } from "../Component.js";
|
|
6
|
-
import { isDevEnvironment } from "../../engine/debug/debug.js";
|
|
7
8
|
import { getParam } from "../../engine/engine_utils.js";
|
|
9
|
+
import { Behaviour } from "../Component.js";
|
|
8
10
|
|
|
9
11
|
const debugParam = getParam("debugviewbox");
|
|
10
12
|
|
|
@@ -19,26 +21,34 @@ export class ViewBox extends Behaviour {
|
|
|
19
21
|
@serializable()
|
|
20
22
|
debug: boolean = false;
|
|
21
23
|
|
|
22
|
-
awake() {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
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
|
+
|
|
28
32
|
onEnable(): void {
|
|
29
33
|
if (debugParam || this.debug || isDevEnvironment()) console.debug("[ViewBox] Using camera fov:", this.referenceFieldOfView);
|
|
30
34
|
ViewBox.instances.push(this);
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
onDisable(): void {
|
|
38
|
+
if (debugParam || this.debug) console.debug("[ViewBox] Disabled");
|
|
34
39
|
const idx = ViewBox.instances.indexOf(this);
|
|
35
40
|
if (idx !== -1) ViewBox.instances.splice(idx, 1);
|
|
41
|
+
this._projectedBoxElement?.remove();
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
onBeforeRender() {
|
|
39
45
|
if (this.context.isInXR) return;
|
|
40
46
|
const isActive = ViewBox.instances[ViewBox.instances.length - 1] === this;
|
|
41
|
-
if (!isActive)
|
|
47
|
+
if (!isActive) {
|
|
48
|
+
if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0x333333);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00);
|
|
42
52
|
|
|
43
53
|
// calculate box size to fit the camera frustrum size at the current position (just scale)
|
|
44
54
|
const camera = this.context.mainCamera;
|
|
@@ -53,11 +63,8 @@ export class ViewBox extends Behaviour {
|
|
|
53
63
|
return;
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const domY = domRect.y;
|
|
59
|
-
const domWidth = domRect.width;
|
|
60
|
-
const domHeight = domRect.height;
|
|
66
|
+
const domWidth = this.context.domWidth;
|
|
67
|
+
const domHeight = this.context.domHeight;
|
|
61
68
|
|
|
62
69
|
let rectPosX = 0;
|
|
63
70
|
let rectPosY = 0;
|
|
@@ -77,44 +84,80 @@ export class ViewBox extends Behaviour {
|
|
|
77
84
|
diffHeight = domHeight / rectHeight;
|
|
78
85
|
}
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
|
|
81
88
|
const view = camera.view;
|
|
82
89
|
const zoom = camera.zoom;
|
|
83
90
|
const aspect = camera.aspect;
|
|
84
91
|
const fov = camera.fov;
|
|
85
92
|
camera.view = null;
|
|
86
93
|
camera.zoom = 1;
|
|
87
|
-
// camera.aspect = rectWidth / rectHeight;
|
|
88
94
|
camera.fov = this.referenceFieldOfView;
|
|
89
95
|
camera.updateProjectionMatrix();
|
|
90
96
|
|
|
91
97
|
const boxPosition = this.gameObject.worldPosition;
|
|
92
98
|
const boxScale = this.gameObject.worldScale;
|
|
93
99
|
|
|
100
|
+
const cameraPosition = camera.worldPosition;
|
|
101
|
+
const distance = cameraPosition.distanceTo(boxPosition);
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
// #region camera fixes
|
|
105
|
+
// If the camera is inside the box, move it out
|
|
106
|
+
const boxSizeMax = Math.max(boxScale.x, boxScale.y, boxScale.z);
|
|
107
|
+
const direction = getTempVector(cameraPosition).sub(boxPosition);
|
|
108
|
+
if (distance < boxSizeMax) {
|
|
109
|
+
// move camera out of bounds
|
|
110
|
+
if (this.debug || debugParam) console.warn("[ViewBox] Moving camera out of bounds", distance, "<", boxSizeMax);
|
|
111
|
+
const positionDirection = getTempVector(direction);
|
|
112
|
+
positionDirection.y *= .00000001; // stay on horizontal plane mostly
|
|
113
|
+
positionDirection.normalize();
|
|
114
|
+
const lengthToMove = (boxSizeMax - distance) * 10; // move a bit more than needed
|
|
115
|
+
const newPosition = cameraPosition.add(positionDirection.multiplyScalar(lengthToMove));
|
|
116
|
+
camera.worldPosition = newPosition.lerp(cameraPosition, 1 - this.context.time.deltaTime);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Ensure the camera looks at the ViewBox
|
|
120
|
+
// TOOD: smooth lookat over multiple frames if we have multiple viewboxes
|
|
121
|
+
// const dot = direction.normalize().dot(camera.worldForward);
|
|
122
|
+
// if (dot < .9) {
|
|
123
|
+
// console.log(dot);
|
|
124
|
+
// const targetRotation = direction;
|
|
125
|
+
// const rotation = getTempQuaternion();
|
|
126
|
+
// rotation.setFromUnitVectors(camera.worldForward.multiplyScalar(-1), targetRotation);
|
|
127
|
+
// camera.worldQuaternion = rotation;
|
|
128
|
+
// camera.updateMatrixWorld();
|
|
129
|
+
// }
|
|
130
|
+
const boxPositionInCameraSpace = getTempVector(boxPosition);
|
|
131
|
+
camera.worldToLocal(boxPositionInCameraSpace);
|
|
132
|
+
camera.lookAt(boxPosition);
|
|
133
|
+
camera.updateMatrixWorld();
|
|
94
134
|
|
|
95
135
|
|
|
96
|
-
//
|
|
97
|
-
const distance = camera.worldPosition.distanceTo(boxPosition);
|
|
136
|
+
// #region calculate fit
|
|
98
137
|
const vFOV = this.referenceFieldOfView * Math.PI / 180; // convert vertical fov to radians
|
|
99
138
|
const height = 2 * Math.tan(vFOV / 2) * distance; // visible height
|
|
100
139
|
const width = height * camera.aspect; // visible width
|
|
101
140
|
|
|
102
|
-
const projectedBox = this.projectBoxIntoCamera(boxPosition, boxScale, camera,
|
|
141
|
+
const projectedBox = this.projectBoxIntoCamera(boxPosition, boxScale, camera, 1);
|
|
142
|
+
// return
|
|
103
143
|
const boxWidth = (projectedBox.maxX - projectedBox.minX);
|
|
104
144
|
const boxHeight = (projectedBox.maxY - projectedBox.minY);
|
|
105
145
|
|
|
106
|
-
// TODO: take the rect size different into account
|
|
107
146
|
const scale = this.fit(
|
|
108
147
|
boxWidth * camera.aspect,
|
|
109
148
|
boxHeight,
|
|
110
149
|
width / diffWidth,
|
|
111
150
|
height / diffHeight
|
|
112
151
|
);
|
|
152
|
+
// console.log({ scale, width, height, boxWidth: boxWidth * camera.aspect, boxHeight, diffWidth, diffHeight, aspect: camera.aspect, distance })
|
|
153
|
+
// this.context.focusRectSettings.zoom = 1.39;
|
|
154
|
+
// if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
|
|
155
|
+
// return
|
|
113
156
|
const vec = getTempVector(boxPosition);
|
|
114
157
|
vec.project(camera);
|
|
115
158
|
this.context.focusRectSettings.offsetX = vec.x;
|
|
116
159
|
this.context.focusRectSettings.offsetY = vec.y;
|
|
117
|
-
this.context.focusRectSettings.zoom = scale;
|
|
160
|
+
this.context.focusRectSettings.zoom = scale / (height * .5);
|
|
118
161
|
// if we don't have a focus rect yet, set it to the dom element
|
|
119
162
|
if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
|
|
120
163
|
|
|
@@ -146,9 +189,8 @@ export class ViewBox extends Behaviour {
|
|
|
146
189
|
|
|
147
190
|
|
|
148
191
|
|
|
149
|
-
private projectBoxIntoCamera(position: Vector3, scale: Vector3, camera: Camera,
|
|
150
|
-
|
|
151
|
-
const factor = .5 * diff;
|
|
192
|
+
private projectBoxIntoCamera(position: Vector3, scale: Vector3, camera: Camera, _factor: number) {
|
|
193
|
+
const factor = .5 * _factor;
|
|
152
194
|
|
|
153
195
|
const corners = [
|
|
154
196
|
getTempVector(-scale.x * factor, -scale.y * factor, -scale.z * factor),
|
|
@@ -174,18 +216,21 @@ export class ViewBox extends Behaviour {
|
|
|
174
216
|
if (c.y > maxY) maxY = c.y;
|
|
175
217
|
}
|
|
176
218
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
219
|
+
if (debugParam) {
|
|
220
|
+
if (!this._projectedBoxElement) {
|
|
221
|
+
this._projectedBoxElement = document.createElement("div");
|
|
222
|
+
}
|
|
223
|
+
if (this._projectedBoxElement.parentElement !== this.context.domElement)
|
|
224
|
+
this.context.domElement.appendChild(this._projectedBoxElement);
|
|
225
|
+
this._projectedBoxElement.style.position = "fixed";
|
|
226
|
+
this._projectedBoxElement.style.outline = "5px dotted red";
|
|
227
|
+
this._projectedBoxElement.style.left = ((minX * .5 + .5) * this.context.domWidth) + "px";
|
|
228
|
+
this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * this.context.domHeight) + "px";
|
|
229
|
+
this._projectedBoxElement.style.width = ((maxX - minX) * .5 * this.context.domWidth) + "px";
|
|
230
|
+
this._projectedBoxElement.style.height = ((maxY - minY) * .5 * this.context.domHeight) + "px";
|
|
231
|
+
this._projectedBoxElement.style.pointerEvents = "none";
|
|
232
|
+
this._projectedBoxElement.style.zIndex = "1000";
|
|
233
|
+
}
|
|
189
234
|
|
|
190
235
|
|
|
191
236
|
return { minX, maxX, minY, maxY };
|