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.
- package/README.md +7 -0
- package/esm2022/angular-three-rapier.mjs +5 -0
- package/esm2022/index.mjs +7 -0
- package/esm2022/lib/colliders.mjs +355 -0
- package/esm2022/lib/debug.mjs +46 -0
- package/esm2022/lib/frame-stepper.mjs +46 -0
- package/esm2022/lib/instanced-rigid-bodies.mjs +185 -0
- package/esm2022/lib/joints.mjs +111 -0
- package/esm2022/lib/mesh-collider.mjs +69 -0
- package/esm2022/lib/physics.mjs +362 -0
- package/esm2022/lib/rigid-body.mjs +536 -0
- package/esm2022/lib/shared.mjs +10 -0
- package/esm2022/lib/types.mjs +2 -0
- package/esm2022/lib/utils.mjs +151 -0
- package/fesm2022/angular-three-rapier.mjs +1831 -0
- package/fesm2022/angular-three-rapier.mjs.map +1 -0
- package/index.d.ts +7 -0
- package/lib/colliders.d.ts +93 -0
- package/lib/debug.d.ts +9 -0
- package/lib/frame-stepper.d.ts +10 -0
- package/lib/instanced-rigid-bodies.d.ts +46 -0
- package/lib/joints.d.ts +64 -0
- package/lib/mesh-collider.d.ts +23 -0
- package/lib/physics.d.ts +50 -0
- package/lib/rigid-body.d.ts +104 -0
- package/lib/shared.d.ts +9 -0
- package/lib/types.d.ts +500 -0
- package/lib/utils.d.ts +26 -0
- package/package.json +51 -0
|
@@ -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
|