@@ -18,3 +18,5 @@
|
|
18
18
|
import "./CameraUtils.js"
|
19
19
|
import "./AnimationUtils.js"
|
20
20
|
|
21
|
+
export { ParticleSystemBaseBehaviour, type QParticle, type QParticleBehaviour } from "./ParticleSystem.js"
|
22
|
+
|
@@ -572,23 +572,8 @@
|
|
572
572
|
}
|
573
573
|
}
|
574
574
|
|
575
|
-
// TODO move this to threeutils
|
576
|
-
// we need a copy for modifying the values to local space
|
577
|
-
private static _worldPositionBuffer: Vector3 = new Vector3();
|
578
|
-
private static _worldQuaternionBuffer: Quaternion = new Quaternion();
|
579
|
-
private static _worldEulerBuffer: Euler = new Euler();
|
580
|
-
|
581
|
-
private _worldPosition: Vector3 | undefined = undefined;
|
582
|
-
private _worldQuaternion: Quaternion | undefined = undefined;
|
583
|
-
private static _tempQuaternionBuffer2: Quaternion = new Quaternion();
|
584
|
-
private _worldEuler: Euler | undefined = undefined;
|
585
|
-
private _worldRotation: Vector3 | undefined = undefined;
|
586
|
-
|
587
575
|
get worldPosition(): Vector3 {
|
588
|
-
|
589
|
-
threeutils.getWorldPosition(this.gameObject, this._worldPosition);
|
590
|
-
// this.gameObject.getWorldPosition(this._worldPosition);
|
591
|
-
return this._worldPosition;
|
576
|
+
return threeutils.getWorldPosition(this.gameObject);
|
592
577
|
}
|
593
578
|
|
594
579
|
set worldPosition(val: Vector3) {
|
@@ -596,48 +581,33 @@
|
|
596
581
|
}
|
597
582
|
|
598
583
|
setWorldPosition(x: number, y: number, z: number) {
|
599
|
-
|
600
|
-
this.worldPosition = Component._worldPositionBuffer;
|
584
|
+
threeutils.setWorldPositionXYZ(this.gameObject, x, y, z);
|
601
585
|
}
|
602
586
|
|
603
587
|
|
604
588
|
get worldQuaternion(): Quaternion {
|
605
|
-
|
606
|
-
return threeutils.getWorldQuaternion(this.gameObject, this._worldQuaternion);
|
589
|
+
return threeutils.getWorldQuaternion(this.gameObject);
|
607
590
|
}
|
608
591
|
set worldQuaternion(val: Quaternion) {
|
609
592
|
threeutils.setWorldQuaternion(this.gameObject, val);
|
610
593
|
}
|
611
594
|
setWorldQuaternion(x: number, y: number, z: number, w: number) {
|
612
|
-
|
613
|
-
this.worldQuaternion = Component._worldQuaternionBuffer;
|
595
|
+
threeutils.setWorldQuaternionXYZW(this.gameObject, x, y, z, w);
|
614
596
|
}
|
615
597
|
|
616
|
-
|
617
598
|
// world euler (in radians)
|
618
599
|
get worldEuler(): Euler {
|
619
|
-
|
620
|
-
this._worldEuler.setFromQuaternion(this.worldQuaternion);
|
621
|
-
return this._worldEuler;
|
600
|
+
return threeutils.getWorldEuler(this.gameObject);
|
622
601
|
}
|
623
602
|
|
624
603
|
// world euler (in radians)
|
625
604
|
set worldEuler(val: Euler) {
|
626
|
-
|
627
|
-
this._worldQuaternion?.setFromEuler(val);
|
628
|
-
this.worldQuaternion = this._worldQuaternion;
|
605
|
+
threeutils.setWorldEuler(this.gameObject, val);
|
629
606
|
}
|
630
607
|
|
631
608
|
// returns rotation in degrees
|
632
609
|
get worldRotation(): Vector3 {
|
633
|
-
|
634
|
-
if (!this._worldRotation) this._worldRotation = new Vector3();
|
635
|
-
const wr = this._worldRotation;
|
636
|
-
wr.set(rot.x, rot.y, rot.z);
|
637
|
-
wr.x = Mathf.toDegrees(wr.x);
|
638
|
-
wr.y = Mathf.toDegrees(wr.y);
|
639
|
-
wr.z = Mathf.toDegrees(wr.z);
|
640
|
-
return wr;
|
610
|
+
return this.gameObject.worldRotation;;
|
641
611
|
}
|
642
612
|
|
643
613
|
set worldRotation(val: Vector3) {
|
@@ -645,14 +615,7 @@
|
|
645
615
|
}
|
646
616
|
|
647
617
|
setWorldRotation(x: number, y: number, z: number, degrees: boolean = true) {
|
648
|
-
|
649
|
-
x = Mathf.toRadians(x);
|
650
|
-
y = Mathf.toRadians(y);
|
651
|
-
z = Mathf.toRadians(z);
|
652
|
-
}
|
653
|
-
Component._worldEulerBuffer.set(x, y, z);
|
654
|
-
Component._worldQuaternionBuffer.setFromEuler(Component._worldEulerBuffer);
|
655
|
-
this.worldQuaternion = Component._worldQuaternionBuffer;
|
618
|
+
threeutils.setWorldRotationXYZ(this.gameObject, x, y, z, degrees);
|
656
619
|
}
|
657
620
|
|
658
621
|
private static _forward: Vector3 = new Vector3();
|
@@ -1,13 +1,31 @@
|
|
1
|
+
import { InstancedMesh, Object3D } from "three";
|
2
|
+
|
1
3
|
export const NEED_UPDATE_INSTANCE_KEY = Symbol("NEEDLE_NEED_UPDATE_INSTANCE");
|
2
4
|
|
3
5
|
export const $isUsingInstancing = Symbol("isUsingInstancing");
|
6
|
+
export const $instancingRenderer = Symbol("instancingRenderer");
|
7
|
+
export const $instancingAutoUpdateBounds = Symbol("instancingAutoUpdateBounds");
|
4
8
|
|
5
9
|
export class InstancingUtil {
|
6
|
-
|
7
|
-
static isUsingInstancing(instance: THREE.Object3D): boolean { return instance[$isUsingInstancing] === true; }
|
8
10
|
|
11
|
+
/** Is this object rendered using a InstancedMesh */
|
12
|
+
static isUsingInstancing(instance: Object3D): boolean { return instance[$isUsingInstancing] === true; }
|
13
|
+
/** Returns the instanced mesh IF the object is rendered by an instanced mesh
|
14
|
+
* @link https://threejs.org/docs/#api/en/objects/InstancedMesh
|
15
|
+
*/
|
16
|
+
static getRenderer(instance: Object3D): InstancedMesh | null { return instance[$instancingRenderer] || null; }
|
17
|
+
|
18
|
+
setAutoUpdateBounds(instance: Object3D, value: boolean) {
|
19
|
+
const renderer = InstancingUtil.getRenderer(instance);
|
20
|
+
if (renderer) {
|
21
|
+
renderer[$instancingAutoUpdateBounds] = value;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
|
9
26
|
// TODO: change this so it does not set matrix world directly but some flag that is only used by instancing
|
10
|
-
|
27
|
+
/** Mark an instanced object dirty so the instance matrix will be updated */
|
28
|
+
static markDirty(go: Object3D | null, recursive: boolean = true) {
|
11
29
|
if (!go) return;
|
12
30
|
// potential optimization:
|
13
31
|
// if(go.matrixWorldNeedsUpdate) return;
|
@@ -11,25 +11,62 @@
|
|
11
11
|
export declare type RaycastTestObjectReturnType = void | boolean | "continue in children";
|
12
12
|
export declare type RaycastTestObjectCallback = (obj: Object3D) => RaycastTestObjectReturnType;
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
declare interface IRaycastOptions {
|
15
|
+
/** Optionally a custom raycaster can be provided. Other properties will then be set on this raycaster */
|
16
|
+
raycaster?: Raycaster;
|
17
|
+
/** Optional ray that can be used for raycasting
|
18
|
+
* @link https://threejs.org/docs/#api/en/math/Ray
|
19
|
+
* */
|
20
|
+
ray?: Ray;
|
21
|
+
/** The camera to use for the raycaster */
|
22
|
+
cam?: Camera | null;
|
23
|
+
/** Point on screen in raycast space / normalized device coordinates (-1 to 1).
|
24
|
+
* @link https://threejs.org/docs/#api/en/core/Raycaster.setFromCamera */
|
25
|
+
screenPoint?: Vector2;
|
26
|
+
/** Raycast results array. You can provide an array here to avoid creating a new one (note that if your array already contains items they will be removed) */
|
27
|
+
results?: Array<Intersection>;
|
28
|
+
/** Objects to raycast against. If no target array is provided the whole scene will be raycasted */
|
29
|
+
targets?: Array<Object3D>;
|
30
|
+
recursive?: boolean;
|
31
|
+
/** Set the raycaster near distance:
|
32
|
+
* The near factor of the raycaster. This value indicates which objects can be discarded based on the distance. This value shouldn't be negative and should be smaller than the far property.
|
33
|
+
* @link https://threejs.org/docs/#api/en/core/Raycaster.near
|
34
|
+
*/
|
35
|
+
minDistance?: number;
|
36
|
+
/** Set the raycaster far distance:
|
37
|
+
* The far factor of the raycaster. This value indicates which objects can be discarded based on the distance. This value shouldn't be negative and should be larger than the near property.
|
38
|
+
* @link https://threejs.org/docs/#api/en/core/Raycaster.far
|
39
|
+
*/
|
40
|
+
maxDistance?: number;
|
41
|
+
/** @link https://threejs.org/docs/#api/en/core/Raycaster.params */
|
42
|
+
lineThreshold?: number;
|
25
43
|
/** raw layer mask, use setLayer to set an individual layer active */
|
26
|
-
layerMask
|
27
|
-
ignore
|
28
|
-
/**
|
44
|
+
layerMask?: Layers | number;
|
45
|
+
ignore?: Object3D[];
|
46
|
+
/** Optional calback function to be called per object before tested for intersections.
|
47
|
+
* This can be used to filter objects.
|
29
48
|
* Return `false` to ignore the object completely or `"continue in children"` to skip the object but continue to traverse its children (if you do raycast with `recursive` enabled)
|
30
49
|
* */
|
31
|
-
testObject?: RaycastTestObjectCallback
|
50
|
+
testObject?: RaycastTestObjectCallback;
|
51
|
+
}
|
32
52
|
|
53
|
+
export class RaycastOptions implements IRaycastOptions {
|
54
|
+
public static readonly AllLayers = 0xFFFFFFFF;
|
55
|
+
|
56
|
+
ray?: Ray;
|
57
|
+
cam?: Camera | null;
|
58
|
+
screenPoint?: Vector2;
|
59
|
+
raycaster?: Raycaster;
|
60
|
+
results?: Array<Intersection>;
|
61
|
+
targets?: Array<Object3D>;
|
62
|
+
recursive?: boolean = true;
|
63
|
+
minDistance?: number;
|
64
|
+
maxDistance?: number;
|
65
|
+
lineThreshold?: number;
|
66
|
+
layerMask?: Layers | number;
|
67
|
+
ignore?: Object3D[];
|
68
|
+
testObject?: RaycastTestObjectCallback;
|
69
|
+
|
33
70
|
screenPointFromOffset(ox: number, oy: number) {
|
34
71
|
if (this.screenPoint === undefined) this.screenPoint = new Vector2();
|
35
72
|
this.screenPoint.x = ox / window.innerWidth * 2 - 1;
|
@@ -51,7 +88,6 @@
|
|
51
88
|
else this.layerMask = mask;
|
52
89
|
}
|
53
90
|
|
54
|
-
public static readonly AllLayers = 0xFFFFFFFF;
|
55
91
|
}
|
56
92
|
|
57
93
|
export class SphereIntersection implements Intersection {
|
@@ -154,7 +190,10 @@
|
|
154
190
|
public raycastFromRay(ray: Ray, options: RaycastOptions | null = null): Array<Intersection> {
|
155
191
|
const opts = options ?? this.defaultRaycastOptions;
|
156
192
|
opts.ray = ray;
|
157
|
-
|
193
|
+
const res = this.raycast(opts);
|
194
|
+
// reset the default options ray
|
195
|
+
if (opts === this.defaultRaycastOptions) opts.ray = undefined;
|
196
|
+
return res;
|
158
197
|
}
|
159
198
|
|
160
199
|
/** raycast against rendered three objects. This might be very slow depending on your scene complexity.
|
@@ -163,7 +202,7 @@
|
|
163
202
|
* Use raycastPhysics for raycasting against physic colliders only. Depending on your scenario this might be faster.
|
164
203
|
* @param options raycast options. If null, default options will be used.
|
165
204
|
*/
|
166
|
-
public raycast(options: RaycastOptions | null = null): Array<Intersection> {
|
205
|
+
public raycast(options: IRaycastOptions | RaycastOptions | null = null): Array<Intersection> {
|
167
206
|
if (!options) options = this.defaultRaycastOptions;
|
168
207
|
const mp = options.screenPoint ?? this.context.input.mousePositionRC;
|
169
208
|
const rc = options.raycaster ?? this.raycaster;
|
@@ -230,7 +269,7 @@
|
|
230
269
|
return results;
|
231
270
|
}
|
232
271
|
|
233
|
-
private intersect(raycaster: Raycaster, objects: Object3D[], results: Intersection[], options:
|
272
|
+
private intersect(raycaster: Raycaster, objects: Object3D[], results: Intersection[], options: IRaycastOptions) {
|
234
273
|
for (const obj of objects) {
|
235
274
|
const testResult = options.testObject?.(obj);
|
236
275
|
if (testResult === false) continue;
|
@@ -239,7 +278,7 @@
|
|
239
278
|
raycaster.intersectObject(obj, false, results);
|
240
279
|
}
|
241
280
|
|
242
|
-
if (options.recursive) {
|
281
|
+
if (options.recursive !== false) {
|
243
282
|
this.intersect(raycaster, obj.children, results, options);
|
244
283
|
}
|
245
284
|
}
|
@@ -48,7 +48,7 @@
|
|
48
48
|
}
|
49
49
|
// this is a hack/workaround for production builds where the name of the sprite is missing
|
50
50
|
// need to remove this!!!!
|
51
|
-
if (this.sprite?.texture?.image?.width === 32 && this.sprite?.texture?.image?.height === 32)
|
51
|
+
if (!this.sprite?.texture?.name?.length && this.sprite?.texture?.image?.width === 32 && this.sprite?.texture?.image?.height === 32)
|
52
52
|
return true;
|
53
53
|
return false;
|
54
54
|
}
|
@@ -12,7 +12,7 @@
|
|
12
12
|
import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldScale } from "../engine/engine_three_utils.js";
|
13
13
|
import { assign } from "../engine/engine_serialization_core.js";
|
14
14
|
import { ParticleSystem as _ParticleSystem, ConstantValue, ConstantColor, BatchedParticleRenderer, TrailBatch, TrailParticle, RenderMode } from "three.quarks";
|
15
|
-
import type { BatchedRenderer, Behavior, BillBoardSettings, BurstParameters, ColorGenerator, EmissionState, EmitSubParticleSystem, EmitterShape, FunctionColorGenerator, FunctionJSON, FunctionValueGenerator, IntervalValue, MeshSettings, Particle, ParticleEmitter, ParticleSystemParameters, PointEmitter, RecordState, RotationGenerator, SizeOverLife, TrailSettings, ValueGenerator } from "three.quarks";
|
15
|
+
import type { BatchedRenderer, Behavior, BehaviorPlugin, BillBoardSettings, BurstParameters, ColorGenerator, EmissionState, EmitSubParticleSystem, EmitterShape, FunctionColorGenerator, FunctionJSON, FunctionValueGenerator, IntervalValue, MeshSettings, Particle, ParticleEmitter, ParticleSystemParameters, PointEmitter, RecordState, RotationGenerator, SizeOverLife, TrailSettings, VFXBatchSettings, ValueGenerator } from "three.quarks";
|
16
16
|
import { createFlatTexture } from "../engine/engine_shaders.js";
|
17
17
|
import { Mathf } from "../engine/engine_math.js";
|
18
18
|
import { Context } from "../engine/engine_setup.js";
|
@@ -25,6 +25,11 @@
|
|
25
25
|
const suppressProgressiveLoading = getParam("noprogressive");
|
26
26
|
const debugProgressiveLoading = getParam("debugprogressive");
|
27
27
|
|
28
|
+
|
29
|
+
export type { Behavior as QParticleBehaviour, Particle as QParticle } from "three.quarks"
|
30
|
+
|
31
|
+
|
32
|
+
|
28
33
|
export enum SubEmitterType {
|
29
34
|
Birth = 0,
|
30
35
|
Collision = 1,
|
@@ -248,29 +253,25 @@
|
|
248
253
|
}
|
249
254
|
}
|
250
255
|
|
251
|
-
abstract class ParticleSystemBaseBehaviour implements Behavior {
|
252
|
-
readonly system: ParticleSystem;
|
256
|
+
export abstract class ParticleSystemBaseBehaviour implements Behavior {
|
253
257
|
|
254
|
-
|
255
|
-
|
256
|
-
// }
|
258
|
+
system!: ParticleSystem;
|
259
|
+
get context() { return this.system.context; }
|
257
260
|
|
258
|
-
constructor(ps
|
259
|
-
this.
|
261
|
+
constructor(ps?: ParticleSystem) {
|
262
|
+
this.type = Object.getPrototypeOf(this).constructor.name || "ParticleSystemBaseBehaviour";
|
263
|
+
if (ps)
|
264
|
+
this.system = ps;
|
260
265
|
}
|
261
266
|
|
262
|
-
|
267
|
+
type: string;
|
263
268
|
|
264
|
-
initialize(_particle: Particle): void {
|
265
|
-
}
|
266
|
-
|
267
|
-
}
|
268
|
-
frameUpdate(_delta: number): void {
|
269
|
-
}
|
269
|
+
initialize(_particle: Particle): void { }
|
270
|
+
update(_particle: Particle, _delta: number): void { }
|
271
|
+
frameUpdate(_delta: number): void { }
|
270
272
|
toJSON() { throw new Error("Method not implemented."); }
|
271
273
|
clone(): Behavior { throw new Error("Method not implemented."); }
|
272
|
-
reset() {
|
273
|
-
}
|
274
|
+
reset() { }
|
274
275
|
}
|
275
276
|
|
276
277
|
const $startFrame = Symbol("startFrame")
|
@@ -843,6 +844,42 @@
|
|
843
844
|
return this._isUsedAsSubsystem;
|
844
845
|
}
|
845
846
|
|
847
|
+
/** Add a custom quarks behaviour to the particle system. You can add a quarks.Behaviour type.
|
848
|
+
* @link https://github.com/Alchemist0823/three.quarks
|
849
|
+
*/
|
850
|
+
addBehaviour(particleSystemBehaviour: Behavior | ParticleSystemBaseBehaviour): boolean {
|
851
|
+
if (!this._particleSystem) {
|
852
|
+
return false;
|
853
|
+
}
|
854
|
+
if (particleSystemBehaviour instanceof ParticleSystemBaseBehaviour) {
|
855
|
+
particleSystemBehaviour.system = this;
|
856
|
+
}
|
857
|
+
if (isDevEnvironment()) console.log("add behaviour", particleSystemBehaviour);
|
858
|
+
this._particleSystem.addBehavior(particleSystemBehaviour);
|
859
|
+
return true;
|
860
|
+
}
|
861
|
+
/** Removes all behaviours from the particle system
|
862
|
+
* **Note:** this will also remove the default behaviours like SizeBehaviour, ColorBehaviour etc.
|
863
|
+
*/
|
864
|
+
removeAllBehaviours() {
|
865
|
+
if (!this._particleSystem) {
|
866
|
+
return false;
|
867
|
+
}
|
868
|
+
this._particleSystem.behaviors.length = 0;
|
869
|
+
return true;
|
870
|
+
}
|
871
|
+
/** Get the particlesystem behaviours. This can be used to fully customize the behaviour of the particles. */
|
872
|
+
get behaviours(): Behavior[] | null {
|
873
|
+
if (!this._particleSystem) return null;
|
874
|
+
return this._particleSystem.behaviors;
|
875
|
+
}
|
876
|
+
/** Get access to the underlying quarks particle system if you need more control
|
877
|
+
* @link https://github.com/Alchemist0823/three.quarks
|
878
|
+
*/
|
879
|
+
get particleSystem(): _ParticleSystem | null {
|
880
|
+
return this._particleSystem ?? null;
|
881
|
+
}
|
882
|
+
|
846
883
|
private _renderer!: ParticleSystemRenderer;
|
847
884
|
private _batchSystem?: BatchedRenderer;
|
848
885
|
private _particleSystem?: _ParticleSystem;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { TypeStore } from "./../engine_typestore.js"
|
2
|
-
|
2
|
+
|
3
3
|
// Import types
|
4
4
|
import { __Ignore } from "../../engine-components/codegen/components.js";
|
5
5
|
import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js";
|
@@ -219,7 +219,7 @@
|
|
219
219
|
import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering.js";
|
220
220
|
import { XRRig } from "../../engine-components/webxr/WebXRRig.js";
|
221
221
|
import { XRState } from "../../engine-components/XRFlag.js";
|
222
|
-
|
222
|
+
|
223
223
|
// Register types
|
224
224
|
TypeStore.add("__Ignore", __Ignore);
|
225
225
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
@@ -8,7 +8,7 @@
|
|
8
8
|
import { AxesHelper, Material, Matrix4, Mesh, Object3D, SkinnedMesh, Texture, Vector4 } from "three";
|
9
9
|
import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects.js";
|
10
10
|
import { NEEDLE_progressive } from "../engine/extensions/NEEDLE_progressive.js";
|
11
|
-
import { InstancingUtil, NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing.js";
|
11
|
+
import { $instancingAutoUpdateBounds, $instancingRenderer, InstancingUtil, NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing.js";
|
12
12
|
import type { IRenderer, ISharedMaterials } from "../engine/engine_types.js";
|
13
13
|
import { ReflectionProbe } from "./ReflectionProbe.js";
|
14
14
|
import { setCustomVisibility } from "../engine/js-extensions/Layers.js";
|
@@ -912,8 +912,8 @@
|
|
912
912
|
this.instanceIndex = instanceIndex;
|
913
913
|
this.object = originalObject;
|
914
914
|
this.instancer = instancer;
|
915
|
+
originalObject[$instancingRenderer] = instancer;
|
915
916
|
GameObject.markAsInstancedRendered(originalObject, true);
|
916
|
-
// this.object.visible = false;
|
917
917
|
}
|
918
918
|
|
919
919
|
updateInstanceMatrix(updateChildren: boolean = false) {
|
@@ -939,7 +939,21 @@
|
|
939
939
|
}
|
940
940
|
|
941
941
|
class InstancedMeshRenderer {
|
942
|
-
|
942
|
+
/** The three instanced mesh
|
943
|
+
* @link https://threejs.org/docs/#api/en/objects/InstancedMesh
|
944
|
+
*/
|
945
|
+
get mesh() {
|
946
|
+
return this.inst;
|
947
|
+
}
|
948
|
+
get visible(): boolean {
|
949
|
+
return this.inst.visible;
|
950
|
+
}
|
951
|
+
set visible(val: boolean) {
|
952
|
+
this.inst.visible = val;
|
953
|
+
}
|
954
|
+
get castShadow(): boolean {
|
955
|
+
return this.inst.castShadow;
|
956
|
+
}
|
943
957
|
set castShadow(val: boolean) {
|
944
958
|
this.inst.castShadow = val;
|
945
959
|
}
|
@@ -947,6 +961,14 @@
|
|
947
961
|
this.inst.receiveShadow = val;
|
948
962
|
}
|
949
963
|
|
964
|
+
updateBounds(box: boolean = true, sphere: boolean = true) {
|
965
|
+
this._needUpdateBounds = false;
|
966
|
+
if (box)
|
967
|
+
this.inst.computeBoundingBox();
|
968
|
+
if (sphere)
|
969
|
+
this.inst.computeBoundingSphere();
|
970
|
+
}
|
971
|
+
|
950
972
|
public name: string = "";
|
951
973
|
public geo: THREE.BufferGeometry;
|
952
974
|
public material: THREE.Material;
|
@@ -963,6 +985,8 @@
|
|
963
985
|
return this.currentCount >= this.maxCount;
|
964
986
|
}
|
965
987
|
|
988
|
+
private _needUpdateBounds: boolean = false;
|
989
|
+
|
966
990
|
constructor(name: string, geo: THREE.BufferGeometry, material: THREE.Material, count: number, context: Context) {
|
967
991
|
this.name = name;
|
968
992
|
this.geo = geo;
|
@@ -973,9 +997,18 @@
|
|
973
997
|
material = new THREE.MeshBasicMaterial({ color: this.randomColor() });
|
974
998
|
}
|
975
999
|
this.inst = new THREE.InstancedMesh(geo, material, count);
|
1000
|
+
this.inst[$instancingAutoUpdateBounds] = true;
|
1001
|
+
this.inst.onBeforeRender = () => {
|
1002
|
+
if (this._needUpdateBounds && this.inst[$instancingAutoUpdateBounds] === true) {
|
1003
|
+
if (debugInstancing)
|
1004
|
+
console.log("Update instancing bounds", this.name);
|
1005
|
+
this.updateBounds();
|
1006
|
+
}
|
1007
|
+
};
|
976
1008
|
this.inst.count = 0;
|
977
1009
|
this.inst.layers.set(2);
|
978
1010
|
this.inst.visible = true;
|
1011
|
+
|
979
1012
|
// this.inst.castShadow = true;
|
980
1013
|
// this.inst.receiveShadow = true;
|
981
1014
|
this.context.scene.add(this.inst);
|
@@ -1023,6 +1056,7 @@
|
|
1023
1056
|
this.inst.setMatrixAt(handle.instanceIndex, handle.object.matrixWorld);
|
1024
1057
|
this.inst.instanceMatrix.needsUpdate = true;
|
1025
1058
|
this.inst.count += 1;
|
1059
|
+
this._needUpdateBounds = true;
|
1026
1060
|
|
1027
1061
|
if (this.inst.count > 0)
|
1028
1062
|
this.inst.visible = true;
|
@@ -1061,6 +1095,7 @@
|
|
1061
1095
|
if (this.inst.count > 0)
|
1062
1096
|
this.inst.count -= 1;
|
1063
1097
|
handle.instanceIndex = -1;
|
1098
|
+
this._needUpdateBounds = true;
|
1064
1099
|
|
1065
1100
|
if (this.inst.count <= 0)
|
1066
1101
|
this.inst.visible = false;
|
@@ -1071,5 +1106,6 @@
|
|
1071
1106
|
updateInstance(mat: THREE.Matrix4, index: number) {
|
1072
1107
|
this.inst.setMatrixAt(index, mat);
|
1073
1108
|
this.inst.instanceMatrix.needsUpdate = true;
|
1109
|
+
this._needUpdateBounds = true;
|
1074
1110
|
}
|
1075
1111
|
}
|
@@ -137,6 +137,9 @@
|
|
137
137
|
@validate()
|
138
138
|
autoMass: boolean = true;
|
139
139
|
|
140
|
+
/** By default the mass will be automatically calculated (see `autoMass`) by the physics engine using the collider sizes
|
141
|
+
* To set the mass manually you can either set the `mass` value or set `autoMass` to `false`
|
142
|
+
*/
|
140
143
|
@serializable()
|
141
144
|
set mass(value: number) {
|
142
145
|
if (value === this._mass) return;
|
@@ -166,10 +169,12 @@
|
|
166
169
|
@serializable()
|
167
170
|
isKinematic: boolean = false;
|
168
171
|
|
172
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#damping */
|
169
173
|
@validate()
|
170
174
|
@serializable()
|
171
175
|
drag: number = 0;
|
172
176
|
|
177
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#damping */
|
173
178
|
@validate()
|
174
179
|
@serializable()
|
175
180
|
angularDrag: number = 1;
|
@@ -178,58 +183,77 @@
|
|
178
183
|
@serializable()
|
179
184
|
detectCollisions: boolean = true;
|
180
185
|
|
186
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#sleeping */
|
181
187
|
@validate()
|
182
188
|
@serializable()
|
183
189
|
sleepThreshold: number = 0.01;
|
184
190
|
|
191
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#continuous-collision-detection */
|
185
192
|
@validate()
|
186
193
|
@serializable()
|
187
194
|
collisionDetectionMode: CollisionDetectionMode = CollisionDetectionMode.Discrete;
|
188
195
|
|
196
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
189
197
|
get lockPositionX() {
|
190
198
|
return (this.constraints & RigidbodyConstraints.FreezePositionX) !== 0;
|
191
199
|
}
|
200
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
192
201
|
get lockPositionY() {
|
193
202
|
return (this.constraints & RigidbodyConstraints.FreezePositionY) !== 0;
|
194
203
|
}
|
204
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
195
205
|
get lockPositionZ() {
|
196
206
|
return (this.constraints & RigidbodyConstraints.FreezePositionZ) !== 0;
|
197
207
|
}
|
208
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
198
209
|
get lockRotationX() {
|
199
210
|
return (this.constraints & RigidbodyConstraints.FreezeRotationX) !== 0;
|
200
211
|
}
|
212
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
201
213
|
get lockRotationY() {
|
202
214
|
return (this.constraints & RigidbodyConstraints.FreezeRotationY) !== 0;
|
203
215
|
}
|
216
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
204
217
|
get lockRotationZ() {
|
205
218
|
return (this.constraints & RigidbodyConstraints.FreezeRotationZ) !== 0;
|
206
219
|
}
|
207
220
|
|
221
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
208
222
|
set lockPositionX(v: boolean) {
|
209
223
|
if (v) this.constraints |= RigidbodyConstraints.FreezePositionX;
|
210
224
|
else this.constraints &= ~RigidbodyConstraints.FreezePositionX;
|
211
225
|
}
|
226
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
212
227
|
set lockPositionY(v: boolean) {
|
213
228
|
if (v) this.constraints |= RigidbodyConstraints.FreezePositionY;
|
214
229
|
else this.constraints &= ~RigidbodyConstraints.FreezePositionY;
|
215
230
|
}
|
231
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
216
232
|
set lockPositionZ(v: boolean) {
|
217
233
|
if (v) this.constraints |= RigidbodyConstraints.FreezePositionZ;
|
218
234
|
else this.constraints &= ~RigidbodyConstraints.FreezePositionZ;
|
219
235
|
}
|
236
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
220
237
|
set lockRotationX(v: boolean) {
|
221
238
|
if (v) this.constraints |= RigidbodyConstraints.FreezeRotationX;
|
222
239
|
else this.constraints &= ~RigidbodyConstraints.FreezeRotationX;
|
223
240
|
}
|
241
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
224
242
|
set lockRotationY(v: boolean) {
|
225
243
|
if (v) this.constraints |= RigidbodyConstraints.FreezeRotationY;
|
226
244
|
else this.constraints &= ~RigidbodyConstraints.FreezeRotationY;
|
227
245
|
}
|
246
|
+
/** @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#locking-translationsrotations */
|
228
247
|
set lockRotationZ(v: boolean) {
|
229
248
|
if (v) this.constraints |= RigidbodyConstraints.FreezeRotationZ;
|
230
249
|
else this.constraints &= ~RigidbodyConstraints.FreezeRotationZ;
|
231
250
|
}
|
232
251
|
|
252
|
+
/** Gravity is such a common force that it is implemented as a special case (even if it could easily be implemented by the user using force application). Note however that a change of gravity won't automatically wake-up the sleeping bodies so keep in mind that you may want to wake them up manually before a gravity change.
|
253
|
+
*
|
254
|
+
* It is possible to change the way gravity affects a specific rigid-body by setting the rigid-body's gravity scale to a value other than 1.0. The magnitude of the gravity applied to this body will be multiplied by this scaling factor. Therefore, a gravity scale set to 0.0 will disable gravity for the rigid-body whereas a gravity scale set to 2.0 will make it twice as strong. A negative value will flip the direction of the gravity for this rigid-body.
|
255
|
+
* @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#gravity
|
256
|
+
*/
|
233
257
|
set gravityScale(val: number) {
|
234
258
|
this._gravityScale = val;
|
235
259
|
}
|
@@ -240,7 +264,7 @@
|
|
240
264
|
private _gravityScale: number = 1;
|
241
265
|
|
242
266
|
/** Rigidbodies with higher dominance will be immune to forces originating from contacts with rigidbodies of lower dominance.
|
243
|
-
*
|
267
|
+
* @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#dominance
|
244
268
|
*/
|
245
269
|
@validate()
|
246
270
|
dominanceGroup : number = 0;
|
@@ -299,6 +323,7 @@
|
|
299
323
|
}
|
300
324
|
}
|
301
325
|
|
326
|
+
/** Will reset forces before setting the object world position */
|
302
327
|
public teleport(pt: { x: number, y: number, z: number }, localspace: boolean = true) {
|
303
328
|
this._watch?.reset(true);
|
304
329
|
if (localspace) this.gameObject.position.set(pt.x, pt.y, pt.z);
|
@@ -325,18 +350,27 @@
|
|
325
350
|
this.resetTorques();
|
326
351
|
}
|
327
352
|
|
353
|
+
/** When a dynamic rigid-body doesn't move (or moves very slowly) during a few seconds, it will be marked as sleeping by the physics pipeline. Rigid-bodies marked as sleeping are no longer simulated by the physics engine until they are woken up. That way the physics engine doesn't waste any computational resources simulating objects that don't actually move. They are woken up automatically whenever another non-sleeping rigid-body starts interacting with them (either with a joint, or with one of its attached colliders generating contacts).
|
354
|
+
* @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#sleeping */
|
328
355
|
public wakeUp() {
|
329
356
|
this.context.physics.engine?.wakeup(this);
|
330
357
|
}
|
331
358
|
|
359
|
+
/** Forces affect the rigid-body's acceleration whereas impulses affect the rigid-body's velocity
|
360
|
+
* the acceleration change is equal to the force divided by the mass:
|
361
|
+
* @link see https://rapier.rs/docs/user_guides/javascript/rigid_bodies#forces-and-impulses */
|
332
362
|
public applyForce(vec: Vector3 | Vec3, _rel?: THREE.Vector3, wakeup: boolean = true) {
|
333
363
|
this.context.physics.engine?.addForce(this, vec, wakeup);
|
334
364
|
}
|
335
365
|
|
366
|
+
/** Forces affect the rigid-body's acceleration whereas impulses affect the rigid-body's velocity
|
367
|
+
* the velocity change is equal to the impulse divided by the mass
|
368
|
+
* @link see https://rapier.rs/docs/user_guides/javascript/rigid_bodies#forces-and-impulses */
|
336
369
|
public applyImpulse(vec: Vector3 | Vec3, wakeup: boolean = true) {
|
337
370
|
this.context.physics.engine?.applyImpulse(this, vec, wakeup);
|
338
371
|
}
|
339
372
|
|
373
|
+
/** @link see https://rapier.rs/docs/user_guides/javascript/rigid_bodies#forces-and-impulses */
|
340
374
|
public setForce(x: Vector3 | Vec3 | number, y?: number, z?: number, wakeup: boolean = true) {
|
341
375
|
this.context.physics.engine?.resetForces(this, wakeup);
|
342
376
|
if (typeof x === "number") {
|
@@ -349,6 +383,8 @@
|
|
349
383
|
}
|
350
384
|
}
|
351
385
|
|
386
|
+
/** The velocity of a dynamic rigid-body controls how fast it is moving in time. The velocity is applied at the center-of-mass of the rigid-body. This method returns the current linear velocity of the rigid-body.
|
387
|
+
* @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#velocity */
|
352
388
|
public getVelocity(): Vector3 {
|
353
389
|
const vel = this.context.physics.engine?.getLinearVelocity(this);
|
354
390
|
if (!vel) return this._currentVelocity.set(0, 0, 0);
|
@@ -368,6 +404,17 @@
|
|
368
404
|
this.context.physics.engine?.setLinearVelocity(this, { x: x, y: y, z: z }, wakeup);
|
369
405
|
}
|
370
406
|
|
407
|
+
/** The velocity of a dynamic rigid-body controls how fast it is moving in time. The velocity is applied at the center-of-mass of the rigid-body. This method returns the current angular velocity of the rigid-body.
|
408
|
+
* @link https://rapier.rs/docs/user_guides/javascript/rigid_bodies#velocity */
|
409
|
+
public getAngularVelocity(): Vector3 {
|
410
|
+
const vel = this.context.physics.engine?.getAngularVelocity(this);
|
411
|
+
if (!vel) return this._currentVelocity.set(0, 0, 0);
|
412
|
+
this._currentVelocity.x = vel.x;
|
413
|
+
this._currentVelocity.y = vel.y;
|
414
|
+
this._currentVelocity.z = vel.z;
|
415
|
+
return this._currentVelocity;
|
416
|
+
}
|
417
|
+
|
371
418
|
public setAngularVelocity(x: number | Vector3, y?: number, z?: number, wakeup: boolean = true) {
|
372
419
|
if (x instanceof Vector3) {
|
373
420
|
const vec = x;
|
@@ -378,35 +425,24 @@
|
|
378
425
|
this.context.physics.engine?.setAngularVelocity(this, { x: x, y: y, z: z }, wakeup);
|
379
426
|
}
|
380
427
|
|
381
|
-
|
382
|
-
const vel = this.context.physics.engine?.getAngularVelocity(this);
|
383
|
-
if (!vel) return this._currentVelocity.set(0, 0, 0);
|
384
|
-
this._currentVelocity.x = vel.x;
|
385
|
-
this._currentVelocity.y = vel.y;
|
386
|
-
this._currentVelocity.z = vel.z;
|
387
|
-
return this._currentVelocity;
|
388
|
-
}
|
389
|
-
|
428
|
+
/** Set the angular velocity of a rigidbody (equivalent to calling `setAngularVelocity`) */
|
390
429
|
public setTorque(x: number | Vector3, y: number, z: number) {
|
391
430
|
this.setAngularVelocity(x, y, z);
|
392
431
|
}
|
393
432
|
|
433
|
+
/** Returns the rigidbody velocity smoothed over ~ 10 frames */
|
394
434
|
public get smoothedVelocity(): Vector3 {
|
395
435
|
this._smoothedVelocityGetter.copy(this._smoothedVelocity);
|
396
436
|
return this._smoothedVelocityGetter.multiplyScalar(1 / this.context.time.deltaTime);
|
397
437
|
}
|
398
438
|
|
399
|
-
// public get smoothedVelocity() { return this._smoothedVelocity; }
|
400
439
|
|
401
|
-
|
402
|
-
|
403
440
|
/**d
|
404
|
-
* @deprecated not used anymore
|
441
|
+
* @deprecated not used anymore and will be removed in a future update
|
405
442
|
*/
|
406
443
|
public setBodyFromGameObject(_velocity: THREE.Vector3 | null | { x: number, y: number, z: number } = null) { }
|
407
444
|
|
408
445
|
|
409
|
-
|
410
446
|
private captureVelocity() {
|
411
447
|
const wp = getWorldPosition(this.gameObject);
|
412
448
|
Rigidbody.tempPosition.copy(wp);
|