NgtcPhysics
angular-three-cannon
1import { NgtcPhysics } from 'angular-three-cannon';
1@Component({2 imports: [NgtcPhysics],3 template: `4 <ngtc-physics>5 <!-- objects that are subject to physics -->6 </ngtc-physics>7 `,8 schemas: [CUSTOM_ELEMENTS_SCHEMA],9})10export class SceneGraph {}
angular-three-cannon/body provides a variety of body shapes in form of Custom Inject Function that can be used to create physics bodies.
angular-three-cannon/body
1import { injectBox } from 'angular-three-cannon/body';2 3@Component({4 selector: 'app-box',5 template: `6 <ngt-mesh #mesh [receiveShadow]="true" [castShadow]="true">7 <ngt-box-geometry />8 </ngt-mesh>9 `,10 imports: [NgtArgs],11 schemas: [CUSTOM_ELEMENTS_SCHEMA],12 changeDetection: ChangeDetectionStrategy.OnPush,13})14export class Box {15 mesh = viewChild.required<ElementRef<Mesh>>('mesh');16 box = injectBox(() => ({ mass: 1 }), this.mesh);17}
injectBox accepts some physics properties as an argument and an () => ElementRef<Object3D> as the second argument to tie the body to the object.
injectBox
() => ElementRef<Object3D>
The returned value is a Signal<NgtcBodyPublicApi | null> that can be used to control the body, set position, rotation, and subscribe to changes.
Signal<NgtcBodyPublicApi | null>
1import { injectBox } from 'angular-three-cannon/body';2 3@Component({4 selector: 'app-box',5 template: `6 <ngt-mesh #mesh [receiveShadow]="true" [castShadow]="true">7 <ngt-box-geometry />8 </ngt-mesh>9 `,10 imports: [NgtArgs],11 schemas: [CUSTOM_ELEMENTS_SCHEMA],12 changeDetection: ChangeDetectionStrategy.OnPush,13})14export class Box {15 mesh = viewChild.required<ElementRef<Mesh>>('mesh');16 box = injectBox(() => ({ mass: 1 }), this.mesh);17 18 constructor() {19 injectBeforeRender(({ clock }) => {20 const api = this.box();21 if (!api) return;22 api.position.set(Math.sin(clock.getElapsedTime()) * 5, 0, 0);23 })24 }25}
1import {2 ChangeDetectionStrategy,3 Component,4 CUSTOM_ELEMENTS_SCHEMA,5 effect,6 type ElementRef,7 input,8 viewChild,9 viewChildren,10} from '@angular/core';11import type { Triplet } from '@pmndrs/cannon-worker-api';12import { extend, injectStore, NgtArgs, NgtCanvas, type NgtVector3 } from 'angular-three';13import { NgtcPhysics } from 'angular-three-cannon';14import { injectBox, injectPlane } from 'angular-three-cannon/body';15import type { Mesh } from 'three';16import * as THREE from 'three';17 18extend(THREE);19 20@Component({21 selector: 'app-plane',22 template: `23 <ngt-mesh #mesh [receiveShadow]="true">24 <ngt-plane-geometry *args="[1000, 1000]" />25 <ngt-shadow-material color="#171717" [transparent]="true" [opacity]="0.4" />26 </ngt-mesh>27 `,28 schemas: [CUSTOM_ELEMENTS_SCHEMA],29 changeDetection: ChangeDetectionStrategy.OnPush,30 imports: [NgtArgs],31})32export class Plane {33 meshRef = viewChild.required<ElementRef<Mesh>>('mesh');34 constructor() {35 injectPlane(() => ({ rotation: [-Math.PI / 2, 0, 0], position: [0, -2.5, 0] }), this.meshRef);36 }37}38 39@Component({40 selector: 'app-cube',41 template: `42 <ngt-mesh #mesh [receiveShadow]="true" [castShadow]="true">43 <ngt-box-geometry />44 <ngt-mesh-lambert-material color="hotpink" />45 </ngt-mesh>46 `,47 schemas: [CUSTOM_ELEMENTS_SCHEMA],48 changeDetection: ChangeDetectionStrategy.OnPush,49})50export class Cube {51 position = input<NgtVector3>([0, 5, 0]);52 53 meshRef = viewChild.required<ElementRef<Mesh>>('mesh');54 55 boxApi = injectBox(56 () => ({ mass: 1, position: this.position() as Triplet, rotation: [0.4, 0.2, 0.5], args: [1, 1, 1] }),57 this.meshRef,58 );59}60 61@Component({62 template: `63 <ngt-color attach="background" *args="['lightblue']" />64 <ngt-ambient-light />65 <ngt-directional-light [position]="10" [castShadow]="true">66 <ngt-vector2 *args="[2048, 2048]" attach="shadow.mapSize" />67 </ngt-directional-light>68 <ngtc-physics>69 <app-plane />70 @for (position of cubePositions; track $index) {71 <app-cube [position]="position" />72 }73 </ngtc-physics>74 `,75 imports: [Plane, Cube, NgtArgs, NgtcPhysics],76 schemas: [CUSTOM_ELEMENTS_SCHEMA],77 changeDetection: ChangeDetectionStrategy.OnPush,78})79export class SceneGraph {80 cubePositions: Triplet[] = [81 [0.1, 5, 0],82 [0, 10, -1],83 [0, 20, -2],84 ];85 86 cubes = viewChildren(Cube);87 88 constructor() {89 const store = injectStore();90 91 effect((onCleanup) => {92 const cubes = this.cubes();93 if (!cubes.length) return;94 95 const sub = store.snapshot.pointerMissed$.subscribe(() => {96 cubes.forEach((cube, index) => {97 cube.boxApi()?.position.set(...this.cubePositions[index]);98 cube.boxApi()?.rotation.set(0.4, 0.2, 0.5);99 });100 });101 onCleanup(() => sub.unsubscribe());102 });103 }104}105 106@Component({107 template: `108 <ngt-canvas109 [sceneGraph]="sceneGraph"110 [camera]="{ position: [-1, 5, 5], fov: 45 }"111 [shadows]="true"112 [dpr]="[1, 2]"113 [gl]="{ alpha: false }"114 />115 `,116 changeDetection: ChangeDetectionStrategy.OnPush,117 imports: [NgtCanvas],118 host: { class: 'cannon-sample' },119})120export default class CannonSample {121 sceneGraph = SceneGraph;122}