@@ -41,8 +41,20 @@
|
|
41
41
|
if (!(val instanceof AnimatorController)) {
|
42
42
|
if (debug) console.log("Assign animator controller", val, this);
|
43
43
|
this._animatorController = new AnimatorController(val);
|
44
|
+
if (this.__didAwake)
|
45
|
+
this._animatorController.bind(this);
|
44
46
|
}
|
45
|
-
else
|
47
|
+
else {
|
48
|
+
if (val.animator && val.animator !== this) {
|
49
|
+
console.warn("AnimatorController can not be bound to multiple animators", val.model?.name)
|
50
|
+
if (!val.model) {
|
51
|
+
console.error("AnimatorController has no model");
|
52
|
+
}
|
53
|
+
val = new AnimatorController(val.model);
|
54
|
+
}
|
55
|
+
this._animatorController = val;
|
56
|
+
this._animatorController.bind(this);
|
57
|
+
}
|
46
58
|
}
|
47
59
|
else this._animatorController = null;
|
48
60
|
}
|
@@ -5,7 +5,7 @@
|
|
5
5
|
import { deepClone, getParam } from "../engine/engine_utils.js";
|
6
6
|
import { Context } from "../engine/engine_setup.js";
|
7
7
|
import { TypeStore } from "../engine/engine_typestore.js";
|
8
|
-
import { assign } from "../engine/engine_serialization_core.js";
|
8
|
+
import { SerializationContext, TypeSerializer, assign } from "../engine/engine_serialization_core.js";
|
9
9
|
import { Mathf } from "../engine/engine_math.js";
|
10
10
|
import { isAnimationAction } from "../engine/engine_three_utils.js";
|
11
11
|
import { isDevEnvironment } from "../engine/debug/index.js";
|
@@ -124,7 +124,9 @@
|
|
124
124
|
|
125
125
|
setFloat(name: string | number, val: number) {
|
126
126
|
const key = typeof name === "string" ? "name" : "hash";
|
127
|
-
|
127
|
+
const filtered = this.model?.parameters?.filter(p => p[key] === name);
|
128
|
+
filtered.forEach(p => p.value = val);
|
129
|
+
return filtered?.length > 0;
|
128
130
|
}
|
129
131
|
|
130
132
|
getFloat(name: string | number): number {
|
@@ -197,7 +199,8 @@
|
|
197
199
|
// }
|
198
200
|
|
199
201
|
bind(animator: Animator) {
|
200
|
-
if (
|
202
|
+
if (!animator) console.error("AnimatorController.bind: animator is null");
|
203
|
+
else if (this.animator !== animator) {
|
201
204
|
this.animator = animator;
|
202
205
|
this._mixer = new AnimationMixer(this.animator.gameObject);
|
203
206
|
this.createActions(this.animator);
|
@@ -292,9 +295,6 @@
|
|
292
295
|
}
|
293
296
|
|
294
297
|
private evaluateTransitions() {
|
295
|
-
|
296
|
-
const currentLayer = 0;
|
297
|
-
|
298
298
|
let didEnterStateThisFrame = false;
|
299
299
|
if (!this._activeState) {
|
300
300
|
this.setStartTransition();
|
@@ -370,6 +370,16 @@
|
|
370
370
|
// action.time += this.context.time.deltaTime
|
371
371
|
// console.log(action?.time, action?.getEffectiveWeight())
|
372
372
|
|
373
|
+
// update timescale
|
374
|
+
if (action) {
|
375
|
+
let speedFactor = state.speed ?? 1;
|
376
|
+
if (state.speedParameter)
|
377
|
+
speedFactor *= this.getFloat(state.speedParameter);
|
378
|
+
if (speedFactor !== undefined) {
|
379
|
+
action.timeScale = speedFactor * this._speed;
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
373
383
|
let didTriggerLooping = false;
|
374
384
|
if (state.motion.isLooping && action) {
|
375
385
|
// we dont use the three loop state here because it prevents the transition check above
|
@@ -472,11 +482,22 @@
|
|
472
482
|
if (action) {
|
473
483
|
|
474
484
|
offsetNormalized = Math.max(0, Math.min(1, offsetNormalized));
|
485
|
+
if (state.cycleOffsetParameter) {
|
486
|
+
const val = this.getFloat(state.cycleOffsetParameter);
|
487
|
+
if (typeof val === "number") {
|
488
|
+
offsetNormalized += val;
|
489
|
+
offsetNormalized %= 1;
|
490
|
+
}
|
491
|
+
else if (debug) console.warn("AnimatorController cycle offset parameter is not a number", state.cycleOffsetParameter);
|
492
|
+
}
|
493
|
+
else if (typeof state.cycleOffset === "number") {
|
494
|
+
offsetNormalized += state.cycleOffset
|
495
|
+
offsetNormalized %= 1;
|
496
|
+
}
|
497
|
+
|
475
498
|
if (action.isRunning())
|
476
499
|
action.stop();
|
477
500
|
action.reset();
|
478
|
-
action.timeScale = this._speed;
|
479
|
-
if (state.speed !== undefined) action.timeScale *= state.speed;
|
480
501
|
action.enabled = true;
|
481
502
|
const duration = state.motion.clip!.duration;
|
482
503
|
action.time = offsetNormalized * duration;
|
@@ -982,4 +1003,18 @@
|
|
982
1003
|
}
|
983
1004
|
return null;
|
984
1005
|
}
|
985
|
-
}
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
|
1009
|
+
|
1010
|
+
class AnimatorControllerSerializator extends TypeSerializer {
|
1011
|
+
onSerialize(_: any, _context: SerializationContext) {
|
1012
|
+
|
1013
|
+
}
|
1014
|
+
onDeserialize(data: AnimatorControllerModel & { __type?: string }, context: SerializationContext) {
|
1015
|
+
if (context.type === AnimatorController && data.__type === "AnimatorController")
|
1016
|
+
return new AnimatorController(data);
|
1017
|
+
return undefined;
|
1018
|
+
}
|
1019
|
+
}
|
1020
|
+
new AnimatorControllerSerializator(AnimatorController);
|
@@ -85,6 +85,20 @@
|
|
85
85
|
}
|
86
86
|
|
87
87
|
get isGrounded(): boolean { return this._activeGroundCollisions.size > 0; }
|
88
|
+
|
89
|
+
private _contactVelocity: Vector3 = new Vector3();
|
90
|
+
get contactVelocity(): Vector3 {
|
91
|
+
this._contactVelocity.set(0, 0, 0);
|
92
|
+
for (const col of this._activeGroundCollisions) {
|
93
|
+
const vel = this.context.physics.engine?.getLinearVelocity(col.collider);
|
94
|
+
if (!vel) continue;
|
95
|
+
// const friction = col.collider.sharedMaterial?.dynamicFriction || 1;
|
96
|
+
this._contactVelocity.x += vel.x;
|
97
|
+
this._contactVelocity.y += vel.y;
|
98
|
+
this._contactVelocity.z += vel.z;
|
99
|
+
}
|
100
|
+
return this._contactVelocity;
|
101
|
+
}
|
88
102
|
}
|
89
103
|
|
90
104
|
export class CharacterControllerInput extends Behaviour {
|
@@ -53,6 +53,12 @@
|
|
53
53
|
updateProperties = () => {
|
54
54
|
this.context.physics.engine?.updateProperties(this);
|
55
55
|
}
|
56
|
+
|
57
|
+
/** Requests an update of the physics material in the physics engine */
|
58
|
+
updatePhysicsMaterial(){
|
59
|
+
this.context.physics.engine?.updatePhysicsMaterial(this);
|
60
|
+
|
61
|
+
}
|
56
62
|
}
|
57
63
|
|
58
64
|
|
@@ -18,7 +18,7 @@
|
|
18
18
|
import { foreachComponent } from './engine_gameobject.js';
|
19
19
|
|
20
20
|
import { ActiveCollisionTypes, ActiveEvents, CoefficientCombineRule, Ball, Collider, ColliderDesc, EventQueue, JointData, QueryFilterFlags, RigidBody, RigidBodyType, ShapeColliderTOI, World, Ray, ShapeType, Cuboid } from '@dimforge/rapier3d-compat';
|
21
|
-
import { CollisionDetectionMode, PhysicsMaterialCombine } from '../engine/engine_physics.types.js';
|
21
|
+
import { CollisionDetectionMode, PhysicsMaterial, PhysicsMaterialCombine } from '../engine/engine_physics.types.js';
|
22
22
|
import { Gizmos } from './engine_gizmos.js';
|
23
23
|
import { Mathf } from './engine_math.js';
|
24
24
|
import { SphereOverlapResult } from './engine_types.js';
|
@@ -149,6 +149,8 @@
|
|
149
149
|
const body = col[$bodyKey];
|
150
150
|
if (body) {
|
151
151
|
this.internalUpdateColliderProperties(col, body);
|
152
|
+
if(col.sharedMaterial)
|
153
|
+
this.updatePhysicsMaterial(col);
|
152
154
|
}
|
153
155
|
}
|
154
156
|
else {
|
@@ -169,9 +171,9 @@
|
|
169
171
|
const body = this.internal_getRigidbody(rigidbody);
|
170
172
|
body?.applyImpulse(force, wakeup)
|
171
173
|
}
|
172
|
-
getLinearVelocity(
|
174
|
+
getLinearVelocity(comp: IRigidbody | ICollider): Vec3 | null {
|
173
175
|
this.validate();
|
174
|
-
const body = this.internal_getRigidbody(
|
176
|
+
const body = this.internal_getRigidbody(comp);
|
175
177
|
if (body) {
|
176
178
|
const vel = body.linvel();
|
177
179
|
return vel;
|
@@ -557,7 +559,7 @@
|
|
557
559
|
// Prevent negative scales
|
558
560
|
scale.x = Math.abs(scale.x);
|
559
561
|
scale.y = Math.abs(scale.y);
|
560
|
-
const desc = ColliderDesc.capsule(height * .5 * scale.y
|
562
|
+
const desc = ColliderDesc.capsule(height * .5 * radius * scale.y, radius * scale.x);
|
561
563
|
this.createCollider(collider, desc, center);
|
562
564
|
}
|
563
565
|
|
@@ -614,6 +616,55 @@
|
|
614
616
|
}
|
615
617
|
}
|
616
618
|
|
619
|
+
updatePhysicsMaterial(col: ICollider) {
|
620
|
+
if (!col) return;
|
621
|
+
const physicsMaterial = col.sharedMaterial;
|
622
|
+
const rapier_collider = col[$bodyKey] as Collider;
|
623
|
+
if (!rapier_collider) return;
|
624
|
+
|
625
|
+
if (physicsMaterial) {
|
626
|
+
if (physicsMaterial.bounciness !== undefined)
|
627
|
+
rapier_collider.setRestitution(physicsMaterial.bounciness);
|
628
|
+
|
629
|
+
if (physicsMaterial.bounceCombine !== undefined) {
|
630
|
+
switch (physicsMaterial.bounceCombine) {
|
631
|
+
case PhysicsMaterialCombine.Average:
|
632
|
+
rapier_collider.setRestitutionCombineRule(CoefficientCombineRule.Average);
|
633
|
+
break;
|
634
|
+
case PhysicsMaterialCombine.Maximum:
|
635
|
+
rapier_collider.setRestitutionCombineRule(CoefficientCombineRule.Max);
|
636
|
+
break;
|
637
|
+
case PhysicsMaterialCombine.Minimum:
|
638
|
+
rapier_collider.setRestitutionCombineRule(CoefficientCombineRule.Min);
|
639
|
+
break;
|
640
|
+
case PhysicsMaterialCombine.Multiply:
|
641
|
+
rapier_collider.setRestitutionCombineRule(CoefficientCombineRule.Multiply);
|
642
|
+
break;
|
643
|
+
}
|
644
|
+
}
|
645
|
+
|
646
|
+
if (physicsMaterial.dynamicFriction !== undefined)
|
647
|
+
rapier_collider.setFriction(physicsMaterial.dynamicFriction);
|
648
|
+
|
649
|
+
if (physicsMaterial.frictionCombine !== undefined) {
|
650
|
+
switch (physicsMaterial.frictionCombine) {
|
651
|
+
case PhysicsMaterialCombine.Average:
|
652
|
+
rapier_collider.setFrictionCombineRule(CoefficientCombineRule.Average);
|
653
|
+
break;
|
654
|
+
case PhysicsMaterialCombine.Maximum:
|
655
|
+
rapier_collider.setFrictionCombineRule(CoefficientCombineRule.Max);
|
656
|
+
break;
|
657
|
+
case PhysicsMaterialCombine.Minimum:
|
658
|
+
rapier_collider.setFrictionCombineRule(CoefficientCombineRule.Min);
|
659
|
+
break;
|
660
|
+
case PhysicsMaterialCombine.Multiply:
|
661
|
+
rapier_collider.setFrictionCombineRule(CoefficientCombineRule.Multiply);
|
662
|
+
break;
|
663
|
+
}
|
664
|
+
}
|
665
|
+
}
|
666
|
+
}
|
667
|
+
|
617
668
|
/** Get the rapier body for a Needle component */
|
618
669
|
getBody(obj: ICollider | IRigidbody): null | any {
|
619
670
|
if (!obj) return null;
|
@@ -651,7 +702,7 @@
|
|
651
702
|
// TODO: we might want to update this if the material changes
|
652
703
|
const physicsMaterial = collider.sharedMaterial;
|
653
704
|
if (physicsMaterial) {
|
654
|
-
|
705
|
+
|
655
706
|
if (physicsMaterial.bounciness !== undefined)
|
656
707
|
desc.setRestitution(physicsMaterial.bounciness);
|
657
708
|
|
@@ -761,7 +812,8 @@
|
|
761
812
|
return { rigidBody: rigidBody, useExplicitMassProperties: useExplicitMassProperties };
|
762
813
|
}
|
763
814
|
|
764
|
-
private internal_getRigidbody(rb: IRigidbody): RigidBody | null {
|
815
|
+
private internal_getRigidbody(rb: IRigidbody | ICollider): RigidBody | null {
|
816
|
+
if ((rb as ICollider).isCollider === true) return rb[$colliderRigidbody] as RigidBody;
|
765
817
|
return rb[$bodyKey] as RigidBody;
|
766
818
|
}
|
767
819
|
|
@@ -1172,7 +1224,8 @@
|
|
1172
1224
|
if (pt) {
|
1173
1225
|
const dist = manifold.contactDist(i);
|
1174
1226
|
const friction = manifold.solverContactFriction(i);
|
1175
|
-
const
|
1227
|
+
const tangentVelocity = manifold.solverContactTangentVelocity(i);
|
1228
|
+
const contact = new ContactPoint(pt, dist, normal, impulse, friction, tangentVelocity);
|
1176
1229
|
contacts.push(contact);
|
1177
1230
|
if (debugCollisions) {
|
1178
1231
|
Gizmos.DrawDirection(pt, normal, 0xff0000, 3, true);
|
@@ -501,6 +501,13 @@
|
|
501
501
|
// e.g. when @serializable(Texture) and the texture is already resolved via json pointer from gltf
|
502
502
|
// then we dont need to do anything else
|
503
503
|
if (!typeIsFunction && currentValue instanceof type) return currentValue;
|
504
|
+
|
505
|
+
// try to resolve the serializer for a type only once
|
506
|
+
if (!typeContext) {
|
507
|
+
typeContext = {
|
508
|
+
serializer: helper.getSerializerForConstructor(type)
|
509
|
+
}
|
510
|
+
}
|
504
511
|
|
505
512
|
// if the value was already resolved via the persistent asset extension dont try to override that again
|
506
513
|
if (currentValue && typeof currentValue === "object" && isPersistentAsset(currentValue)) {
|
@@ -517,12 +524,21 @@
|
|
517
524
|
|
518
525
|
if (currentValue && type !== undefined) {
|
519
526
|
try {
|
520
|
-
|
521
|
-
//
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
527
|
+
let instance = null;
|
528
|
+
// It's still possible that the type has an explicit serializer
|
529
|
+
// e.g. AnimatorController (or if a user registers a serializer for an arbitrary type and then marks a persistent asset with @serializable)
|
530
|
+
// we have this for @serializable(AnimatorController)
|
531
|
+
if (typeContext.serializer) {
|
532
|
+
instance = typeContext.serializer.onDeserialize(data, context);
|
533
|
+
}
|
534
|
+
if (!instance) {
|
535
|
+
// we create a concrete instance for a persistent asset here
|
536
|
+
// hence we want to have the same instance across all usages of this asset
|
537
|
+
instance = new type();
|
538
|
+
if (debugExtension)
|
539
|
+
console.log("Create concrete instance for persistent asset", currentValue, "instance:", instance);
|
540
|
+
assign(instance, currentValue);
|
541
|
+
}
|
526
542
|
// save it so if another component references the same persistent asset it will automatically use the concrete instance
|
527
543
|
currentValue["__concreteInstance"] = instance;
|
528
544
|
currentValue = instance;
|
@@ -534,13 +550,6 @@
|
|
534
550
|
return currentValue;
|
535
551
|
}
|
536
552
|
|
537
|
-
// try to resolve the serializer for a type only once
|
538
|
-
if (!typeContext) {
|
539
|
-
typeContext = {
|
540
|
-
serializer: helper.getSerializerForConstructor(type)
|
541
|
-
}
|
542
|
-
}
|
543
|
-
|
544
553
|
// if the type is an array resolve each entries recursively
|
545
554
|
if (Array.isArray(data)) {
|
546
555
|
const newArr: any[] = [];
|
@@ -554,7 +563,7 @@
|
|
554
563
|
return newArr;
|
555
564
|
}
|
556
565
|
|
557
|
-
const ser = typeContext
|
566
|
+
const ser = typeContext?.serializer;
|
558
567
|
if (ser) {
|
559
568
|
return ser.onDeserialize(data, context);
|
560
569
|
}
|
@@ -244,6 +244,8 @@
|
|
244
244
|
attachedRigidbody: IRigidbody | null;
|
245
245
|
isTrigger: boolean;
|
246
246
|
sharedMaterial?: PhysicsMaterial;
|
247
|
+
updateProperties(): void;
|
248
|
+
updatePhysicsMaterial(): void;
|
247
249
|
}
|
248
250
|
|
249
251
|
export declare interface ISphereCollider extends ICollider {
|
@@ -309,6 +311,7 @@
|
|
309
311
|
|
310
312
|
private readonly _point: Vec3;
|
311
313
|
private readonly _normal: Vec3;
|
314
|
+
private readonly _tangentVelocity: Vec3;
|
312
315
|
|
313
316
|
readonly distance: number;
|
314
317
|
readonly impulse: number;
|
@@ -326,12 +329,19 @@
|
|
326
329
|
return target.set(this._normal.x, this._normal.y, this._normal.z);
|
327
330
|
}
|
328
331
|
|
329
|
-
|
332
|
+
/** */
|
333
|
+
get tangentVelocity() {
|
334
|
+
const target = contactsVectorBuffer.get();
|
335
|
+
return target.set(this._tangentVelocity.x, this._tangentVelocity.y, this._tangentVelocity.z);
|
336
|
+
}
|
337
|
+
|
338
|
+
constructor(point: Vec3, dist: number, normal: Vec3, impulse: number, friction: number, tangentVelocity: Vec3) {
|
330
339
|
this._point = point;
|
331
340
|
this.distance = dist;
|
332
341
|
this._normal = normal;
|
333
342
|
this.impulse = impulse;
|
334
343
|
this.friction = friction;
|
344
|
+
this._tangentVelocity = tangentVelocity;
|
335
345
|
}
|
336
346
|
}
|
337
347
|
|
@@ -437,6 +447,8 @@
|
|
437
447
|
addCapsuleCollider(collider: ICollider, center: Vector3, radius: number, height: number);
|
438
448
|
addMeshCollider(collider: ICollider, mesh: Mesh, convex: boolean, scale: Vector3);
|
439
449
|
|
450
|
+
updatePhysicsMaterial(collider:ICollider);
|
451
|
+
|
440
452
|
// Rigidbody methods
|
441
453
|
wakeup(rb: IRigidbody);
|
442
454
|
updateProperties(rb: IRigidbody | ICollider);
|
@@ -444,7 +456,8 @@
|
|
444
456
|
resetTorques(rb: IRigidbody, wakeup: boolean);
|
445
457
|
addForce(rb: IRigidbody, vec: Vec3, wakeup: boolean);
|
446
458
|
applyImpulse(rb: IRigidbody, vec: Vec3, wakeup: boolean);
|
447
|
-
|
459
|
+
/** Returns the linear velocity of a rigidbody or the rigidbody of a collider */
|
460
|
+
getLinearVelocity(rb: IRigidbody | ICollider): Vec3 | null;
|
448
461
|
getAngularVelocity(rb: IRigidbody): Vec3 | null;
|
449
462
|
setAngularVelocity(rb: IRigidbody, vec: Vec3, wakeup: boolean);
|
450
463
|
setLinearVelocity(rb: IRigidbody, vec: Vec3, wakeup: boolean);
|
@@ -32,10 +32,17 @@
|
|
32
32
|
export declare type State = {
|
33
33
|
name: string,
|
34
34
|
hash: number;
|
35
|
-
speed?: number;
|
36
35
|
motion: Motion,
|
37
36
|
transitions: Transition[],
|
38
37
|
behaviours: StateMachineBehaviourModel[],
|
38
|
+
/** The base speed of the animation */
|
39
|
+
speed?: number;
|
40
|
+
/** Set to a animator controller float parameter name to multiply this ontop of the speed value */
|
41
|
+
speedParameter?: string;
|
42
|
+
/** Cycle offset normalized 0-1, used when starting a animation */
|
43
|
+
cycleOffset?: number;
|
44
|
+
/** If set to a parameter then this is used instead of the CycleOffset value to offset the animation start time */
|
45
|
+
cycleOffsetParameter?: string;
|
39
46
|
}
|
40
47
|
|
41
48
|
export declare type StateMachineBehaviourModel = {
|
@@ -43,16 +43,16 @@
|
|
43
43
|
this.controls?.addEventListener("start", callback as any);
|
44
44
|
}
|
45
45
|
|
46
|
+
/** When enabled the scene will be automatically fitted into the camera view in onEnable */
|
46
47
|
@serializable()
|
48
|
+
autoFit: boolean = false;
|
49
|
+
@serializable()
|
47
50
|
enableRotate: boolean = true;
|
48
51
|
@serializable()
|
49
52
|
autoRotate: boolean = false;
|
50
53
|
@serializable()
|
51
54
|
autoRotateSpeed: number = 1.0;
|
52
|
-
/** When enabled the scene will be automatically fitted into the camera view in onEnable */
|
53
55
|
@serializable()
|
54
|
-
autoFit: boolean = false;
|
55
|
-
@serializable()
|
56
56
|
enableKeys: boolean = true;
|
57
57
|
@serializable()
|
58
58
|
enableDamping: boolean = true;
|
@@ -70,7 +70,11 @@
|
|
70
70
|
lookAtConstraint: LookAtConstraint | null = null;
|
71
71
|
@serializable()
|
72
72
|
lookAtConstraint01: number = 1;
|
73
|
+
|
74
|
+
/** If true user input interrupts the camera from animating to a target */
|
73
75
|
@serializable()
|
76
|
+
allowInterrupt: boolean = true;
|
77
|
+
@serializable()
|
74
78
|
middleClickToFocus: boolean = true;
|
75
79
|
@serializable()
|
76
80
|
doubleClickToFocus: boolean = true;
|
@@ -81,6 +85,8 @@
|
|
81
85
|
useSlerp: boolean = true;
|
82
86
|
|
83
87
|
debugLog: boolean = false;
|
88
|
+
|
89
|
+
/** The speed at which the camera target and the camera will be lerping to their destinations (if set via script or user input) */
|
84
90
|
targetLerpSpeed = 5;
|
85
91
|
|
86
92
|
/** When enabled OrbitControls will automatically raycast find a look at target in start */
|
@@ -253,10 +259,10 @@
|
|
253
259
|
if (!this._controls) return;
|
254
260
|
if (this._cameraObject !== this.context.mainCamera) return;
|
255
261
|
|
256
|
-
if (this.context.input.getPointerDown(
|
262
|
+
if (this.context.input.getPointerDown(1) || this.context.input.getPointerDown(2) || this.context.input.mouseWheelChanged || (this.context.input.getPointerPressed(0) && this.context.input.getPointerPositionDelta(0)?.length() || 0 > .1)) {
|
257
263
|
this._inputs += 1;
|
258
264
|
}
|
259
|
-
if (this._inputs > 0) {
|
265
|
+
if (this._inputs > 0 && this.allowInterrupt) {
|
260
266
|
// if a user has disabled rotation but enabled auto rotate we don't want to change it when we receive input
|
261
267
|
if (this.enableRotate) {
|
262
268
|
this.autoRotate = false;
|
@@ -271,10 +277,6 @@
|
|
271
277
|
if (focusAtPointer) {
|
272
278
|
this.setTargetFromRaycast();
|
273
279
|
}
|
274
|
-
else if (this.context.input.getPointerDown(0) || this.context.input.mouseWheelChanged) {
|
275
|
-
this._lerpToTargetPosition = false;
|
276
|
-
this._lerpCameraToTarget = false;
|
277
|
-
}
|
278
280
|
|
279
281
|
if (this._lerpToTargetPosition || this._lerpCameraToTarget) {
|
280
282
|
const step = this.context.time.deltaTime * this.targetLerpSpeed;
|
@@ -293,7 +295,9 @@
|
|
293
295
|
else {
|
294
296
|
this._cameraObject?.position.lerp(this._cameraTargetPosition, step);
|
295
297
|
}
|
296
|
-
|
298
|
+
const minDist = this.autoRotate ? .02 : .001;
|
299
|
+
const dist = this._cameraObject.position.distanceTo(this._cameraTargetPosition);
|
300
|
+
if (dist < minDist) {
|
297
301
|
this._lerpCameraToTarget = false;
|
298
302
|
}
|
299
303
|
}
|
@@ -307,7 +311,6 @@
|
|
307
311
|
}
|
308
312
|
}
|
309
313
|
|
310
|
-
if (!freeCam && this.lookAtConstraint?.locked) this.setFromTargetPosition(0, this.lookAtConstraint01);
|
311
314
|
|
312
315
|
if (this._controls) {
|
313
316
|
if (this.debugLog)
|
@@ -328,9 +331,12 @@
|
|
328
331
|
}
|
329
332
|
//@ts-ignore
|
330
333
|
// this._controls.zoomToCursor = this.zoomToCursor;
|
331
|
-
if (!this.context.isInXR)
|
334
|
+
if (!this.context.isInXR) {
|
335
|
+
if (!freeCam && this.lookAtConstraint?.locked) this.setFromTargetPosition(0, this.lookAtConstraint01);
|
332
336
|
this._controls.update();
|
337
|
+
}
|
333
338
|
}
|
339
|
+
|
334
340
|
}
|
335
341
|
|
336
342
|
/** Moves the camera to position smoothly. @deprecated use `setCameraTargetPosition` */
|
@@ -183,7 +183,11 @@
|
|
183
183
|
await delay(200);
|
184
184
|
}
|
185
185
|
this.invokeStateChangedMethodsOnTracks();
|
186
|
-
|
186
|
+
// Update timeline in LateUpdate to give other scripts time to react to the updated state
|
187
|
+
// e.g. if we animate OrbitControls look at target we want those changes to be applied in onBeforeRender
|
188
|
+
// if we use onBeforeRender here it will be called *after* the regular onBeforeRender events
|
189
|
+
// which is too late
|
190
|
+
this._internalUpdateRoutine = this.startCoroutine(this.internalUpdate(), FrameEvent.LateUpdate);
|
187
191
|
}
|
188
192
|
|
189
193
|
pause() {
|
@@ -478,7 +482,7 @@
|
|
478
482
|
this._signalTracks.length = 0;
|
479
483
|
|
480
484
|
if (!this.playableAsset) return;
|
481
|
-
|
485
|
+
let audioListener: AudioListener | null = GameObject.findObjectOfType(AudioListener, this.context);
|
482
486
|
for (const track of this.playableAsset!.tracks) {
|
483
487
|
const type = track.type;
|
484
488
|
const registered = PlayableDirector.createTrackFunctions[type];
|
@@ -557,7 +561,10 @@
|
|
557
561
|
audio.director = this;
|
558
562
|
audio.track = track;
|
559
563
|
this._audioTracks.push(audio);
|
560
|
-
if (!audioListener)
|
564
|
+
if (!audioListener) {
|
565
|
+
// If the scene doesnt have an AudioListener we add one to the main camera
|
566
|
+
audioListener = this.context.mainCameraComponent?.gameObject.addNewComponent(AudioListener)!;
|
567
|
+
}
|
561
568
|
audio.listener = audioListener.listener;
|
562
569
|
for (let i = 0; i < track.clips.length; i++) {
|
563
570
|
const clipModel = track.clips[i];
|
@@ -648,7 +648,7 @@
|
|
648
648
|
}
|
649
649
|
if (AudioSource.userInteractionRegistered === false) continue;
|
650
650
|
if (audio === null || !audio.buffer) continue;
|
651
|
-
audio.playbackRate = this.director.context.time.timeScale;
|
651
|
+
audio.playbackRate = this.director.context.time.timeScale * this.director.speed;
|
652
652
|
audio.loop = model.asset.loop;
|
653
653
|
if (time >= model.start && time <= model.end && time < this.director.duration) {
|
654
654
|
if (this.director.isPlaying == false) {
|
@@ -4,6 +4,7 @@
|
|
4
4
|
import { InstancingUtil } from "../../engine/engine_instancing.js";
|
5
5
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
6
6
|
import { Context } from "../../engine/engine_context.js";
|
7
|
+
import { isQuest } from "../../engine/engine_utils.js";
|
7
8
|
|
8
9
|
// https://github.com/takahirox/takahirox.github.io/blob/master/js.mmdeditor/examples/js/controls/DeviceOrientationControls.js
|
9
10
|
|
@@ -99,8 +100,8 @@
|
|
99
100
|
|
100
101
|
if (this.webAR) this.webAR.setReticleActive(false);
|
101
102
|
this.placeAt(rig, poseMatrix);
|
102
|
-
if (hit && pose)
|
103
|
-
|
103
|
+
if (hit && pose && !isQuest()) // TODO anchors seem to behave differently with an XRRig
|
104
|
+
this.onCreatePlacementAnchor(hit, pose);
|
104
105
|
|
105
106
|
return true;
|
106
107
|
}
|