Skip to content

Commit c4ae8ab

Browse files
committed
docs: start compound body (investigate to see if use-cannon hooks reacts on props change)
1 parent 9f36571 commit c4ae8ab

File tree

4 files changed

+180
-4
lines changed

4 files changed

+180
-4
lines changed

apps/demo/src/app/app.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ import { RouterOutlet } from '@angular/router';
88
imports: [RouterOutlet],
99
})
1010
export class AppComponent {}
11+

apps/demo/src/app/app.routes.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Route } from '@angular/router';
22

33
export const routes: Route[] = [
4-
{ path: '', redirectTo: 'cubes', pathMatch: 'full' },
5-
{ path: 'cubes', loadComponent: () => import('./simple-cubes/simple-cubes.component') },
4+
{ path: '', redirectTo: 'simple-cubes', pathMatch: 'full' },
5+
{ path: 'simple-cubes', loadComponent: () => import('./simple-cubes/simple-cubes.component') },
6+
{ path: 'compound-body', loadComponent: () => import('./compound-body/compound-body.component') },
67
];
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { NgIf } from '@angular/common';
2+
import {
3+
ChangeDetectorRef,
4+
Component,
5+
CUSTOM_ELEMENTS_SCHEMA,
6+
EventEmitter,
7+
inject,
8+
Input,
9+
OnInit,
10+
Output,
11+
} from '@angular/core';
12+
import { Triplet } from '@pmndrs/cannon-worker-api';
13+
import { NgtArgs, NgtCanvas, NgtRxStore } from 'angular-three';
14+
import { NgtcPhysics } from 'angular-three-cannon';
15+
import { NgtcDebug } from 'angular-three-cannon/debug';
16+
import { injectCompoundBody, injectPlane } from 'angular-three-cannon/services';
17+
18+
@Component({
19+
selector: 'demo-plane',
20+
standalone: true,
21+
template: `
22+
<ngt-group [ref]="plane.ref" [rotation]="rotation">
23+
<ngt-mesh>
24+
<ngt-plane-geometry *args="[8, 8]" />
25+
<ngt-mesh-basic-material color="#ffb385" />
26+
</ngt-mesh>
27+
<ngt-mesh [receiveShadow]="true">
28+
<ngt-plane-geometry *args="[8, 8]" />
29+
<ngt-shadow-material color="lightsalmon" />
30+
</ngt-mesh>
31+
</ngt-group>
32+
`,
33+
imports: [NgtArgs],
34+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
35+
})
36+
export class Plane {
37+
@Input() rotation: Triplet = [0, 0, 0];
38+
readonly plane = injectPlane<THREE.Mesh>(() => ({ type: 'Static', rotation: this.rotation }));
39+
}
40+
41+
@Component({
42+
selector: 'demo-compound-body',
43+
standalone: true,
44+
template: `
45+
<ngt-group [ref]="compound.ref" [rotation]="rotation" [position]="position">
46+
<ngt-mesh [castShadow]="true">
47+
<ngt-box-geometry *args="boxSize" />
48+
<ngt-mesh-normal-material />
49+
</ngt-mesh>
50+
<ngt-mesh [castShadow]="true" [position]="[1, 0, 0]">
51+
<ngt-sphere-geometry *args="[sphereRadius, 16, 16]" />
52+
<ngt-mesh-normal-material />
53+
</ngt-mesh>
54+
</ngt-group>
55+
`,
56+
imports: [NgtArgs],
57+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
58+
})
59+
export class CompoundBody extends NgtRxStore implements OnInit {
60+
@Input() isTrigger = false;
61+
@Input() mass = 12;
62+
@Input() position: Triplet = [0, 0, 0];
63+
@Input() rotation: Triplet = [0, 0, 0];
64+
65+
@Output() positionChange = new EventEmitter<Triplet>();
66+
@Output() rotationChange = new EventEmitter<Triplet>();
67+
68+
readonly boxSize = [1, 1, 1];
69+
readonly sphereRadius = 0.65;
70+
71+
readonly compound = injectCompoundBody<THREE.Group>(() => ({
72+
isTrigger: this.isTrigger,
73+
mass: this.mass,
74+
position: this.position,
75+
rotation: this.rotation,
76+
shapes: [
77+
{ args: this.boxSize, position: [0, 0, 0], rotation: [0, 0, 0], type: 'Box' },
78+
{ args: [this.sphereRadius], position: [1, 0, 0], rotation: [0, 0, 0], type: 'Sphere' },
79+
],
80+
}));
81+
82+
ngOnInit() {
83+
this.effect(this.compound.ref.$, () => {
84+
let positionSub: () => void;
85+
let rotationSub: () => void;
86+
87+
if (this.positionChange.observed) {
88+
positionSub = this.compound.api.position.subscribe(this.positionChange.emit.bind(this.positionChange));
89+
}
90+
91+
if (this.rotationChange.observed) {
92+
rotationSub = this.compound.api.rotation.subscribe(this.rotationChange.emit.bind(this.rotationChange));
93+
}
94+
95+
return () => {
96+
positionSub?.();
97+
rotationSub?.();
98+
};
99+
});
100+
}
101+
}
102+
103+
@Component({
104+
standalone: true,
105+
template: `
106+
<ngt-color *args="['#f6d186']" attach="background" />
107+
<ngt-hemisphere-light [intensity]="0.35" />
108+
<ngt-spot-light [position]="[5, 5, 5]" [angle]="0.3" [penumbra]="1" [intensity]="2" [castShadow]="true">
109+
<ngt-vector2 *args="[1028, 1028]" attach="shadow.mapSize" />
110+
</ngt-spot-light>
111+
112+
<ngtc-physics [iterations]="6">
113+
<ngtc-debug [scale]="1.1" color="black">
114+
<demo-plane [rotation]="[-Math.PI / 2, 0, 0]" />
115+
<demo-compound-body [position]="[1.5, 5, 0.5]" [rotation]="[1.25, 0, 0]" />
116+
<demo-compound-body
117+
[position]="[2.5, 3, 0.25]"
118+
[rotation]="[1.25, -1.25, 0]"
119+
(positionChange)="!copy && (position = $any($event))"
120+
(rotationChange)="!copy && (rotation = $any($event))"
121+
/>
122+
<demo-compound-body *ngIf="ready" [position]="[2.5, 4, 0.25]" [rotation]="[1.25, -1.25, 0]" />
123+
<demo-compound-body
124+
*ngIf="copy"
125+
[position]="position"
126+
[rotation]="rotation"
127+
[mass]="0"
128+
[isTrigger]="true"
129+
/>
130+
</ngtc-debug>
131+
</ngtc-physics>
132+
`,
133+
imports: [NgtArgs, NgtcPhysics, NgtcDebug, NgIf, CompoundBody, Plane],
134+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
135+
})
136+
export class Scene implements OnInit {
137+
ready = false;
138+
copy = false;
139+
140+
position: Triplet = [0, 0, 0];
141+
rotation: Triplet = [0, 0, 0];
142+
143+
readonly Math = Math;
144+
145+
private readonly cdr = inject(ChangeDetectorRef);
146+
147+
ngOnInit(): void {
148+
setTimeout(() => {
149+
this.ready = true;
150+
this.cdr.detectChanges();
151+
}, 2000);
152+
153+
setTimeout(() => {
154+
this.copy = true;
155+
this.cdr.detectChanges();
156+
}, 1000);
157+
}
158+
}
159+
160+
@Component({
161+
standalone: true,
162+
template: `
163+
<ngt-canvas
164+
[sceneGraph]="SceneGraph"
165+
[shadows]="true"
166+
[gl]="{ alpha: false }"
167+
[camera]="{ fov: 50, position: [-2, 1, 7] }"
168+
/>
169+
`,
170+
imports: [NgtCanvas],
171+
})
172+
export default class DemoCompoundBody {
173+
readonly SceneGraph = Scene;
174+
}

libs/angular-three-cannon/src/lib/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { NgtInjectedRef } from 'angular-three';
21
import type {
32
BodyProps,
43
CannonWorkerAPI,
@@ -9,8 +8,9 @@ import type {
98
SubscriptionTarget,
109
Triplet,
1110
} from '@pmndrs/cannon-worker-api';
12-
import type { NgtcEvents } from './store';
11+
import { NgtInjectedRef } from 'angular-three';
1312
import * as THREE from 'three';
13+
import type { NgtcEvents } from './store';
1414

1515
export class NgtcUtils {
1616
static incrementingId = 0;

0 commit comments

Comments
 (0)