@needle-tools/engine 4.10.4 → 4.10.5-next.267a93d
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 +5 -0
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-ITqL-yYC.min.js → needle-engine.bundle-Bj33kRGE.min.js} +114 -114
- package/dist/{needle-engine.bundle-DfqZ3U87.umd.cjs → needle-engine.bundle-BozXof79.umd.cjs} +117 -117
- package/dist/{needle-engine.bundle-CQzbMO5P.js → needle-engine.bundle-DXZewFGi.js} +2204 -2198
- package/dist/needle-engine.d.ts +15 -15
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/debug/debug_overlay.js +1 -1
- package/lib/engine/engine_context.js +1 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine-components/Renderer.js +33 -32
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/RendererLightmap.d.ts +6 -5
- package/lib/engine-components/RendererLightmap.js +35 -21
- package/lib/engine-components/RendererLightmap.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +16 -2
- package/lib/engine-components/web/CursorFollow.js +41 -7
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/package.json +2 -2
- package/src/engine/debug/debug_overlay.ts +1 -1
- package/src/engine/engine_context.ts +1 -0
- package/src/engine-components/Renderer.ts +38 -37
- package/src/engine-components/RendererLightmap.ts +42 -24
- package/src/engine-components/web/CursorFollow.ts +46 -9
|
@@ -9,6 +9,9 @@ import { getTempVector } from "../../engine/engine_three_utils.js";
|
|
|
9
9
|
import { Behaviour } from "../Component.js";
|
|
10
10
|
/**
|
|
11
11
|
* The CursorFollow component makes the object follow the cursor (or touch) position on screen.
|
|
12
|
+
*
|
|
13
|
+
* - Example: [Look At Cursor sample](https://engine.needle.tools/samples/look-at-cursor-interactive-3d-header/). This sample combines the CursorFollow component with a LookAt component to create an interactive 3D header that looks at the cursor.
|
|
14
|
+
*
|
|
12
15
|
* @category Web
|
|
13
16
|
* @group Components
|
|
14
17
|
* @component
|
|
@@ -19,31 +22,59 @@ export class CursorFollow extends Behaviour {
|
|
|
19
22
|
* @default 0
|
|
20
23
|
*/
|
|
21
24
|
damping = 0;
|
|
25
|
+
/**
|
|
26
|
+
* When enabled the object will follow the cursor even outside of the needle-engine canvas. This is useful for example for look at effects where you have a small needle-engine element on your page and you want the 3D object to keep looking at the cursor even when it's outside of the canvas.
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
useFullPage = true;
|
|
22
30
|
/**
|
|
23
31
|
* If true, the initial distance to the camera is maintained when following the cursor.
|
|
24
32
|
* @default true
|
|
25
33
|
*/
|
|
26
34
|
keepDistance = true;
|
|
27
|
-
awake() {
|
|
28
|
-
this._distance = -1;
|
|
29
|
-
}
|
|
30
35
|
_distance = -1;
|
|
31
|
-
updateDistance() {
|
|
32
|
-
if (this.keepDistance && this._distance !== -1) {
|
|
36
|
+
updateDistance(force = false) {
|
|
37
|
+
if (!force && (this.keepDistance && this._distance !== -1)) {
|
|
33
38
|
return;
|
|
34
39
|
}
|
|
35
40
|
this._distance = this.gameObject.worldPosition.distanceTo(this.context.mainCamera.worldPosition);
|
|
36
41
|
}
|
|
37
42
|
/** @internal */
|
|
43
|
+
awake() {
|
|
44
|
+
this._distance = -1;
|
|
45
|
+
}
|
|
46
|
+
onEnable() {
|
|
47
|
+
this._distance = -1;
|
|
48
|
+
window.addEventListener('pointermove', this._onPointerMove);
|
|
49
|
+
}
|
|
50
|
+
onDisable() {
|
|
51
|
+
window.removeEventListener('pointermove', this._onPointerMove);
|
|
52
|
+
}
|
|
53
|
+
_ndc_x = 0;
|
|
54
|
+
_ndc_y = 0;
|
|
55
|
+
_onPointerMove = (e) => {
|
|
56
|
+
if (!this.useFullPage)
|
|
57
|
+
return;
|
|
58
|
+
const x = e.clientX;
|
|
59
|
+
const y = e.clientY;
|
|
60
|
+
const domx = this.context.domX;
|
|
61
|
+
const domy = this.context.domY;
|
|
62
|
+
const domw = this.context.domWidth;
|
|
63
|
+
const domh = this.context.domHeight;
|
|
64
|
+
this._ndc_x = (x - domx) / domw * 2 - 1;
|
|
65
|
+
this._ndc_y = -(y - domy) / domh * 2 + 1;
|
|
66
|
+
};
|
|
67
|
+
/** @internal */
|
|
38
68
|
update() {
|
|
39
69
|
// continuously update distance in case camera or object moves
|
|
40
70
|
this.updateDistance();
|
|
71
|
+
const x = this.useFullPage ? this._ndc_x : this.context.input.mousePositionRC.x;
|
|
72
|
+
const y = this.useFullPage ? this._ndc_y : this.context.input.mousePositionRC.y;
|
|
41
73
|
// follow cursor in screenspace but maintain initial distance from camera
|
|
42
|
-
const cursor = this.context.input.mousePositionRC;
|
|
43
74
|
const camera = this.context.mainCamera;
|
|
44
75
|
const cameraPosition = camera.worldPosition;
|
|
45
76
|
// create ray from camera through cursor position
|
|
46
|
-
const rayDirection = getTempVector(
|
|
77
|
+
const rayDirection = getTempVector(x, y, 1).unproject(camera);
|
|
47
78
|
rayDirection.sub(cameraPosition).normalize();
|
|
48
79
|
// position object at initial distance along the ray
|
|
49
80
|
const newPosition = rayDirection.multiplyScalar(this._distance).add(cameraPosition);
|
|
@@ -60,6 +91,9 @@ export class CursorFollow extends Behaviour {
|
|
|
60
91
|
__decorate([
|
|
61
92
|
serializable()
|
|
62
93
|
], CursorFollow.prototype, "damping", void 0);
|
|
94
|
+
__decorate([
|
|
95
|
+
serializable()
|
|
96
|
+
], CursorFollow.prototype, "useFullPage", void 0);
|
|
63
97
|
__decorate([
|
|
64
98
|
serializable()
|
|
65
99
|
], CursorFollow.prototype, "keepDistance", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.5-next.267a93d",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
|
|
5
5
|
"main": "dist/needle-engine.min.js",
|
|
6
6
|
"exports": {
|
|
@@ -169,4 +169,4 @@
|
|
|
169
169
|
"module": "lib/needle-engine.js",
|
|
170
170
|
"typings": "lib/needle-engine.d.ts",
|
|
171
171
|
"types": "lib/needle-engine.d.ts"
|
|
172
|
-
}
|
|
172
|
+
}
|
|
@@ -244,7 +244,7 @@ function getLogsContainer(domElement: HTMLElement): HTMLElement {
|
|
|
244
244
|
padding-top: 0px;
|
|
245
245
|
max-width: 70%;
|
|
246
246
|
max-height: calc(100% - 5px);
|
|
247
|
-
z-index:
|
|
247
|
+
z-index: 100000;
|
|
248
248
|
pointer-events: scroll;
|
|
249
249
|
display: flex;
|
|
250
250
|
align-items: end;
|
|
@@ -1627,6 +1627,7 @@ export class Context implements IContext {
|
|
|
1627
1627
|
// but as a fallback we still register them (if e.g. there's no camera for compile async)
|
|
1628
1628
|
looputils.runPrewarm(this);
|
|
1629
1629
|
this._currentFrameEvent = FrameEvent.Undefined;
|
|
1630
|
+
nodeFrame.camera = this.mainCamera as Camera | null;
|
|
1630
1631
|
nodeFrame.update();
|
|
1631
1632
|
this.renderNow();
|
|
1632
1633
|
this._currentFrameEvent = FrameEvent.OnAfterRender;
|
|
@@ -359,7 +359,7 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
359
359
|
get sharedMaterials(): SharedMaterialArray {
|
|
360
360
|
|
|
361
361
|
// @ts-ignore (original materials will be set during deserialization)
|
|
362
|
-
if(this._originalMaterials === undefined) return null;
|
|
362
|
+
if (this._originalMaterials === undefined) return null;
|
|
363
363
|
|
|
364
364
|
// @ts-ignore during deserialization code might access this property *before* the setter and then create an empty array
|
|
365
365
|
if (this.__isDeserializing === true) return null;
|
|
@@ -476,41 +476,43 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
476
476
|
? this._lightmapTextureOverride
|
|
477
477
|
: this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
|
|
478
478
|
if (tex) {
|
|
479
|
-
if (!this._lightmaps)
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
479
|
+
if (!this._lightmaps) this._lightmaps = [];
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
const rm = new RendererLightmap(this);
|
|
483
|
+
rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
|
|
484
|
+
this._lightmaps.push(rm);
|
|
485
|
+
|
|
486
|
+
// if (type === "Mesh") {
|
|
487
|
+
// const mat = this.gameObject["material"];
|
|
488
|
+
// if (!mat?.isMeshBasicMaterial) {
|
|
489
|
+
// if (this._lightmaps.length <= 0) {
|
|
490
|
+
// }
|
|
491
|
+
// const rm = this._lightmaps[0];
|
|
492
|
+
// rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
|
|
493
|
+
// }
|
|
494
|
+
// else {
|
|
495
|
+
// if (mat)
|
|
496
|
+
// console.warn("Lightmapping is not supported on MeshBasicMaterial", mat.name)
|
|
497
|
+
// }
|
|
498
|
+
// }
|
|
499
|
+
// // for multi materials we need to loop through children
|
|
500
|
+
// // and then we add a lightmap renderer component to each of them
|
|
501
|
+
// else if (this.isMultiMaterialObject(this.gameObject) && this.sharedMaterials.length > 0) {
|
|
502
|
+
// for (let i = 0; i < this.gameObject.children.length; i++) {
|
|
503
|
+
// const child = this.gameObject.children[i];
|
|
504
|
+
// if (!child["material"]?.isMeshBasicMaterial) {
|
|
505
|
+
// let rm: RendererLightmap | undefined = undefined;
|
|
506
|
+
// if (i >= this._lightmaps.length) {
|
|
507
|
+
// rm = new RendererLightmap(child as Mesh, this.context);
|
|
508
|
+
// this._lightmaps.push(rm);
|
|
509
|
+
// }
|
|
510
|
+
// else
|
|
511
|
+
// rm = this._lightmaps[i];
|
|
512
|
+
// rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
|
|
513
|
+
// }
|
|
514
|
+
// }
|
|
515
|
+
// }
|
|
514
516
|
}
|
|
515
517
|
else {
|
|
516
518
|
if (debugRenderer) console.warn("Lightmap not found", this.sourceId, this.lightmapIndex);
|
|
@@ -742,7 +744,6 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
742
744
|
const environmentIntensity = this.context.mainCameraComponent?.environmentIntensity ?? 1;
|
|
743
745
|
material.envMapIntensity = Math.max(0, environmentIntensity * this.context.sceneLighting.environmentIntensity / factor);
|
|
744
746
|
}
|
|
745
|
-
|
|
746
747
|
if (this._lightmaps) {
|
|
747
748
|
for (const lm of this._lightmaps) {
|
|
748
749
|
lm.updateLightmapUniforms(material);
|
|
@@ -3,6 +3,7 @@ import { Material, Mesh, MeshPhysicalMaterial, ShaderMaterial, Texture, Vector4,
|
|
|
3
3
|
|
|
4
4
|
import type { Context } from "../engine/engine_setup.js";
|
|
5
5
|
import { getParam } from "../engine/engine_utils.js";
|
|
6
|
+
import { type Renderer } from "./Renderer.js";
|
|
6
7
|
|
|
7
8
|
const debug = getParam("debuglightmaps");
|
|
8
9
|
|
|
@@ -31,15 +32,15 @@ export class RendererLightmap {
|
|
|
31
32
|
private lightmapIndex: number = -1;
|
|
32
33
|
private lightmapScaleOffset: Vector4 = new Vector4(1, 1, 0, 0);
|
|
33
34
|
|
|
34
|
-
private
|
|
35
|
-
private
|
|
35
|
+
private readonly renderer: Renderer;
|
|
36
|
+
private get context(): Context { return this.renderer.context; }
|
|
37
|
+
private get gameObject() { return this.renderer.gameObject; }
|
|
36
38
|
private lightmapTexture: Texture | null = null;
|
|
37
39
|
private lightmapScaleOffsetUniform = { value: new Vector4(1, 1, 0, 0) };
|
|
38
40
|
private lightmapUniform: { value: Texture | null } = { value: null };
|
|
39
41
|
|
|
40
|
-
constructor(
|
|
41
|
-
this.
|
|
42
|
-
this.context = context;
|
|
42
|
+
constructor(renderer: Renderer) {
|
|
43
|
+
this.renderer = renderer;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
init(lightmapIndex: number, lightmapScaleOffset: Vector4, lightmapTexture: Texture) {
|
|
@@ -55,7 +56,7 @@ export class RendererLightmap {
|
|
|
55
56
|
console.log("Lightmap:", this.gameObject.name, lightmapIndex, "\nScaleOffset:", lightmapScaleOffset, "\nTexture:", lightmapTexture)
|
|
56
57
|
this.setLightmapDebugMaterial();
|
|
57
58
|
}
|
|
58
|
-
else if(debug) console.log("Use debuglightmaps=show to render lightmaps only in the scene.")
|
|
59
|
+
else if (debug) console.log("Use debuglightmaps=show to render lightmaps only in the scene.")
|
|
59
60
|
this.applyLightmap();
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -88,32 +89,49 @@ export class RendererLightmap {
|
|
|
88
89
|
console.assert(this.gameObject.type === "Mesh", "Lightmap only works on meshes", this);
|
|
89
90
|
|
|
90
91
|
const mesh = this.gameObject as unknown as Mesh;
|
|
91
|
-
if (!mesh.geometry.getAttribute("uv1"))
|
|
92
|
+
if (!mesh.geometry.getAttribute("uv1")) {
|
|
92
93
|
mesh.geometry.setAttribute("uv1", mesh.geometry.getAttribute("uv"));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < this.renderer.sharedMaterials.length; i++) {
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
const mat = this.renderer.sharedMaterials[i];
|
|
99
|
+
if (!mat) continue;
|
|
100
|
+
|
|
101
|
+
const newMat = this.ensureLightmapMaterial(mat);
|
|
102
|
+
if (mat !== newMat) {
|
|
103
|
+
this.renderer.sharedMaterials[i] = newMat;
|
|
98
104
|
}
|
|
99
|
-
|
|
100
|
-
else {
|
|
101
|
-
this.gameObject.material = this.ensureLightmapMaterial(this.gameObject.material);
|
|
105
|
+
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
// if (Array.isArray(this.gameObject.material)) {
|
|
109
|
+
// const mats: Material[] = this.gameObject.material;
|
|
110
|
+
// for (let i = 0; i < mats.length; i++) {
|
|
111
|
+
// mats[i] = this.ensureLightmapMaterial(mats[i]);
|
|
112
|
+
// }
|
|
113
|
+
// }
|
|
114
|
+
// else {
|
|
115
|
+
// this.gameObject.material = this.ensureLightmapMaterial(this.gameObject.material);
|
|
116
|
+
// }
|
|
117
|
+
|
|
104
118
|
if (this.lightmapIndex >= 0 && this.lightmapTexture) {
|
|
105
119
|
// always on channel 1 for now. We could optimize this by passing the correct lightmap index along
|
|
106
120
|
this.lightmapTexture.channel = 1;
|
|
107
|
-
const mat
|
|
108
|
-
|
|
109
|
-
for (const entry of mat) {
|
|
110
|
-
this.assignLightmapTexture(entry as any);
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
else if (mat) {
|
|
115
|
-
this.assignLightmapTexture(mat);
|
|
121
|
+
for (const mat of this.renderer.sharedMaterials) {
|
|
122
|
+
if (mat) this.assignLightmapTexture(mat);
|
|
116
123
|
}
|
|
124
|
+
|
|
125
|
+
// const mat = this.gameObject.material;
|
|
126
|
+
// if (Array.isArray(mat)) {
|
|
127
|
+
// for (const entry of mat) {
|
|
128
|
+
// this.assignLightmapTexture(entry as any);
|
|
129
|
+
|
|
130
|
+
// }
|
|
131
|
+
// }
|
|
132
|
+
// else if (mat) {
|
|
133
|
+
// this.assignLightmapTexture(mat);
|
|
134
|
+
// }
|
|
117
135
|
}
|
|
118
136
|
}
|
|
119
137
|
|
|
@@ -127,7 +145,7 @@ export class RendererLightmap {
|
|
|
127
145
|
if (material["NEEDLE:lightmap-material-version"] == undefined) {
|
|
128
146
|
if (debug) console.warn("Cloning material for lightmap " + material.name);
|
|
129
147
|
const mat: Material = material.clone();
|
|
130
|
-
if(!mat.name?.includes("(lightmap)")) mat.name = material.name + " (lightmap)";
|
|
148
|
+
if (!mat.name?.includes("(lightmap)")) mat.name = material.name + " (lightmap)";
|
|
131
149
|
material = mat;
|
|
132
150
|
material.onBeforeCompile = this.onBeforeCompile;
|
|
133
151
|
}
|
|
@@ -3,7 +3,10 @@ import { getTempVector } from "../../engine/engine_three_utils.js";
|
|
|
3
3
|
import { Behaviour } from "../Component.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The CursorFollow component makes the object follow the cursor (or touch) position on screen.
|
|
6
|
+
* The CursorFollow component makes the object follow the cursor (or touch) position on screen.
|
|
7
|
+
*
|
|
8
|
+
* - Example: [Look At Cursor sample](https://engine.needle.tools/samples/look-at-cursor-interactive-3d-header/). This sample combines the CursorFollow component with a LookAt component to create an interactive 3D header that looks at the cursor.
|
|
9
|
+
*
|
|
7
10
|
* @category Web
|
|
8
11
|
* @group Components
|
|
9
12
|
* @component
|
|
@@ -17,6 +20,13 @@ export class CursorFollow extends Behaviour {
|
|
|
17
20
|
@serializable()
|
|
18
21
|
damping: number = 0;
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* When enabled the object will follow the cursor even outside of the needle-engine canvas. This is useful for example for look at effects where you have a small needle-engine element on your page and you want the 3D object to keep looking at the cursor even when it's outside of the canvas.
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
@serializable()
|
|
28
|
+
useFullPage: boolean = true;
|
|
29
|
+
|
|
20
30
|
/**
|
|
21
31
|
* If true, the initial distance to the camera is maintained when following the cursor.
|
|
22
32
|
* @default true
|
|
@@ -24,31 +34,58 @@ export class CursorFollow extends Behaviour {
|
|
|
24
34
|
@serializable()
|
|
25
35
|
keepDistance: boolean = true;
|
|
26
36
|
|
|
37
|
+
|
|
38
|
+
private _distance: number = -1;
|
|
39
|
+
updateDistance(force:boolean = false) {
|
|
40
|
+
if (!force && (this.keepDistance && this._distance !== -1)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this._distance = this.gameObject.worldPosition.distanceTo(this.context.mainCamera.worldPosition);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @internal */
|
|
27
47
|
awake() {
|
|
28
48
|
this._distance = -1;
|
|
29
49
|
}
|
|
30
50
|
|
|
31
|
-
|
|
51
|
+
onEnable(): void {
|
|
52
|
+
this._distance = -1;
|
|
53
|
+
window.addEventListener('pointermove', this._onPointerMove);
|
|
54
|
+
}
|
|
55
|
+
onDisable(): void {
|
|
56
|
+
window.removeEventListener('pointermove', this._onPointerMove);
|
|
57
|
+
}
|
|
32
58
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
private _ndc_x = 0;
|
|
60
|
+
private _ndc_y = 0;
|
|
61
|
+
|
|
62
|
+
private _onPointerMove = (e:PointerEvent) => {
|
|
63
|
+
if(!this.useFullPage) return;
|
|
64
|
+
const x = e.clientX;
|
|
65
|
+
const y = e.clientY;
|
|
66
|
+
const domx = this.context.domX;
|
|
67
|
+
const domy = this.context.domY;
|
|
68
|
+
const domw = this.context.domWidth;
|
|
69
|
+
const domh = this.context.domHeight;
|
|
70
|
+
this._ndc_x = (x - domx) / domw * 2 - 1;
|
|
71
|
+
this._ndc_y = - (y - domy) / domh * 2 + 1;
|
|
38
72
|
}
|
|
39
73
|
|
|
74
|
+
|
|
40
75
|
/** @internal */
|
|
41
76
|
update() {
|
|
42
77
|
// continuously update distance in case camera or object moves
|
|
43
78
|
this.updateDistance();
|
|
44
79
|
|
|
80
|
+
const x = this.useFullPage ? this._ndc_x : this.context.input.mousePositionRC.x;
|
|
81
|
+
const y = this.useFullPage ? this._ndc_y : this.context.input.mousePositionRC.y;
|
|
82
|
+
|
|
45
83
|
// follow cursor in screenspace but maintain initial distance from camera
|
|
46
|
-
const cursor = this.context.input.mousePositionRC;
|
|
47
84
|
const camera = this.context.mainCamera;
|
|
48
85
|
const cameraPosition = camera.worldPosition;
|
|
49
86
|
|
|
50
87
|
// create ray from camera through cursor position
|
|
51
|
-
const rayDirection = getTempVector(
|
|
88
|
+
const rayDirection = getTempVector(x, y, 1).unproject(camera);
|
|
52
89
|
rayDirection.sub(cameraPosition).normalize();
|
|
53
90
|
|
|
54
91
|
// position object at initial distance along the ray
|