@@ -3,9 +3,11 @@
|
|
3
3
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
4
4
|
import { Event, Group, Mesh, Object3D, Vector3 } from "three"
|
5
5
|
// import { IColliderProvider, registerColliderProvider } from "../engine/engine_physics.js";
|
6
|
-
import { ICollider } from "../engine/engine_types.js";
|
6
|
+
import { IBoxCollider, ICollider, ISphereCollider } from "../engine/engine_types.js";
|
7
7
|
import { getWorldScale } from "../engine/engine_three_utils.js";
|
8
8
|
import { PhysicsMaterial } from "../engine/engine_physics.types.js";
|
9
|
+
import { validate } from "../engine/engine_util_decorator.js";
|
10
|
+
import { unwatchWrite, watchWrite } from "../engine/engine_utils.js";
|
9
11
|
|
10
12
|
|
11
13
|
export class Collider extends Behaviour implements ICollider {
|
@@ -48,26 +50,43 @@
|
|
48
50
|
return this.context.physics.engine?.getBody(this);
|
49
51
|
}
|
50
52
|
|
53
|
+
updateProperties = () => {
|
54
|
+
this.context.physics.engine?.updateProperties(this);
|
55
|
+
}
|
51
56
|
}
|
52
57
|
|
53
58
|
|
54
|
-
export class SphereCollider extends Collider {
|
59
|
+
export class SphereCollider extends Collider implements ISphereCollider {
|
55
60
|
|
61
|
+
@validate()
|
56
62
|
@serializable()
|
57
63
|
radius: number = .5;
|
64
|
+
|
58
65
|
@serializable(Vector3)
|
59
66
|
center: Vector3 = new Vector3(0, 0, 0);
|
60
67
|
|
61
68
|
onEnable() {
|
62
69
|
super.onEnable();
|
63
|
-
this.context.physics.engine?.addSphereCollider(this, this.center
|
70
|
+
this.context.physics.engine?.addSphereCollider(this, this.center);
|
71
|
+
watchWrite(this.gameObject.scale, this.updateProperties);
|
64
72
|
}
|
73
|
+
|
74
|
+
onDisable(): void {
|
75
|
+
super.onDisable();
|
76
|
+
unwatchWrite(this.gameObject.scale, this.updateProperties);
|
77
|
+
}
|
78
|
+
|
79
|
+
onValidate(): void {
|
80
|
+
this.updateProperties();
|
81
|
+
}
|
65
82
|
}
|
66
83
|
|
67
|
-
export class BoxCollider extends Collider {
|
84
|
+
export class BoxCollider extends Collider implements IBoxCollider {
|
68
85
|
|
86
|
+
@validate()
|
69
87
|
@serializable(Vector3)
|
70
88
|
size: Vector3 = new Vector3(1, 1, 1);
|
89
|
+
|
71
90
|
@serializable(Vector3)
|
72
91
|
center: Vector3 = new Vector3(0, 0, 0);
|
73
92
|
|
@@ -75,6 +94,10 @@
|
|
75
94
|
super.onEnable();
|
76
95
|
this.context.physics.engine?.addBoxCollider(this, this.center, this.size);
|
77
96
|
}
|
97
|
+
|
98
|
+
onValidate(): void {
|
99
|
+
this.updateProperties();
|
100
|
+
}
|
78
101
|
}
|
79
102
|
|
80
103
|
|
@@ -156,7 +156,10 @@
|
|
156
156
|
|
157
157
|
name: string;
|
158
158
|
alias: string | undefined | null;
|
159
|
-
/** When the renderer or camera are managed by an external process (e.g. when running in r3f context)
|
159
|
+
/** When the renderer or camera are managed by an external process (e.g. when running in r3f context).
|
160
|
+
* When this is false you are responsible to call update(timestamp, xframe.
|
161
|
+
* It is also currently assumed that rendering is handled performed by an external process
|
162
|
+
* */
|
160
163
|
isManagedExternally: boolean = false;
|
161
164
|
/** set to true to pause the update loop. You can receive an event for it in your components.
|
162
165
|
* Note that script updates will not be called when paused */
|
@@ -903,15 +906,19 @@
|
|
903
906
|
console.warn("Can not start render loop while creating context");
|
904
907
|
return false;
|
905
908
|
}
|
906
|
-
this.renderer.setAnimationLoop((timestamp, frame: XRFrame | null) =>
|
909
|
+
this.renderer.setAnimationLoop((timestamp, frame: XRFrame | null) => {
|
910
|
+
if (this.isManagedExternally) return;
|
911
|
+
this.update(timestamp, frame)
|
912
|
+
});
|
907
913
|
return true;
|
908
914
|
}
|
909
915
|
|
916
|
+
/** Performs a full update step including script callbacks, rendering (unless isManagedExternally is set to false) and post render callbacks */
|
910
917
|
public update(timestamp: DOMHighResTimeStamp, frame?: XRFrame | null) {
|
911
918
|
if (frame === undefined) frame = null;
|
912
919
|
if (isDevEnvironment() || debug || looputils.hasNewScripts()) {
|
913
920
|
try {
|
914
|
-
this.
|
921
|
+
this.internalStep(timestamp, frame);
|
915
922
|
}
|
916
923
|
catch (err) {
|
917
924
|
if ((isDevEnvironment() || debug) && err instanceof Error)
|
@@ -923,7 +930,7 @@
|
|
923
930
|
}
|
924
931
|
}
|
925
932
|
else {
|
926
|
-
this.
|
933
|
+
this.internalStep(timestamp, frame);
|
927
934
|
}
|
928
935
|
}
|
929
936
|
|
@@ -931,7 +938,15 @@
|
|
931
938
|
private _accumulatedTime = 0;
|
932
939
|
private _dispatchReadyAfterFrame = false;
|
933
940
|
|
934
|
-
|
941
|
+
// TODO: we need to skip after render callbacks if the render loop is managed externally. When changing this we also need to to update the r3f sample
|
942
|
+
private internalStep(timestamp: DOMHighResTimeStamp, frame: XRFrame | null) {
|
943
|
+
if (this.internalOnBeforeRender(timestamp, frame) === false) return;
|
944
|
+
this.internalOnRender();
|
945
|
+
this.internalOnAfterRender();
|
946
|
+
}
|
947
|
+
|
948
|
+
private internalOnBeforeRender(timestamp: DOMHighResTimeStamp, frame: XRFrame | null) {
|
949
|
+
|
935
950
|
this._xrFrame = frame;
|
936
951
|
|
937
952
|
this._currentFrameEvent = FrameEvent.Undefined;
|
@@ -944,7 +959,7 @@
|
|
944
959
|
if (typeof targetFrameRate === "object") targetFrameRate = targetFrameRate.value!;
|
945
960
|
// if(debug) console.log(this._accumulatedTime, (1 / (targetFrameRate)))
|
946
961
|
if (this._accumulatedTime < (1 / (targetFrameRate + 1))) {
|
947
|
-
return;
|
962
|
+
return false;
|
948
963
|
}
|
949
964
|
this._accumulatedTime = 0;
|
950
965
|
}
|
@@ -952,7 +967,7 @@
|
|
952
967
|
this._stats?.begin();
|
953
968
|
|
954
969
|
Context.Current = this;
|
955
|
-
if (this.onHandlePaused()) return;
|
970
|
+
if (this.onHandlePaused()) return false;
|
956
971
|
|
957
972
|
Context.Current = this;
|
958
973
|
this.time.update();
|
@@ -987,7 +1002,7 @@
|
|
987
1002
|
}
|
988
1003
|
}
|
989
1004
|
this.executeCoroutines(FrameEvent.EarlyUpdate);
|
990
|
-
if (this.onHandlePaused()) return;
|
1005
|
+
if (this.onHandlePaused()) return false;
|
991
1006
|
|
992
1007
|
this._currentFrameEvent = FrameEvent.Update;
|
993
1008
|
|
@@ -1000,7 +1015,7 @@
|
|
1000
1015
|
}
|
1001
1016
|
}
|
1002
1017
|
this.executeCoroutines(FrameEvent.Update);
|
1003
|
-
if (this.onHandlePaused()) return;
|
1018
|
+
if (this.onHandlePaused()) return false;
|
1004
1019
|
|
1005
1020
|
this._currentFrameEvent = FrameEvent.LateUpdate;
|
1006
1021
|
|
@@ -1015,7 +1030,7 @@
|
|
1015
1030
|
|
1016
1031
|
// this.mainLight = null;
|
1017
1032
|
this.executeCoroutines(FrameEvent.LateUpdate);
|
1018
|
-
if (this.onHandlePaused()) return;
|
1033
|
+
if (this.onHandlePaused()) return false;
|
1019
1034
|
|
1020
1035
|
if (this.physics.engine) {
|
1021
1036
|
const physicsSteps = 1;
|
@@ -1030,7 +1045,7 @@
|
|
1030
1045
|
this.physics.engine.postStep();
|
1031
1046
|
}
|
1032
1047
|
|
1033
|
-
if (this.onHandlePaused()) return;
|
1048
|
+
if (this.onHandlePaused()) return false;
|
1034
1049
|
|
1035
1050
|
if (this.isVisibleToUser || this.runInBackground) {
|
1036
1051
|
|
@@ -1058,15 +1073,22 @@
|
|
1058
1073
|
}
|
1059
1074
|
}
|
1060
1075
|
|
1076
|
+
}
|
1061
1077
|
|
1062
|
-
|
1063
|
-
|
1064
|
-
this._currentFrameEvent = FrameEvent.Undefined;
|
1065
|
-
this.renderNow();
|
1066
|
-
this._currentFrameEvent = FrameEvent.OnAfterRender;
|
1067
|
-
}
|
1078
|
+
return true;
|
1079
|
+
}
|
1068
1080
|
|
1081
|
+
private internalOnRender() {
|
1082
|
+
if (!this.isManagedExternally) {
|
1083
|
+
looputils.runPrewarm(this);
|
1084
|
+
this._currentFrameEvent = FrameEvent.Undefined;
|
1085
|
+
this.renderNow();
|
1086
|
+
this._currentFrameEvent = FrameEvent.OnAfterRender;
|
1087
|
+
}
|
1088
|
+
}
|
1069
1089
|
|
1090
|
+
private internalOnAfterRender() {
|
1091
|
+
if (this.isVisibleToUser || this.runInBackground) {
|
1070
1092
|
for (let i = 0; i < this.scripts_onAfterRender.length; i++) {
|
1071
1093
|
const script = this.scripts_onAfterRender[i];
|
1072
1094
|
if (!script.activeAndEnabled) continue;
|
@@ -13,10 +13,12 @@
|
|
13
13
|
IGameObject,
|
14
14
|
Vec2,
|
15
15
|
IContext,
|
16
|
+
ISphereCollider,
|
17
|
+
IBoxCollider,
|
16
18
|
} from './engine_types.js';
|
17
19
|
import { foreachComponent } from './engine_gameobject.js';
|
18
20
|
|
19
|
-
import { ActiveCollisionTypes, ActiveEvents, CoefficientCombineRule, Ball, Collider, ColliderDesc, EventQueue, JointData, QueryFilterFlags, RigidBody, RigidBodyType, ShapeColliderTOI, World, Ray } from '@dimforge/rapier3d-compat';
|
21
|
+
import { ActiveCollisionTypes, ActiveEvents, CoefficientCombineRule, Ball, Collider, ColliderDesc, EventQueue, JointData, QueryFilterFlags, RigidBody, RigidBodyType, ShapeColliderTOI, World, Ray, ShapeType, Cuboid } from '@dimforge/rapier3d-compat';
|
20
22
|
import { CollisionDetectionMode, PhysicsMaterialCombine } from '../engine/engine_physics.types.js';
|
21
23
|
import { Gizmos } from './engine_gizmos.js';
|
22
24
|
import { Mathf } from './engine_math.js';
|
@@ -40,8 +42,8 @@
|
|
40
42
|
let RAPIER: undefined | any = undefined;
|
41
43
|
declare const NEEDLE_USE_RAPIER: boolean;
|
42
44
|
globalThis["NEEDLE_USE_RAPIER"] = globalThis["NEEDLE_USE_RAPIER"] !== undefined ? globalThis["NEEDLE_USE_RAPIER"] : true;
|
43
|
-
if(debugPhysics)
|
44
|
-
console.log("Use Rapier", NEEDLE_USE_RAPIER, globalThis["NEEDLE_USE_RAPIER"]
|
45
|
+
if (debugPhysics)
|
46
|
+
console.log("Use Rapier", NEEDLE_USE_RAPIER, globalThis["NEEDLE_USE_RAPIER"])
|
45
47
|
|
46
48
|
if (NEEDLE_USE_RAPIER) {
|
47
49
|
ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, evt => {
|
@@ -79,6 +81,7 @@
|
|
79
81
|
this.bodies.splice(index, 1);
|
80
82
|
this.objects.splice(index, 1);
|
81
83
|
|
84
|
+
|
82
85
|
// Remove the collider from the physics world
|
83
86
|
if (rapierBody instanceof Collider) {
|
84
87
|
const rapierCollider = rapierBody as Collider;
|
@@ -88,7 +91,13 @@
|
|
88
91
|
const rapierRigidbody: RigidBody | null = rapierCollider.parent();
|
89
92
|
if (rapierRigidbody && rapierRigidbody.numColliders() <= 0) {
|
90
93
|
const rigidbody = rapierRigidbody[$componentKey] as IRigidbody;
|
91
|
-
|
94
|
+
if (rigidbody) {
|
95
|
+
// If the collider was attached to a rigidbody and this rigidbody now has no colliders anymore we should ignore it - because the Rigidbody component will delete itself
|
96
|
+
}
|
97
|
+
else {
|
98
|
+
// But if there is no explicit rigidbody needle component then the colliders did create it implictly and thus we need to remove it here:
|
99
|
+
this.world?.removeRigidBody(rapierRigidbody);
|
100
|
+
}
|
92
101
|
}
|
93
102
|
}
|
94
103
|
// Remove the rigidbody from the physics world
|
@@ -133,12 +142,23 @@
|
|
133
142
|
}
|
134
143
|
}
|
135
144
|
|
136
|
-
updateProperties(
|
145
|
+
updateProperties(obj: IRigidbody | ICollider) {
|
137
146
|
this.validate();
|
138
|
-
|
139
|
-
if (
|
140
|
-
|
147
|
+
|
148
|
+
if ((obj as ICollider).isCollider) {
|
149
|
+
const col = obj as ICollider;
|
150
|
+
const body = col[$bodyKey];
|
151
|
+
if (body) {
|
152
|
+
this.internalUpdateColliderProperties(col, body);
|
153
|
+
}
|
141
154
|
}
|
155
|
+
else {
|
156
|
+
const rb = obj as IRigidbody;
|
157
|
+
const physicsBody = this.internal_getRigidbody(rb);
|
158
|
+
if (physicsBody) {
|
159
|
+
this.internalUpdateRigidbodyProperties(rb, physicsBody);
|
160
|
+
}
|
161
|
+
}
|
142
162
|
}
|
143
163
|
addForce(rigidbody: IRigidbody, force: Vec3, wakeup: boolean) {
|
144
164
|
this.validate();
|
@@ -212,7 +232,7 @@
|
|
212
232
|
}
|
213
233
|
|
214
234
|
private async internalInitialization() {
|
215
|
-
if(debugPhysics) console.log("Initialize rapier physics engine");
|
235
|
+
if (debugPhysics) console.log("Initialize rapier physics engine");
|
216
236
|
// NEEDLE_PHYSICS_INIT_START
|
217
237
|
// use .env file with VITE_NEEDLE_USE_RAPIER=false to treeshake rapier
|
218
238
|
// @ts-ignore
|
@@ -229,7 +249,7 @@
|
|
229
249
|
this._hasCreatedWorld = true;
|
230
250
|
if (RAPIER === undefined) {
|
231
251
|
if (debugPhysics) console.log("Import Rapier");
|
232
|
-
const _rapier
|
252
|
+
const _rapier = await import("@dimforge/rapier3d-compat");
|
233
253
|
if (debugPhysics) console.log("Init Rapier");
|
234
254
|
await _rapier.init();
|
235
255
|
// only assign after all loads are done to avoid a race condition
|
@@ -512,7 +532,7 @@
|
|
512
532
|
this.createCollider(collider, desc, center);
|
513
533
|
}
|
514
534
|
|
515
|
-
async addSphereCollider(collider: ICollider, center: Vector3
|
535
|
+
async addSphereCollider(collider: ICollider, center: Vector3) {
|
516
536
|
if (!this._isInitialized)
|
517
537
|
await this.initialize(collider.context);
|
518
538
|
if (!collider.activeAndEnabled) return;
|
@@ -520,12 +540,9 @@
|
|
520
540
|
if (debugPhysics) console.warn("Physics are disabled");
|
521
541
|
return;
|
522
542
|
}
|
523
|
-
const
|
524
|
-
const scale = getWorldScale(obj, this._tempPosition).multiplyScalar(radius);
|
525
|
-
// Prevent negative scales
|
526
|
-
scale.x = Math.abs(scale.x);
|
527
|
-
const desc = ColliderDesc.ball(scale.x);
|
543
|
+
const desc = ColliderDesc.ball(.5);
|
528
544
|
this.createCollider(collider, desc, center);
|
545
|
+
this.updateProperties(collider);
|
529
546
|
}
|
530
547
|
|
531
548
|
async addCapsuleCollider(collider: ICollider, center: Vector3, height: number, radius: number) {
|
@@ -572,7 +589,7 @@
|
|
572
589
|
if (Math.abs(scale.x - 1) > 0.0001 || Math.abs(scale.y - 1) > 0.0001 || Math.abs(scale.z - 1) > 0.0001) {
|
573
590
|
const key = geo.uuid + "_" + scale.x + "_" + scale.y + "_" + scale.z + "_" + convex;
|
574
591
|
if (this._meshCache.has(key)) {
|
575
|
-
if(debugPhysics) console.warn("Use cached mesh collider")
|
592
|
+
if (debugPhysics) console.warn("Use cached mesh collider")
|
576
593
|
positions = this._meshCache.get(key)!;
|
577
594
|
}
|
578
595
|
else {
|
@@ -606,8 +623,8 @@
|
|
606
623
|
}
|
607
624
|
|
608
625
|
/** Get the Needle Engine component for a rapier object */
|
609
|
-
getComponent(rapierObject:object)
|
610
|
-
if(!rapierObject) return null;
|
626
|
+
getComponent(rapierObject: object): IComponent | null {
|
627
|
+
if (!rapierObject) return null;
|
611
628
|
const component = rapierObject[$componentKey];
|
612
629
|
return component;
|
613
630
|
}
|
@@ -714,7 +731,7 @@
|
|
714
731
|
}
|
715
732
|
rigidBody[$componentKey] = rb;
|
716
733
|
rb[$bodyKey] = rigidBody;
|
717
|
-
this.
|
734
|
+
this.internalUpdateRigidbodyProperties(rb, rigidBody);
|
718
735
|
this.getRigidbodyRelativeMatrix(collider.gameObject, rb.gameObject, _matrix);
|
719
736
|
|
720
737
|
}
|
@@ -739,7 +756,35 @@
|
|
739
756
|
return rb[$bodyKey] as RigidBody;
|
740
757
|
}
|
741
758
|
|
742
|
-
private
|
759
|
+
private internalUpdateColliderProperties(col: ICollider, collider: Collider) {
|
760
|
+
const shape = collider.shape;
|
761
|
+
switch (shape.type) {
|
762
|
+
// Sphere Collider
|
763
|
+
case ShapeType.Ball:
|
764
|
+
{
|
765
|
+
const ball = shape as Ball;
|
766
|
+
const sc = col as ISphereCollider;
|
767
|
+
const changed = ball.radius !== sc.radius;
|
768
|
+
const obj = col.gameObject;
|
769
|
+
const scale = getWorldScale(obj, this._tempPosition).multiplyScalar(sc.radius);
|
770
|
+
// Prevent negative scales
|
771
|
+
ball.radius = Math.abs(scale.x);
|
772
|
+
if (changed)
|
773
|
+
collider.setShape(ball);
|
774
|
+
break;
|
775
|
+
}
|
776
|
+
case ShapeType.Cuboid:
|
777
|
+
const cuboid = shape as Cuboid;
|
778
|
+
const sc = col as IBoxCollider;
|
779
|
+
cuboid.halfExtents.x = sc.size.x * 0.5;
|
780
|
+
cuboid.halfExtents.y = sc.size.y * 0.5;
|
781
|
+
cuboid.halfExtents.z = sc.size.z * 0.5;
|
782
|
+
collider.setShape(cuboid);
|
783
|
+
break;
|
784
|
+
}
|
785
|
+
}
|
786
|
+
|
787
|
+
private internalUpdateRigidbodyProperties(rb: IRigidbody, rigidbody: RigidBody) {
|
743
788
|
// continuous collision detection
|
744
789
|
// https://rapier.rs/docs/user_guides/javascript/rigid_bodies#continuous-collision-detection
|
745
790
|
rigidbody.enableCcd(rb.collisionDetectionMode !== CollisionDetectionMode.Discrete);
|
@@ -244,6 +244,14 @@
|
|
244
244
|
sharedMaterial?: PhysicsMaterial;
|
245
245
|
}
|
246
246
|
|
247
|
+
export declare interface ISphereCollider extends ICollider {
|
248
|
+
radius: number;
|
249
|
+
}
|
250
|
+
|
251
|
+
export declare interface IBoxCollider extends ICollider {
|
252
|
+
size: Vec3;
|
253
|
+
}
|
254
|
+
|
247
255
|
export declare interface IRigidbody extends IComponent {
|
248
256
|
constraints: RigidbodyConstraints;
|
249
257
|
isKinematic: boolean;
|
@@ -255,7 +263,7 @@
|
|
255
263
|
useGravity: boolean;
|
256
264
|
gravityScale: number;
|
257
265
|
dominanceGroup: number;
|
258
|
-
|
266
|
+
|
259
267
|
collisionDetectionMode: CollisionDetectionMode;
|
260
268
|
|
261
269
|
lockPositionX: boolean;
|
@@ -422,14 +430,14 @@
|
|
422
430
|
sphereOverlap(point: Vector3, radius: number): Array<SphereOverlapResult>;
|
423
431
|
|
424
432
|
// Collider methods
|
425
|
-
addSphereCollider(collider: ICollider, center: Vector3
|
433
|
+
addSphereCollider(collider: ICollider, center: Vector3);
|
426
434
|
addBoxCollider(collider: ICollider, center: Vector3, size: Vector3);
|
427
435
|
addCapsuleCollider(collider: ICollider, center: Vector3, radius: number, height: number);
|
428
436
|
addMeshCollider(collider: ICollider, mesh: Mesh, convex: boolean, scale: Vector3);
|
429
437
|
|
430
438
|
// Rigidbody methods
|
431
439
|
wakeup(rb: IRigidbody);
|
432
|
-
updateProperties(rb: IRigidbody);
|
440
|
+
updateProperties(rb: IRigidbody | ICollider);
|
433
441
|
resetForces(rb: IRigidbody, wakeup: boolean);
|
434
442
|
resetTorques(rb: IRigidbody, wakeup: boolean);
|
435
443
|
addForce(rb: IRigidbody, vec: Vec3, wakeup: boolean);
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import { $isAssigningProperties } from "./engine_serialization_core.js";
|
2
|
-
import { LogType, showBalloonMessage } from "./debug/index.js";
|
2
|
+
import { LogType, isDevEnvironment, showBalloonMessage } from "./debug/index.js";
|
3
3
|
import { Constructor, IComponent } from "./engine_types.js";
|
4
|
+
import { Quaternion, Vector2, Vector3, Vector4 } from "three";
|
5
|
+
import { Watch, unwatchWrite, watchWrite } from "./engine_utils.js";
|
4
6
|
|
5
7
|
|
6
8
|
declare type setter = (v: any) => void;
|
@@ -41,12 +43,30 @@
|
|
41
43
|
const awake = target.__internalAwake;
|
42
44
|
target.__internalAwake = function () {
|
43
45
|
|
46
|
+
if (!this.onValidate) {
|
47
|
+
if(isDevEnvironment()) console.warn("Usage of @validate decorate detected but there is no onValidate method in your class: \"" + target.constructor?.name + "\"")
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
|
44
51
|
// only build wrapper once per type
|
45
52
|
if (this[$prop] === undefined) {
|
46
53
|
|
47
54
|
// make sure the field is initialized in a hidden property
|
48
55
|
this[$prop] = this[propertyKey];
|
49
56
|
|
57
|
+
// For complex types we need to watch the write operation (the underlying values)
|
58
|
+
// Since the object itself doesnt change (normally)
|
59
|
+
// This is relevant if we want to use @validate() on e.g. a Vector3 which is animated from an animationclip
|
60
|
+
const _val = this[propertyKey];
|
61
|
+
if (_val instanceof Vector2 ||
|
62
|
+
_val instanceof Vector3 ||
|
63
|
+
_val instanceof Vector4 ||
|
64
|
+
_val instanceof Quaternion) {
|
65
|
+
const vec = this[propertyKey];
|
66
|
+
const cb = () => { this.onValidate(propertyKey); }
|
67
|
+
watchWrite(vec, cb)
|
68
|
+
}
|
69
|
+
|
50
70
|
Object.defineProperty(this, propertyKey, {
|
51
71
|
set: function (v) {
|
52
72
|
if (this[$isAssigningProperties] === true) {
|
@@ -1,4 +1,5 @@
|
|
1
1
|
// use for typesafe interface method calls
|
2
|
+
import { Quaternion, Vector, Vector2, Vector3, Vector4 } from "three";
|
2
3
|
import { SourceIdentifier } from "./engine_types.js";
|
3
4
|
|
4
5
|
// https://schneidenbach.gitbooks.io/typescript-cookbook/content/nameof-operator.html
|
@@ -276,6 +277,7 @@
|
|
276
277
|
|
277
278
|
export interface IWatch {
|
278
279
|
subscribeWrite(callback: WriteCallback);
|
280
|
+
unsubscribeWrite(callback: WriteCallback);
|
279
281
|
apply();
|
280
282
|
revoke();
|
281
283
|
dispose();
|
@@ -287,6 +289,11 @@
|
|
287
289
|
subscribeWrite(callback: WriteCallback) {
|
288
290
|
this.writeCallbacks.push(callback);
|
289
291
|
}
|
292
|
+
unsubscribeWrite(callback: WriteCallback) {
|
293
|
+
const i = this.writeCallbacks.indexOf(callback);
|
294
|
+
if (i === -1) return;
|
295
|
+
this.writeCallbacks.splice(i, 1);
|
296
|
+
}
|
290
297
|
private writeCallbacks: (WriteCallback)[] = [];
|
291
298
|
|
292
299
|
constructor(object: object, prop: string) {
|
@@ -372,6 +379,11 @@
|
|
372
379
|
w.subscribeWrite(callback);
|
373
380
|
}
|
374
381
|
}
|
382
|
+
unsubscribeWrite(callback: WriteCallback) {
|
383
|
+
for (const w of this._watches) {
|
384
|
+
w.unsubscribeWrite(callback);
|
385
|
+
}
|
386
|
+
}
|
375
387
|
|
376
388
|
apply() {
|
377
389
|
for (const w of this._watches) {
|
@@ -393,7 +405,36 @@
|
|
393
405
|
}
|
394
406
|
}
|
395
407
|
|
408
|
+
const watchesKey = Symbol("needle:watches");
|
409
|
+
/** Subscribe to an object being written to
|
410
|
+
* Currently supporting Vector3
|
411
|
+
*/
|
412
|
+
export function watchWrite(vec: Vector, cb: Function) {
|
413
|
+
if (!vec[watchesKey]) {
|
414
|
+
if (vec instanceof Vector2) {
|
415
|
+
vec[watchesKey] = new Watch(vec, ["x", "y"]);
|
416
|
+
}
|
417
|
+
else if (vec instanceof Vector3) {
|
418
|
+
vec[watchesKey] = new Watch(vec, ["x", "y", "z"]);
|
419
|
+
}
|
420
|
+
else if (vec instanceof Vector4 || vec instanceof Quaternion) {
|
421
|
+
vec[watchesKey] = new Watch(vec, ["x", "y", "z", "w"]);
|
422
|
+
}
|
423
|
+
else {
|
424
|
+
return false;
|
425
|
+
}
|
426
|
+
}
|
427
|
+
vec[watchesKey].subscribeWrite(cb);
|
428
|
+
return true;
|
429
|
+
}
|
430
|
+
export function unwatchWrite(vec: Vector, cb: Function) {
|
431
|
+
if (!vec) return;
|
432
|
+
const watch = vec[watchesKey];
|
433
|
+
if (!watch) return;
|
434
|
+
watch.unsubscribeWrite(cb);
|
435
|
+
};
|
396
436
|
|
437
|
+
|
397
438
|
export function isMobileDevice() {
|
398
439
|
return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1);
|
399
440
|
}
|
@@ -13,7 +13,7 @@
|
|
13
13
|
// should we split it into multiple extensions?
|
14
14
|
|
15
15
|
export const EXTENSION_NAME = "NEEDLE_lightmaps";
|
16
|
-
const debug = getParam("debuglightmapsextension")
|
16
|
+
const debug = getParam("debuglightmapsextension") || getParam("debuglightmaps")
|
17
17
|
|
18
18
|
export enum LightmapType {
|
19
19
|
Lightmap = 0,
|
@@ -65,6 +65,8 @@
|
|
65
65
|
const dependencies: Array<Promise<any>> = [];
|
66
66
|
for (const entry of arr) {
|
67
67
|
if (entry.pointer) {
|
68
|
+
if (debug)
|
69
|
+
console.log(entry);
|
68
70
|
let res: Promise<any> | null = null;
|
69
71
|
// Check if the pointer is a json pointer:
|
70
72
|
if (entry.pointer.startsWith("/textures/")) {
|
@@ -42,7 +42,10 @@
|
|
42
42
|
this.lightmapScaleOffset = lightmapScaleOffset;
|
43
43
|
this.lightmapTexture = lightmapTexture;
|
44
44
|
|
45
|
-
if (debug)
|
45
|
+
if (debug) {
|
46
|
+
console.log("Lightmap:", this.gameObject.name, lightmapIndex, "\nScaleOffset:", lightmapScaleOffset, "\nTexture:", lightmapTexture)
|
47
|
+
this.setLightmapDebugMaterial();
|
48
|
+
}
|
46
49
|
this.applyLightmap();
|
47
50
|
}
|
48
51
|
|
@@ -134,14 +137,8 @@
|
|
134
137
|
vec2 lUv = vUv1.xy * lightmapScaleOffset.xy + vec2(lightmapScaleOffset.z, (1. - (lightmapScaleOffset.y + lightmapScaleOffset.w)));
|
135
138
|
|
136
139
|
vec4 lightMapTexel = texture2D( lightMap, lUv);
|
137
|
-
// The range of RGBM lightmaps goes from 0 to 34.49 (5^2.2) in linear space, and from 0 to 5 in gamma space.
|
138
|
-
// lightMapTexel.rgb *= lightMapTexel.a * 8.; // no idea where that "8" comes from... heuristically derived
|
139
|
-
// lightMapTexel.a = 1.;
|
140
|
-
// lightMapTexel = conv_sRGBToLinear(lightMapTexel);
|
141
|
-
// lightMapTexel.rgb = vec3(1.);
|
142
|
-
|
143
|
-
// gl_FragColor = vec4(vUv1.xy, 0, 1);
|
144
140
|
gl_FragColor = lightMapTexel;
|
141
|
+
gl_FragColor.a = 1.;
|
145
142
|
}
|
146
143
|
`,
|
147
144
|
defines: { USE_LIGHTMAP: '' }
|
@@ -1,11 +1,14 @@
|
|
1
1
|
import { Behaviour, GameObject } from "../Component.js";
|
2
|
-
import { Matrix4, Object3D } from "three";
|
2
|
+
import { Matrix4, Object3D, Plane, Quaternion, Ray, Raycaster, Vector2, Vector3 } from "three";
|
3
3
|
import { WebAR, WebXR } from "./WebXR.js";
|
4
4
|
import { InstancingUtil } from "../../engine/engine_instancing.js";
|
5
5
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
6
|
+
import { Context } from "../../engine/engine_context.js";
|
6
7
|
|
7
8
|
// https://github.com/takahirox/takahirox.github.io/blob/master/js.mmdeditor/examples/js/controls/DeviceOrientationControls.js
|
8
9
|
|
10
|
+
const tempMatrix = new Matrix4();
|
11
|
+
|
9
12
|
export class WebARSessionRoot extends Behaviour {
|
10
13
|
|
11
14
|
webAR: WebAR | null = null;
|
@@ -17,7 +20,11 @@
|
|
17
20
|
@serializable()
|
18
21
|
invertForward: boolean = false;
|
19
22
|
|
23
|
+
/** Preview feature: enable touch transform */
|
20
24
|
@serializable()
|
25
|
+
arTouchTransform: boolean = false;
|
26
|
+
|
27
|
+
@serializable()
|
21
28
|
get arScale(): number {
|
22
29
|
return this._arScale;
|
23
30
|
}
|
@@ -30,6 +37,7 @@
|
|
30
37
|
private readonly _initalMatrix = new Matrix4();
|
31
38
|
private readonly _selectStartFn = this.onSelectStart.bind(this);
|
32
39
|
private readonly _selectEndFn = this.onSelectEnd.bind(this);
|
40
|
+
private userInput?: WebXRSessionRootUserInput;
|
33
41
|
|
34
42
|
start() {
|
35
43
|
const xr = GameObject.findObjectOfType(WebXR);
|
@@ -49,6 +57,7 @@
|
|
49
57
|
private _anchor: XRAnchor | null = null;
|
50
58
|
|
51
59
|
onBegin(session: XRSession) {
|
60
|
+
|
52
61
|
this._placementPose = null;
|
53
62
|
this.gameObject.visible = false;
|
54
63
|
this.gameObject.matrixAutoUpdate = false;
|
@@ -77,9 +86,7 @@
|
|
77
86
|
|
78
87
|
onUpdate(rig: Object3D | null, _session: XRSession, hit: XRHitTestResult | null, pose: XRPose | null | undefined): boolean {
|
79
88
|
|
80
|
-
|
81
89
|
if (pose && !this._placementPose) {
|
82
|
-
|
83
90
|
if (!this._gotFirstHitTestResult) {
|
84
91
|
this._gotFirstHitTestResult = true;
|
85
92
|
this.dispatchEvent(new CustomEvent('canPlaceSession', { detail: { pose: pose } }));
|
@@ -94,19 +101,11 @@
|
|
94
101
|
this.placeAt(rig, poseMatrix);
|
95
102
|
if (hit && pose)
|
96
103
|
this.onCreatePlacementAnchor(hit, pose);
|
104
|
+
|
97
105
|
return true;
|
98
106
|
}
|
99
107
|
}
|
100
108
|
return false;
|
101
|
-
|
102
|
-
// if (this._placementPose) {
|
103
|
-
// this.gameObject.matrixAutoUpdate = false;
|
104
|
-
// const matrix = pose?.transform.matrix;
|
105
|
-
// if (matrix) {
|
106
|
-
// this.gameObject.matrix.fromArray(matrix);
|
107
|
-
// }
|
108
|
-
// this.gameObject.visible = true;
|
109
|
-
// }
|
110
109
|
}
|
111
110
|
|
112
111
|
private onCreatePlacementAnchor(hit: XRHitTestResult, pose: XRPose) {
|
@@ -118,45 +117,55 @@
|
|
118
117
|
}
|
119
118
|
|
120
119
|
private _anchorMatrix: Matrix4 = new Matrix4();
|
120
|
+
|
121
121
|
onBeforeRender(frame: XRFrame | null): void {
|
122
|
-
if (frame && this.
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
122
|
+
if (frame && this._rig) {
|
123
|
+
if (this._anchor) {
|
124
|
+
const referenceSpace = this.context.renderer.xr.getReferenceSpace();
|
125
|
+
if (referenceSpace) {
|
126
|
+
const pose = frame.getPose(this._anchor.anchorSpace, referenceSpace);
|
127
|
+
if (pose) {
|
128
|
+
const poseMatrix = this._anchorMatrix.fromArray(pose.transform.matrix).invert();
|
129
|
+
this.placeAt(this._rig, poseMatrix);
|
130
|
+
return;
|
131
|
+
}
|
129
132
|
}
|
130
133
|
}
|
134
|
+
else if (this._placementPose) {
|
135
|
+
this.placeAt(this._rig, this._placementPose!);
|
136
|
+
}
|
131
137
|
}
|
132
138
|
}
|
133
139
|
|
134
140
|
private _invertedSessionRootMatrix: Matrix4 = new Matrix4();
|
141
|
+
private _invertForwardMatrix: Matrix4 = new Matrix4().makeRotationY(Math.PI);
|
142
|
+
|
135
143
|
placeAt(rig: Object3D | null, mat: Matrix4) {
|
136
144
|
if (!this._placementPose) this._placementPose = new Matrix4();
|
137
145
|
this._placementPose.copy(mat);
|
146
|
+
|
138
147
|
// apply session root offset
|
139
148
|
const invertedSessionRoot = this._invertedSessionRootMatrix.copy(this.gameObject.matrixWorld).invert();
|
140
149
|
this._placementPose.premultiply(invertedSessionRoot);
|
141
150
|
if (rig) {
|
142
|
-
|
143
151
|
if (this.invertForward) {
|
144
|
-
|
145
|
-
this._placementPose.premultiply(rot);
|
152
|
+
this._placementPose.premultiply(this._invertForwardMatrix);
|
146
153
|
}
|
154
|
+
|
155
|
+
if (!this.userInput) this.userInput = new WebXRSessionRootUserInput(this.context);
|
156
|
+
this.userInput.enable();
|
157
|
+
|
147
158
|
this._rig = rig;
|
148
|
-
|
149
159
|
this.setScale(this.arScale);
|
150
160
|
}
|
151
161
|
else this._rig = null;
|
152
|
-
// this.gameObject.matrix.copy(this._placementPose);
|
153
|
-
// if (rig) {
|
154
|
-
// this.gameObject.matrix.premultiply(rig.matrixWorld)
|
155
|
-
// }
|
156
162
|
this.gameObject.visible = true;
|
157
163
|
}
|
158
164
|
|
159
165
|
onEnd(rig: Object3D | null, _session: XRSession) {
|
166
|
+
this.userInput?.disable();
|
167
|
+
this.userInput?.reset();
|
168
|
+
|
160
169
|
this._placementPose = null;
|
161
170
|
this.gameObject.visible = false;
|
162
171
|
this.gameObject.matrixAutoUpdate = false;
|
@@ -200,10 +209,235 @@
|
|
200
209
|
}
|
201
210
|
// we apply the transform to the rig because we want to move the user's position for easy networking
|
202
211
|
rig.matrixAutoUpdate = false;
|
212
|
+
if (this.arTouchTransform && this.userInput) {
|
213
|
+
this.userInput.applyMatrixTo(this._placementPose);
|
214
|
+
// rig.matrix.premultiply(this.userInput.offset);
|
215
|
+
}
|
203
216
|
rig.matrix.multiplyMatrices(tempMatrix.makeScale(scale, scale, scale), this._placementPose);
|
204
217
|
rig.matrix.decompose(rig.position, rig.quaternion, rig.scale);
|
205
218
|
rig.updateMatrixWorld();
|
206
219
|
}
|
207
220
|
}
|
208
221
|
|
209
|
-
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
class WebXRSessionRootUserInput {
|
226
|
+
private static up = new Vector3(0, 1, 0);
|
227
|
+
private static zero = new Vector3(0, 0, 0);
|
228
|
+
private static one = new Vector3(1, 1, 1);
|
229
|
+
|
230
|
+
oneFingerDrag: boolean = true;
|
231
|
+
twoFingerRotate: boolean = true;
|
232
|
+
twoFingerScale: boolean = true;
|
233
|
+
|
234
|
+
readonly context: Context;
|
235
|
+
readonly offset: Matrix4;
|
236
|
+
readonly plane: Plane;
|
237
|
+
|
238
|
+
private _scale: number = 1;
|
239
|
+
|
240
|
+
// readonly translate: Vector3 = new Vector3();
|
241
|
+
// readonly rotation: Quaternion = new Quaternion();
|
242
|
+
// readonly scale: Vector3 = new Vector3(1, 1, 1);
|
243
|
+
|
244
|
+
constructor(context: Context) {
|
245
|
+
this.context = context;
|
246
|
+
this.offset = new Matrix4()
|
247
|
+
this.plane = new Plane();
|
248
|
+
this.plane.setFromNormalAndCoplanarPoint(WebXRSessionRootUserInput.up, WebXRSessionRootUserInput.zero);
|
249
|
+
}
|
250
|
+
|
251
|
+
private _enabled: boolean = false;
|
252
|
+
enable() {
|
253
|
+
if (this._enabled) return;
|
254
|
+
this._enabled = true;
|
255
|
+
window.addEventListener('touchstart', this.touchStart, { passive: false });
|
256
|
+
window.addEventListener('touchmove', this.touchMove, { passive: false });
|
257
|
+
window.addEventListener('touchend', this.touchEnd, { passive: false });
|
258
|
+
}
|
259
|
+
disable() {
|
260
|
+
if (!this._enabled) return;
|
261
|
+
this._enabled = false;
|
262
|
+
window.removeEventListener('touchstart', this.touchStart);
|
263
|
+
window.removeEventListener('touchmove', this.touchMove);
|
264
|
+
window.removeEventListener('touchend', this.touchEnd);
|
265
|
+
}
|
266
|
+
reset() {
|
267
|
+
this._scale = 1;
|
268
|
+
this.offset.identity();
|
269
|
+
}
|
270
|
+
applyMatrixTo(matrix: Matrix4) {
|
271
|
+
matrix.premultiply(this.offset);
|
272
|
+
// if (this._needsUpdate)
|
273
|
+
// this.updateMatrix();
|
274
|
+
// matrix.premultiply(this._rotationMatrix);
|
275
|
+
// matrix.premultiply(this.offset).premultiply(this._rotationMatrix)
|
276
|
+
}
|
277
|
+
|
278
|
+
// private _needsUpdate: boolean = true;
|
279
|
+
// private _rotationMatrix: Matrix4 = new Matrix4();
|
280
|
+
// private updateMatrix() {
|
281
|
+
// this._needsUpdate = false;
|
282
|
+
// this._rotationMatrix.makeRotationFromQuaternion(this.rotation);
|
283
|
+
// this.offset.compose(this.translate, new Quaternion(), this.scale);
|
284
|
+
// // const rot = this._tempMatrix.makeRotationY(this.angle);
|
285
|
+
// // this.translate.applyMatrix4(rot);
|
286
|
+
// // this.offset.elements[12] = this.translate.x;
|
287
|
+
// // this.offset.elements[13] = this.translate.y;
|
288
|
+
// // this.offset.elements[14] = this.translate.z;
|
289
|
+
// // this.offset.premultiply(rot);
|
290
|
+
// // const s = this.scale;
|
291
|
+
// // this.offset.premultiply(this._tempMatrix.makeScale(s, s, s));
|
292
|
+
// }
|
293
|
+
|
294
|
+
private prev: Map<number, { x: number, z: number, screenx: number, screeny: number }> = new Map();
|
295
|
+
private _didMultitouch: boolean = false;
|
296
|
+
|
297
|
+
private touchStart = (evt: TouchEvent) => {
|
298
|
+
for (let i = 0; i < evt.changedTouches.length; i++) {
|
299
|
+
const touch = evt.changedTouches[i];
|
300
|
+
if (!this.prev.has(touch.identifier))
|
301
|
+
this.prev.set(touch.identifier, {
|
302
|
+
x: 0,
|
303
|
+
z: 0,
|
304
|
+
screenx: 0,
|
305
|
+
screeny: 0,
|
306
|
+
});
|
307
|
+
const prev = this.prev.get(touch.identifier);
|
308
|
+
if (prev) {
|
309
|
+
const pos = this.getPositionOnPlane(touch.clientX, touch.clientY);
|
310
|
+
prev.x = pos.x;
|
311
|
+
prev.z = pos.z;
|
312
|
+
prev.screenx = touch.clientX;
|
313
|
+
prev.screeny = touch.clientY;
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
private touchEnd = (evt: TouchEvent) => {
|
318
|
+
if (evt.touches.length <= 0) {
|
319
|
+
this._didMultitouch = false;
|
320
|
+
}
|
321
|
+
}
|
322
|
+
private touchMove = (evt: TouchEvent) => {
|
323
|
+
if (evt.defaultPrevented) return;
|
324
|
+
|
325
|
+
if (evt.touches.length === 1) {
|
326
|
+
// if we had multiple touches before due to e.g. pinching / rotating
|
327
|
+
// and stopping one of the touches, we don't want to move the scene suddenly
|
328
|
+
// this will be resettet when all touches stop
|
329
|
+
if (this._didMultitouch) {
|
330
|
+
return;
|
331
|
+
}
|
332
|
+
const touch = evt.touches[0];
|
333
|
+
const prev = this.prev.get(touch.identifier);
|
334
|
+
if (!prev) return;
|
335
|
+
const pos = this.getPositionOnPlane(touch.clientX, touch.clientY);
|
336
|
+
const dx = pos.x - prev.x;
|
337
|
+
const dy = pos.z - prev.z;
|
338
|
+
if (dx === 0 && dy === 0) return;
|
339
|
+
if (this.oneFingerDrag)
|
340
|
+
this.addMovement(dx, dy);
|
341
|
+
prev.x = pos.x;
|
342
|
+
prev.z = pos.z;
|
343
|
+
prev.screenx = touch.clientX;
|
344
|
+
prev.screeny = touch.clientY;
|
345
|
+
return;
|
346
|
+
}
|
347
|
+
else if (evt.touches.length === 2) {
|
348
|
+
this._didMultitouch = true;
|
349
|
+
const touch1 = evt.touches[0];
|
350
|
+
const touch2 = evt.touches[1];
|
351
|
+
const prev1 = this.prev.get(touch1.identifier);
|
352
|
+
const prev2 = this.prev.get(touch2.identifier);
|
353
|
+
if (!prev1 || !prev2) return;
|
354
|
+
|
355
|
+
if (this.twoFingerRotate) {
|
356
|
+
const angle1 = Math.atan2(touch1.clientY - touch2.clientY, touch1.clientX - touch2.clientX);
|
357
|
+
const lastAngle = Math.atan2(prev1.screeny - prev2.screeny, prev1.screenx - prev2.screenx);
|
358
|
+
const angleDiff = angle1 - lastAngle;
|
359
|
+
if (Math.abs(angleDiff) > 0.001) {
|
360
|
+
this.addRotation(angleDiff);
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
if (this.twoFingerScale) {
|
365
|
+
const distx = touch1.clientX - touch2.clientX;
|
366
|
+
const disty = touch1.clientY - touch2.clientY;
|
367
|
+
const dist = Math.sqrt(distx * distx + disty * disty);
|
368
|
+
const lastDistx = prev1.screenx - prev2.screenx;
|
369
|
+
const lastDisty = prev1.screeny - prev2.screeny;
|
370
|
+
const lastDist = Math.sqrt(lastDistx * lastDistx + lastDisty * lastDisty);
|
371
|
+
const distDiff = dist - lastDist;
|
372
|
+
if (Math.abs(distDiff) > 2) {
|
373
|
+
this.addScale(distDiff)
|
374
|
+
}
|
375
|
+
}
|
376
|
+
|
377
|
+
prev1.screenx = touch1.clientX;
|
378
|
+
prev1.screeny = touch1.clientY;
|
379
|
+
prev2.screenx = touch2.clientX;
|
380
|
+
prev2.screeny = touch2.clientY;
|
381
|
+
}
|
382
|
+
}
|
383
|
+
|
384
|
+
private readonly _raycaster: Raycaster = new Raycaster();
|
385
|
+
private readonly _intersection: Vector3 = new Vector3();
|
386
|
+
private readonly _screenPos: Vector3 = new Vector3();
|
387
|
+
|
388
|
+
private getPositionOnPlane(tx: number, ty: number): Vector3 {
|
389
|
+
const camera = this.context.mainCamera!;
|
390
|
+
this._screenPos.x = (tx / window.innerWidth) * 2 - 1;
|
391
|
+
this._screenPos.y = -(ty / window.innerHeight) * 2 + 1;
|
392
|
+
this._screenPos.z = 1;
|
393
|
+
|
394
|
+
this._screenPos.unproject(camera);
|
395
|
+
this._raycaster.set(camera.position, this._screenPos.sub(camera.position));
|
396
|
+
this._raycaster.ray.intersectPlane(this.plane, this._intersection);
|
397
|
+
return this._intersection;
|
398
|
+
}
|
399
|
+
|
400
|
+
private addMovement(dx: number, dz: number) {
|
401
|
+
// this.translate.x -= dx;
|
402
|
+
// this.translate.z -= dz;
|
403
|
+
// this._needsUpdate = true;
|
404
|
+
// return
|
405
|
+
// some arbitrary factor
|
406
|
+
dx *= .75;
|
407
|
+
dz *= .75;
|
408
|
+
// increase diff if the scene is scaled small
|
409
|
+
dx /= this._scale;
|
410
|
+
dz /= this._scale;
|
411
|
+
// apply it
|
412
|
+
this.offset.elements[12] -= dx;
|
413
|
+
this.offset.elements[14] -= dz;
|
414
|
+
};
|
415
|
+
|
416
|
+
private readonly _tempMatrix: Matrix4 = new Matrix4();
|
417
|
+
|
418
|
+
private addScale(diff: number) {
|
419
|
+
diff /= window.innerWidth
|
420
|
+
|
421
|
+
// this.scale.x *= 1 + diff;
|
422
|
+
// this.scale.y *= 1 + diff;
|
423
|
+
// this.scale.z *= 1 + diff;
|
424
|
+
// this._needsUpdate = true;
|
425
|
+
// return;
|
426
|
+
|
427
|
+
|
428
|
+
// we use this factor to modify the translation factor (in apply movement)
|
429
|
+
this._scale *= 1 + diff;
|
430
|
+
// apply the scale
|
431
|
+
this._tempMatrix.makeScale(1 - diff, 1 - diff, 1 - diff);
|
432
|
+
this.offset.premultiply(this._tempMatrix);
|
433
|
+
}
|
434
|
+
|
435
|
+
|
436
|
+
private addRotation(rot: number) {
|
437
|
+
// this.rotation.multiply(new Quaternion().setFromAxisAngle(WebXRSessionRootUserInput.up, rot));
|
438
|
+
// this._needsUpdate = true;
|
439
|
+
// return;
|
440
|
+
this._tempMatrix.makeRotationY(rot);
|
441
|
+
this.offset.premultiply(this._tempMatrix);
|
442
|
+
}
|
443
|
+
}
|