Skip to content

Commit c7e6e45

Browse files
committed
feat(rapier/addons): add attractor in addons secondary entry point
1 parent 8ba12fe commit c7e6e45

File tree

13 files changed

+559
-1
lines changed

13 files changed

+559
-1
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, signal, viewChild } from '@angular/core';
2+
import { ActiveCollisionTypes } from '@dimforge/rapier3d-compat';
3+
import { injectBeforeRender } from 'angular-three';
4+
import { NgtrRigidBody } from 'angular-three-rapier';
5+
import * as THREE from 'three';
6+
7+
@Component({
8+
selector: 'app-ball',
9+
template: `
10+
<ngt-object3D
11+
rigidBody="kinematicPosition"
12+
[options]="{ colliders: 'ball', activeCollisionTypes }"
13+
(collisionEnter)="color.set('green')"
14+
(collisionExit)="color.set('blue')"
15+
>
16+
<ngt-mesh>
17+
<ngt-sphere-geometry />
18+
<ngt-mesh-standard-material [color]="color()" />
19+
</ngt-mesh>
20+
</ngt-object3D>
21+
`,
22+
changeDetection: ChangeDetectionStrategy.OnPush,
23+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
24+
imports: [NgtrRigidBody],
25+
})
26+
export class Ball {
27+
protected readonly activeCollisionTypes = ActiveCollisionTypes.DEFAULT | ActiveCollisionTypes.KINEMATIC_FIXED;
28+
29+
private rigidBodyRef = viewChild.required(NgtrRigidBody);
30+
31+
protected color = signal('blue');
32+
33+
constructor() {
34+
injectBeforeRender(({ clock }) => {
35+
const rb = this.rigidBodyRef().rigidBody();
36+
if (!rb) return;
37+
38+
rb.setTranslation({ x: Math.sin(clock.elapsedTime) * 3, y: 0, z: 0 }, true);
39+
});
40+
}
41+
}
42+
43+
@Component({
44+
selector: 'app-wall',
45+
template: `
46+
<ngt-object3D rigidBody="fixed" [options]="{ colliders: 'cuboid' }">
47+
<ngt-mesh [geometry]="boxGeometry">
48+
<!-- TODO: rigidBody does not work with *args -->
49+
<!-- <ngt-box-geometry *args="[0.5, 5, 2]" />-->
50+
<ngt-mesh-standard-material transparent [opacity]="0.5" />
51+
</ngt-mesh>
52+
</ngt-object3D>
53+
`,
54+
changeDetection: ChangeDetectionStrategy.OnPush,
55+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
56+
imports: [NgtrRigidBody],
57+
})
58+
export class Wall {
59+
protected boxGeometry = new THREE.BoxGeometry(0.5, 5, 2);
60+
}
61+
62+
@Component({
63+
selector: 'app-active-collision-types-rapier',
64+
template: `
65+
<ngt-group>
66+
<app-ball />
67+
<app-wall />
68+
</ngt-group>
69+
`,
70+
changeDetection: ChangeDetectionStrategy.OnPush,
71+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
72+
imports: [Ball, Wall],
73+
})
74+
export default class ActiveCollisionTypesExample {}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2+
import { NgtArgs, NgtVector3 } from 'angular-three';
3+
import { NgtrInstancedRigidBodies, NgtrInteractionGroups, NgtrRigidBody } from 'angular-three-rapier';
4+
import { NgtrAttractor } from 'angular-three-rapier/addons';
5+
import { NgtsHTML } from 'angular-three-soba/misc';
6+
import * as THREE from 'three';
7+
8+
@Component({
9+
selector: 'app-attractors-rapier',
10+
template: `
11+
<ngt-group>
12+
<ngt-object3D [instancedRigidBodies]="instances" [options]="{ colliders: 'ball' }">
13+
<ngt-instanced-mesh *args="[ballGeometry, undefined, 100]" castShadow>
14+
<!-- // TODO: rigidBody does not work with *args-->
15+
<!-- <ngt-sphere-geometry *args="[1]" />-->
16+
<ngt-mesh-physical-material [roughness]="0.5" [metalness]="0.5" color="green" />
17+
</ngt-instanced-mesh>
18+
</ngt-object3D>
19+
20+
<ngt-object3D
21+
rigidBody
22+
[options]="{ colliders: 'ball' }"
23+
[position]="[-21, 50, 0]"
24+
[interactionGroups]="[1]"
25+
>
26+
<ngt-mesh>
27+
<ngt-sphere-geometry />
28+
</ngt-mesh>
29+
<ngts-html>
30+
<div htmlContent>Nested Attractor</div>
31+
</ngts-html>
32+
<ngt-object3D attractor [interactionGroups]="[1, 2]" [options]="{ strength: 4 }" />
33+
</ngt-object3D>
34+
35+
<ngt-group [position.x]="20">
36+
<ngt-object3D attractor [options]="{ strength: -2, range: 20 }" />
37+
<ngts-html>
38+
<div htmlContent>Repeller</div>
39+
</ngts-html>
40+
</ngt-group>
41+
42+
<ngt-group [position.x]="-20">
43+
<ngt-object3D attractor [options]="{ strength: 10, range: 20 }" />
44+
<ngts-html>
45+
<div htmlContent>Attractor</div>
46+
</ngts-html>
47+
</ngt-group>
48+
</ngt-group>
49+
`,
50+
51+
changeDetection: ChangeDetectionStrategy.OnPush,
52+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
53+
imports: [NgtrInstancedRigidBodies, NgtArgs, NgtrRigidBody, NgtrInteractionGroups, NgtsHTML, NgtrAttractor],
54+
})
55+
export default class AttractorsExample {
56+
protected instances = Array.from({ length: 100 }, (_, index) => ({
57+
key: index,
58+
position: [Math.floor(Math.random() * 30), Math.random() * 30 * 0.5, 0] as NgtVector3,
59+
}));
60+
protected ballGeometry = new THREE.SphereGeometry(1);
61+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
computed,
5+
CUSTOM_ELEMENTS_SCHEMA,
6+
Directive,
7+
inject,
8+
input,
9+
} from '@angular/core';
10+
import { injectBeforeRender, NgtVector3 } from 'angular-three';
11+
import { injectRevoluteJoint, NgtrRigidBody } from 'angular-three-rapier';
12+
import * as THREE from 'three';
13+
14+
@Directive({ selector: '[rigidBody][wheel]' })
15+
export class WheelJoint {
16+
body = input.required<NgtrRigidBody>({ alias: 'wheel' });
17+
bodyAnchor = input.required<NgtVector3>({ alias: 'position' });
18+
19+
private wheel = inject(NgtrRigidBody, { host: true });
20+
private rigidBody = computed(() => this.body().rigidBody());
21+
22+
constructor() {
23+
const revoluteJoint = injectRevoluteJoint(this.rigidBody, this.wheel.rigidBody, {
24+
data: () => ({ body1Anchor: this.bodyAnchor(), body2Anchor: [0, 0, 0], axis: [0, 0, 1] }),
25+
});
26+
27+
injectBeforeRender(() => {
28+
const joint = revoluteJoint();
29+
if (!joint) return;
30+
joint.configureMotorVelocity(20, 10);
31+
});
32+
}
33+
}
34+
35+
@Component({
36+
selector: 'app-car-rapier',
37+
template: `
38+
<ngt-group>
39+
<ngt-object3D #body="rigidBody" rigidBody="dynamic" [options]="{ colliders: 'cuboid' }">
40+
<ngt-mesh castShadow receiveShadow name="chassis" [scale]="[6, 1, 1.9]">
41+
<ngt-box-geometry />
42+
<ngt-mesh-standard-material color="red" />
43+
</ngt-mesh>
44+
</ngt-object3D>
45+
46+
@for (position of wheelPositions; track $index) {
47+
<ngt-object3D
48+
rigidBody="dynamic"
49+
[position]="position"
50+
[options]="{ colliders: 'hull' }"
51+
[wheel]="body"
52+
>
53+
<ngt-mesh castShadow receiveShadow [rotation.x]="Math.PI / 2" [geometry]="cylinderGeometry">
54+
<!-- TODO: rigidBody does not work with *args-->
55+
<!-- <ngt-cylinder-geometry *args="[1, 1, 1, 32]" />-->
56+
<ngt-mesh-standard-material color="grey" />
57+
</ngt-mesh>
58+
</ngt-object3D>
59+
}
60+
</ngt-group>
61+
`,
62+
changeDetection: ChangeDetectionStrategy.OnPush,
63+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
64+
imports: [NgtrRigidBody, WheelJoint],
65+
})
66+
export default class CarExample {
67+
protected cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 32);
68+
protected wheelPositions: NgtVector3[] = [
69+
[-3, 0, 2],
70+
[-3, 0, -2],
71+
[3, 0, 2],
72+
[3, 0, -2],
73+
];
74+
protected readonly Math = Math;
75+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, viewChild } from '@angular/core';
2+
import { injectBeforeRender } from 'angular-three';
3+
import { NgtrRigidBody } from 'angular-three-rapier';
4+
import * as THREE from 'three';
5+
6+
@Component({
7+
selector: 'app-ball',
8+
template: `
9+
<ngt-object3D rigidBody [options]="{ colliders: 'ball' }">
10+
<ngt-mesh castShadow receiveShadow>
11+
<ngt-sphere-geometry />
12+
<ngt-mesh-physical-material color="red" />
13+
</ngt-mesh>
14+
</ngt-object3D>
15+
`,
16+
changeDetection: ChangeDetectionStrategy.OnPush,
17+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
18+
imports: [NgtrRigidBody],
19+
})
20+
export class Ball {
21+
private rigidBodyRef = viewChild.required(NgtrRigidBody);
22+
23+
constructor() {
24+
injectBeforeRender(() => {
25+
const rigidBody = this.rigidBodyRef().rigidBody();
26+
if (!rigidBody) return;
27+
if (rigidBody.translation().y < -10) {
28+
rigidBody.setTranslation({ x: Math.random() * 2, y: 20, z: 0 }, true);
29+
rigidBody.setLinvel({ x: 0, y: 0, z: 0 }, true);
30+
}
31+
});
32+
}
33+
}
34+
35+
@Component({
36+
selector: 'app-kinematics-rapier',
37+
template: `
38+
<ngt-group>
39+
<app-ball />
40+
<app-ball />
41+
<app-ball />
42+
<app-ball />
43+
<app-ball />
44+
45+
<ngt-object3D
46+
#torus="rigidBody"
47+
rigidBody="kinematicPosition"
48+
[options]="{ colliders: 'trimesh', restitution: 1 }"
49+
[position]="[0, 2, 0]"
50+
>
51+
<ngt-mesh castShadow receiveShadow [scale]="5">
52+
<ngt-torus-geometry />
53+
<ngt-mesh-physical-material />
54+
</ngt-mesh>
55+
</ngt-object3D>
56+
57+
<ngt-object3D
58+
#platform="rigidBody"
59+
rigidBody="kinematicPosition"
60+
[options]="{ colliders: 'cuboid' }"
61+
[position]="[0, -8, 0]"
62+
>
63+
<ngt-mesh castShadow receiveShadow [geometry]="boxGeometry">
64+
<!-- TODO: rigidBody does not work with *args-->
65+
<!-- <ngt-box-geometry *args="[40, 1, 40]" />-->
66+
<ngt-mesh-physical-material />
67+
</ngt-mesh>
68+
</ngt-object3D>
69+
</ngt-group>
70+
`,
71+
changeDetection: ChangeDetectionStrategy.OnPush,
72+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
73+
imports: [Ball, NgtrRigidBody],
74+
})
75+
export default class KinematicsExample {
76+
protected boxGeometry = new THREE.BoxGeometry(40, 1, 40);
77+
78+
private torusRef = viewChild.required('torus', { read: NgtrRigidBody });
79+
private platformRef = viewChild.required('platform', { read: NgtrRigidBody });
80+
81+
constructor() {
82+
injectBeforeRender(() => {
83+
const now = performance.now();
84+
85+
const torus = this.torusRef().rigidBody();
86+
if (torus) {
87+
const euler = new THREE.Euler(now / 1000, 0, 0);
88+
torus.setNextKinematicRotation(new THREE.Quaternion().setFromEuler(euler));
89+
}
90+
91+
const platform = this.platformRef().rigidBody();
92+
if (platform) {
93+
platform.setNextKinematicTranslation({
94+
x: Math.sin(now / 100),
95+
y: -8 + Math.sin(now / 50) * 0.5,
96+
z: 0,
97+
});
98+
}
99+
});
100+
}
101+
}

apps/examples/src/app/rapier/rapier.routes.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,22 @@ const routes: Routes = [
4545
path: 'contact-force-events',
4646
loadComponent: () => import('./contact-force-events/contact-force-events'),
4747
},
48+
{
49+
path: 'active-collision-types',
50+
loadComponent: () => import('./active-collision-types/active-collision-types'),
51+
},
52+
{
53+
path: 'attractors',
54+
loadComponent: () => import('./attractors/attractors'),
55+
},
56+
{
57+
path: 'kinematics',
58+
loadComponent: () => import('./kinematics/kinematics'),
59+
},
60+
{
61+
path: 'car',
62+
loadComponent: () => import('./car/car'),
63+
},
4864
{
4965
path: '',
5066
redirectTo: 'basic',

apps/examples/src/app/rapier/rapier.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,9 @@ export default class Rapier {
4343
'all-colliders',
4444
'sensors',
4545
'contact-force-events',
46+
'active-collision-types',
47+
'attractors',
48+
'kinematics',
49+
'car',
4650
];
4751
}

libs/rapier/addons/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# angular-three-rapier/addons
2+
3+
Secondary entry point of `angular-three-rapier`. It can be used by importing from `angular-three-rapier/addons`.

libs/rapier/addons/ng-package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"lib": {
3+
"entryFile": "src/index.ts"
4+
}
5+
}

libs/rapier/addons/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/attractor';

0 commit comments

Comments
 (0)