angular-three-rapier 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1831 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, viewChild, Component, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, afterNextRender, Directive, inject, DestroyRef, signal, computed, untracked, effect, model, output, ElementRef, viewChildren } from '@angular/core';
3
+ import { Vector3 as Vector3$1, Quaternion as Quaternion$1, EventQueue, ColliderDesc, ActiveEvents, RigidBodyDesc } from '@dimforge/rapier3d-compat';
4
+ import { extend, injectBeforeRender, pick, vector3, injectStore, getEmitter, hasListener, getLocalState, resolveRef } from 'angular-three';
5
+ import { mergeInputs } from 'ngxtension/inject-inputs';
6
+ import { Group, LineSegments, LineBasicMaterial, BufferAttribute, Quaternion, Euler, Vector3, Object3D, Matrix4, MathUtils, DynamicDrawUsage } from 'three';
7
+ import { injectAutoEffect } from 'ngxtension/auto-effect';
8
+ import { mergeVertices } from 'three-stdlib';
9
+ import { assertInjector } from 'ngxtension/assert-injector';
10
+
11
+ class NgtrDebug {
12
+ world = input.required();
13
+ lineSegmentsRef = viewChild.required('lineSegments');
14
+ constructor() {
15
+ extend({ Group, LineSegments, LineBasicMaterial, BufferAttribute });
16
+ injectBeforeRender(() => {
17
+ const [world, lineSegments] = [this.world(), this.lineSegmentsRef().nativeElement];
18
+ if (!world || !lineSegments)
19
+ return;
20
+ const buffers = world.debugRender();
21
+ lineSegments.geometry.setAttribute('position', new BufferAttribute(buffers.vertices, 3));
22
+ lineSegments.geometry.setAttribute('color', new BufferAttribute(buffers.colors, 4));
23
+ });
24
+ }
25
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrDebug, deps: [], target: i0.ɵɵFactoryTarget.Component });
26
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.2.0", type: NgtrDebug, isStandalone: true, selector: "ngtr-debug", inputs: { world: { classPropertyName: "world", publicName: "world", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "lineSegmentsRef", first: true, predicate: ["lineSegments"], descendants: true, isSignal: true }], ngImport: i0, template: `
27
+ <ngt-group>
28
+ <ngt-line-segments #lineSegments [frustumCulled]="false">
29
+ <ngt-line-basic-material color="white" [vertexColors]="true" />
30
+ <ngt-buffer-geometry />
31
+ </ngt-line-segments>
32
+ </ngt-group>
33
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
34
+ }
35
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrDebug, decorators: [{
36
+ type: Component,
37
+ args: [{
38
+ selector: 'ngtr-debug',
39
+ standalone: true,
40
+ template: `
41
+ <ngt-group>
42
+ <ngt-line-segments #lineSegments [frustumCulled]="false">
43
+ <ngt-line-basic-material color="white" [vertexColors]="true" />
44
+ <ngt-buffer-geometry />
45
+ </ngt-line-segments>
46
+ </ngt-group>
47
+ `,
48
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
49
+ changeDetection: ChangeDetectionStrategy.OnPush,
50
+ }]
51
+ }], ctorParameters: () => [] });
52
+
53
+ class NgtrFrameStepper {
54
+ ready = input(false);
55
+ updatePriority = input(0);
56
+ stepFn = input.required();
57
+ type = input.required();
58
+ constructor() {
59
+ const autoEffect = injectAutoEffect();
60
+ afterNextRender(() => {
61
+ autoEffect((injector) => {
62
+ const ready = this.ready();
63
+ if (!ready)
64
+ return;
65
+ const [type, updatePriority, stepFn] = [this.type(), this.updatePriority(), this.stepFn()];
66
+ if (type === 'follow') {
67
+ return injectBeforeRender(({ delta }) => {
68
+ stepFn(delta);
69
+ }, { priority: updatePriority, injector });
70
+ }
71
+ let lastFrame = 0;
72
+ let raf = 0;
73
+ const loop = () => {
74
+ const now = performance.now();
75
+ const delta = now - lastFrame;
76
+ raf = requestAnimationFrame(loop);
77
+ stepFn(delta);
78
+ lastFrame = now;
79
+ };
80
+ raf = requestAnimationFrame(loop);
81
+ return () => {
82
+ cancelAnimationFrame(raf);
83
+ };
84
+ });
85
+ });
86
+ }
87
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrFrameStepper, deps: [], target: i0.ɵɵFactoryTarget.Directive });
88
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrFrameStepper, isStandalone: true, selector: "ngtr-frame-stepper", inputs: { ready: { classPropertyName: "ready", publicName: "ready", isSignal: true, isRequired: false, transformFunction: null }, updatePriority: { classPropertyName: "updatePriority", publicName: "updatePriority", isSignal: true, isRequired: false, transformFunction: null }, stepFn: { classPropertyName: "stepFn", publicName: "stepFn", isSignal: true, isRequired: true, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
89
+ }
90
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrFrameStepper, decorators: [{
91
+ type: Directive,
92
+ args: [{ standalone: true, selector: 'ngtr-frame-stepper' }]
93
+ }], ctorParameters: () => [] });
94
+
95
+ const _quaternion = new Quaternion();
96
+ const _euler = new Euler();
97
+ const _vector3 = new Vector3();
98
+ const _object3d = new Object3D();
99
+ const _matrix4 = new Matrix4();
100
+ const _position = new Vector3();
101
+ const _rotation = new Quaternion();
102
+ const _scale = new Vector3();
103
+
104
+ /**
105
+ * Creates a proxy that will create a singleton instance of the given class
106
+ * when a property is accessed, and not before.
107
+ *
108
+ * @returns A proxy and a reset function, so that the instance can created again
109
+ */
110
+ const createSingletonProxy = (
111
+ /**
112
+ * A function that returns a new instance of the class
113
+ */
114
+ createInstance) => {
115
+ let instance;
116
+ const handler = {
117
+ get(target, prop) {
118
+ if (!instance) {
119
+ instance = createInstance();
120
+ }
121
+ return Reflect.get(instance, prop);
122
+ },
123
+ set(target, prop, value) {
124
+ if (!instance) {
125
+ instance = createInstance();
126
+ }
127
+ return Reflect.set(instance, prop, value);
128
+ },
129
+ };
130
+ const proxy = new Proxy({}, handler);
131
+ const reset = () => {
132
+ instance = undefined;
133
+ };
134
+ const set = (newInstance) => {
135
+ instance = newInstance;
136
+ };
137
+ /**
138
+ * Return the proxy and a reset function
139
+ */
140
+ return { proxy, reset, set };
141
+ };
142
+ function rapierQuaternionToQuaternion({ x, y, z, w }) {
143
+ return _quaternion.set(x, y, z, w);
144
+ }
145
+ function vector3ToRapierVector(v) {
146
+ if (Array.isArray(v)) {
147
+ return new Vector3$1(v[0], v[1], v[2]);
148
+ }
149
+ if (typeof v === 'number') {
150
+ return new Vector3$1(v, v, v);
151
+ }
152
+ const vector = v;
153
+ return new Vector3$1(vector.x, vector.y, vector.z);
154
+ }
155
+ function quaternionToRapierQuaternion(v) {
156
+ if (Array.isArray(v)) {
157
+ return new Quaternion$1(v[0], v[1], v[2], v[3]);
158
+ }
159
+ return new Quaternion$1(v.x, v.y, v.z, v.w);
160
+ }
161
+ function isChildOfMeshCollider(child) {
162
+ let flag = false;
163
+ child.traverseAncestors((a) => {
164
+ if (a.userData['ngtRapierType'] === 'MeshCollider')
165
+ flag = true;
166
+ });
167
+ return flag;
168
+ }
169
+ const autoColliderMap = {
170
+ cuboid: 'cuboid',
171
+ ball: 'ball',
172
+ hull: 'convexHull',
173
+ trimesh: 'trimesh',
174
+ };
175
+ function getColliderArgsFromGeometry(geometry, colliders) {
176
+ switch (colliders) {
177
+ case 'cuboid': {
178
+ geometry.computeBoundingBox();
179
+ const { boundingBox } = geometry;
180
+ const size = boundingBox.getSize(new Vector3());
181
+ return {
182
+ args: [size.x / 2, size.y / 2, size.z / 2],
183
+ offset: boundingBox.getCenter(new Vector3()),
184
+ };
185
+ }
186
+ case 'ball': {
187
+ geometry.computeBoundingSphere();
188
+ const { boundingSphere } = geometry;
189
+ const radius = boundingSphere.radius;
190
+ return {
191
+ args: [radius],
192
+ offset: boundingSphere.center,
193
+ };
194
+ }
195
+ case 'trimesh': {
196
+ const clonedGeometry = geometry.index ? geometry.clone() : mergeVertices(geometry);
197
+ return {
198
+ args: [clonedGeometry.attributes['position'].array, clonedGeometry.index?.array],
199
+ offset: new Vector3(),
200
+ };
201
+ }
202
+ case 'hull': {
203
+ const clonedGeometry = geometry.clone();
204
+ return {
205
+ args: [clonedGeometry.attributes['position'].array],
206
+ offset: new Vector3(),
207
+ };
208
+ }
209
+ }
210
+ return { args: [], offset: new Vector3() };
211
+ }
212
+ function createColliderOptions(object, options, ignoreMeshColliders = true) {
213
+ const childColliderOptions = [];
214
+ object.updateWorldMatrix(true, false);
215
+ const invertedParentMatrixWorld = object.matrixWorld.clone().invert();
216
+ const colliderFromChild = (child) => {
217
+ if (child.isMesh) {
218
+ if (ignoreMeshColliders && isChildOfMeshCollider(child))
219
+ return;
220
+ const worldScale = child.getWorldScale(_scale);
221
+ const shape = autoColliderMap[options.colliders || 'cuboid'];
222
+ child.updateWorldMatrix(true, false);
223
+ _matrix4.copy(child.matrixWorld).premultiply(invertedParentMatrixWorld).decompose(_position, _rotation, _scale);
224
+ const rotationEuler = new Euler().setFromQuaternion(_rotation, 'XYZ');
225
+ const { geometry } = child;
226
+ const { args, offset } = getColliderArgsFromGeometry(geometry, options.colliders || 'cuboid');
227
+ const { mass, linearDamping, angularDamping, canSleep, ccd, gravityScale, softCcdPrediction, ...rest } = options;
228
+ childColliderOptions.push({
229
+ colliderOptions: rest,
230
+ args,
231
+ shape,
232
+ rotation: [rotationEuler.x, rotationEuler.y, rotationEuler.z],
233
+ position: [
234
+ _position.x + offset.x * worldScale.x,
235
+ _position.y + offset.y * worldScale.y,
236
+ _position.z + offset.z * worldScale.z,
237
+ ],
238
+ scale: [worldScale.x, worldScale.y, worldScale.z],
239
+ });
240
+ }
241
+ };
242
+ if (options.includeInvisible) {
243
+ object.traverse(colliderFromChild);
244
+ }
245
+ else {
246
+ object.traverseVisible(colliderFromChild);
247
+ }
248
+ return childColliderOptions;
249
+ }
250
+
251
+ const defaultOptions$1 = {
252
+ gravity: [0, -9.81, 0],
253
+ allowedLinearError: 0.001,
254
+ numSolverIterations: 4,
255
+ numAdditionalFrictionIterations: 4,
256
+ numInternalPgsIterations: 1,
257
+ predictionDistance: 0.002,
258
+ minIslandSize: 128,
259
+ maxCcdSubsteps: 1,
260
+ erp: 0.8,
261
+ lengthUnit: 1,
262
+ colliders: 'cuboid',
263
+ updateLoop: 'follow',
264
+ interpolate: true,
265
+ paused: false,
266
+ timeStep: 1 / 60,
267
+ debug: false,
268
+ };
269
+ class NgtrPhysics {
270
+ options = input(defaultOptions$1, { transform: mergeInputs(defaultOptions$1) });
271
+ updatePriority = pick(this.options, 'updatePriority');
272
+ updateLoop = pick(this.options, 'updateLoop');
273
+ numSolverIterations = pick(this.options, 'numSolverIterations');
274
+ numAdditionalFrictionIterations = pick(this.options, 'numAdditionalFrictionIterations');
275
+ numInternalPgsIterations = pick(this.options, 'numInternalPgsIterations');
276
+ allowedLinearError = pick(this.options, 'allowedLinearError');
277
+ minIslandSize = pick(this.options, 'minIslandSize');
278
+ maxCcdSubsteps = pick(this.options, 'maxCcdSubsteps');
279
+ predictionDistance = pick(this.options, 'predictionDistance');
280
+ erp = pick(this.options, 'erp');
281
+ lengthUnit = pick(this.options, 'lengthUnit');
282
+ timeStep = pick(this.options, 'timeStep');
283
+ interpolate = pick(this.options, 'interpolate');
284
+ paused = pick(this.options, 'paused');
285
+ debug = pick(this.options, 'debug');
286
+ colliders = pick(this.options, 'colliders');
287
+ gravity = vector3(this.options, 'gravity');
288
+ store = injectStore();
289
+ destroyRef = inject(DestroyRef);
290
+ rapierConstruct = signal(null);
291
+ rapier = this.rapierConstruct.asReadonly();
292
+ ready = computed(() => !!this.rapier());
293
+ worldSingleton = computed(() => {
294
+ const rapier = this.rapier();
295
+ if (!rapier)
296
+ return null;
297
+ return createSingletonProxy(() => new rapier.World(untracked(this.gravity)));
298
+ });
299
+ rigidBodyStates = new Map();
300
+ colliderStates = new Map();
301
+ rigidBodyEvents = new Map();
302
+ colliderEvents = new Map();
303
+ beforeStepCallbacks = new Set();
304
+ afterStepCallbacks = new Set();
305
+ eventQueue = computed(() => {
306
+ const rapier = this.rapier();
307
+ if (!rapier)
308
+ return null;
309
+ return new EventQueue(false);
310
+ });
311
+ steppingState = { accumulator: 0, previousState: {} };
312
+ constructor() {
313
+ import('@dimforge/rapier3d-compat')
314
+ .then((rapier) => rapier.init().then(() => rapier))
315
+ .then(this.rapierConstruct.set.bind(this.rapierConstruct))
316
+ .catch((err) => {
317
+ console.error(`[NGT] Failed to load rapier3d-compat`, err);
318
+ return Promise.reject(err);
319
+ });
320
+ effect(() => {
321
+ this.updateWorldEffect();
322
+ });
323
+ this.destroyRef.onDestroy(() => {
324
+ const world = this.worldSingleton();
325
+ if (world) {
326
+ world.proxy.free();
327
+ world.reset();
328
+ }
329
+ });
330
+ }
331
+ step(delta) {
332
+ if (!this.paused()) {
333
+ this.internalStep(delta);
334
+ }
335
+ }
336
+ updateWorldEffect() {
337
+ const world = this.worldSingleton();
338
+ if (!world)
339
+ return;
340
+ world.proxy.gravity = this.gravity();
341
+ world.proxy.integrationParameters.numSolverIterations = this.numSolverIterations();
342
+ world.proxy.integrationParameters.numAdditionalFrictionIterations = this.numAdditionalFrictionIterations();
343
+ world.proxy.integrationParameters.numInternalPgsIterations = this.numInternalPgsIterations();
344
+ world.proxy.integrationParameters.normalizedAllowedLinearError = this.allowedLinearError();
345
+ world.proxy.integrationParameters.minIslandSize = this.minIslandSize();
346
+ world.proxy.integrationParameters.maxCcdSubsteps = this.maxCcdSubsteps();
347
+ world.proxy.integrationParameters.normalizedPredictionDistance = this.predictionDistance();
348
+ /**
349
+ * NOTE: we don't know if this is the correct way to set for contact_natural_frequency or not.
350
+ * but at least, it gets the `contact_erp` value to be very close with setting `erp`
351
+ */
352
+ world.proxy.integrationParameters.contact_natural_frequency = this.erp() * 1_000;
353
+ world.proxy.lengthUnit = this.lengthUnit();
354
+ }
355
+ internalStep(delta) {
356
+ const worldSingleton = this.worldSingleton();
357
+ if (!worldSingleton)
358
+ return;
359
+ const eventQueue = this.eventQueue();
360
+ if (!eventQueue)
361
+ return;
362
+ const world = worldSingleton.proxy;
363
+ const [timeStep, interpolate, paused] = [this.timeStep(), this.interpolate(), this.paused()];
364
+ /* Check if the timestep is supposed to be variable. We'll do this here
365
+ once so we don't have to string-check every frame. */
366
+ const timeStepVariable = timeStep === 'vary';
367
+ /**
368
+ * Fixed timeStep simulation progression
369
+ * @see https://gafferongames.com/post/fix_your_timestep/
370
+ */
371
+ const clampedDelta = MathUtils.clamp(delta, 0, 0.5);
372
+ const stepWorld = (innerDelta) => {
373
+ // Trigger beforeStep callbacks
374
+ this.beforeStepCallbacks.forEach((callback) => {
375
+ callback(world);
376
+ });
377
+ world.timestep = innerDelta;
378
+ world.step(eventQueue);
379
+ // Trigger afterStep callbacks
380
+ this.afterStepCallbacks.forEach((callback) => {
381
+ callback(world);
382
+ });
383
+ };
384
+ if (timeStepVariable) {
385
+ stepWorld(clampedDelta);
386
+ }
387
+ else {
388
+ // don't step time forwards if paused
389
+ // Increase accumulator
390
+ this.steppingState.accumulator += clampedDelta;
391
+ while (this.steppingState.accumulator >= timeStep) {
392
+ // Set up previous state
393
+ // needed for accurate interpolations if the world steps more than once
394
+ if (interpolate) {
395
+ this.steppingState.previousState = {};
396
+ world.forEachRigidBody((body) => {
397
+ this.steppingState.previousState[body.handle] = {
398
+ position: body.translation(),
399
+ rotation: body.rotation(),
400
+ };
401
+ });
402
+ }
403
+ stepWorld(timeStep);
404
+ this.steppingState.accumulator -= timeStep;
405
+ }
406
+ }
407
+ const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : this.steppingState.accumulator / timeStep;
408
+ // Update meshes
409
+ this.rigidBodyStates.forEach((state, handle) => {
410
+ const rigidBody = world.getRigidBody(handle);
411
+ const events = this.rigidBodyEvents.get(handle);
412
+ if (events?.onSleep || events?.onWake) {
413
+ if (rigidBody.isSleeping() && !state.isSleeping)
414
+ events?.onSleep?.();
415
+ if (!rigidBody.isSleeping() && state.isSleeping)
416
+ events?.onWake?.();
417
+ state.isSleeping = rigidBody.isSleeping();
418
+ }
419
+ if (!rigidBody || (rigidBody.isSleeping() && !('isInstancedMesh' in state.object)) || !state.setMatrix) {
420
+ return;
421
+ }
422
+ // New states
423
+ let t = rigidBody.translation();
424
+ let r = rigidBody.rotation();
425
+ let previousState = this.steppingState.previousState[handle];
426
+ if (previousState) {
427
+ // Get previous simulated world position
428
+ _matrix4
429
+ .compose(previousState.position, rapierQuaternionToQuaternion(previousState.rotation), state.scale)
430
+ .premultiply(state.invertedWorldMatrix)
431
+ .decompose(_position, _rotation, _scale);
432
+ // Apply previous tick position
433
+ if (state.meshType == 'mesh') {
434
+ state.object.position.copy(_position);
435
+ state.object.quaternion.copy(_rotation);
436
+ }
437
+ }
438
+ // Get new position
439
+ _matrix4
440
+ .compose(t, rapierQuaternionToQuaternion(r), state.scale)
441
+ .premultiply(state.invertedWorldMatrix)
442
+ .decompose(_position, _rotation, _scale);
443
+ if (state.meshType == 'instancedMesh') {
444
+ state.setMatrix(_matrix4);
445
+ }
446
+ else {
447
+ // Interpolate to new position
448
+ state.object.position.lerp(_position, interpolationAlpha);
449
+ state.object.quaternion.slerp(_rotation, interpolationAlpha);
450
+ }
451
+ });
452
+ eventQueue.drainCollisionEvents((handle1, handle2, started) => {
453
+ const source1 = this.getSourceFromColliderHandle(handle1);
454
+ const source2 = this.getSourceFromColliderHandle(handle2);
455
+ // Collision Events
456
+ if (!source1?.collider.object || !source2?.collider.object) {
457
+ return;
458
+ }
459
+ const collisionPayload1 = this.getCollisionPayloadFromSource(source1, source2);
460
+ const collisionPayload2 = this.getCollisionPayloadFromSource(source2, source1);
461
+ if (started) {
462
+ world.contactPair(source1.collider.object, source2.collider.object, (manifold, flipped) => {
463
+ /* RigidBody events */
464
+ source1.rigidBody.events?.onCollisionEnter?.({ ...collisionPayload1, manifold, flipped });
465
+ source2.rigidBody.events?.onCollisionEnter?.({ ...collisionPayload2, manifold, flipped });
466
+ /* Collider events */
467
+ source1.collider.events?.onCollisionEnter?.({ ...collisionPayload1, manifold, flipped });
468
+ source2.collider.events?.onCollisionEnter?.({ ...collisionPayload2, manifold, flipped });
469
+ });
470
+ }
471
+ else {
472
+ source1.rigidBody.events?.onCollisionExit?.(collisionPayload1);
473
+ source2.rigidBody.events?.onCollisionExit?.(collisionPayload2);
474
+ source1.collider.events?.onCollisionExit?.(collisionPayload1);
475
+ source2.collider.events?.onCollisionExit?.(collisionPayload2);
476
+ }
477
+ // Sensor Intersections
478
+ if (started) {
479
+ if (world.intersectionPair(source1.collider.object, source2.collider.object)) {
480
+ source1.rigidBody.events?.onIntersectionEnter?.(collisionPayload1);
481
+ source2.rigidBody.events?.onIntersectionEnter?.(collisionPayload2);
482
+ source1.collider.events?.onIntersectionEnter?.(collisionPayload1);
483
+ source2.collider.events?.onIntersectionEnter?.(collisionPayload2);
484
+ }
485
+ }
486
+ else {
487
+ source1.rigidBody.events?.onIntersectionExit?.(collisionPayload1);
488
+ source2.rigidBody.events?.onIntersectionExit?.(collisionPayload2);
489
+ source1.collider.events?.onIntersectionExit?.(collisionPayload1);
490
+ source2.collider.events?.onIntersectionExit?.(collisionPayload2);
491
+ }
492
+ });
493
+ eventQueue.drainContactForceEvents((event) => {
494
+ const source1 = this.getSourceFromColliderHandle(event.collider1());
495
+ const source2 = this.getSourceFromColliderHandle(event.collider2());
496
+ // Collision Events
497
+ if (!source1?.collider.object || !source2?.collider.object) {
498
+ return;
499
+ }
500
+ const collisionPayload1 = this.getCollisionPayloadFromSource(source1, source2);
501
+ const collisionPayload2 = this.getCollisionPayloadFromSource(source2, source1);
502
+ source1.rigidBody.events?.onContactForce?.({
503
+ ...collisionPayload1,
504
+ totalForce: event.totalForce(),
505
+ totalForceMagnitude: event.totalForceMagnitude(),
506
+ maxForceDirection: event.maxForceDirection(),
507
+ maxForceMagnitude: event.maxForceMagnitude(),
508
+ });
509
+ source2.rigidBody.events?.onContactForce?.({
510
+ ...collisionPayload2,
511
+ totalForce: event.totalForce(),
512
+ totalForceMagnitude: event.totalForceMagnitude(),
513
+ maxForceDirection: event.maxForceDirection(),
514
+ maxForceMagnitude: event.maxForceMagnitude(),
515
+ });
516
+ source1.collider.events?.onContactForce?.({
517
+ ...collisionPayload1,
518
+ totalForce: event.totalForce(),
519
+ totalForceMagnitude: event.totalForceMagnitude(),
520
+ maxForceDirection: event.maxForceDirection(),
521
+ maxForceMagnitude: event.maxForceMagnitude(),
522
+ });
523
+ source2.collider.events?.onContactForce?.({
524
+ ...collisionPayload2,
525
+ totalForce: event.totalForce(),
526
+ totalForceMagnitude: event.totalForceMagnitude(),
527
+ maxForceDirection: event.maxForceDirection(),
528
+ maxForceMagnitude: event.maxForceMagnitude(),
529
+ });
530
+ });
531
+ world.forEachActiveRigidBody(() => {
532
+ this.store.snapshot.invalidate();
533
+ });
534
+ }
535
+ getSourceFromColliderHandle(handle) {
536
+ const world = this.worldSingleton();
537
+ if (!world)
538
+ return;
539
+ const collider = world.proxy.getCollider(handle);
540
+ const colEvents = this.colliderEvents.get(handle);
541
+ const colliderState = this.colliderStates.get(handle);
542
+ const rigidBodyHandle = collider.parent()?.handle;
543
+ const rigidBody = rigidBodyHandle !== undefined ? world.proxy.getRigidBody(rigidBodyHandle) : undefined;
544
+ const rigidBodyEvents = rigidBody && rigidBodyHandle !== undefined ? this.rigidBodyEvents.get(rigidBodyHandle) : undefined;
545
+ const rigidBodyState = rigidBodyHandle !== undefined ? this.rigidBodyStates.get(rigidBodyHandle) : undefined;
546
+ return {
547
+ collider: { object: collider, events: colEvents, state: colliderState },
548
+ rigidBody: { object: rigidBody, events: rigidBodyEvents, state: rigidBodyState },
549
+ };
550
+ }
551
+ getCollisionPayloadFromSource(target, other) {
552
+ return {
553
+ target: {
554
+ rigidBody: target.rigidBody.object,
555
+ collider: target.collider.object,
556
+ colliderObject: target.collider.state?.object,
557
+ rigidBodyObject: target.rigidBody.state?.object,
558
+ },
559
+ other: {
560
+ rigidBody: other.rigidBody.object,
561
+ collider: other.collider.object,
562
+ colliderObject: other.collider.state?.object,
563
+ rigidBodyObject: other.rigidBody.state?.object,
564
+ },
565
+ };
566
+ }
567
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrPhysics, deps: [], target: i0.ɵɵFactoryTarget.Component });
568
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.0", type: NgtrPhysics, isStandalone: true, selector: "ngtr-physics", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
569
+ @if (debug()) {
570
+ <ngtr-debug [world]="worldSingleton()?.proxy" />
571
+ }
572
+ <ngtr-frame-stepper
573
+ [ready]="ready()"
574
+ [stepFn]="step.bind(this)"
575
+ [type]="updateLoop()"
576
+ [updatePriority]="updatePriority()"
577
+ />
578
+ <ng-content />
579
+ `, isInline: true, dependencies: [{ kind: "component", type: NgtrDebug, selector: "ngtr-debug", inputs: ["world"] }, { kind: "directive", type: NgtrFrameStepper, selector: "ngtr-frame-stepper", inputs: ["ready", "updatePriority", "stepFn", "type"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
580
+ }
581
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrPhysics, decorators: [{
582
+ type: Component,
583
+ args: [{
584
+ selector: 'ngtr-physics',
585
+ standalone: true,
586
+ template: `
587
+ @if (debug()) {
588
+ <ngtr-debug [world]="worldSingleton()?.proxy" />
589
+ }
590
+ <ngtr-frame-stepper
591
+ [ready]="ready()"
592
+ [stepFn]="step.bind(this)"
593
+ [type]="updateLoop()"
594
+ [updatePriority]="updatePriority()"
595
+ />
596
+ <ng-content />
597
+ `,
598
+ changeDetection: ChangeDetectionStrategy.OnPush,
599
+ imports: [NgtrDebug, NgtrFrameStepper],
600
+ }]
601
+ }], ctorParameters: () => [] });
602
+
603
+ const colliderDefaultOptions = {
604
+ contactSkin: 0,
605
+ };
606
+ class NgtrAnyCollider {
607
+ position = input([0, 0, 0]);
608
+ rotation = input([0, 0, 0]);
609
+ scale = input([1, 1, 1]);
610
+ quaternion = input([0, 0, 0, 1]);
611
+ userData = input({});
612
+ name = input();
613
+ options = input(colliderDefaultOptions, { transform: mergeInputs(rigidBodyDefaultOptions) });
614
+ // TODO: change this to input required when Angular allows setting hostDirective input
615
+ shape = model(undefined, { alias: 'ngtrCollider' });
616
+ args = model([]);
617
+ collisionEnter = output();
618
+ collisionExit = output();
619
+ intersectionEnter = output();
620
+ intersectionExit = output();
621
+ contactForce = output();
622
+ sensor = pick(this.options, 'sensor');
623
+ collisionGroups = pick(this.options, 'collisionGroups');
624
+ solverGroups = pick(this.options, 'solverGroups');
625
+ friction = pick(this.options, 'friction');
626
+ frictionCombineRule = pick(this.options, 'frictionCombineRule');
627
+ restitution = pick(this.options, 'restitution');
628
+ restitutionCombineRule = pick(this.options, 'restitutionCombineRule');
629
+ activeCollisionTypes = pick(this.options, 'activeCollisionTypes');
630
+ contactSkin = pick(this.options, 'contactSkin');
631
+ mass = pick(this.options, 'mass');
632
+ massProperties = pick(this.options, 'massProperties');
633
+ density = pick(this.options, 'density');
634
+ rigidBody = inject(NgtrRigidBody, { optional: true });
635
+ physics = inject(NgtrPhysics);
636
+ objectRef = inject(ElementRef);
637
+ scaledArgs = computed(() => {
638
+ const [shape, args] = [
639
+ this.shape(),
640
+ this.args(),
641
+ ];
642
+ const cloned = args.slice();
643
+ // Heightfield uses a vector
644
+ if (shape === 'heightfield') {
645
+ const s = cloned[3];
646
+ s.x *= this.worldScale.x;
647
+ s.y *= this.worldScale.y;
648
+ s.z *= this.worldScale.z;
649
+ return cloned;
650
+ }
651
+ // Trimesh and convex scale the vertices
652
+ if (shape === 'trimesh' || shape === 'convexHull') {
653
+ cloned[0] = this.scaleVertices(cloned[0], this.worldScale);
654
+ return cloned;
655
+ }
656
+ // prefill with some extra
657
+ const scaleArray = [this.worldScale.x, this.worldScale.y, this.worldScale.z, this.worldScale.x, this.worldScale.x];
658
+ return cloned.map((arg, index) => scaleArray[index] * arg);
659
+ });
660
+ collider = computed(() => {
661
+ const worldSingleton = this.physics.worldSingleton();
662
+ if (!worldSingleton)
663
+ return null;
664
+ const [shape, args, rigidBody] = [this.shape(), this.scaledArgs(), this.rigidBody?.rigidBody()];
665
+ // @ts-expect-error - we know the type of the data
666
+ const desc = ColliderDesc[shape](...args);
667
+ if (!desc)
668
+ return null;
669
+ return worldSingleton.proxy.createCollider(desc, rigidBody ?? undefined);
670
+ });
671
+ constructor() {
672
+ extend({ Object3D });
673
+ effect((onCleanup) => {
674
+ const cleanup = this.createColliderStateEffect();
675
+ onCleanup(() => cleanup?.());
676
+ });
677
+ effect((onCleanup) => {
678
+ const cleanup = this.createColliderEventsEffect();
679
+ onCleanup(() => cleanup?.());
680
+ });
681
+ effect(() => {
682
+ this.updateColliderEffect();
683
+ this.updateMassPropertiesEffect();
684
+ });
685
+ }
686
+ get worldScale() {
687
+ return this.objectRef.nativeElement.getWorldScale(new Vector3());
688
+ }
689
+ setShape(shape) {
690
+ this.shape.set(shape);
691
+ }
692
+ setArgs(args) {
693
+ this.args.set(args);
694
+ }
695
+ createColliderStateEffect() {
696
+ const collider = this.collider();
697
+ if (!collider)
698
+ return;
699
+ const worldSingleton = this.physics.worldSingleton();
700
+ if (!worldSingleton)
701
+ return;
702
+ const state = this.createColliderState(collider, this.objectRef.nativeElement, this.rigidBody?.objectRef.nativeElement);
703
+ this.physics.colliderStates.set(collider.handle, state);
704
+ return () => {
705
+ this.physics.colliderStates.delete(collider.handle);
706
+ if (worldSingleton.proxy.getCollider(collider.handle)) {
707
+ worldSingleton.proxy.removeCollider(collider, true);
708
+ }
709
+ };
710
+ }
711
+ createColliderEventsEffect() {
712
+ const collider = this.collider();
713
+ if (!collider)
714
+ return;
715
+ const worldSingleton = this.physics.worldSingleton();
716
+ if (!worldSingleton)
717
+ return;
718
+ const collisionEnter = getEmitter(this.collisionEnter);
719
+ const collisionExit = getEmitter(this.collisionExit);
720
+ const intersectionEnter = getEmitter(this.intersectionEnter);
721
+ const intersectionExit = getEmitter(this.intersectionExit);
722
+ const contactForce = getEmitter(this.contactForce);
723
+ const hasCollisionEvent = hasListener(this.collisionEnter, this.collisionExit, this.intersectionEnter, this.intersectionExit, this.rigidBody?.collisionEnter, this.rigidBody?.collisionExit, this.rigidBody?.intersectionEnter, this.rigidBody?.intersectionExit);
724
+ const hasContactForceEvent = hasListener(this.contactForce, this.rigidBody?.contactForce);
725
+ if (hasCollisionEvent && hasContactForceEvent) {
726
+ collider.setActiveEvents(ActiveEvents.COLLISION_EVENTS | ActiveEvents.CONTACT_FORCE_EVENTS);
727
+ }
728
+ else if (hasCollisionEvent) {
729
+ collider.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
730
+ }
731
+ else if (hasContactForceEvent) {
732
+ collider.setActiveEvents(ActiveEvents.CONTACT_FORCE_EVENTS);
733
+ }
734
+ this.physics.colliderEvents.set(collider.handle, {
735
+ onCollisionEnter: collisionEnter,
736
+ onCollisionExit: collisionExit,
737
+ onIntersectionEnter: intersectionEnter,
738
+ onIntersectionExit: intersectionExit,
739
+ onContactForce: contactForce,
740
+ });
741
+ return () => {
742
+ this.physics.colliderEvents.delete(collider.handle);
743
+ };
744
+ }
745
+ updateColliderEffect() {
746
+ const collider = this.collider();
747
+ if (!collider)
748
+ return;
749
+ const worldSingleton = this.physics.worldSingleton();
750
+ if (!worldSingleton)
751
+ return;
752
+ const state = this.physics.colliderStates.get(collider.handle);
753
+ if (!state)
754
+ return;
755
+ // Update collider position based on the object's position
756
+ const parentWorldScale = state.object.parent.getWorldScale(_vector3);
757
+ const parentInvertedWorldMatrix = state.worldParent?.matrixWorld.clone().invert();
758
+ state.object.updateWorldMatrix(true, false);
759
+ _matrix4.copy(state.object.matrixWorld);
760
+ if (parentInvertedWorldMatrix) {
761
+ _matrix4.premultiply(parentInvertedWorldMatrix);
762
+ }
763
+ _matrix4.decompose(_position, _rotation, _scale);
764
+ if (collider.parent()) {
765
+ collider.setTranslationWrtParent({
766
+ x: _position.x * parentWorldScale.x,
767
+ y: _position.y * parentWorldScale.y,
768
+ z: _position.z * parentWorldScale.z,
769
+ });
770
+ collider.setRotationWrtParent(_rotation);
771
+ }
772
+ else {
773
+ collider.setTranslation({
774
+ x: _position.x * parentWorldScale.x,
775
+ y: _position.y * parentWorldScale.y,
776
+ z: _position.z * parentWorldScale.z,
777
+ });
778
+ collider.setRotation(_rotation);
779
+ }
780
+ const [sensor, collisionGroups, solverGroups, friction, frictionCombineRule, restitution, restitutionCombineRule, activeCollisionTypes, contactSkin,] = [
781
+ this.sensor(),
782
+ this.collisionGroups(),
783
+ this.solverGroups(),
784
+ this.friction(),
785
+ this.frictionCombineRule(),
786
+ this.restitution(),
787
+ this.restitutionCombineRule(),
788
+ this.activeCollisionTypes(),
789
+ this.contactSkin(),
790
+ ];
791
+ if (sensor !== undefined)
792
+ collider.setSensor(sensor);
793
+ if (collisionGroups !== undefined)
794
+ collider.setCollisionGroups(collisionGroups);
795
+ if (solverGroups !== undefined)
796
+ collider.setSolverGroups(solverGroups);
797
+ if (friction !== undefined)
798
+ collider.setFriction(friction);
799
+ if (frictionCombineRule !== undefined)
800
+ collider.setFrictionCombineRule(frictionCombineRule);
801
+ if (restitution !== undefined)
802
+ collider.setRestitution(restitution);
803
+ if (restitutionCombineRule !== undefined)
804
+ collider.setRestitutionCombineRule(restitutionCombineRule);
805
+ if (activeCollisionTypes !== undefined)
806
+ collider.setActiveCollisionTypes(activeCollisionTypes);
807
+ if (contactSkin !== undefined)
808
+ collider.setContactSkin(contactSkin);
809
+ }
810
+ updateMassPropertiesEffect() {
811
+ const collider = this.collider();
812
+ if (!collider)
813
+ return;
814
+ const [mass, massProperties, density] = [this.mass(), this.massProperties(), this.density()];
815
+ if (density !== undefined) {
816
+ if (mass !== undefined || massProperties !== undefined) {
817
+ throw new Error('[NGT Rapier] Cannot set mass and massProperties along with density');
818
+ }
819
+ collider.setDensity(density);
820
+ return;
821
+ }
822
+ if (mass !== undefined) {
823
+ if (massProperties !== undefined) {
824
+ throw new Error('[NGT Rapier] Cannot set massProperties along with mass');
825
+ }
826
+ collider.setMass(mass);
827
+ return;
828
+ }
829
+ if (massProperties !== undefined) {
830
+ collider.setMassProperties(massProperties.mass, massProperties.centerOfMass, massProperties.principalAngularInertia, massProperties.angularInertiaLocalFrame);
831
+ return;
832
+ }
833
+ }
834
+ createColliderState(collider, object, rigidBodyObject) {
835
+ return { collider, worldParent: rigidBodyObject || undefined, object };
836
+ }
837
+ scaleVertices(vertices, scale) {
838
+ const scaledVerts = Array.from(vertices);
839
+ for (let i = 0; i < vertices.length / 3; i++) {
840
+ scaledVerts[i * 3] *= scale.x;
841
+ scaledVerts[i * 3 + 1] *= scale.y;
842
+ scaledVerts[i * 3 + 2] *= scale.z;
843
+ }
844
+ return scaledVerts;
845
+ }
846
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrAnyCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
847
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrAnyCollider, isStandalone: true, selector: "ngt-object3D[ngtrCollider]", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, quaternion: { classPropertyName: "quaternion", publicName: "quaternion", isSignal: true, isRequired: false, transformFunction: null }, userData: { classPropertyName: "userData", publicName: "userData", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "ngtrCollider", isSignal: true, isRequired: false, transformFunction: null }, args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { shape: "ngtrColliderChange", args: "argsChange", collisionEnter: "collisionEnter", collisionExit: "collisionExit", intersectionEnter: "intersectionEnter", intersectionExit: "intersectionExit", contactForce: "contactForce" }, host: { properties: { "position": "position()", "rotation": "rotation()", "scale": "scale()", "quaternion": "quaternion()", "userData": "userData()", "name": "name()" } }, ngImport: i0 });
848
+ }
849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrAnyCollider, decorators: [{
850
+ type: Directive,
851
+ args: [{
852
+ selector: 'ngt-object3D[ngtrCollider]',
853
+ standalone: true,
854
+ host: {
855
+ '[position]': 'position()',
856
+ '[rotation]': 'rotation()',
857
+ '[scale]': 'scale()',
858
+ '[quaternion]': 'quaternion()',
859
+ '[userData]': 'userData()',
860
+ '[name]': 'name()',
861
+ },
862
+ }]
863
+ }], ctorParameters: () => [] });
864
+ const RIGID_BODY_TYPE_MAP = {
865
+ fixed: 1,
866
+ dynamic: 0,
867
+ kinematicPosition: 2,
868
+ kinematicVelocity: 3,
869
+ };
870
+ const rigidBodyDefaultOptions = {
871
+ canSleep: true,
872
+ linearVelocity: [0, 0, 0],
873
+ angularVelocity: [0, 0, 0],
874
+ gravityScale: 1,
875
+ dominanceGroup: 0,
876
+ ccd: false,
877
+ softCcdPrediction: 0,
878
+ contactSkin: 0,
879
+ };
880
+ class NgtrRigidBody {
881
+ type = input('dynamic', {
882
+ alias: 'ngtrRigidBody',
883
+ transform: (value) => {
884
+ if (value === '' || value === undefined)
885
+ return 'dynamic';
886
+ return value;
887
+ },
888
+ });
889
+ position = input([0, 0, 0]);
890
+ rotation = input([0, 0, 0]);
891
+ scale = input([1, 1, 1]);
892
+ quaternion = input([0, 0, 0, 1]);
893
+ userData = input({});
894
+ options = input(rigidBodyDefaultOptions, { transform: mergeInputs(rigidBodyDefaultOptions) });
895
+ wake = output();
896
+ sleep = output();
897
+ collisionEnter = output();
898
+ collisionExit = output();
899
+ intersectionEnter = output();
900
+ intersectionExit = output();
901
+ contactForce = output();
902
+ canSleep = pick(this.options, 'canSleep');
903
+ colliders = pick(this.options, 'colliders');
904
+ transformState = pick(this.options, 'transformState');
905
+ gravityScale = pick(this.options, 'gravityScale');
906
+ dominanceGroup = pick(this.options, 'dominanceGroup');
907
+ ccd = pick(this.options, 'ccd');
908
+ softCcdPrediction = pick(this.options, 'softCcdPrediction');
909
+ additionalSolverIterations = pick(this.options, 'additionalSolverIterations');
910
+ linearDamping = pick(this.options, 'linearDamping');
911
+ angularDamping = pick(this.options, 'angularDamping');
912
+ lockRotations = pick(this.options, 'lockRotations');
913
+ lockTranslations = pick(this.options, 'lockTranslations');
914
+ enabledRotations = pick(this.options, 'enabledRotations');
915
+ enabledTranslations = pick(this.options, 'enabledTranslations');
916
+ angularVelocity = pick(this.options, 'angularVelocity');
917
+ linearVelocity = pick(this.options, 'linearVelocity');
918
+ objectRef = inject(ElementRef);
919
+ physics = inject(NgtrPhysics);
920
+ bodyType = computed(() => RIGID_BODY_TYPE_MAP[this.type()]);
921
+ bodyDesc = computed(() => {
922
+ const [canSleep, bodyType] = [this.canSleep(), untracked(this.bodyType), this.colliders()];
923
+ return new RigidBodyDesc(bodyType).setCanSleep(canSleep);
924
+ });
925
+ rigidBody = computed(() => {
926
+ const worldSingleton = this.physics.worldSingleton();
927
+ if (!worldSingleton)
928
+ return null;
929
+ return worldSingleton.proxy.createRigidBody(this.bodyDesc());
930
+ });
931
+ childColliderOptions = computed(() => {
932
+ const colliders = this.colliders();
933
+ // if self colliders is false explicitly, disable auto colliders for this object entirely.
934
+ if (colliders === false)
935
+ return [];
936
+ const physicsColliders = this.physics.colliders();
937
+ // if physics colliders is false explicitly, disable auto colliders for this object entirely.
938
+ if (physicsColliders === false)
939
+ return [];
940
+ const options = this.options();
941
+ // if colliders on object is not set, use physics colliders
942
+ if (!options.colliders)
943
+ options.colliders = physicsColliders;
944
+ const objectLocalState = getLocalState(this.objectRef.nativeElement);
945
+ // track object's children
946
+ objectLocalState?.nonObjects();
947
+ return createColliderOptions(this.objectRef.nativeElement, options, true);
948
+ });
949
+ constructor() {
950
+ extend({ Object3D });
951
+ effect((onCleanup) => {
952
+ const cleanup = this.createRigidBodyStateEffect();
953
+ onCleanup(() => cleanup?.());
954
+ });
955
+ effect((onCleanup) => {
956
+ const cleanup = this.createRigidBodyEventsEffect();
957
+ onCleanup(() => cleanup?.());
958
+ });
959
+ effect(() => {
960
+ this.updateRigidBodyEffect();
961
+ });
962
+ }
963
+ createRigidBodyStateEffect() {
964
+ const worldSingleton = this.physics.worldSingleton();
965
+ if (!worldSingleton)
966
+ return;
967
+ const body = this.rigidBody();
968
+ if (!body)
969
+ return;
970
+ const transformState = untracked(this.transformState);
971
+ const state = this.createRigidBodyState(body, this.objectRef.nativeElement);
972
+ this.physics.rigidBodyStates.set(body.handle, transformState ? transformState(state) : state);
973
+ return () => {
974
+ this.physics.rigidBodyStates.delete(body.handle);
975
+ if (worldSingleton.proxy.getRigidBody(body.handle)) {
976
+ worldSingleton.proxy.removeRigidBody(body);
977
+ }
978
+ };
979
+ }
980
+ createRigidBodyEventsEffect() {
981
+ const worldSingleton = this.physics.worldSingleton();
982
+ if (!worldSingleton)
983
+ return;
984
+ const body = this.rigidBody();
985
+ if (!body)
986
+ return;
987
+ const wake = getEmitter(this.wake);
988
+ const sleep = getEmitter(this.sleep);
989
+ const collisionEnter = getEmitter(this.collisionEnter);
990
+ const collisionExit = getEmitter(this.collisionExit);
991
+ const intersectionEnter = getEmitter(this.intersectionEnter);
992
+ const intersectionExit = getEmitter(this.intersectionExit);
993
+ const contactForce = getEmitter(this.contactForce);
994
+ this.physics.rigidBodyEvents.set(body.handle, {
995
+ onWake: wake,
996
+ onSleep: sleep,
997
+ onCollisionEnter: collisionEnter,
998
+ onCollisionExit: collisionExit,
999
+ onIntersectionEnter: intersectionEnter,
1000
+ onIntersectionExit: intersectionExit,
1001
+ onContactForce: contactForce,
1002
+ });
1003
+ return () => {
1004
+ this.physics.rigidBodyEvents.delete(body.handle);
1005
+ };
1006
+ }
1007
+ updateRigidBodyEffect() {
1008
+ const worldSingleton = this.physics.worldSingleton();
1009
+ if (!worldSingleton)
1010
+ return;
1011
+ const body = this.rigidBody();
1012
+ if (!body)
1013
+ return;
1014
+ const state = this.physics.rigidBodyStates.get(body.handle);
1015
+ if (!state)
1016
+ return;
1017
+ state.object.updateWorldMatrix(true, false);
1018
+ _matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
1019
+ body.setTranslation(_position, true);
1020
+ body.setRotation(_rotation, true);
1021
+ const [gravityScale, additionalSolverIterations, linearDamping, angularDamping, lockRotations, lockTranslations, enabledRotations, enabledTranslations, angularVelocity, linearVelocity, ccd, softCcdPrediction, dominanceGroup, userData, bodyType,] = [
1022
+ this.gravityScale(),
1023
+ this.additionalSolverIterations(),
1024
+ this.linearDamping(),
1025
+ this.angularDamping(),
1026
+ this.lockRotations(),
1027
+ this.lockTranslations(),
1028
+ this.enabledRotations(),
1029
+ this.enabledTranslations(),
1030
+ this.angularVelocity(),
1031
+ this.linearVelocity(),
1032
+ this.ccd(),
1033
+ this.softCcdPrediction(),
1034
+ this.dominanceGroup(),
1035
+ this.userData(),
1036
+ this.bodyType(),
1037
+ ];
1038
+ body.setGravityScale(gravityScale, true);
1039
+ if (additionalSolverIterations !== undefined)
1040
+ body.setAdditionalSolverIterations(additionalSolverIterations);
1041
+ if (linearDamping !== undefined)
1042
+ body.setLinearDamping(linearDamping);
1043
+ if (angularDamping !== undefined)
1044
+ body.setAngularDamping(angularDamping);
1045
+ body.setDominanceGroup(dominanceGroup);
1046
+ if (enabledRotations !== undefined)
1047
+ body.setEnabledRotations(...enabledRotations, true);
1048
+ if (enabledTranslations !== undefined)
1049
+ body.setEnabledTranslations(...enabledTranslations, true);
1050
+ if (lockRotations !== undefined)
1051
+ body.lockRotations(lockRotations, true);
1052
+ if (lockTranslations !== undefined)
1053
+ body.lockTranslations(lockTranslations, true);
1054
+ body.setAngvel({ x: angularVelocity[0], y: angularVelocity[1], z: angularVelocity[2] }, true);
1055
+ body.setLinvel({ x: linearVelocity[0], y: linearVelocity[1], z: linearVelocity[2] }, true);
1056
+ body.enableCcd(ccd);
1057
+ body.setSoftCcdPrediction(softCcdPrediction);
1058
+ if (userData !== undefined)
1059
+ body.userData = userData;
1060
+ if (bodyType !== body.bodyType())
1061
+ body.setBodyType(bodyType, true);
1062
+ }
1063
+ createRigidBodyState(rigidBody, object, setMatrix, getMatrix, worldScale, meshType = 'mesh') {
1064
+ object.updateWorldMatrix(true, false);
1065
+ const invertedWorldMatrix = object.parent.matrixWorld.clone().invert();
1066
+ return {
1067
+ object,
1068
+ rigidBody,
1069
+ invertedWorldMatrix,
1070
+ setMatrix: setMatrix
1071
+ ? setMatrix
1072
+ : (matrix) => {
1073
+ object.matrix.copy(matrix);
1074
+ },
1075
+ getMatrix: getMatrix ? getMatrix : (matrix) => matrix.copy(object.matrix),
1076
+ scale: worldScale || object.getWorldScale(_scale).clone(),
1077
+ isSleeping: false,
1078
+ meshType,
1079
+ };
1080
+ }
1081
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRigidBody, deps: [], target: i0.ɵɵFactoryTarget.Component });
1082
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.0", type: NgtrRigidBody, isStandalone: true, selector: "ngt-object3D[ngtrRigidBody]", inputs: { type: { classPropertyName: "type", publicName: "ngtrRigidBody", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, quaternion: { classPropertyName: "quaternion", publicName: "quaternion", isSignal: true, isRequired: false, transformFunction: null }, userData: { classPropertyName: "userData", publicName: "userData", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wake: "wake", sleep: "sleep", collisionEnter: "collisionEnter", collisionExit: "collisionExit", intersectionEnter: "intersectionEnter", intersectionExit: "intersectionExit", contactForce: "contactForce" }, host: { properties: { "position": "position()", "rotation": "rotation()", "scale": "scale()", "quaternion": "quaternion()", "userData": "userData()" } }, exportAs: ["rigidBody"], ngImport: i0, template: `
1083
+ <ng-content />
1084
+ @for (childColliderOption of childColliderOptions(); track $index) {
1085
+ <ngt-object3D
1086
+ [ngtrCollider]="childColliderOption.shape"
1087
+ [args]="childColliderOption.args"
1088
+ [position]="childColliderOption.position"
1089
+ [rotation]="childColliderOption.rotation"
1090
+ [scale]="childColliderOption.scale"
1091
+ [name]="objectRef.nativeElement.name + '-collider-' + $index"
1092
+ [options]="childColliderOption.colliderOptions"
1093
+ />
1094
+ }
1095
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgtrAnyCollider, selector: "ngt-object3D[ngtrCollider]", inputs: ["position", "rotation", "scale", "quaternion", "userData", "name", "options", "ngtrCollider", "args"], outputs: ["ngtrColliderChange", "argsChange", "collisionEnter", "collisionExit", "intersectionEnter", "intersectionExit", "contactForce"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1096
+ }
1097
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRigidBody, decorators: [{
1098
+ type: Component,
1099
+ args: [{
1100
+ selector: 'ngt-object3D[ngtrRigidBody]',
1101
+ exportAs: 'rigidBody',
1102
+ standalone: true,
1103
+ template: `
1104
+ <ng-content />
1105
+ @for (childColliderOption of childColliderOptions(); track $index) {
1106
+ <ngt-object3D
1107
+ [ngtrCollider]="childColliderOption.shape"
1108
+ [args]="childColliderOption.args"
1109
+ [position]="childColliderOption.position"
1110
+ [rotation]="childColliderOption.rotation"
1111
+ [scale]="childColliderOption.scale"
1112
+ [name]="objectRef.nativeElement.name + '-collider-' + $index"
1113
+ [options]="childColliderOption.colliderOptions"
1114
+ />
1115
+ }
1116
+ `,
1117
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
1118
+ changeDetection: ChangeDetectionStrategy.OnPush,
1119
+ host: {
1120
+ '[position]': 'position()',
1121
+ '[rotation]': 'rotation()',
1122
+ '[scale]': 'scale()',
1123
+ '[quaternion]': 'quaternion()',
1124
+ '[userData]': 'userData()',
1125
+ },
1126
+ imports: [NgtrAnyCollider],
1127
+ }]
1128
+ }], ctorParameters: () => [] });
1129
+
1130
+ const ANY_COLLIDER_HOST_DIRECTIVE = {
1131
+ directive: NgtrAnyCollider,
1132
+ inputs: ['options', 'name', 'scale', 'position', 'quaternion', 'rotation', 'userData'],
1133
+ outputs: ['collisionEnter', 'collisionExit', 'intersectionEnter', 'intersectionExit', 'contactForce'],
1134
+ };
1135
+ class NgtrCuboidCollider {
1136
+ args = input.required();
1137
+ constructor() {
1138
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1139
+ anyCollider.setShape('cuboid');
1140
+ effect(() => {
1141
+ const args = this.args();
1142
+ untracked(() => {
1143
+ anyCollider.setArgs(args);
1144
+ });
1145
+ });
1146
+ }
1147
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrCuboidCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1148
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrCuboidCollider, isStandalone: true, selector: "ngt-object3D[ngtrCuboidCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1149
+ }
1150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrCuboidCollider, decorators: [{
1151
+ type: Directive,
1152
+ args: [{
1153
+ selector: 'ngt-object3D[ngtrCuboidCollider]',
1154
+ standalone: true,
1155
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1156
+ }]
1157
+ }], ctorParameters: () => [] });
1158
+ class NgtrCapsuleCollider {
1159
+ args = input.required();
1160
+ constructor() {
1161
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1162
+ anyCollider.setShape('capsule');
1163
+ effect(() => {
1164
+ const args = this.args();
1165
+ untracked(() => {
1166
+ anyCollider.setArgs(args);
1167
+ });
1168
+ });
1169
+ }
1170
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrCapsuleCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1171
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrCapsuleCollider, isStandalone: true, selector: "ngt-object3D[ngtrCapsuleCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1172
+ }
1173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrCapsuleCollider, decorators: [{
1174
+ type: Directive,
1175
+ args: [{
1176
+ selector: 'ngt-object3D[ngtrCapsuleCollider]',
1177
+ standalone: true,
1178
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1179
+ }]
1180
+ }], ctorParameters: () => [] });
1181
+ class NgtrBallCollider {
1182
+ args = input.required();
1183
+ constructor() {
1184
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1185
+ anyCollider.setShape('ball');
1186
+ effect(() => {
1187
+ const args = this.args();
1188
+ untracked(() => {
1189
+ anyCollider.setArgs(args);
1190
+ });
1191
+ });
1192
+ }
1193
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrBallCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1194
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrBallCollider, isStandalone: true, selector: "ngt-object3D[ngtrBallCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1195
+ }
1196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrBallCollider, decorators: [{
1197
+ type: Directive,
1198
+ args: [{
1199
+ selector: 'ngt-object3D[ngtrBallCollider]',
1200
+ standalone: true,
1201
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1202
+ }]
1203
+ }], ctorParameters: () => [] });
1204
+ class NgtrConvexHullCollider {
1205
+ args = input.required();
1206
+ constructor() {
1207
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1208
+ anyCollider.setShape('roundConvexHull');
1209
+ effect(() => {
1210
+ const args = this.args();
1211
+ untracked(() => {
1212
+ anyCollider.setArgs(args);
1213
+ });
1214
+ });
1215
+ }
1216
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrConvexHullCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1217
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrConvexHullCollider, isStandalone: true, selector: "ngt-object3D[ngtrConvexHullCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1218
+ }
1219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrConvexHullCollider, decorators: [{
1220
+ type: Directive,
1221
+ args: [{
1222
+ selector: 'ngt-object3D[ngtrConvexHullCollider]',
1223
+ standalone: true,
1224
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1225
+ }]
1226
+ }], ctorParameters: () => [] });
1227
+ class NgtrHeightfieldCollider {
1228
+ args = input.required();
1229
+ constructor() {
1230
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1231
+ anyCollider.setShape('heightfield');
1232
+ effect(() => {
1233
+ const args = this.args();
1234
+ untracked(() => {
1235
+ anyCollider.setArgs(args);
1236
+ });
1237
+ });
1238
+ }
1239
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrHeightfieldCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1240
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrHeightfieldCollider, isStandalone: true, selector: "ngt-object3D[ngtrHeightfieldCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1241
+ }
1242
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrHeightfieldCollider, decorators: [{
1243
+ type: Directive,
1244
+ args: [{
1245
+ selector: 'ngt-object3D[ngtrHeightfieldCollider]',
1246
+ standalone: true,
1247
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1248
+ }]
1249
+ }], ctorParameters: () => [] });
1250
+ class NgtrTrimeshCollider {
1251
+ args = input.required();
1252
+ constructor() {
1253
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1254
+ anyCollider.setShape('trimesh');
1255
+ effect(() => {
1256
+ const args = this.args();
1257
+ untracked(() => {
1258
+ anyCollider.setArgs(args);
1259
+ });
1260
+ });
1261
+ }
1262
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrTrimeshCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1263
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrTrimeshCollider, isStandalone: true, selector: "ngt-object3D[ngtrTrimeshCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1264
+ }
1265
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrTrimeshCollider, decorators: [{
1266
+ type: Directive,
1267
+ args: [{
1268
+ selector: 'ngt-object3D[ngtrTrimeshCollider]',
1269
+ standalone: true,
1270
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1271
+ }]
1272
+ }], ctorParameters: () => [] });
1273
+ class NgtrPolylineCollider {
1274
+ args = input.required();
1275
+ constructor() {
1276
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1277
+ anyCollider.setShape('polyline');
1278
+ effect(() => {
1279
+ const args = this.args();
1280
+ untracked(() => {
1281
+ anyCollider.setArgs(args);
1282
+ });
1283
+ });
1284
+ }
1285
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrPolylineCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1286
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrPolylineCollider, isStandalone: true, selector: "ngt-object3D[ngtrPolylineCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1287
+ }
1288
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrPolylineCollider, decorators: [{
1289
+ type: Directive,
1290
+ args: [{
1291
+ selector: 'ngt-object3D[ngtrPolylineCollider]',
1292
+ standalone: true,
1293
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1294
+ }]
1295
+ }], ctorParameters: () => [] });
1296
+ class NgtrRoundCuboidCollider {
1297
+ args = input.required();
1298
+ constructor() {
1299
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1300
+ anyCollider.setShape('roundCuboid');
1301
+ effect(() => {
1302
+ const args = this.args();
1303
+ untracked(() => {
1304
+ anyCollider.setArgs(args);
1305
+ });
1306
+ });
1307
+ }
1308
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundCuboidCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1309
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrRoundCuboidCollider, isStandalone: true, selector: "ngt-object3D[ngtrRoundCuboidCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1310
+ }
1311
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundCuboidCollider, decorators: [{
1312
+ type: Directive,
1313
+ args: [{
1314
+ selector: 'ngt-object3D[ngtrRoundCuboidCollider]',
1315
+ standalone: true,
1316
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1317
+ }]
1318
+ }], ctorParameters: () => [] });
1319
+ class NgtrCylinderCollider {
1320
+ args = input.required();
1321
+ constructor() {
1322
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1323
+ anyCollider.setShape('cylinder');
1324
+ effect(() => {
1325
+ const args = this.args();
1326
+ untracked(() => {
1327
+ anyCollider.setArgs(args);
1328
+ });
1329
+ });
1330
+ }
1331
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrCylinderCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1332
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrCylinderCollider, isStandalone: true, selector: "ngt-object3D[ngtrCylinderCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1333
+ }
1334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrCylinderCollider, decorators: [{
1335
+ type: Directive,
1336
+ args: [{
1337
+ selector: 'ngt-object3D[ngtrCylinderCollider]',
1338
+ standalone: true,
1339
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1340
+ }]
1341
+ }], ctorParameters: () => [] });
1342
+ class NgtrRoundCylinderCollider {
1343
+ args = input.required();
1344
+ constructor() {
1345
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1346
+ anyCollider.setShape('roundCylinder');
1347
+ effect(() => {
1348
+ const args = this.args();
1349
+ untracked(() => {
1350
+ anyCollider.setArgs(args);
1351
+ });
1352
+ });
1353
+ }
1354
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundCylinderCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1355
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrRoundCylinderCollider, isStandalone: true, selector: "ngt-object3D[ngtrRoundCylinderCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1356
+ }
1357
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundCylinderCollider, decorators: [{
1358
+ type: Directive,
1359
+ args: [{
1360
+ selector: 'ngt-object3D[ngtrRoundCylinderCollider]',
1361
+ standalone: true,
1362
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1363
+ }]
1364
+ }], ctorParameters: () => [] });
1365
+ class NgtrConeCollider {
1366
+ args = input.required();
1367
+ constructor() {
1368
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1369
+ anyCollider.setShape('cone');
1370
+ effect(() => {
1371
+ const args = this.args();
1372
+ untracked(() => {
1373
+ anyCollider.setArgs(args);
1374
+ });
1375
+ });
1376
+ }
1377
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrConeCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1378
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrConeCollider, isStandalone: true, selector: "ngt-object3D[ngtrConeCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1379
+ }
1380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrConeCollider, decorators: [{
1381
+ type: Directive,
1382
+ args: [{
1383
+ selector: 'ngt-object3D[ngtrConeCollider]',
1384
+ standalone: true,
1385
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1386
+ }]
1387
+ }], ctorParameters: () => [] });
1388
+ class NgtrRoundConeCollider {
1389
+ args = input.required();
1390
+ constructor() {
1391
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1392
+ anyCollider.setShape('roundCone');
1393
+ effect(() => {
1394
+ const args = this.args();
1395
+ untracked(() => {
1396
+ anyCollider.setArgs(args);
1397
+ });
1398
+ });
1399
+ }
1400
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundConeCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1401
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrRoundConeCollider, isStandalone: true, selector: "ngt-object3D[ngtrRoundConeCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1402
+ }
1403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundConeCollider, decorators: [{
1404
+ type: Directive,
1405
+ args: [{
1406
+ selector: 'ngt-object3D[ngtrRoundConeCollider]',
1407
+ standalone: true,
1408
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1409
+ }]
1410
+ }], ctorParameters: () => [] });
1411
+ class NgtrConvexMeshCollider {
1412
+ args = input.required();
1413
+ constructor() {
1414
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1415
+ anyCollider.setShape('convexMesh');
1416
+ effect(() => {
1417
+ const args = this.args();
1418
+ untracked(() => {
1419
+ anyCollider.setArgs(args);
1420
+ });
1421
+ });
1422
+ }
1423
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrConvexMeshCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1424
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrConvexMeshCollider, isStandalone: true, selector: "ngt-object3D[ngtrConvexMeshCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1425
+ }
1426
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrConvexMeshCollider, decorators: [{
1427
+ type: Directive,
1428
+ args: [{
1429
+ selector: 'ngt-object3D[ngtrConvexMeshCollider]',
1430
+ standalone: true,
1431
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1432
+ }]
1433
+ }], ctorParameters: () => [] });
1434
+ class NgtrRoundConvexHullCollider {
1435
+ args = input.required();
1436
+ constructor() {
1437
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1438
+ anyCollider.setShape('roundConvexHull');
1439
+ effect(() => {
1440
+ const args = this.args();
1441
+ untracked(() => {
1442
+ anyCollider.setArgs(args);
1443
+ });
1444
+ });
1445
+ }
1446
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundConvexHullCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1447
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrRoundConvexHullCollider, isStandalone: true, selector: "ngt-object3D[ngtrRoundConvexHullCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1448
+ }
1449
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundConvexHullCollider, decorators: [{
1450
+ type: Directive,
1451
+ args: [{
1452
+ selector: 'ngt-object3D[ngtrRoundConvexHullCollider]',
1453
+ standalone: true,
1454
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1455
+ }]
1456
+ }], ctorParameters: () => [] });
1457
+ class NgtrRoundConvexMeshCollider {
1458
+ args = input.required();
1459
+ constructor() {
1460
+ const anyCollider = inject(NgtrAnyCollider, { host: true });
1461
+ anyCollider.setShape('roundConvexMesh');
1462
+ effect(() => {
1463
+ const args = this.args();
1464
+ untracked(() => {
1465
+ anyCollider.setArgs(args);
1466
+ });
1467
+ });
1468
+ }
1469
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundConvexMeshCollider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1470
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.0", type: NgtrRoundConvexMeshCollider, isStandalone: true, selector: "ngt-object3D[ngtrRoundConvexMeshCollider]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, hostDirectives: [{ directive: NgtrAnyCollider, inputs: ["options", "options", "name", "name", "scale", "scale", "position", "position", "quaternion", "quaternion", "rotation", "rotation", "userData", "userData"], outputs: ["collisionEnter", "collisionEnter", "collisionExit", "collisionExit", "intersectionEnter", "intersectionEnter", "intersectionExit", "intersectionExit", "contactForce", "contactForce"] }], ngImport: i0 });
1471
+ }
1472
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrRoundConvexMeshCollider, decorators: [{
1473
+ type: Directive,
1474
+ args: [{
1475
+ selector: 'ngt-object3D[ngtrRoundConvexMeshCollider]',
1476
+ standalone: true,
1477
+ hostDirectives: [ANY_COLLIDER_HOST_DIRECTIVE],
1478
+ }]
1479
+ }], ctorParameters: () => [] });
1480
+
1481
+ const defaultOptions = rigidBodyDefaultOptions;
1482
+ class NgtrInstancedRigidBodies {
1483
+ position = input([0, 0, 0]);
1484
+ rotation = input([0, 0, 0]);
1485
+ scale = input([1, 1, 1]);
1486
+ quaternion = input([0, 0, 0, 1]);
1487
+ userData = input({});
1488
+ instances = input([], {
1489
+ alias: 'ngtrInstancedRigidBodies',
1490
+ transform: (value) => {
1491
+ if (value === '')
1492
+ return [];
1493
+ return value;
1494
+ },
1495
+ });
1496
+ options = input(defaultOptions, { transform: mergeInputs(defaultOptions) });
1497
+ instanceWrapperRef = viewChild.required('instanceWrapper');
1498
+ rigidBodyRefs = viewChildren(NgtrRigidBody);
1499
+ physics = inject(NgtrPhysics);
1500
+ objectRef = inject(ElementRef);
1501
+ colliders = pick(this.options, 'colliders');
1502
+ instancedMesh = computed(() => {
1503
+ const instanceWrapper = this.instanceWrapperRef().nativeElement;
1504
+ if (!instanceWrapper)
1505
+ return null;
1506
+ const localState = getLocalState(instanceWrapper);
1507
+ if (!localState)
1508
+ return null;
1509
+ // track object's children
1510
+ localState.objects();
1511
+ const firstChild = instanceWrapper.children[0];
1512
+ if (!firstChild || !firstChild.isInstancedMesh)
1513
+ return null;
1514
+ return firstChild;
1515
+ });
1516
+ instancesOptions = computed(() => {
1517
+ const [instances, options, instancedMesh] = [this.instances(), untracked(this.options), this.instancedMesh()];
1518
+ if (!instancedMesh)
1519
+ return [];
1520
+ return instances.map((instance, index) => ({
1521
+ ...instance,
1522
+ options: {
1523
+ ...options,
1524
+ ...(instance.options || {}),
1525
+ transformState: (state) => {
1526
+ return {
1527
+ ...state,
1528
+ getMatrix: (matrix) => {
1529
+ instancedMesh.getMatrixAt(index, matrix);
1530
+ return matrix;
1531
+ },
1532
+ setMatrix: (matrix) => {
1533
+ instancedMesh.setMatrixAt(index, matrix);
1534
+ instancedMesh.instanceMatrix.needsUpdate = true;
1535
+ },
1536
+ meshType: 'instancedMesh',
1537
+ };
1538
+ },
1539
+ },
1540
+ key: `${instance.key}-${index}` + `${instancedMesh?.uuid || ''}`,
1541
+ }));
1542
+ });
1543
+ childColliderOptions = computed(() => {
1544
+ const colliders = this.colliders();
1545
+ // if self colliders is false explicitly, disable auto colliders for this object entirely.
1546
+ if (colliders === false)
1547
+ return [];
1548
+ const physicsColliders = this.physics.colliders();
1549
+ // if physics colliders is false explicitly, disable auto colliders for this object entirely.
1550
+ if (physicsColliders === false)
1551
+ return [];
1552
+ const options = this.options();
1553
+ // if colliders on object is not set, use physics colliders
1554
+ if (!options.colliders)
1555
+ options.colliders = physicsColliders;
1556
+ const objectLocalState = getLocalState(this.objectRef.nativeElement);
1557
+ // track object's children
1558
+ objectLocalState?.nonObjects();
1559
+ return createColliderOptions(this.objectRef.nativeElement, options);
1560
+ });
1561
+ constructor() {
1562
+ extend({ Object3D });
1563
+ effect(() => {
1564
+ this.setInstancedMeshMatrixEffect();
1565
+ });
1566
+ }
1567
+ setInstancedMeshMatrixEffect() {
1568
+ const instancedMesh = this.instancedMesh();
1569
+ if (!instancedMesh)
1570
+ return;
1571
+ instancedMesh.instanceMatrix.setUsage(DynamicDrawUsage);
1572
+ }
1573
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrInstancedRigidBodies, deps: [], target: i0.ɵɵFactoryTarget.Component });
1574
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.0", type: NgtrInstancedRigidBodies, isStandalone: true, selector: "ngt-object3D[ngtrInstancedRigidBodies]", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, quaternion: { classPropertyName: "quaternion", publicName: "quaternion", isSignal: true, isRequired: false, transformFunction: null }, userData: { classPropertyName: "userData", publicName: "userData", isSignal: true, isRequired: false, transformFunction: null }, instances: { classPropertyName: "instances", publicName: "ngtrInstancedRigidBodies", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "position": "position()", "rotation": "rotation()", "scale": "scale()", "quaternion": "quaternion()", "userData": "userData()" } }, viewQueries: [{ propertyName: "instanceWrapperRef", first: true, predicate: ["instanceWrapper"], descendants: true, isSignal: true }, { propertyName: "rigidBodyRefs", predicate: NgtrRigidBody, descendants: true, isSignal: true }], exportAs: ["instancedRigidBodies"], ngImport: i0, template: `
1575
+ <ngt-object3D #instanceWrapper>
1576
+ <ng-content />
1577
+ </ngt-object3D>
1578
+
1579
+ @for (instance of instancesOptions(); track instance.key) {
1580
+ <ngt-object3D
1581
+ [ngtrRigidBody]="instance.type"
1582
+ [options]="instance.options"
1583
+ [position]="instance.position"
1584
+ [rotation]="instance.rotation"
1585
+ [scale]="instance.scale"
1586
+ [quaternion]="instance.quaternion"
1587
+ [userData]="instance.userData"
1588
+ [name]="instance.key + '-instanced-rigid-body-' + $index"
1589
+ >
1590
+ <ng-content select="[data-colliders]" />
1591
+
1592
+ @for (childColliderOption of childColliderOptions(); track $index) {
1593
+ <ngt-object3D
1594
+ [ngtrCollider]="childColliderOption.shape"
1595
+ [args]="childColliderOption.args"
1596
+ [position]="childColliderOption.position"
1597
+ [rotation]="childColliderOption.rotation"
1598
+ [scale]="childColliderOption.scale"
1599
+ [name]="objectRef.nativeElement.name + '-instanced-collider-' + $index"
1600
+ [options]="childColliderOption.colliderOptions"
1601
+ />
1602
+ }
1603
+ </ngt-object3D>
1604
+ }
1605
+ `, isInline: true, dependencies: [{ kind: "component", type: NgtrRigidBody, selector: "ngt-object3D[ngtrRigidBody]", inputs: ["ngtrRigidBody", "position", "rotation", "scale", "quaternion", "userData", "options"], outputs: ["wake", "sleep", "collisionEnter", "collisionExit", "intersectionEnter", "intersectionExit", "contactForce"], exportAs: ["rigidBody"] }, { kind: "directive", type: NgtrAnyCollider, selector: "ngt-object3D[ngtrCollider]", inputs: ["position", "rotation", "scale", "quaternion", "userData", "name", "options", "ngtrCollider", "args"], outputs: ["ngtrColliderChange", "argsChange", "collisionEnter", "collisionExit", "intersectionEnter", "intersectionExit", "contactForce"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1606
+ }
1607
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrInstancedRigidBodies, decorators: [{
1608
+ type: Component,
1609
+ args: [{
1610
+ selector: 'ngt-object3D[ngtrInstancedRigidBodies]',
1611
+ exportAs: 'instancedRigidBodies',
1612
+ standalone: true,
1613
+ template: `
1614
+ <ngt-object3D #instanceWrapper>
1615
+ <ng-content />
1616
+ </ngt-object3D>
1617
+
1618
+ @for (instance of instancesOptions(); track instance.key) {
1619
+ <ngt-object3D
1620
+ [ngtrRigidBody]="instance.type"
1621
+ [options]="instance.options"
1622
+ [position]="instance.position"
1623
+ [rotation]="instance.rotation"
1624
+ [scale]="instance.scale"
1625
+ [quaternion]="instance.quaternion"
1626
+ [userData]="instance.userData"
1627
+ [name]="instance.key + '-instanced-rigid-body-' + $index"
1628
+ >
1629
+ <ng-content select="[data-colliders]" />
1630
+
1631
+ @for (childColliderOption of childColliderOptions(); track $index) {
1632
+ <ngt-object3D
1633
+ [ngtrCollider]="childColliderOption.shape"
1634
+ [args]="childColliderOption.args"
1635
+ [position]="childColliderOption.position"
1636
+ [rotation]="childColliderOption.rotation"
1637
+ [scale]="childColliderOption.scale"
1638
+ [name]="objectRef.nativeElement.name + '-instanced-collider-' + $index"
1639
+ [options]="childColliderOption.colliderOptions"
1640
+ />
1641
+ }
1642
+ </ngt-object3D>
1643
+ }
1644
+ `,
1645
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
1646
+ changeDetection: ChangeDetectionStrategy.OnPush,
1647
+ host: {
1648
+ '[position]': 'position()',
1649
+ '[rotation]': 'rotation()',
1650
+ '[scale]': 'scale()',
1651
+ '[quaternion]': 'quaternion()',
1652
+ '[userData]': 'userData()',
1653
+ },
1654
+ imports: [NgtrRigidBody, NgtrRigidBody, NgtrAnyCollider],
1655
+ }]
1656
+ }], ctorParameters: () => [] });
1657
+
1658
+ function injectImpulseJoint(bodyA, bodyB, { injector, data }) {
1659
+ return assertInjector(injectImpulseJoint, injector, () => {
1660
+ const physics = inject(NgtrPhysics);
1661
+ const newJoint = computed(() => {
1662
+ const worldSingleton = physics.worldSingleton();
1663
+ if (!worldSingleton)
1664
+ return null;
1665
+ const a = typeof bodyA === 'function' ? resolveRef(bodyA()) : resolveRef(bodyA);
1666
+ const b = typeof bodyB === 'function' ? resolveRef(bodyB()) : resolveRef(bodyB);
1667
+ if (!a || !b)
1668
+ return null;
1669
+ const jointData = typeof data === 'function' ? data() : data;
1670
+ if (!jointData)
1671
+ return null;
1672
+ return worldSingleton.proxy.createImpulseJoint(jointData, a, b, true);
1673
+ });
1674
+ effect((onCleanup) => {
1675
+ const worldSingleton = physics.worldSingleton();
1676
+ if (!worldSingleton)
1677
+ return;
1678
+ const joint = newJoint();
1679
+ if (!joint)
1680
+ return;
1681
+ onCleanup(() => {
1682
+ if (worldSingleton.proxy.getImpulseJoint(joint.handle)) {
1683
+ worldSingleton.proxy.removeImpulseJoint(joint, true);
1684
+ }
1685
+ });
1686
+ });
1687
+ return newJoint;
1688
+ });
1689
+ }
1690
+ function createJoint(jointDataFn) {
1691
+ return function _injectJoint(bodyA, bodyB, { injector, data }) {
1692
+ return assertInjector(_injectJoint, injector, () => {
1693
+ const physics = inject(NgtrPhysics);
1694
+ const jointData = computed(() => {
1695
+ const rapier = physics.rapier();
1696
+ if (!rapier)
1697
+ return null;
1698
+ return jointDataFn(rapier, data);
1699
+ });
1700
+ return injectImpulseJoint(bodyA, bodyB, { injector, data: jointData });
1701
+ });
1702
+ };
1703
+ }
1704
+ /**
1705
+ * A fixed joint ensures that two rigid-bodies don't move relative to each other.
1706
+ * Fixed joints are characterized by one local frame (represented by an isometry) on each rigid-body.
1707
+ * The fixed-joint makes these frames coincide in world-space.
1708
+ *
1709
+ * @category Hooks - Joints
1710
+ */
1711
+ const injectFixedJoint = createJoint((rapier, data) => rapier.JointData.fixed(vector3ToRapierVector(data.body1Anchor), quaternionToRapierQuaternion(data.body1LocalFrame), vector3ToRapierVector(data.body2Anchor), quaternionToRapierQuaternion(data.body2LocalFrame)));
1712
+ /**
1713
+ * The spherical joint ensures that two points on the local-spaces of two rigid-bodies always coincide (it prevents any relative
1714
+ * translational motion at this points). This is typically used to simulate ragdolls arms, pendulums, etc.
1715
+ * They are characterized by one local anchor on each rigid-body. Each anchor represents the location of the
1716
+ * points that need to coincide on the local-space of each rigid-body.
1717
+ *
1718
+ * @category Hooks - Joints
1719
+ */
1720
+ const injectSphericalJoint = createJoint((rapier, data) => rapier.JointData.spherical(vector3ToRapierVector(data.body1Anchor), vector3ToRapierVector(data.body2Anchor)));
1721
+ /**
1722
+ * The revolute joint prevents any relative movement between two rigid-bodies, except for relative
1723
+ * rotations along one axis. This is typically used to simulate wheels, fans, etc.
1724
+ * They are characterized by one local anchor as well as one local axis on each rigid-body.
1725
+ *
1726
+ * @category Hooks - Joints
1727
+ */
1728
+ const injectRevoluteJoint = createJoint((rapier, data) => {
1729
+ const jointData = rapier.JointData.revolute(vector3ToRapierVector(data.body1Anchor), vector3ToRapierVector(data.body2Anchor), vector3ToRapierVector(data.axis));
1730
+ if (data.limits) {
1731
+ jointData.limitsEnabled = true;
1732
+ jointData.limits = data.limits;
1733
+ }
1734
+ return jointData;
1735
+ });
1736
+ /**
1737
+ * The prismatic joint prevents any relative movement between two rigid-bodies, except for relative translations along one axis.
1738
+ * It is characterized by one local anchor as well as one local axis on each rigid-body. In 3D, an optional
1739
+ * local tangent axis can be specified for each rigid-body.
1740
+ *
1741
+ * @category Hooks - Joints
1742
+ */
1743
+ const injectPrismaticJoint = createJoint((rapier, data) => {
1744
+ const jointData = rapier.JointData.prismatic(vector3ToRapierVector(data.body1Anchor), vector3ToRapierVector(data.body2Anchor), vector3ToRapierVector(data.axis));
1745
+ if (data.limits) {
1746
+ jointData.limitsEnabled = true;
1747
+ jointData.limits = data.limits;
1748
+ }
1749
+ return jointData;
1750
+ });
1751
+ /**
1752
+ * The rope joint limits the max distance between two bodies.
1753
+ * @category Hooks - Joints
1754
+ */
1755
+ const injectRopeJoint = createJoint((rapier, data) => rapier.JointData.rope(data.length, vector3ToRapierVector(data.body1Anchor), vector3ToRapierVector(data.body2Anchor)));
1756
+ /**
1757
+ * The spring joint applies a force proportional to the distance between two objects.
1758
+ * @category Hooks - Joints
1759
+ */
1760
+ const injectSpringJoint = createJoint((rapier, data) => {
1761
+ return rapier.JointData.spring(data.restLength, data.stiffness, data.damping, vector3ToRapierVector(data.body1Anchor), vector3ToRapierVector(data.body2Anchor));
1762
+ });
1763
+
1764
+ class NgtrMeshCollider {
1765
+ colliders = input.required({ alias: 'ngtrMeshCollider' });
1766
+ objectRef = inject(ElementRef);
1767
+ rigidBody = inject(NgtrRigidBody);
1768
+ physics = inject(NgtrPhysics);
1769
+ childColliderOptions = computed(() => {
1770
+ const rigidBodyOptions = this.rigidBody.options();
1771
+ rigidBodyOptions.colliders = this.colliders();
1772
+ const objectLocalState = getLocalState(this.objectRef.nativeElement);
1773
+ // track object's children
1774
+ objectLocalState?.nonObjects();
1775
+ objectLocalState?.objects();
1776
+ return createColliderOptions(this.objectRef.nativeElement, rigidBodyOptions, false);
1777
+ });
1778
+ constructor() {
1779
+ extend({ Object3D });
1780
+ if (!this.objectRef.nativeElement.userData) {
1781
+ this.objectRef.nativeElement.userData = {};
1782
+ }
1783
+ this.objectRef.nativeElement.userData['ngtrRapierType'] = 'MeshCollider';
1784
+ }
1785
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrMeshCollider, deps: [], target: i0.ɵɵFactoryTarget.Component });
1786
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.0", type: NgtrMeshCollider, isStandalone: true, selector: "ngt-object3D[ngtrMeshCollider]", inputs: { colliders: { classPropertyName: "colliders", publicName: "ngtrMeshCollider", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
1787
+ <ng-content />
1788
+ @for (childColliderOption of childColliderOptions(); track $index) {
1789
+ <ngt-object3D
1790
+ [ngtrCollider]="childColliderOption.shape"
1791
+ [args]="childColliderOption.args"
1792
+ [position]="childColliderOption.position"
1793
+ [rotation]="childColliderOption.rotation"
1794
+ [scale]="childColliderOption.scale"
1795
+ [name]="objectRef.nativeElement.name + '-mesh-collider-' + $index"
1796
+ [options]="childColliderOption.colliderOptions"
1797
+ />
1798
+ }
1799
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgtrAnyCollider, selector: "ngt-object3D[ngtrCollider]", inputs: ["position", "rotation", "scale", "quaternion", "userData", "name", "options", "ngtrCollider", "args"], outputs: ["ngtrColliderChange", "argsChange", "collisionEnter", "collisionExit", "intersectionEnter", "intersectionExit", "contactForce"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1800
+ }
1801
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: NgtrMeshCollider, decorators: [{
1802
+ type: Component,
1803
+ args: [{
1804
+ selector: 'ngt-object3D[ngtrMeshCollider]',
1805
+ standalone: true,
1806
+ template: `
1807
+ <ng-content />
1808
+ @for (childColliderOption of childColliderOptions(); track $index) {
1809
+ <ngt-object3D
1810
+ [ngtrCollider]="childColliderOption.shape"
1811
+ [args]="childColliderOption.args"
1812
+ [position]="childColliderOption.position"
1813
+ [rotation]="childColliderOption.rotation"
1814
+ [scale]="childColliderOption.scale"
1815
+ [name]="objectRef.nativeElement.name + '-mesh-collider-' + $index"
1816
+ [options]="childColliderOption.colliderOptions"
1817
+ />
1818
+ }
1819
+ `,
1820
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
1821
+ changeDetection: ChangeDetectionStrategy.OnPush,
1822
+ imports: [NgtrAnyCollider],
1823
+ }]
1824
+ }], ctorParameters: () => [] });
1825
+
1826
+ /**
1827
+ * Generated bundle index. Do not edit.
1828
+ */
1829
+
1830
+ export { NgtrAnyCollider, NgtrBallCollider, NgtrCapsuleCollider, NgtrConeCollider, NgtrConvexHullCollider, NgtrConvexMeshCollider, NgtrCuboidCollider, NgtrCylinderCollider, NgtrHeightfieldCollider, NgtrInstancedRigidBodies, NgtrMeshCollider, NgtrPhysics, NgtrPolylineCollider, NgtrRigidBody, NgtrRoundConeCollider, NgtrRoundConvexHullCollider, NgtrRoundConvexMeshCollider, NgtrRoundCuboidCollider, NgtrRoundCylinderCollider, NgtrTrimeshCollider, injectFixedJoint, injectPrismaticJoint, injectRevoluteJoint, injectRopeJoint, injectSphericalJoint, injectSpringJoint, rigidBodyDefaultOptions };
1831
+ //# sourceMappingURL=angular-three-rapier.mjs.map