Skip to content

Commit 9c29d5a

Browse files
committed
docs: add cannon documentation
1 parent 2fccad0 commit 9c29d5a

File tree

6 files changed

+464
-0
lines changed

6 files changed

+464
-0
lines changed

apps/astro-docs/astro.config.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ export default defineConfig({
4646
noExternal: [
4747
'angular-three',
4848
'angular-three-soba/**',
49+
'angular-three-cannon/**',
4950
'@angular/common',
5051
'@angular/core',
5152
'@angular/core/rxjs-interop',
5253
'ngxtension/**',
5354
'@pmndrs/vanilla',
55+
'@pmndrs/cannon-worker-api',
5456
],
5557
},
5658
esbuild: {
@@ -145,6 +147,15 @@ export default defineConfig({
145147
collapsed: true,
146148
items: [{ label: 'Introduction', slug: 'soba/introduction' }],
147149
},
150+
{
151+
label: 'Cannon',
152+
collapsed: true,
153+
items: [
154+
{ label: 'Introduction', slug: 'cannon/introduction' },
155+
{ label: 'How it works', slug: 'cannon/how-it-works' },
156+
{ label: 'Debug', slug: 'cannon/debug' },
157+
],
158+
},
148159
],
149160
}),
150161
tailwind({ applyBaseStyles: false }),
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
CUSTOM_ELEMENTS_SCHEMA,
5+
effect,
6+
type ElementRef,
7+
input,
8+
viewChild,
9+
viewChildren,
10+
} from '@angular/core';
11+
import type { Triplet } from '@pmndrs/cannon-worker-api';
12+
import { extend, injectStore, NgtArgs, NgtCanvas, type NgtVector3 } from 'angular-three';
13+
import { NgtcPhysics } from 'angular-three-cannon';
14+
import { injectBox, injectPlane } from 'angular-three-cannon/body';
15+
import { NgtcDebug } from 'angular-three-cannon/debug';
16+
import type { Mesh } from 'three';
17+
import * as THREE from 'three';
18+
19+
extend(THREE);
20+
21+
@Component({
22+
selector: 'app-plane',
23+
standalone: true,
24+
template: `
25+
<ngt-mesh #mesh [receiveShadow]="true">
26+
<ngt-plane-geometry *args="[1000, 1000]" />
27+
<ngt-shadow-material color="#171717" [transparent]="true" [opacity]="0.4" />
28+
</ngt-mesh>
29+
`,
30+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
31+
changeDetection: ChangeDetectionStrategy.OnPush,
32+
imports: [NgtArgs],
33+
})
34+
export class Plane {
35+
meshRef = viewChild.required<ElementRef<Mesh>>('mesh');
36+
constructor() {
37+
injectPlane(() => ({ rotation: [-Math.PI / 2, 0, 0], position: [0, -2.5, 0] }), this.meshRef);
38+
}
39+
}
40+
41+
@Component({
42+
selector: 'app-cube',
43+
standalone: true,
44+
template: `
45+
<ngt-mesh #mesh [receiveShadow]="true" [castShadow]="true">
46+
<ngt-box-geometry />
47+
<ngt-mesh-lambert-material color="hotpink" />
48+
</ngt-mesh>
49+
`,
50+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
51+
changeDetection: ChangeDetectionStrategy.OnPush,
52+
})
53+
export class Cube {
54+
position = input<NgtVector3>([0, 5, 0]);
55+
56+
meshRef = viewChild.required<ElementRef<Mesh>>('mesh');
57+
58+
boxApi = injectBox(
59+
() => ({ mass: 1, position: this.position() as Triplet, rotation: [0.4, 0.2, 0.5], args: [1, 1, 1] }),
60+
this.meshRef,
61+
);
62+
}
63+
64+
@Component({
65+
standalone: true,
66+
template: `
67+
<ngt-color attach="background" *args="['lightblue']" />
68+
<ngt-ambient-light />
69+
<ngt-directional-light [position]="10" [castShadow]="true">
70+
<ngt-vector2 *args="[2048, 2048]" attach="shadow.mapSize" />
71+
</ngt-directional-light>
72+
<ngtc-physics [debug]="{ enabled: true, color: 'red', scale: 1.1 }">
73+
<app-plane />
74+
@for (position of cubePositions; track $index) {
75+
<app-cube [position]="position" />
76+
}
77+
</ngtc-physics>
78+
`,
79+
imports: [Plane, Cube, NgtArgs, NgtcPhysics, NgtcDebug],
80+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
81+
changeDetection: ChangeDetectionStrategy.OnPush,
82+
})
83+
export class SceneGraph {
84+
cubePositions: Triplet[] = [
85+
[0.1, 5, 0],
86+
[0, 10, -1],
87+
[0, 20, -2],
88+
];
89+
90+
cubes = viewChildren(Cube);
91+
92+
constructor() {
93+
const store = injectStore();
94+
95+
effect((onCleanup) => {
96+
const cubes = this.cubes();
97+
if (!cubes.length) return;
98+
99+
const sub = store.snapshot.pointerMissed$.subscribe(() => {
100+
cubes.forEach((cube, index) => {
101+
cube.boxApi()?.position.set(...this.cubePositions[index]);
102+
cube.boxApi()?.rotation.set(0.4, 0.2, 0.5);
103+
});
104+
});
105+
onCleanup(() => sub.unsubscribe());
106+
});
107+
}
108+
}
109+
110+
@Component({
111+
standalone: true,
112+
template: `
113+
<ngt-canvas
114+
[sceneGraph]="sceneGraph"
115+
[camera]="{ position: [-1, 5, 5], fov: 45 }"
116+
[shadows]="true"
117+
[dpr]="[1, 2]"
118+
[gl]="{ alpha: false }"
119+
/>
120+
<span class="absolute bottom-0 right-0 font-mono text-black">* click to reset the cubes</span>
121+
`,
122+
changeDetection: ChangeDetectionStrategy.OnPush,
123+
imports: [NgtCanvas],
124+
host: { class: 'cannon-sample relative inline' },
125+
})
126+
export default class CannonSampleDebug {
127+
sceneGraph = SceneGraph;
128+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
CUSTOM_ELEMENTS_SCHEMA,
5+
effect,
6+
type ElementRef,
7+
input,
8+
viewChild,
9+
viewChildren,
10+
} from '@angular/core';
11+
import type { Triplet } from '@pmndrs/cannon-worker-api';
12+
import { extend, injectStore, NgtArgs, NgtCanvas, type NgtVector3 } from 'angular-three';
13+
import { NgtcPhysics } from 'angular-three-cannon';
14+
import { injectBox, injectPlane } from 'angular-three-cannon/body';
15+
import type { Mesh } from 'three';
16+
import * as THREE from 'three';
17+
18+
extend(THREE);
19+
20+
@Component({
21+
selector: 'app-plane',
22+
standalone: true,
23+
template: `
24+
<ngt-mesh #mesh [receiveShadow]="true">
25+
<ngt-plane-geometry *args="[1000, 1000]" />
26+
<ngt-shadow-material color="#171717" [transparent]="true" [opacity]="0.4" />
27+
</ngt-mesh>
28+
`,
29+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
30+
changeDetection: ChangeDetectionStrategy.OnPush,
31+
imports: [NgtArgs],
32+
})
33+
export class Plane {
34+
meshRef = viewChild.required<ElementRef<Mesh>>('mesh');
35+
constructor() {
36+
injectPlane(() => ({ rotation: [-Math.PI / 2, 0, 0], position: [0, -2.5, 0] }), this.meshRef);
37+
}
38+
}
39+
40+
@Component({
41+
selector: 'app-cube',
42+
standalone: true,
43+
template: `
44+
<ngt-mesh #mesh [receiveShadow]="true" [castShadow]="true">
45+
<ngt-box-geometry />
46+
<ngt-mesh-lambert-material color="hotpink" />
47+
</ngt-mesh>
48+
`,
49+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
50+
changeDetection: ChangeDetectionStrategy.OnPush,
51+
})
52+
export class Cube {
53+
position = input<NgtVector3>([0, 5, 0]);
54+
55+
meshRef = viewChild.required<ElementRef<Mesh>>('mesh');
56+
57+
boxApi = injectBox(
58+
() => ({ mass: 1, position: this.position() as Triplet, rotation: [0.4, 0.2, 0.5], args: [1, 1, 1] }),
59+
this.meshRef,
60+
);
61+
}
62+
63+
@Component({
64+
standalone: true,
65+
template: `
66+
<ngt-color attach="background" *args="['lightblue']" />
67+
<ngt-ambient-light />
68+
<ngt-directional-light [position]="10" [castShadow]="true">
69+
<ngt-vector2 *args="[2048, 2048]" attach="shadow.mapSize" />
70+
</ngt-directional-light>
71+
<ngtc-physics>
72+
<app-plane />
73+
@for (position of cubePositions; track $index) {
74+
<app-cube [position]="position" />
75+
}
76+
</ngtc-physics>
77+
`,
78+
imports: [Plane, Cube, NgtArgs, NgtcPhysics],
79+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
80+
changeDetection: ChangeDetectionStrategy.OnPush,
81+
})
82+
export class SceneGraph {
83+
cubePositions: Triplet[] = [
84+
[0.1, 5, 0],
85+
[0, 10, -1],
86+
[0, 20, -2],
87+
];
88+
89+
cubes = viewChildren(Cube);
90+
91+
constructor() {
92+
const store = injectStore();
93+
94+
effect((onCleanup) => {
95+
const cubes = this.cubes();
96+
if (!cubes.length) return;
97+
98+
const sub = store.snapshot.pointerMissed$.subscribe(() => {
99+
cubes.forEach((cube, index) => {
100+
cube.boxApi()?.position.set(...this.cubePositions[index]);
101+
cube.boxApi()?.rotation.set(0.4, 0.2, 0.5);
102+
});
103+
});
104+
onCleanup(() => sub.unsubscribe());
105+
});
106+
}
107+
}
108+
109+
@Component({
110+
standalone: true,
111+
template: `
112+
<ngt-canvas
113+
[sceneGraph]="sceneGraph"
114+
[camera]="{ position: [-1, 5, 5], fov: 45 }"
115+
[shadows]="true"
116+
[dpr]="[1, 2]"
117+
[gl]="{ alpha: false }"
118+
/>
119+
`,
120+
changeDetection: ChangeDetectionStrategy.OnPush,
121+
imports: [NgtCanvas],
122+
host: { class: 'cannon-sample' },
123+
})
124+
export default class CannonSample {
125+
sceneGraph = SceneGraph;
126+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
title: Debug
3+
description: Details about the Angular Three Cannon Debug
4+
---
5+
6+
import CannonSampleDebug from '../../../components/cannon/sample-debug';
7+
8+
`angular-three-cannon/debug` provides a debug experience for Cannon.js physics engine using `cannon-es-debugger`. This
9+
allows us to visualize how Cannon _sees_ the 3D scene graph.
10+
11+
### Installation
12+
13+
```shell
14+
npm install cannon-es-debugger
15+
# yarn add cannon-es-debugger
16+
# pnpm add cannon-es-debugger
17+
```
18+
19+
### Import `NgtcDebug` from `angular-three-cannon/debug`
20+
21+
```angular-ts
22+
import { NgtcDebug } from 'angular-three-cannon/debug';
23+
```
24+
25+
### Attach `[debug]` to the `ngtc-physics` component
26+
27+
```angular-ts
28+
@Component({
29+
standalone: true,
30+
imports: [NgtcDebug, NgtcPhysics],
31+
template: `
32+
<ngtc-physics debug>
33+
<!-- objects that are subject to physics -->
34+
</ngtc-physics>
35+
`
36+
})
37+
export class SceneGraph {}
38+
```
39+
40+
### Passing options to `NgtcDebug`
41+
42+
We can pas options to `NgtcDebug` by passing in an object to the `debug` input
43+
44+
```angular-ts
45+
@Component({
46+
standalone: true,
47+
imports: [NgtcDebug, NgtcPhysics],
48+
template: `
49+
<ngtc-physics
50+
[debug]="{ enabled: true, color: 'red', scale: 1.1 }"
51+
>
52+
<!-- objects that are subject to physics -->
53+
</ngtc-physics>
54+
`
55+
})
56+
export class SceneGraph {}
57+
```
58+
59+
:::note
60+
61+
If the _debug_ wireframes are not visible when `NgtcDebug` is enabled, make sure to set the `args` for the Body Shape.
62+
63+
```diff lang="angular-ts"
64+
injectBox(
65+
() => ({
66+
mass: 1,
67+
+ args: [1, 1, 1],
68+
}),
69+
this.meshRef
70+
);
71+
```
72+
73+
:::
74+
75+
### Example
76+
77+
<div class="h-96 w-full border border-dashed border-accent-500 rounded">
78+
<CannonSampleDebug client:only />
79+
</div>

0 commit comments

Comments
 (0)