Skip to content

Commit 558f4fd

Browse files
committed
docs(examples): update rapier routes
1 parent c1c0ffc commit 558f4fd

File tree

19 files changed

+975
-33
lines changed

19 files changed

+975
-33
lines changed

apps/kitchen-sink-new/src/app/app.routes.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ export const appRoutes: Route[] = [
1919
// loadChildren: () => import('./soba/soba.routes'),
2020
// title: 'Soba - Angular Three Demo',
2121
// },
22-
// {
23-
// path: 'rapier',
24-
// loadComponent: () => import('./rapier/rapier'),
25-
// loadChildren: () => import('./rapier/rapier.routes'),
26-
// title: 'Rapier - Angular Three Demo',
27-
// },
22+
{
23+
path: 'rapier',
24+
loadComponent: () => import('./rapier/rapier'),
25+
loadChildren: () => import('./rapier/rapier.routes'),
26+
title: 'Rapier - Angular Three Demo',
27+
},
2828
{
2929
path: 'misc',
3030
loadComponent: () => import('./misc/misc'),
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
2+
import { injectBeforeRender } from 'angular-three';
3+
import { NgtrCuboidCollider, NgtrPhysics, NgtrRigidBody } from 'angular-three-rapier';
4+
import { NgtsPerspectiveCamera } from 'angular-three-soba/cameras';
5+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
6+
7+
@Component({
8+
template: `
9+
<ngts-perspective-camera [options]="{ makeDefault: true, position: [5, 5, 5] }" />
10+
11+
<ngtr-physics [options]="{ debug: true }">
12+
<ng-template>
13+
<ngt-object3D rigidBody [options]="{ colliders: 'hull' }" [position]="[0, 5, 0]">
14+
<ngt-mesh>
15+
<ngt-torus-geometry />
16+
</ngt-mesh>
17+
</ngt-object3D>
18+
19+
@if (currentCollider() === 1) {
20+
<ngt-object3D cuboidCollider [args]="[1, 0.5, 1]" (collisionExit)="currentCollider.set(2)" />
21+
} @else if (currentCollider() === 2) {
22+
<ngt-object3D
23+
cuboidCollider
24+
[position]="[0, -1, 0]"
25+
[args]="[3, 0.5, 3]"
26+
(collisionExit)="currentCollider.set(3)"
27+
/>
28+
} @else if (currentCollider() === 3) {
29+
<ngt-object3D
30+
cuboidCollider
31+
[position]="[0, -3, 0]"
32+
[args]="[6, 0.5, 6]"
33+
(collisionExit)="currentCollider.set(4)"
34+
/>
35+
} @else {
36+
<ngt-object3D cuboidCollider [position]="[0, -6, 0]" [args]="[20, 0.5, 20]" />
37+
}
38+
</ng-template>
39+
</ngtr-physics>
40+
41+
<ngts-orbit-controls />
42+
`,
43+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
44+
changeDetection: ChangeDetectionStrategy.OnPush,
45+
host: { class: 'experience-basic-rapier' },
46+
imports: [NgtrPhysics, NgtrRigidBody, NgtrCuboidCollider, NgtsOrbitControls, NgtsPerspectiveCamera],
47+
})
48+
export class Basic {
49+
protected currentCollider = signal(1);
50+
51+
constructor() {
52+
injectBeforeRender(({ camera }) => {
53+
const currentCollider = this.currentCollider();
54+
if (currentCollider === 2) {
55+
camera.position.lerp({ x: 10, y: 10, z: 10 }, 0.1);
56+
} else if (currentCollider === 3) {
57+
camera.position.lerp({ x: 15, y: 15, z: 15 }, 0.1);
58+
} else if (currentCollider === 4) {
59+
camera.position.lerp({ x: 20, y: 40, z: 40 }, 0.1);
60+
}
61+
});
62+
}
63+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
CUSTOM_ELEMENTS_SCHEMA,
5+
effect,
6+
ElementRef,
7+
inject,
8+
viewChild,
9+
} from '@angular/core';
10+
import { injectBeforeRender, NgtArgs } from 'angular-three';
11+
import { NgtrInstancedRigidBodies, NgtrPhysics } from 'angular-three-rapier';
12+
import { Color, InstancedMesh, Vector3 } from 'three';
13+
14+
const BALLS = 1000;
15+
16+
@Component({
17+
template: `
18+
<ngt-group>
19+
<ngt-object3D [instancedRigidBodies]="bodies" [options]="{ colliders: 'ball', linearDamping: 5 }">
20+
<ngt-instanced-mesh #instancedMesh *args="[undefined, undefined, BALLS]" castShadow>
21+
<ngt-sphere-geometry *args="[0.2]" />
22+
<ngt-mesh-physical-material [roughness]="0" [metalness]="0.5" color="yellow" />
23+
</ngt-instanced-mesh>
24+
</ngt-object3D>
25+
</ngt-group>
26+
`,
27+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
28+
changeDetection: ChangeDetectionStrategy.OnPush,
29+
host: { class: 'cluster-rapier' },
30+
imports: [NgtrInstancedRigidBodies, NgtArgs],
31+
})
32+
export class ClusterExample {
33+
protected readonly BALLS = BALLS;
34+
protected bodies = Array.from({ length: BALLS }, (_, index) => {
35+
return {
36+
key: index,
37+
position: [Math.floor(index / 30), (index % 30) * 0.5, 0] as [number, number, number],
38+
};
39+
});
40+
41+
private rigidBodiesRef = viewChild.required(NgtrInstancedRigidBodies);
42+
private instancedMeshRef = viewChild<ElementRef<InstancedMesh>>('instancedMesh');
43+
44+
private physics = inject(NgtrPhysics);
45+
46+
constructor() {
47+
injectBeforeRender(() => {
48+
const paused = this.physics.paused();
49+
if (paused) return;
50+
51+
const rigidBodies = this.rigidBodiesRef().rigidBodyRefs();
52+
rigidBodies.forEach((body) => {
53+
const rigidBody = body.rigidBody();
54+
if (rigidBody) {
55+
const { x, y, z } = rigidBody.translation();
56+
const p = new Vector3(x, y, z);
57+
p.normalize().multiplyScalar(-0.01);
58+
rigidBody.applyImpulse(p, true);
59+
}
60+
});
61+
});
62+
63+
effect(() => {
64+
const instancedMesh = this.instancedMeshRef()?.nativeElement;
65+
if (!instancedMesh) return;
66+
67+
for (let i = 0; i < BALLS; i++) {
68+
instancedMesh.setColorAt(i, new Color(Math.random() * 0xffffff));
69+
}
70+
if (instancedMesh.instanceColor) instancedMesh.instanceColor.needsUpdate = true;
71+
});
72+
}
73+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Basic } from './basic/basic';
2+
import { ClusterExample } from './cluster/cluster';
3+
import { InstancedMeshExample } from './instanced-mesh/instanced-mesh';
4+
import { JointsExample } from './joints/joints';
5+
import { PerformanceExample } from './performance/performance';
6+
import { RopeJointExample } from './rope-joint/rope-joint';
7+
import { SpringExample } from './spring/spring';
8+
9+
export const SCENES_MAP = {
10+
basic: Basic,
11+
instancedMesh: InstancedMeshExample,
12+
performance: PerformanceExample,
13+
joints: JointsExample,
14+
cluster: ClusterExample,
15+
ropeJoint: RopeJointExample,
16+
spring: SpringExample,
17+
} as const;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
CUSTOM_ELEMENTS_SCHEMA,
5+
effect,
6+
ElementRef,
7+
signal,
8+
viewChild,
9+
} from '@angular/core';
10+
import { injectStore, NgtArgs, NgtThreeEvent } from 'angular-three';
11+
import { NgtrInstancedRigidBodies, NgtrInstancedRigidBodyOptions } from 'angular-three-rapier';
12+
import { Color, InstancedMesh } from 'three';
13+
import { injectSuzanne } from '../suzanne';
14+
15+
const MAX_COUNT = 2000;
16+
17+
@Component({
18+
template: `
19+
<ngt-group>
20+
@if (gltf(); as gltf) {
21+
<ngt-object3D
22+
#instancedRigidBodies="instancedRigidBodies"
23+
[instancedRigidBodies]="bodies()"
24+
[options]="{ colliders: 'hull' }"
25+
>
26+
<ngt-instanced-mesh
27+
*args="[gltf.nodes.Suzanne.geometry, undefined, MAX_COUNT]"
28+
#instancedMesh
29+
castShadow
30+
[count]="bodies().length"
31+
(click)="onClick(instancedRigidBodies, $any($event))"
32+
>
33+
<ngt-mesh-physical-material />
34+
</ngt-instanced-mesh>
35+
</ngt-object3D>
36+
}
37+
</ngt-group>
38+
`,
39+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
40+
changeDetection: ChangeDetectionStrategy.OnPush,
41+
host: { class: 'instanced-mesh-rapier' },
42+
imports: [NgtrInstancedRigidBodies, NgtArgs],
43+
})
44+
export class InstancedMeshExample {
45+
protected readonly MAX_COUNT = MAX_COUNT;
46+
47+
private instancedMeshRef = viewChild<ElementRef<InstancedMesh>>('instancedMesh');
48+
49+
protected gltf = injectSuzanne();
50+
private store = injectStore();
51+
52+
protected bodies = signal(Array.from({ length: 100 }, () => this.createBody()));
53+
54+
constructor() {
55+
effect(() => {
56+
const instancedMesh = this.instancedMeshRef()?.nativeElement;
57+
if (!instancedMesh) return;
58+
59+
for (let i = 0; i < MAX_COUNT; i++) {
60+
instancedMesh.setColorAt(i, new Color(Math.random() * 0xffffff));
61+
}
62+
if (instancedMesh.instanceColor) {
63+
instancedMesh.instanceColor.needsUpdate = true;
64+
}
65+
});
66+
67+
effect((onCleanup) => {
68+
const sub = this.store.snapshot.pointerMissed$.subscribe(() => {
69+
this.bodies.update((prev) => [...prev, this.createBody()]);
70+
});
71+
onCleanup(() => sub.unsubscribe());
72+
});
73+
}
74+
75+
private createBody(): NgtrInstancedRigidBodyOptions {
76+
return {
77+
key: Math.random(),
78+
position: [Math.random() * 20, Math.random() * 20, Math.random() * 20],
79+
rotation: [Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2],
80+
scale: [0.5 + Math.random(), 0.5 + Math.random(), 0.5 + Math.random()],
81+
};
82+
}
83+
84+
onClick(instancedRigidBodies: NgtrInstancedRigidBodies, event: NgtThreeEvent<MouseEvent>) {
85+
if (event.instanceId !== undefined) {
86+
instancedRigidBodies
87+
.rigidBodyRefs()
88+
.at(event.instanceId)
89+
?.rigidBody()
90+
?.applyTorqueImpulse({ x: 0, y: 50, z: 0 }, true);
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)