@@ -15,45 +15,65 @@
|
|
15
15
|
* Collider is the base class for all colliders. A collider is a physical shape that is used to detect collisions with other objects in the scene.
|
16
16
|
* Colliders are used in combination with a Rigidbody to create physical interactions between objects.
|
17
17
|
* Colliders are registered with the physics engine when they are enabled and removed when they are disabled.
|
18
|
-
* @category Physics
|
18
|
+
* @category Physics
|
19
|
+
* @inheritdoc
|
19
20
|
*/
|
20
21
|
export class Collider extends Behaviour implements ICollider {
|
21
22
|
|
23
|
+
/** @internal */
|
22
24
|
get isCollider(): any {
|
23
25
|
return true;
|
24
26
|
}
|
25
27
|
|
28
|
+
/**
|
29
|
+
* The Rigidbody that this collider is attached to.
|
30
|
+
*/
|
26
31
|
@serializable(Rigidbody)
|
27
32
|
attachedRigidbody: Rigidbody | null = null;
|
28
33
|
|
34
|
+
/**
|
35
|
+
* When `true` the collider will not be used for collision detection but will still trigger events.
|
36
|
+
*/
|
29
37
|
@serializable()
|
30
38
|
isTrigger: boolean = false;
|
31
39
|
|
40
|
+
/**
|
41
|
+
* The physics material that is used for the collider. This material defines physical properties of the collider such as friction and bounciness.
|
42
|
+
*/
|
32
43
|
@serializable()
|
33
44
|
sharedMaterial?: PhysicsMaterial;
|
34
45
|
|
46
|
+
/**
|
47
|
+
* The layers that the collider is assigned to.
|
48
|
+
*/
|
35
49
|
@serializable()
|
36
50
|
membership: number[] = [0];
|
51
|
+
|
52
|
+
/**
|
53
|
+
* The layers that the collider will interact with.
|
54
|
+
* @inheritdoc
|
55
|
+
*/
|
37
56
|
@serializable()
|
38
57
|
filter?: number[];
|
39
58
|
|
59
|
+
/** @internal */
|
40
60
|
awake() {
|
41
61
|
super.awake();
|
42
62
|
if (!this.attachedRigidbody)
|
43
63
|
this.attachedRigidbody = this.gameObject.getComponentInParent(Rigidbody);
|
44
64
|
}
|
45
|
-
|
65
|
+
/** @internal */
|
46
66
|
start() {
|
47
67
|
if (!this.attachedRigidbody)
|
48
68
|
this.attachedRigidbody = this.gameObject.getComponentInParent(Rigidbody);
|
49
69
|
}
|
50
|
-
|
70
|
+
/** @internal */
|
51
71
|
onEnable() {
|
52
72
|
// a rigidbody is not assigned if we export an asset
|
53
73
|
if (!this.attachedRigidbody)
|
54
74
|
this.attachedRigidbody = this.gameObject.getComponentInParent(Rigidbody);
|
55
75
|
}
|
56
|
-
|
76
|
+
/** @internal */
|
57
77
|
onDisable() {
|
58
78
|
this.context.physics.engine?.removeBody(this);
|
59
79
|
}
|
@@ -63,6 +83,9 @@
|
|
63
83
|
return this.context.physics.engine?.getBody(this);
|
64
84
|
}
|
65
85
|
|
86
|
+
/**
|
87
|
+
* Apply the collider properties to the physics engine.
|
88
|
+
*/
|
66
89
|
updateProperties = () => {
|
67
90
|
this.context.physics.engine?.updateProperties(this);
|
68
91
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ActiveCollisionTypes, ActiveEvents, Ball, CoefficientCombineRule, Collider, ColliderDesc, Cuboid, EventQueue, JointData, QueryFilterFlags, Ray, RigidBody, RigidBodyType, ShapeColliderTOI, ShapeType, World } from '@dimforge/rapier3d-compat';
|
1
|
+
import { ActiveCollisionTypes, ActiveEvents, Ball, CoefficientCombineRule, Collider, ColliderDesc, Cuboid, EventQueue, JointData, QueryFilterFlags, Ray, RigidBody, RigidBodyDesc, RigidBodyType, ShapeColliderTOI, ShapeType, World } from '@dimforge/rapier3d-compat';
|
2
2
|
import { BufferAttribute, BufferGeometry, LineBasicMaterial, LineSegments, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from 'three'
|
3
3
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
4
4
|
|
@@ -295,9 +295,20 @@
|
|
295
295
|
|
296
296
|
private rapierRay = new Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 });
|
297
297
|
private raycastVectorsBuffer = new CircularBuffer(() => new Vector3(), 10);
|
298
|
-
public raycast(origin?: Vec2 | Vec3, direction?: Vec3,
|
298
|
+
public raycast(origin?: Vec2 | Vec3, direction?: Vec3, options?: {
|
299
|
+
maxDistance?: number,
|
300
|
+
/** True if you want to also hit objects when the raycast starts from inside a collider */
|
301
|
+
solid?: boolean,
|
302
|
+
queryFilterFlags?: QueryFilterFlags,
|
303
|
+
filterGroups?: number,
|
304
|
+
/** Return false to ignore this collider */
|
305
|
+
filterPredicate?: (c: ICollider) => boolean,
|
306
|
+
})
|
299
307
|
: null | { point: Vector3, collider: ICollider } {
|
300
308
|
|
309
|
+
let maxDistance = options?.maxDistance;
|
310
|
+
let solid = options?.solid;
|
311
|
+
|
301
312
|
if (maxDistance === undefined) maxDistance = Infinity;
|
302
313
|
if (solid === undefined) solid = true;
|
303
314
|
|
@@ -306,9 +317,12 @@
|
|
306
317
|
|
307
318
|
if (this.debugRenderRaycasts || showPhysicsRaycasts) Gizmos.DrawRay(ray.origin, ray.dir, 0x0000ff, 1);
|
308
319
|
|
309
|
-
const hit = this.world?.castRay(ray, maxDistance, solid,
|
320
|
+
const hit = this.world?.castRay(ray, maxDistance, solid, options?.queryFilterFlags, options?.filterGroups, undefined, undefined, (c) => {
|
321
|
+
const component = c[$componentKey];
|
322
|
+
if (options?.filterPredicate) return options.filterPredicate(component);
|
323
|
+
|
310
324
|
// ignore objects in the IgnoreRaycast=2 layer
|
311
|
-
return !
|
325
|
+
return !component?.gameObject.layers.isEnabled(2);
|
312
326
|
});
|
313
327
|
if (hit) {
|
314
328
|
const point = ray.pointAt(hit.toi);
|
@@ -320,9 +334,20 @@
|
|
320
334
|
return null;
|
321
335
|
}
|
322
336
|
|
323
|
-
public raycastAndGetNormal(origin?: Vec2 | Vec3, direction?: Vec3,
|
337
|
+
public raycastAndGetNormal(origin?: Vec2 | Vec3, direction?: Vec3, options?: {
|
338
|
+
maxDistance?: number,
|
339
|
+
/** True if you want to also hit objects when the raycast starts from inside a collider */
|
340
|
+
solid?: boolean,
|
341
|
+
queryFilterFlags?: QueryFilterFlags,
|
342
|
+
filterGroups?: number,
|
343
|
+
/** Return false to ignore this collider */
|
344
|
+
filterPredicate?: (c: ICollider) => boolean,
|
345
|
+
})
|
324
346
|
: null | { point: Vector3, normal: Vector3, collider: ICollider } {
|
325
347
|
|
348
|
+
let maxDistance = options?.maxDistance;
|
349
|
+
let solid = options?.solid;
|
350
|
+
|
326
351
|
if (maxDistance === undefined) maxDistance = Infinity;
|
327
352
|
if (solid === undefined) solid = true;
|
328
353
|
|
@@ -331,9 +356,11 @@
|
|
331
356
|
|
332
357
|
if (this.debugRenderRaycasts || showPhysicsRaycasts) Gizmos.DrawRay(ray.origin, ray.dir, 0x0000ff, 1);
|
333
358
|
|
334
|
-
const hit = this.world?.castRayAndGetNormal(ray, maxDistance, solid,
|
359
|
+
const hit = this.world?.castRayAndGetNormal(ray, maxDistance, solid, options?.queryFilterFlags, options?.filterGroups, undefined, undefined, (c) => {
|
360
|
+
const component = c[$componentKey];
|
361
|
+
if (options?.filterPredicate) return options.filterPredicate(component);
|
335
362
|
// ignore objects in the IgnoreRaycast=2 layer
|
336
|
-
return !
|
363
|
+
return !component?.gameObject.layers.isEnabled(2);
|
337
364
|
});
|
338
365
|
if (hit) {
|
339
366
|
const point = ray.pointAt(hit.toi);
|
@@ -802,30 +829,36 @@
|
|
802
829
|
}
|
803
830
|
}
|
804
831
|
|
805
|
-
/**
|
832
|
+
/**
|
833
|
+
* Updates the collision groups of a collider.
|
834
|
+
*
|
835
|
+
* @param collider - The collider to update.
|
836
|
+
*/
|
806
837
|
private updateColliderCollisionGroups(collider: ICollider) {
|
807
|
-
const body = collider[$bodyKey];
|
838
|
+
const body = collider[$bodyKey] as Collider;
|
808
839
|
const members = collider.membership;
|
809
840
|
let memberMask = 0;
|
810
841
|
if (members == undefined) {
|
811
|
-
memberMask =
|
842
|
+
memberMask = 0xffff;
|
812
843
|
}
|
813
844
|
else {
|
814
845
|
for (let i = 0; i < members.length; i++) {
|
815
846
|
const member = members[i];
|
816
|
-
|
847
|
+
if (member > 31) console.error(`Rapier only supports 32 layers, layer ${member} is not supported`);
|
848
|
+
else memberMask |= 1 << Math.floor(member);
|
817
849
|
}
|
818
850
|
}
|
819
851
|
|
820
852
|
const mask = collider.filter;
|
821
853
|
let filterMask = 0;
|
822
854
|
if (mask == undefined) {
|
823
|
-
filterMask =
|
855
|
+
filterMask = 0xffff;
|
824
856
|
}
|
825
857
|
else {
|
826
858
|
for (let i = 0; i < mask.length; i++) {
|
827
859
|
const member = mask[i];
|
828
|
-
|
860
|
+
if (member > 31) console.error(`Rapier only supports 32 layers, layer ${member} is not supported`);
|
861
|
+
else filterMask |= 1 << Math.floor(member);
|
829
862
|
}
|
830
863
|
}
|
831
864
|
body.setCollisionGroups((memberMask << 16) | filterMask);
|
@@ -843,10 +876,11 @@
|
|
843
876
|
const kinematic = rb.isKinematic && !debugColliderPlacement;
|
844
877
|
if (debugPhysics)
|
845
878
|
console.log("Create rigidbody", kinematic);
|
846
|
-
const rigidBodyDesc = kinematic ? RAPIER.RigidBodyDesc.kinematicPositionBased() : RAPIER.RigidBodyDesc.dynamic();
|
879
|
+
const rigidBodyDesc = (kinematic ? RAPIER.RigidBodyDesc.kinematicPositionBased() : RAPIER.RigidBodyDesc.dynamic()) as RigidBodyDesc;
|
847
880
|
const pos = getWorldPosition(collider.attachedRigidbody.gameObject);
|
848
881
|
rigidBodyDesc.setTranslation(pos.x, pos.y, pos.z);
|
849
882
|
rigidBodyDesc.setRotation(getWorldQuaternion(collider.attachedRigidbody.gameObject));
|
883
|
+
rigidBodyDesc.centerOfMass = new RAPIER.Vector3(rb.centerOfMass.x, rb.centerOfMass.y, rb.centerOfMass.z);
|
850
884
|
rigidBody = this.world.createRigidBody(rigidBodyDesc);
|
851
885
|
this.bodies.push(rigidBody);
|
852
886
|
this.objects.push(rb);
|
@@ -125,14 +125,14 @@
|
|
125
125
|
|
126
126
|
export class Physics {
|
127
127
|
|
128
|
-
/**@deprecated use this.context.physics.engine.raycast */
|
128
|
+
/**@deprecated use `this.context.physics.engine.raycast` {@link IPhysicsEngine.raycast} */
|
129
129
|
public raycastPhysicsFast(origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined, maxDistance: number = Infinity, solid: boolean = true) {
|
130
|
-
return this.context.physics.engine?.raycast(origin, direction, maxDistance, solid) ?? null;
|
130
|
+
return this.context.physics.engine?.raycast(origin, direction, { maxDistance, solid }) ?? null;
|
131
131
|
}
|
132
132
|
|
133
|
-
/**@deprecated use this.context.physics.engine.raycastAndGetNormal */
|
133
|
+
/**@deprecated use `this.context.physics.engine.raycastAndGetNormal` {@link IPhysicsEngine.raycastAndGetNormal} */
|
134
134
|
public raycastPhysicsFastAndGetNormal(origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined, maxDistance: number = Infinity, solid: boolean = true) {
|
135
|
-
return this.context.physics.engine?.raycastAndGetNormal(origin, direction, maxDistance, solid) ?? null;
|
135
|
+
return this.context.physics.engine?.raycastAndGetNormal(origin, direction, { maxDistance, solid }) ?? null;
|
136
136
|
}
|
137
137
|
|
138
138
|
/**@deprecated use this.context.physics.engine.sphereOverlap */
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import type { QueryFilterFlags } from "@dimforge/rapier3d-compat";
|
1
2
|
import { Camera, Color, Material, Mesh, Object3D, Quaternion, Ray, Scene, WebGLRenderer } from "three";
|
2
3
|
import { Vector3 } from "three";
|
3
4
|
import { type GLTF as GLTF3 } from "three/examples/jsm/loaders/GLTFLoader.js";
|
@@ -251,12 +252,14 @@
|
|
251
252
|
updatePhysicsMaterial(): void;
|
252
253
|
/** The collider membership indicates what groups the collider is part of (e.g. group 2 and 3)
|
253
254
|
* An `undefined` array indicates that the collider is part of all groups
|
254
|
-
* Note: Make sure to call updateProperties after having changed this property
|
255
|
+
* Note: Make sure to call updateProperties after having changed this property
|
256
|
+
* Default: [0]
|
255
257
|
*/
|
256
258
|
membership?: number[];
|
257
259
|
/** The collider filter indicates what groups the collider can interact with (e.g. group 3 and 4)
|
258
260
|
* An `undefined` array indicates that the collider can interact with all groups
|
259
|
-
* Note: Make sure to call updateProperties after having changed this property
|
261
|
+
* Note: Make sure to call updateProperties after having changed this property
|
262
|
+
* Default: undefined
|
260
263
|
*/
|
261
264
|
filter?: number[];
|
262
265
|
}
|
@@ -278,6 +281,7 @@
|
|
278
281
|
drag: number;
|
279
282
|
angularDrag: number;
|
280
283
|
useGravity: boolean;
|
284
|
+
centerOfMass: Vec3;
|
281
285
|
gravityScale: number;
|
282
286
|
dominanceGroup: number;
|
283
287
|
|
@@ -436,12 +440,31 @@
|
|
436
440
|
/** Fast raycast against physics colliders
|
437
441
|
* @param origin ray origin in screen or worldspace
|
438
442
|
* @param direction ray direction in worldspace
|
439
|
-
* @param
|
440
|
-
* @param solid if true it will also hit the collider if origin is already inside it
|
443
|
+
* @param options additional options
|
441
444
|
*/
|
442
|
-
raycast(origin?: Vec2 | Vec3, direction?: Vec3,
|
445
|
+
raycast(origin?: Vec2 | Vec3, direction?: Vec3, options?: {
|
446
|
+
maxDistance?: number,
|
447
|
+
/** True if you want to also hit objects when the raycast starts from inside a collider */
|
448
|
+
solid?: boolean,
|
449
|
+
queryFilterFlags?: QueryFilterFlags,
|
450
|
+
/** Raycast filter groups. Groups are used to apply the collision group rules for the scene query. The scene query will only consider hits with colliders with collision groups compatible with this collision group (using the bitwise test described in the collision groups section). See https://rapier.rs/docs/user_guides/javascript/colliders#collision-groups-and-solver-groups
|
451
|
+
* For example membership 0x0001 and filter 0x0002 should be 0x00010002 */
|
452
|
+
filterGroups?: number,
|
453
|
+
/** Return false to ignore this collider */
|
454
|
+
filterPredicate?: (collider: ICollider) => boolean
|
455
|
+
}): RaycastResult;
|
443
456
|
/** raycast that also gets the normal vector. If you don't need it use raycast() */
|
444
|
-
raycastAndGetNormal(origin?: Vec2 | Vec3, direction?: Vec3,
|
457
|
+
raycastAndGetNormal(origin?: Vec2 | Vec3, direction?: Vec3, options?: {
|
458
|
+
maxDistance?: number,
|
459
|
+
/** True if you want to also hit objects when the raycast starts from inside a collider */
|
460
|
+
solid?: boolean,
|
461
|
+
queryFilterFlags?: QueryFilterFlags,
|
462
|
+
/** Raycast filter groups. Groups are used to apply the collision group rules for the scene query. The scene query will only consider hits with colliders with collision groups compatible with this collision group (using the bitwise test described in the collision groups section). See https://rapier.rs/docs/user_guides/javascript/colliders#collision-groups-and-solver-groups
|
463
|
+
* For example membership 0x0001 and filter 0x0002 should be 0x00010002 */
|
464
|
+
filterGroups?: number,
|
465
|
+
/** Return false to ignore this collider */
|
466
|
+
filterPredicate?: (collider: ICollider) => boolean
|
467
|
+
}): RaycastResult;
|
445
468
|
sphereOverlap(point: Vector3, radius: number): Array<SphereOverlapResult>;
|
446
469
|
|
447
470
|
// Collider methods
|
@@ -94,8 +94,6 @@
|
|
94
94
|
private _hasReflection: boolean = false;
|
95
95
|
private _ambientLightObj?: AmbientLight;
|
96
96
|
private _hemisphereLightObj?: HemisphereLight;
|
97
|
-
// used when skybox is used to support ambient intensity for "non custom shaders"
|
98
|
-
private _lightProbeObj?: LightProbe;
|
99
97
|
|
100
98
|
awake() {
|
101
99
|
if (this.sourceId) {
|
@@ -154,7 +152,6 @@
|
|
154
152
|
if (this._ambientLightObj) {
|
155
153
|
this.gameObject.add(this._ambientLightObj)
|
156
154
|
}
|
157
|
-
if (this._lightProbeObj) this._lightProbeObj.removeFromParent();
|
158
155
|
}
|
159
156
|
else if (this.ambientMode === AmbientMode.Trilight) {
|
160
157
|
if (this.ambientTrilight) {
|
@@ -180,8 +177,6 @@
|
|
180
177
|
onDisable() {
|
181
178
|
if (debug)
|
182
179
|
console.warn("💡⚫ <<< Disable lighting:", this.sourceId, this);
|
183
|
-
if (this._lightProbeObj)
|
184
|
-
this._lightProbeObj.removeFromParent();
|
185
180
|
if (this._ambientLightObj)
|
186
181
|
this._ambientLightObj.removeFromParent();
|
187
182
|
if (this._hemisphereLightObj)
|
@@ -371,7 +371,20 @@
|
|
371
371
|
width: 100%;
|
372
372
|
}
|
373
373
|
|
374
|
-
|
374
|
+
@media (max-width: 300px) or (max-height: 350px){
|
375
|
+
.options {
|
376
|
+
display: none;
|
377
|
+
padding: 0;
|
378
|
+
margin: 0;
|
379
|
+
border: none;
|
380
|
+
}
|
381
|
+
.logo.any-options {
|
382
|
+
border: none !important;
|
383
|
+
padding: 0;
|
384
|
+
margin-bottom: 0 !important;
|
385
|
+
padding-bottom: 0 !important;
|
386
|
+
}
|
387
|
+
}
|
375
388
|
|
376
389
|
/* dark mode */
|
377
390
|
/*
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/* eslint-disable */
|
2
2
|
import { TypeStore } from "./../engine_typestore.js"
|
3
|
-
|
3
|
+
|
4
4
|
// Import types
|
5
5
|
import { __Ignore } from "../../engine-components/codegen/components.js";
|
6
6
|
import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js";
|
@@ -220,7 +220,7 @@
|
|
220
220
|
import { XRFlag } from "../../engine-components/webxr/XRFlag.js";
|
221
221
|
import { XRRig } from "../../engine-components/webxr/WebXRRig.js";
|
222
222
|
import { XRState } from "../../engine-components/webxr/XRFlag.js";
|
223
|
-
|
223
|
+
|
224
224
|
// Register types
|
225
225
|
TypeStore.add("__Ignore", __Ignore);
|
226
226
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
@@ -167,8 +167,11 @@
|
|
167
167
|
@serializable()
|
168
168
|
useGravity: boolean = true;
|
169
169
|
|
170
|
-
|
171
|
-
|
170
|
+
/**
|
171
|
+
* The center of mass is the point around which the mass of the rigid-body is evenly distributed. It is used to compute the torque applied to the rigid-body when forces are applied to it.
|
172
|
+
*/
|
173
|
+
@serializable(Vector3)
|
174
|
+
centerOfMass: Vector3 = new Vector3(0, 0, 0);
|
172
175
|
|
173
176
|
/**
|
174
177
|
* Constraints are used to lock the position or rotation of an object in a specific axis.
|
@@ -282,22 +282,14 @@
|
|
282
282
|
this.lastWorldRotation.copy(wr);
|
283
283
|
|
284
284
|
|
285
|
-
// if (this._model.isOwned === false && this.autoOwnership) {
|
286
|
-
// this.requestOwnership();
|
287
|
-
// }
|
288
|
-
|
289
285
|
if (!this._model) return;
|
290
|
-
|
286
|
+
|
291
287
|
if (!this._model || this._model.hasOwnership === undefined || !this._model.hasOwnership) {
|
292
|
-
if
|
293
|
-
this.rb.isKinematic = this._model.isOwned ?? false;
|
294
|
-
this.rb.setVelocity(0, 0, 0);
|
295
|
-
}
|
288
|
+
// if we're not the owner of this synced transform then don't send any data
|
296
289
|
return;
|
297
290
|
}
|
298
291
|
|
299
292
|
// local user is owner:
|
300
|
-
|
301
293
|
if (this.rb) {
|
302
294
|
if (this._wasKinematic !== undefined) {
|
303
295
|
if (debug)
|
@@ -305,13 +297,8 @@
|
|
305
297
|
this.rb.isKinematic = this._wasKinematic;
|
306
298
|
}
|
307
299
|
|
308
|
-
//
|
309
|
-
|
310
|
-
if (debug)
|
311
|
-
console.log("RESET", this.name)
|
312
|
-
this.gameObject.position.set(0, 1, 0);
|
313
|
-
this.rb.setVelocity(0, 0, 0);
|
314
|
-
}
|
300
|
+
// TODO: if the SyncedTransform has a dynamic rigidbody we should probably synchronize the Rigidbody's properties (velocity etc)
|
301
|
+
// Or the Rigidbody should be synchronized separately in which case the SyncedTransform should be passive
|
315
302
|
}
|
316
303
|
|
317
304
|
const updateInterval = 10;
|