@needle-tools/engine 4.4.0-alpha.3 → 4.4.0-alpha.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/CHANGELOG.md +22 -1
- package/dist/gltf-progressive.js +54 -54
- package/dist/gltf-progressive.light.js +54 -54
- package/dist/gltf-progressive.light.min.js +7 -7
- package/dist/gltf-progressive.min.js +7 -7
- package/dist/needle-engine.bundle.js +9120 -8879
- package/dist/needle-engine.bundle.light.js +9124 -8883
- package/dist/needle-engine.bundle.light.min.js +155 -142
- package/dist/needle-engine.bundle.light.umd.cjs +125 -112
- package/dist/needle-engine.bundle.min.js +155 -142
- package/dist/needle-engine.bundle.umd.cjs +127 -114
- package/dist/needle-engine.d.ts +9 -9
- package/dist/needle-engine.js +544 -543
- package/dist/needle-engine.light.js +544 -543
- package/dist/needle-engine.light.min.js +1 -1
- package/dist/needle-engine.light.umd.cjs +1 -1
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/postprocessing.js +38 -38
- package/dist/postprocessing.light.js +38 -38
- package/dist/postprocessing.light.min.js +2 -2
- package/dist/postprocessing.min.js +2 -2
- package/dist/three-examples.js +1205 -1205
- package/dist/three-examples.light.js +1205 -1205
- package/dist/three-examples.light.min.js +15 -15
- package/dist/three-examples.min.js +15 -15
- package/dist/three-mesh-ui.js +9 -9
- package/dist/three-mesh-ui.light.js +9 -9
- package/dist/three-mesh-ui.light.min.js +9 -9
- package/dist/three-mesh-ui.min.js +9 -9
- package/dist/three.js +239 -238
- package/dist/three.light.js +239 -238
- package/dist/three.light.min.js +27 -27
- package/dist/three.light.umd.cjs +15 -15
- package/dist/three.min.js +27 -27
- package/dist/three.umd.cjs +15 -15
- package/dist/vendor.js +24 -24
- package/dist/vendor.light.js +24 -24
- package/dist/vendor.light.min.js +2 -2
- package/dist/vendor.min.js +2 -2
- package/lib/engine/engine_input.d.ts +7 -0
- package/lib/engine/engine_input.js +12 -0
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_serialization_decorator.js +4 -0
- package/lib/engine/engine_serialization_decorator.js.map +1 -1
- package/lib/engine/engine_three_utils.d.ts +26 -1
- package/lib/engine/engine_three_utils.js +43 -0
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_utils_format.js +11 -5
- package/lib/engine/engine_utils_format.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -0
- package/lib/engine/webcomponents/needle menu/needle-menu.js +3 -3
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine-components/Camera.d.ts +2 -2
- package/lib/engine-components/Camera.js +4 -6
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +32 -13
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/Component.d.ts +4 -1
- package/lib/engine-components/Component.js +4 -1
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/ContactShadows.js +8 -2
- package/lib/engine-components/ContactShadows.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +3 -0
- package/lib/engine-components/OrbitControls.js +34 -10
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/SyncedTransform.d.ts +3 -2
- package/lib/engine-components/SyncedTransform.js +28 -18
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +6 -2
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/utils/EnvironmentScene.d.ts +5 -0
- package/lib/engine-components/utils/EnvironmentScene.js +206 -0
- package/lib/engine-components/utils/EnvironmentScene.js.map +1 -0
- package/package.json +1 -1
- package/src/engine/engine_input.ts +13 -0
- package/src/engine/engine_serialization_decorator.ts +4 -0
- package/src/engine/engine_three_utils.ts +52 -1
- package/src/engine/engine_utils_format.ts +13 -5
- package/src/engine/webcomponents/needle menu/needle-menu.ts +3 -3
- package/src/engine-components/Camera.ts +9 -6
- package/src/engine-components/CameraUtils.ts +35 -13
- package/src/engine-components/Component.ts +5 -2
- package/src/engine-components/ContactShadows.ts +8 -2
- package/src/engine-components/OrbitControls.ts +35 -10
- package/src/engine-components/SyncedTransform.ts +37 -25
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +7 -2
- package/src/engine-components/utils/EnvironmentScene.ts +246 -0
- package/src/engine/webcomponents/needle menu/dist/needle-menu.js +0 -662
|
@@ -417,6 +417,19 @@ export class Input implements IInput {
|
|
|
417
417
|
/** Was a double click detected for the primary pointer? */
|
|
418
418
|
get doubleClick(): boolean { return this._pointerDoubleClick[0]; }
|
|
419
419
|
|
|
420
|
+
/**
|
|
421
|
+
* Get a connected Gamepad
|
|
422
|
+
* Note: For a gamepad to be available to the browser it must have received input before while the page was focused.
|
|
423
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
|
|
424
|
+
* @returns The gamepad or null if no gamepad is connected
|
|
425
|
+
*/
|
|
426
|
+
getGamepad(index: number = 0): Gamepad | null {
|
|
427
|
+
if (typeof navigator !== "undefined" && "getGamepads" in navigator) {
|
|
428
|
+
return navigator.getGamepads()[index] || null;
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
|
|
420
433
|
private readonly _setCursorTypes: CursorTypeName[] = [];
|
|
421
434
|
|
|
422
435
|
/** @deprecated use setCursor("pointer") */
|
|
@@ -26,6 +26,10 @@ export const serializable = function <T>(type?: Constructor<T> | null | Array<Co
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return function (_target: any, _propertyKey: string | { name: string }) {
|
|
29
|
+
if (!_target) {
|
|
30
|
+
console.error("Found @serializable decorator without a target");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
29
33
|
// The _propertyKey parameter is a string in TS4 with experimentalDecorators
|
|
30
34
|
// but a ClassFieldDecoratorContext in TS5 without.
|
|
31
35
|
// Seems when a different TS version is used in VSCode for editor checking, we get errors here
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnimationAction, Box3, Box3Helper, Color, Euler, GridHelper, Layers, Material, Mesh, MeshStandardMaterial, Object3D, PerspectiveCamera, PlaneGeometry, Quaternion, Scene, ShadowMaterial, Texture, Uniform, Vector3 } from "three";
|
|
1
|
+
import { AnimationAction, Box3, Box3Helper, Camera, Color, Euler, GridHelper, Layers, Material, Mesh, MeshStandardMaterial, Object3D, PerspectiveCamera, PlaneGeometry, Quaternion, Scene, ShadowMaterial, Texture, Uniform, Vector2Like, Vector3 } from "three";
|
|
2
2
|
import { ShaderMaterial, WebGLRenderer } from "three";
|
|
3
3
|
import { GroundedSkybox } from "three/examples/jsm/objects/GroundedSkybox.js";
|
|
4
4
|
|
|
@@ -71,6 +71,57 @@ export function lookAtObject(object: Object3D, target: Object3D, keepUpDirection
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Look at a 2D point in screen space
|
|
76
|
+
* @param object the object to look at the point
|
|
77
|
+
* @param target the target point in 2D screen space XY e.g. from a mouse event
|
|
78
|
+
* @param camera the camera to use for the lookAt
|
|
79
|
+
* @param factor the factor to multiply the distance from the camera to the object. Default is 1
|
|
80
|
+
* @returns the target point in world space
|
|
81
|
+
*
|
|
82
|
+
* @example Needle Engine Component
|
|
83
|
+
* ```ts
|
|
84
|
+
* export class MyLookAtComponent extends Behaviour {
|
|
85
|
+
* update() {
|
|
86
|
+
* lookAtScreenPoint(this.gameObject, this.context.input.mousePosition, this.context.mainCamera);
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @example Look at from browser mouse move event
|
|
92
|
+
* ```ts
|
|
93
|
+
* window.addEventListener("mousemove", (e) => {
|
|
94
|
+
* lookAtScreenPoint(object, new Vector2(e.clientX, e.clientY), camera);
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export function lookAtScreenPoint(object: Object3D, target: Vector2Like, camera: Camera, factor : number = 1): Vector3 | null {
|
|
99
|
+
|
|
100
|
+
if (camera) {
|
|
101
|
+
const pos = getTempVector(0, 0, 0);
|
|
102
|
+
const ndcx = (target.x / window.innerWidth) * 2 - 1;
|
|
103
|
+
const ndcy = -(target.y / window.innerHeight) * 2 + 1;
|
|
104
|
+
pos.set(
|
|
105
|
+
ndcx,
|
|
106
|
+
ndcy,
|
|
107
|
+
0
|
|
108
|
+
);
|
|
109
|
+
pos.unproject(camera);
|
|
110
|
+
// get distance from object to camera
|
|
111
|
+
const camPos = camera.worldPosition;
|
|
112
|
+
const dist = object.worldPosition.distanceTo(camPos);
|
|
113
|
+
// Create direction from camera through cursor point
|
|
114
|
+
const dir = pos.sub(camPos);
|
|
115
|
+
dir.multiplyScalar(factor * 3.6 * dist);
|
|
116
|
+
const targetPoint = camera.worldPosition.add(dir);
|
|
117
|
+
object.lookAt(targetPoint);
|
|
118
|
+
return targetPoint;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
74
125
|
const _tempVecs = new CircularBuffer(() => new Vector3(), 100);
|
|
75
126
|
|
|
76
127
|
/** Gets a temporary vector. If a vector is passed in it will be copied to the temporary vector
|
|
@@ -59,13 +59,21 @@ export async function tryDetermineFileTypeFromURL(url: string, useExtension: boo
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
|
|
62
63
|
// If the URL doesnt contain a filetype we need to check the header
|
|
63
64
|
// This is the case for example if we load a file from a data url
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
|
|
66
|
+
if(url.startsWith("blob:")) {
|
|
67
|
+
// We can't modify the blob URL
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const newUrl = new URL(url);
|
|
71
|
+
// Adding a URL parameter to avoid the brower to bust the full cache
|
|
72
|
+
// If we don't do this the file that might already be disc cached will be deleted from the cache
|
|
73
|
+
newUrl.searchParams.append("range", "true");
|
|
74
|
+
url = newUrl.toString();
|
|
75
|
+
}
|
|
76
|
+
const header = await fetch(url, {
|
|
69
77
|
method: "GET",
|
|
70
78
|
headers: {
|
|
71
79
|
"range": "bytes=0-32"
|
|
@@ -697,7 +697,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
697
697
|
if (res == true && hasCommercialLicense() && !debugNonCommercial) {
|
|
698
698
|
let visible = this._userRequestedLogoVisible;
|
|
699
699
|
if (visible === undefined) visible = false;
|
|
700
|
-
this
|
|
700
|
+
this.___onSetLogoVisible(visible);
|
|
701
701
|
}
|
|
702
702
|
}));
|
|
703
703
|
} catch (e) {
|
|
@@ -827,10 +827,10 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
827
827
|
if (!localNetwork) return;
|
|
828
828
|
}
|
|
829
829
|
}
|
|
830
|
-
this
|
|
830
|
+
this.___onSetLogoVisible(visible);
|
|
831
831
|
}
|
|
832
832
|
|
|
833
|
-
|
|
833
|
+
private ___onSetLogoVisible(visible: boolean) {
|
|
834
834
|
this.logoContainer.style.display = "";
|
|
835
835
|
this.logoContainer.style.opacity = "1";
|
|
836
836
|
this.logoContainer.style.visibility = "visible";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EquirectangularReflectionMapping, Euler, Frustum, Matrix4, OrthographicCamera, PerspectiveCamera, Ray, Vector3 } from "three";
|
|
1
|
+
import { Color, EquirectangularReflectionMapping, Euler, Frustum, Matrix4, OrthographicCamera, PerspectiveCamera, Ray, Vector3 } from "three";
|
|
2
2
|
import { Texture } from "three";
|
|
3
3
|
|
|
4
4
|
import { showBalloonMessage } from "../engine/debug/index.js";
|
|
@@ -289,15 +289,18 @@ export class Camera extends Behaviour implements ICamera {
|
|
|
289
289
|
public get backgroundColor(): RGBAColor | null {
|
|
290
290
|
return this._backgroundColor ?? null;
|
|
291
291
|
}
|
|
292
|
-
public set backgroundColor(val: RGBAColor | null) {
|
|
292
|
+
public set backgroundColor(val: RGBAColor | Color | null) {
|
|
293
293
|
if (!val) return;
|
|
294
294
|
if (!this._backgroundColor) {
|
|
295
|
-
|
|
296
|
-
this._backgroundColor = val.clone();
|
|
295
|
+
this._backgroundColor = new RGBAColor(1, 1, 1, 1);
|
|
297
296
|
}
|
|
298
|
-
|
|
297
|
+
|
|
298
|
+
this._backgroundColor.copy(val);
|
|
299
|
+
|
|
299
300
|
// set background color to solid if provided color doesnt have any alpha channel
|
|
300
|
-
if (val.alpha === undefined)
|
|
301
|
+
if ((!("alpha" in val) || val.alpha === undefined)) {
|
|
302
|
+
this._backgroundColor.alpha = 1;
|
|
303
|
+
}
|
|
301
304
|
this.applyClearFlagsIfIsActiveCamera();
|
|
302
305
|
}
|
|
303
306
|
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { PerspectiveCamera } from "three";
|
|
1
|
+
import { Color, PerspectiveCamera, PMREMGenerator } from "three";
|
|
2
2
|
|
|
3
3
|
import { getCameraController } from "../engine/engine_camera.js";
|
|
4
4
|
import { addNewComponent, getOrAddComponent } from "../engine/engine_components.js";
|
|
5
5
|
import { Context } from "../engine/engine_context.js";
|
|
6
6
|
import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry.js";
|
|
7
7
|
import { NeedleEngineHTMLElement } from "../engine/engine_element.js";
|
|
8
|
+
import { createFlatTexture, createTrilightTexture } from "../engine/engine_shaders.js";
|
|
8
9
|
import type { ICamera, IContext } from "../engine/engine_types.js";
|
|
9
10
|
import { getParam } from "../engine/engine_utils.js";
|
|
10
11
|
import { RGBAColor } from "../engine/js-extensions/index.js";
|
|
11
12
|
import { Camera, ClearFlags } from "./Camera.js";
|
|
12
13
|
import { OrbitControls } from "./OrbitControls.js";
|
|
14
|
+
import EnvironmentScene from "./utils/EnvironmentScene.js";
|
|
13
15
|
|
|
14
16
|
const debug = getParam("debugmissingcamera");
|
|
15
17
|
|
|
@@ -30,27 +32,47 @@ ContextRegistry.registerCallback(ContextEvent.MissingCamera, (evt) => {
|
|
|
30
32
|
|
|
31
33
|
const camInstance = new Camera();
|
|
32
34
|
camInstance.sourceId = evt.files?.[0]?.src ?? "unknown"
|
|
35
|
+
camInstance.fieldOfView = 35;
|
|
33
36
|
|
|
37
|
+
const transparentAttribute = evt.context.domElement.getAttribute("transparent");
|
|
38
|
+
if (transparentAttribute != undefined) {
|
|
39
|
+
camInstance.clearFlags = ClearFlags.Uninitialized;
|
|
40
|
+
}
|
|
34
41
|
// Set the clearFlags to a skybox if we have one OR if the user set a skybox image attribute
|
|
35
|
-
if (evt.context.domElement.getAttribute("skybox-image")?.length || evt.context.domElement.getAttribute("background-image")?.length || (evt.context as Context).lightmaps.tryGetSkybox(camInstance.sourceId)) {
|
|
42
|
+
else if (evt.context.domElement.getAttribute("skybox-image")?.length || evt.context.domElement.getAttribute("background-image")?.length || (evt.context as Context).lightmaps.tryGetSkybox(camInstance.sourceId)) {
|
|
36
43
|
camInstance.clearFlags = ClearFlags.Skybox;
|
|
44
|
+
// TODO: can we store the backgroundBlurriness in the gltf file somewhere except inside the camera?
|
|
45
|
+
// e.g. when we export a scene from blender without a camera in the scene
|
|
46
|
+
camInstance.backgroundBlurriness = .2; // same as in blender 0.5
|
|
37
47
|
}
|
|
38
|
-
else
|
|
39
|
-
// TODO provide a nice default skybox
|
|
48
|
+
else {
|
|
40
49
|
camInstance.clearFlags = ClearFlags.SolidColor;
|
|
41
|
-
camInstance.backgroundColor = new RGBAColor(0.5, 0.5, 0.5, 1);
|
|
42
|
-
camInstance.fieldOfView = 35;
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
let backgroundColor = "#efefef";
|
|
52
|
+
if(typeof window !== undefined && (window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
53
|
+
backgroundColor = "#1f1f1f";
|
|
54
|
+
}
|
|
55
|
+
scene.background = new Color(backgroundColor); // dont set it on the camera because this might be controlled from "background-color" attribute which is set on the scene directly. If the camera has a background color, it will override the scene's background color
|
|
56
|
+
|
|
57
|
+
// Generate a default environment map if none is set
|
|
58
|
+
if (!scene.environment) {
|
|
59
|
+
// const backgroundColorAttribute = evt.context.domElement.getAttribute("background-color") ?? "#fff";
|
|
60
|
+
// const backgroundColor = new Color(backgroundColorAttribute);
|
|
61
|
+
const pmremGenerator = new PMREMGenerator(evt.context.renderer);
|
|
62
|
+
const env = new EnvironmentScene("neutral");
|
|
63
|
+
// const background = scene.background;
|
|
64
|
+
// const col0 = new Color(.1, .1, .1);
|
|
65
|
+
// const col1 = new Color(.3, .3, .3);
|
|
66
|
+
// const col2 = new Color(1, 1, 1);
|
|
67
|
+
// const envmap = createTrilightTexture(col0, col1, col2, 32, 32);
|
|
68
|
+
// scene.background = envmap;
|
|
69
|
+
scene.environment = pmremGenerator.fromScene(env, .025).texture;
|
|
70
|
+
// scene.background = background;
|
|
71
|
+
}
|
|
47
72
|
}
|
|
48
73
|
|
|
49
|
-
// TODO: can we store the backgroundBlurriness in the gltf file somewhere except inside the camera?
|
|
50
|
-
// e.g. when we export a scene from blender without a camera in the scene
|
|
51
|
-
camInstance.backgroundBlurriness = .2; // same as in blender 0.5
|
|
52
|
-
const cam = addNewComponent(cameraObject, camInstance, true) as ICamera;
|
|
53
74
|
|
|
75
|
+
const cam = addNewComponent(cameraObject, camInstance, true) as ICamera;
|
|
54
76
|
cameraObject.position.x = 0;
|
|
55
77
|
cameraObject.position.y = 1;
|
|
56
78
|
cameraObject.position.z = 2;
|
|
@@ -1128,7 +1128,8 @@ export abstract class Component implements IComponent, EventTarget,
|
|
|
1128
1128
|
}
|
|
1129
1129
|
|
|
1130
1130
|
/**
|
|
1131
|
-
* Gets the position of this component's GameObject in world space
|
|
1131
|
+
* Gets the position of this component's GameObject in world space.
|
|
1132
|
+
* Note: This is equivalent to calling `this.gameObject.worldPosition`
|
|
1132
1133
|
*/
|
|
1133
1134
|
get worldPosition(): Vector3 {
|
|
1134
1135
|
return threeutils.getWorldPosition(this.gameObject);
|
|
@@ -1154,6 +1155,7 @@ export abstract class Component implements IComponent, EventTarget,
|
|
|
1154
1155
|
|
|
1155
1156
|
/**
|
|
1156
1157
|
* Gets the rotation of this component's GameObject in world space as a quaternion
|
|
1158
|
+
* Note: This is equivalent to calling `this.gameObject.worldQuaternion`
|
|
1157
1159
|
*/
|
|
1158
1160
|
get worldQuaternion(): Quaternion {
|
|
1159
1161
|
return threeutils.getWorldQuaternion(this.gameObject);
|
|
@@ -1194,7 +1196,8 @@ export abstract class Component implements IComponent, EventTarget,
|
|
|
1194
1196
|
}
|
|
1195
1197
|
|
|
1196
1198
|
/**
|
|
1197
|
-
* Gets the rotation of this component's GameObject in world space as Euler angles (in degrees)
|
|
1199
|
+
* Gets the rotation of this component's GameObject in world space as Euler angles (in degrees)
|
|
1200
|
+
* Note: This is equivalent to calling `this.gameObject.worldRotation`
|
|
1198
1201
|
*/
|
|
1199
1202
|
get worldRotation(): Vector3 {
|
|
1200
1203
|
return this.gameObject.worldRotation;
|
|
@@ -17,9 +17,15 @@ import { Behaviour, GameObject } from "./Component.js";
|
|
|
17
17
|
const debug = getParam("debugcontactshadows");
|
|
18
18
|
|
|
19
19
|
onStart(ctx => {
|
|
20
|
-
const val = ctx.domElement.getAttribute("contactshadows");
|
|
20
|
+
const val = ctx.domElement.getAttribute("contactshadows") || ctx.domElement.getAttribute("contact-shadows");
|
|
21
21
|
if (val != undefined && val != "0" && val != "false") {
|
|
22
|
-
|
|
22
|
+
console.debug("Auto-creating ContactShadows because of `contactshadows` attribute");
|
|
23
|
+
const shadows = ContactShadows.auto(ctx);
|
|
24
|
+
const intensity = parseFloat(val);
|
|
25
|
+
if (!isNaN(intensity)) {
|
|
26
|
+
shadows.opacity = intensity;
|
|
27
|
+
shadows.darkness = intensity;
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
30
|
})
|
|
25
31
|
|
|
@@ -316,6 +316,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
316
316
|
if (DeviceUtilities.isMobileDevice()) this.doubleClickToFocus = true;
|
|
317
317
|
}
|
|
318
318
|
this._controls.addEventListener("start", this.onControlsChangeStarted);
|
|
319
|
+
this._controls.addEventListener("end", this.onControlsChangeEnded);
|
|
319
320
|
|
|
320
321
|
if (!this._startedListeningToKeyEvents && this.enableKeys) {
|
|
321
322
|
this._startedListeningToKeyEvents = true;
|
|
@@ -346,6 +347,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
346
347
|
this._controls.enabled = false;
|
|
347
348
|
this._controls.autoRotate = false;
|
|
348
349
|
this._controls.removeEventListener("start", this.onControlsChangeStarted);
|
|
350
|
+
this._controls.removeEventListener("end", this.onControlsChangeEnded);
|
|
349
351
|
try {
|
|
350
352
|
this._controls.stopListenToKeyEvents();
|
|
351
353
|
} catch { /** this fails if we never listened to key events... */ }
|
|
@@ -366,7 +368,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
366
368
|
this._activePointerEvents.push(_evt);
|
|
367
369
|
}
|
|
368
370
|
private _onPointerDownLate = (evt: NEPointerEvent) => {
|
|
369
|
-
if(evt.used && this._controls) {
|
|
371
|
+
if (evt.used && this._controls) {
|
|
370
372
|
// Disabling orbit controls here because otherwise we get a slight movement when e.g. using DragControls
|
|
371
373
|
this._controls.enabled = false;
|
|
372
374
|
}
|
|
@@ -413,22 +415,45 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
413
415
|
this.setTargetFromRaycast();
|
|
414
416
|
}
|
|
415
417
|
// Automatically update the camera focus
|
|
416
|
-
else if (!evt.used && this.autoTarget) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const hit = hits.length > 0 ? hits[0] : undefined;
|
|
420
|
-
if(hit && hit.distance > this.minZoom && hit.distance < this.maxZoom) {
|
|
421
|
-
if(debug) Gizmos.DrawWireSphere(hit.point, 0.1, 0xff0000, 2);
|
|
422
|
-
this._controls?.target.copy(hits[0].point);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
418
|
+
// else if (!evt.used && this.autoTarget) {
|
|
419
|
+
// this.updateTargetNow();
|
|
420
|
+
// }
|
|
425
421
|
};
|
|
426
422
|
|
|
423
|
+
private updateTargetNow() {
|
|
424
|
+
const ray = new Ray(this._cameraObject?.worldPosition, this._cameraObject?.worldForward.multiplyScalar(-1));
|
|
425
|
+
const hits = this.context.physics.raycastFromRay(ray);
|
|
426
|
+
const hit = hits.length > 0 ? hits[0] : undefined;
|
|
427
|
+
if (hit && hit.distance > this.minZoom && hit.distance < this.maxZoom) {
|
|
428
|
+
if (debug) Gizmos.DrawWireSphere(hit.point, 0.1, 0xff0000, 2);
|
|
429
|
+
this._controls?.target.copy(hits[0].point);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private _orbitStartAngle: number = 0;
|
|
427
434
|
private onControlsChangeStarted = () => {
|
|
435
|
+
if (this._controls) {
|
|
436
|
+
this._orbitStartAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
|
|
437
|
+
}
|
|
428
438
|
if (this._syncedTransform) {
|
|
429
439
|
this._syncedTransform.requestOwnership();
|
|
430
440
|
}
|
|
431
441
|
}
|
|
442
|
+
private onControlsChangeEnded = () => {
|
|
443
|
+
|
|
444
|
+
if (this._controls) {
|
|
445
|
+
if (this.autoTarget) {
|
|
446
|
+
const newAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
|
|
447
|
+
const delta = newAngle - this._orbitStartAngle;
|
|
448
|
+
if (Math.abs(delta) < .01) {
|
|
449
|
+
if (debug) console.debug("OrbitControls: No movement detected, updating target now");
|
|
450
|
+
this.updateTargetNow();
|
|
451
|
+
}
|
|
452
|
+
else if(debug) console.debug("OrbitControls: Movement detected", delta);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
}
|
|
432
457
|
|
|
433
458
|
private _shouldDisable: boolean = false;
|
|
434
459
|
private afterHandleInput(evt: CustomEvent<AfterHandleInputEvent>) {
|
|
@@ -51,7 +51,7 @@ onUpdate((ctx) => {
|
|
|
51
51
|
const threshold = isRunningOnGlitch ? 10 : 40;
|
|
52
52
|
FAST_INTERVAL = Math.floor(FAST_ACTIVE_SYNCTRANSFORMS / threshold);
|
|
53
53
|
FAST_ACTIVE_SYNCTRANSFORMS = 0;
|
|
54
|
-
if(debug && FAST_INTERVAL > 0) console.log("Sync Transform Fast Interval", FAST_INTERVAL);
|
|
54
|
+
if (debug && FAST_INTERVAL > 0) console.log("Sync Transform Fast Interval", FAST_INTERVAL);
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -62,20 +62,20 @@ onUpdate((ctx) => {
|
|
|
62
62
|
*/
|
|
63
63
|
export class SyncedTransform extends Behaviour {
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
// public autoOwnership: boolean = true;
|
|
67
67
|
/** When true, overrides physics behavior when this object is owned by the local user */
|
|
68
68
|
public overridePhysics: boolean = true
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
/** Whether to smoothly interpolate position changes when receiving updates */
|
|
71
71
|
public interpolatePosition: boolean = true;
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
/** Whether to smoothly interpolate rotation changes when receiving updates */
|
|
74
74
|
public interpolateRotation: boolean = true;
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
/** When true, sends updates at a higher frequency, useful for fast-moving objects */
|
|
77
77
|
public fastMode: boolean = false;
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
/** When true, notifies other clients when this object is destroyed */
|
|
80
80
|
public syncDestroy: boolean = false;
|
|
81
81
|
|
|
@@ -135,8 +135,9 @@ export class SyncedTransform extends Behaviour {
|
|
|
135
135
|
this._targetRotation = new Quaternion();
|
|
136
136
|
|
|
137
137
|
// sync instantiate issue was because they shared the same last pos vector!
|
|
138
|
-
this.
|
|
139
|
-
this.
|
|
138
|
+
this.lastPosition = new Vector3();
|
|
139
|
+
this.lastRotation = new Quaternion();
|
|
140
|
+
this.lastScale = new Vector3();
|
|
140
141
|
|
|
141
142
|
this.rb = GameObject.getComponentInChildren(this.gameObject, Rigidbody);
|
|
142
143
|
if (this.rb) {
|
|
@@ -207,6 +208,11 @@ export class SyncedTransform extends Behaviour {
|
|
|
207
208
|
if (!this.interpolateRotation || !this._receivedDataBefore)
|
|
208
209
|
setWorldEuler(this.gameObject, this.tempEuler);
|
|
209
210
|
}
|
|
211
|
+
|
|
212
|
+
const scale = transform.scale();
|
|
213
|
+
if (scale) {
|
|
214
|
+
this.gameObject.scale.set(scale.x(), scale.y(), scale.z());
|
|
215
|
+
}
|
|
210
216
|
}
|
|
211
217
|
this._receivedDataBefore = true;
|
|
212
218
|
|
|
@@ -221,8 +227,9 @@ export class SyncedTransform extends Behaviour {
|
|
|
221
227
|
* Initializes tracking of position and rotation when component is enabled
|
|
222
228
|
*/
|
|
223
229
|
onEnable(): void {
|
|
224
|
-
this.
|
|
225
|
-
this.
|
|
230
|
+
this.lastPosition.copy(this.worldPosition);
|
|
231
|
+
this.lastRotation.copy(this.worldQuaternion);
|
|
232
|
+
this.lastScale.copy(this.gameObject.scale);
|
|
226
233
|
this._needsUpdate = true;
|
|
227
234
|
// console.log("ENABLE", this.guid, this.gameObject.guid, this.lastWorldPos);
|
|
228
235
|
if (this._model) {
|
|
@@ -241,8 +248,9 @@ export class SyncedTransform extends Behaviour {
|
|
|
241
248
|
|
|
242
249
|
|
|
243
250
|
private receivedUpdate = false;
|
|
244
|
-
private
|
|
245
|
-
private
|
|
251
|
+
private lastPosition!: Vector3;
|
|
252
|
+
private lastRotation!: Quaternion;
|
|
253
|
+
private lastScale!: Vector3;
|
|
246
254
|
|
|
247
255
|
/**
|
|
248
256
|
* @internal
|
|
@@ -264,24 +272,27 @@ export class SyncedTransform extends Behaviour {
|
|
|
264
272
|
this._model.requestOwnership();
|
|
265
273
|
}
|
|
266
274
|
|
|
267
|
-
const
|
|
268
|
-
const
|
|
275
|
+
const pos = this.worldPosition;
|
|
276
|
+
const rot = this.worldQuaternion;
|
|
277
|
+
const scale = this.gameObject.scale;
|
|
269
278
|
if (this._model.isOwned && !this.receivedUpdate) {
|
|
270
|
-
const worlddiff = wp.distanceTo(this.lastWorldPos);
|
|
271
|
-
const worldRot = wr.angleTo(this.lastWorldRotation);
|
|
272
279
|
const threshold = this._model.hasOwnership || this.fastMode ? .0001 : .001;
|
|
273
|
-
if (
|
|
280
|
+
if (pos.distanceTo(this.lastPosition) > threshold ||
|
|
281
|
+
rot.angleTo(this.lastRotation) > threshold ||
|
|
282
|
+
scale.distanceTo(this.lastScale) > threshold) {
|
|
274
283
|
// console.log(worlddiff, worldRot);
|
|
275
284
|
if (!this._model.hasOwnership) {
|
|
276
285
|
|
|
277
286
|
if (debug)
|
|
278
|
-
console.log(this.guid, "reset because not owned but", this.gameObject.name, this.
|
|
287
|
+
console.log(this.guid, "reset because not owned but", this.gameObject.name, this.lastPosition);
|
|
279
288
|
|
|
280
|
-
this.worldPosition = this.
|
|
281
|
-
|
|
289
|
+
this.worldPosition = this.lastPosition;
|
|
290
|
+
pos.copy(this.lastPosition);
|
|
282
291
|
|
|
283
|
-
this.worldQuaternion = this.
|
|
284
|
-
|
|
292
|
+
this.worldQuaternion = this.lastRotation;
|
|
293
|
+
rot.copy(this.lastRotation);
|
|
294
|
+
|
|
295
|
+
this.gameObject.scale.copy(this.lastScale);
|
|
285
296
|
|
|
286
297
|
InstancingUtil.markDirty(this.gameObject, true);
|
|
287
298
|
this._needsUpdate = false;
|
|
@@ -323,12 +334,13 @@ export class SyncedTransform extends Behaviour {
|
|
|
323
334
|
|
|
324
335
|
|
|
325
336
|
this.receivedUpdate = false;
|
|
326
|
-
this.
|
|
327
|
-
this.
|
|
337
|
+
this.lastPosition.copy(pos);
|
|
338
|
+
this.lastRotation.copy(rot);
|
|
339
|
+
this.lastScale.copy(scale);
|
|
328
340
|
|
|
329
341
|
|
|
330
342
|
if (!this._model) return;
|
|
331
|
-
|
|
343
|
+
|
|
332
344
|
if (!this._model || this._model.hasOwnership === undefined || !this._model.hasOwnership) {
|
|
333
345
|
// if we're not the owner of this synced transform then don't send any data
|
|
334
346
|
return;
|
|
@@ -281,8 +281,12 @@ export class PostProcessingHandler {
|
|
|
281
281
|
|
|
282
282
|
if (this._passIndices !== null) {
|
|
283
283
|
const newPasses = [composer.passes[0]];
|
|
284
|
-
if (this._passIndices.length > 0
|
|
285
|
-
newPasses.push(...this._passIndices
|
|
284
|
+
if (this._passIndices.length > 0) {
|
|
285
|
+
newPasses.push(...this._passIndices
|
|
286
|
+
.filter(x => x !== 0)
|
|
287
|
+
.map(index => composer.passes[index])
|
|
288
|
+
.filter(pass => pass)
|
|
289
|
+
);
|
|
286
290
|
}
|
|
287
291
|
if (newPasses.length > 0) {
|
|
288
292
|
console.log("[PostProcessing] Passes (selected) →", newPasses);
|
|
@@ -290,6 +294,7 @@ export class PostProcessingHandler {
|
|
|
290
294
|
composer.passes.length = 0;
|
|
291
295
|
for (const pass of newPasses) {
|
|
292
296
|
pass.enabled = true;
|
|
297
|
+
pass.renderToScreen = false; // allows automatic setting for the last pass
|
|
293
298
|
composer.addPass(pass);
|
|
294
299
|
}
|
|
295
300
|
}
|