>(ElementRef);
10 | const nativeElement = elementRef.nativeElement;
11 |
12 | if (!nativeElement.isObject3D) return;
13 |
14 | const localState = getLocalState(nativeElement);
15 | if (!localState) return;
16 |
17 | const document = inject(DOCUMENT);
18 |
19 | injectObjectEvents(() => nativeElement, {
20 | pointerover: () => {
21 | document.body.style.cursor = 'pointer';
22 | },
23 | pointerout: () => {
24 | document.body.style.cursor = 'default';
25 | },
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/soba/simple-sound-analyser/simple-sound-analyser.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { NgtCanvas } from 'angular-three';
3 | import { AudioStore } from './audio.store';
4 | import { Experience } from './experience';
5 | import { Overlay } from './overlay';
6 |
7 | @Component({
8 | template: `
9 |
10 |
11 | `,
12 | imports: [NgtCanvas, Overlay],
13 | changeDetection: ChangeDetectionStrategy.OnPush,
14 | host: { class: 'simple-sound-analyser-soba block h-full w-full' },
15 | styles: `
16 | :host {
17 | background: linear-gradient(15deg, rgb(82, 81, 88) 0%, rgb(255, 247, 248) 100%);
18 | }
19 | `,
20 | providers: [AudioStore],
21 | })
22 | export default class SimpleSoundAnalyser {
23 | protected sceneGraph = Experience;
24 | }
25 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/misc/aviator/lights/lights.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { NgtArgs } from 'angular-three';
3 |
4 | @Component({
5 | selector: 'app-lights',
6 | template: `
7 |
8 |
9 |
10 |
11 |
12 |
13 | `,
14 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
15 | changeDetection: ChangeDetectionStrategy.OnPush,
16 | imports: [NgtArgs],
17 | })
18 | export class Lights {
19 | protected readonly Math = Math;
20 | }
21 |
--------------------------------------------------------------------------------
/libs/plugin/generators.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-three-plugin",
3 | "generators": {
4 | "init": {
5 | "factory": "./src/generators/init/generator",
6 | "schema": "./src/generators/init/schema.json",
7 | "description": "Init Angular Three with proper packages and config"
8 | },
9 | "add-soba": {
10 | "factory": "./src/generators/add-soba/generator",
11 | "schema": "./src/generators/add-soba/schema.json",
12 | "description": "Add angular-three-soba to your project"
13 | }
14 | },
15 | "schematics": {
16 | "ng-add": {
17 | "factory": "./src/generators/init/compat",
18 | "schema": "./src/generators/init/schema.json",
19 | "description": "Add Angular Three with proper packages and config"
20 | },
21 | "add-soba": {
22 | "factory": "./src/generators/add-soba/compat",
23 | "schema": "./src/generators/add-soba/schema.json",
24 | "description": "Add angular-three-soba to your project"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/index.ts:
--------------------------------------------------------------------------------
1 | export * from './ascii';
2 | export * from './bloom';
3 | export * from './brightness-contrast';
4 | export * from './chromatic-abberation';
5 | export * from './color-average';
6 | export * from './color-depth';
7 | export * from './depth';
8 | export * from './depth-of-field';
9 | export * from './dot-screen';
10 | export * from './fxaa';
11 | export * from './glitch';
12 | export * from './god-rays';
13 | export * from './grid';
14 | export * from './hue-saturation';
15 | export * from './lens-flare';
16 | export * from './lut';
17 | export * from './noise';
18 | export * from './outline';
19 | export * from './pixelation';
20 | export * from './scanline';
21 | export * from './sepia';
22 | export * from './shock-wave';
23 | export * from './smaa';
24 | export * from './tilt-shift';
25 | export * from './tilt-shift-2';
26 | export * from './tone-mapping';
27 | export * from './vignette';
28 | export * from './water';
29 |
--------------------------------------------------------------------------------
/apps/ionic-app/ios/App/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Capacitor (5.7.6):
3 | - CapacitorCordova
4 | - CapacitorCordova (5.7.6)
5 |
6 | DEPENDENCIES:
7 | - "Capacitor (from `../../../../node_modules/.pnpm/@capacitor+ios@5.7.6_@capacitor+core@5.7.6/node_modules/@capacitor/ios`)"
8 | - "CapacitorCordova (from `../../../../node_modules/.pnpm/@capacitor+ios@5.7.6_@capacitor+core@5.7.6/node_modules/@capacitor/ios`)"
9 |
10 | EXTERNAL SOURCES:
11 | Capacitor:
12 | :path: "../../../../node_modules/.pnpm/@capacitor+ios@5.7.6_@capacitor+core@5.7.6/node_modules/@capacitor/ios"
13 | CapacitorCordova:
14 | :path: "../../../../node_modules/.pnpm/@capacitor+ios@5.7.6_@capacitor+core@5.7.6/node_modules/@capacitor/ios"
15 |
16 | SPEC CHECKSUMS:
17 | Capacitor: 6e86d6f68a9348c76b6da7349ab3ee4a9edd3b9a
18 | CapacitorCordova: 46df0372f7768ff20aa659ba1658b5d7315ea42a
19 |
20 | PODFILE CHECKSUM: be78ad99b1cc4b98f749bdf09511117bb977cead
21 |
22 | COCOAPODS: 1.15.2
23 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/cannon/basic/basic.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2 | import { NgtCanvas } from 'angular-three';
3 | import { Experience } from './experience';
4 | import { State } from './state';
5 |
6 | @Component({
7 | template: `
8 |
9 |
10 |
11 | |
12 |
13 |
14 | `,
15 | imports: [NgtCanvas],
16 | changeDetection: ChangeDetectionStrategy.OnPush,
17 | host: { class: 'basic-cannon ' },
18 | providers: [State],
19 | })
20 | export default class Basic {
21 | protected scene = Experience;
22 | protected state = inject(State);
23 | }
24 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/soba/camera-scroll/overlay.ts:
--------------------------------------------------------------------------------
1 | import { DOCUMENT } from '@angular/common';
2 | import { ChangeDetectionStrategy, Component, ElementRef, inject, viewChild } from '@angular/core';
3 | import { SCROLL } from './camera-scroll';
4 |
5 | @Component({
6 | selector: 'app-overlay',
7 | templateUrl: './overlay.html',
8 | changeDetection: ChangeDetectionStrategy.OnPush,
9 | styleUrl: './overlay.css',
10 | })
11 | export class Overlay {
12 | private captionRef = viewChild.required>('caption');
13 |
14 | private document = inject(DOCUMENT);
15 | private scroll = inject(SCROLL);
16 |
17 | onScroll(event: Event) {
18 | const window = this.document.defaultView;
19 | if (!window) return;
20 |
21 | const target = event.target as HTMLDivElement;
22 | this.scroll.value = target.scrollTop / (target.scrollHeight - window.innerHeight);
23 | this.captionRef().nativeElement.innerText = this.scroll.value.toFixed(2);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/libs/soba/.storybook/preview-body.html:
--------------------------------------------------------------------------------
1 |
45 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/misc/aviator/airplane/tire.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, input } from '@angular/core';
2 | import { NgtArgs, NgtVector3 } from 'angular-three';
3 | import { COLORS } from '../constants';
4 |
5 | @Component({
6 | selector: 'app-tire',
7 | template: `
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs],
21 | })
22 | export class Tire {
23 | protected readonly COLORS = COLORS;
24 |
25 | position = input([0, 0, 0]);
26 | scale = input(1);
27 | }
28 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/misc/svg-renderer/svg-renderer.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { NgtCanvas, NgtCanvasElement } from 'angular-three';
3 | import { SVGRenderer } from 'three-stdlib';
4 | import { Experience } from './experience';
5 |
6 | @Component({
7 | template: `
8 |
13 | `,
14 | imports: [NgtCanvas],
15 | changeDetection: ChangeDetectionStrategy.OnPush,
16 | })
17 | export default class SVGRendererExample {
18 | sceneGraph = Experience;
19 |
20 | svgRendererFactory = (canvas: NgtCanvasElement) => {
21 | const renderer = new SVGRenderer();
22 |
23 | if (canvas instanceof HTMLCanvasElement) {
24 | canvas.style.display = 'none';
25 | canvas.parentElement?.appendChild(renderer.domElement);
26 | }
27 |
28 | return renderer;
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/rapier/wrapper.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { NgtCanvas } from 'angular-three';
3 | import { ToggleButton } from '../toggle-button';
4 | import { debug, interpolate, paused, RapierWrapperDefault } from './wrapper-default';
5 |
6 | @Component({
7 | template: `
8 |
9 |
10 |
11 |
12 |
13 |
14 | `,
15 | changeDetection: ChangeDetectionStrategy.OnPush,
16 | imports: [NgtCanvas, ToggleButton],
17 | })
18 | export default class RapierWrapper {
19 | protected sceneGraph = RapierWrapperDefault;
20 |
21 | protected debug = debug;
22 | protected interpolate = interpolate;
23 | protected paused = paused;
24 | }
25 |
--------------------------------------------------------------------------------
/libs/plugin/src/generators/init/files/experience/experience.component.ts__tmpl__:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, Component, viewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core';
2 | import { extend, injectBeforeRender } from 'angular-three';
3 | import { Mesh, BoxGeometry, MeshBasicMaterial } from 'three';
4 |
5 | extend({ Mesh, BoxGeometry, MeshBasicMaterial });
6 |
7 | @Component({
8 | template: `
9 |
10 |
11 |
12 |
13 | `,
14 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
15 | changeDetection: ChangeDetectionStrategy.OnPush,
16 | })
17 | export class Experience {
18 | meshRef = viewChild.required>('mesh');
19 |
20 | constructor() {
21 | injectBeforeRender(({ delta }) => {
22 | const mesh = this.meshRef().nativeElement;
23 | mesh.rotation.x += delta;
24 | mesh.rotation.y += delta;
25 | })
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/libs/soba/materials/src/lib/point-material.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, DestroyRef, inject, input } from '@angular/core';
2 | import { NgtArgs, NgtAttachable } from 'angular-three';
3 | import { PointMaterial } from 'angular-three-soba/shaders';
4 | import { PointsMaterialParameters } from 'three';
5 |
6 | @Component({
7 | selector: 'ngts-point-material',
8 | template: `
9 |
10 |
11 |
12 | `,
13 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
14 | changeDetection: ChangeDetectionStrategy.OnPush,
15 | imports: [NgtArgs],
16 | })
17 | export class NgtsPointMaterial {
18 | attach = input('material');
19 | options = input({} as PointsMaterialParameters);
20 |
21 | material = new PointMaterial(this.options());
22 |
23 | constructor() {
24 | inject(DestroyRef).onDestroy(() => this.material.dispose());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/libs/rapier/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-three-rapier",
3 | "version": "0.0.0-replace",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/angular-threejs/angular-three/tree/main/libs/rapier"
10 | },
11 | "author": {
12 | "name": "Chau Tran",
13 | "email": "nartc7789@gmail.com",
14 | "url": "https://nartc.me"
15 | },
16 | "description": "Physics Rapier for Angular Three",
17 | "keywords": [
18 | "angular",
19 | "threejs",
20 | "renderer",
21 | "rapier",
22 | "physics"
23 | ],
24 | "license": "MIT",
25 | "peerDependencies": {
26 | "@angular/common": ">=19.0.0 <20.0.0",
27 | "@angular/core": ">=19.0.0 <20.0.0",
28 | "@dimforge/rapier3d-compat": "~0.14.0",
29 | "three": ">=0.148.0 <0.173.0"
30 | },
31 | "dependencies": {
32 | "tslib": "^2.7.0"
33 | },
34 | "sideEffects": false,
35 | "web-types": [
36 | "../../node_modules/angular-three/web-types.json"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/libs/core/src/lib/renderer/constants.ts:
--------------------------------------------------------------------------------
1 | export const ROUTED_SCENE = '__ngt_renderer_is_routed_scene__';
2 | export const HTML = '__ngt_renderer_is_html';
3 | export const NON_ROOT = '__ngt_renderer_is_non_root__';
4 | export const SPECIAL_INTERNAL_ADD_COMMENT = '__ngt_renderer_add_comment__';
5 | export const SPECIAL_INTERNAL_SET_PARENT_COMMENT = '__ngt_renderer_set_parent_comment__';
6 | export const DOM_PARENT = '__ngt_dom_parent__';
7 |
8 | export const SPECIAL_DOM_TAG = {
9 | NGT_PORTAL: 'ngt-portal',
10 | NGT_PRIMITIVE: 'ngt-primitive',
11 | NGT_VALUE: 'ngt-value',
12 | } as const;
13 |
14 | export const SPECIAL_PROPERTIES = {
15 | RENDER_PRIORITY: 'priority',
16 | ATTACH: 'attach',
17 | RAW_VALUE: 'rawValue',
18 | PARAMETERS: 'parameters',
19 | } as const;
20 |
21 | export const SPECIAL_EVENTS = {
22 | BEFORE_RENDER: 'beforeRender',
23 | UPDATED: 'updated',
24 | ATTACHED: 'attached',
25 | } as const;
26 |
27 | export const THREE_NATIVE_EVENTS = ['added', 'removed', 'childadded', 'childremoved', 'disposed'];
28 |
--------------------------------------------------------------------------------
/apps/astro-docs/src/content/docs/core/api/raw-value.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Raw Value
3 | description: Details about the Angular Three `ngt-value`
4 | ---
5 |
6 | There are occasions where we want to **declaratively** set a value on a property of the parent element, we can use `ngt-value`
7 | element to do this.
8 |
9 | ```angular-html
10 |
11 |
12 |
13 |
14 |
15 | ```
16 |
17 | :::note
18 |
19 | We can achieve the same result by using `parameters` property on the `ngt-point-light` element if we don't have to worry about
20 | the life-cycle of the `ngt-value` or using control-flow with `ngt-value`
21 |
22 | ```angular-html
23 |
24 | ```
25 |
26 | :::
27 |
--------------------------------------------------------------------------------
/apps/astro-docs/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "noImplicitOverride": true,
10 | "noPropertyAccessFromIndexSignature": true,
11 | "noImplicitReturns": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "sourceMap": true,
14 | "declaration": false,
15 | "downlevelIteration": true,
16 | "experimentalDecorators": true,
17 | "moduleResolution": "node",
18 | "importHelpers": true,
19 | "noEmit": false,
20 | "target": "es2020",
21 | "module": "es2020",
22 | "lib": ["es2020", "dom"],
23 | "skipLibCheck": true
24 | },
25 | "angularCompilerOptions": {
26 | "enableI18nLegacyMessageIdFormat": false,
27 | "strictInjectionParameters": true,
28 | "strictInputAccessModifiers": true,
29 | "strictTemplates": true,
30 | "allowJs": false
31 | },
32 | "files": [],
33 | "include": ["src/**/*.ts"]
34 | }
35 |
--------------------------------------------------------------------------------
/libs/soba/abstractions/src/lib/helper.spec.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { NgtTestBed } from 'angular-three/testing';
3 | import { BoxHelper } from 'three';
4 | import { NgtsHelper } from './helper';
5 |
6 | describe(NgtsHelper.name, () => {
7 | @Component({
8 | template: `
9 |
10 |
11 |
12 |
13 | `,
14 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
15 | changeDetection: ChangeDetectionStrategy.OnPush,
16 | imports: [NgtsHelper],
17 | })
18 | class SceneGraph {
19 | protected readonly BoxHelper = BoxHelper;
20 | }
21 |
22 | it('should render properly', async () => {
23 | const { scene, fixture, toGraph } = NgtTestBed.create(SceneGraph);
24 | fixture.detectChanges();
25 |
26 | expect(scene.children.length).toEqual(2);
27 | expect(scene.children[1].type).toEqual('BoxHelper');
28 | expect(toGraph()).toMatchSnapshot();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular Three workspace
2 |
3 | ## Netlify Status
4 |
5 | - Angular Three: [](https://app.netlify.com/sites/angularthree/deploys)
6 | - Angular Three Demo: [](https://app.netlify.com/sites/angularthreedemo/deploys)
7 | - Angular Three Soba: [](https://app.netlify.com/sites/angularthreesoba/deploys)
8 |
9 | Here, you'll find the source code for the entire `angular-three` ecosystem, the documentation, and the examples.
10 |
11 | ## Documentation
12 |
13 | The documentation is available at [angularthree.netlify.app](https://angularthree.netlify.app).
14 |
15 | ## Examples
16 |
17 | The examples are available at [angularthreedemo.netlify.app](https://angularthreedemo.netlify.app).
18 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/soba/bruno-simons-20k/bruno-simons-20k.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { NgtCanvas } from 'angular-three';
3 | import { ToggleButton } from '../../toggle-button';
4 | import { debug, Experience, withN8ao } from './experience';
5 |
6 | @Component({
7 | template: `
8 |
15 |
16 |
17 |
18 |
19 |
20 | `,
21 | changeDetection: ChangeDetectionStrategy.OnPush,
22 | host: { class: 'bruno-simons-2k-soba' },
23 | imports: [NgtCanvas, ToggleButton],
24 | })
25 | export default class BrunoSimons20k {
26 | protected sceneGraph = Experience;
27 | protected debug = debug;
28 | protected withN8ao = withN8ao;
29 | }
30 |
--------------------------------------------------------------------------------
/apps/astro-docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev",
7 | "start": "astro dev",
8 | "build": "astro build",
9 | "preview": "astro preview",
10 | "astro": "astro"
11 | },
12 | "dependencies": {
13 | "@analogjs/astro-angular": "1.9.4",
14 | "@astrojs/check": "^0.9.4",
15 | "@astrojs/mdx": "^4.0.3",
16 | "@astrojs/starlight": "^0.30.3",
17 | "@astrojs/tailwind": "^5.1.4",
18 | "angular-three": "^3.0.0",
19 | "angular-three-cannon": "^3.0.0",
20 | "angular-three-postprocessing": "^3.0.0",
21 | "angular-three-soba": "^3.0.0",
22 | "astro": "^5.1.1",
23 | "sharp": "^0.33.5",
24 | "starlight-blog": "^0.15.0",
25 | "tailwindcss": "^3.4.15"
26 | },
27 | "nx": {},
28 | "devDependencies": {
29 | "@astrojs/starlight-tailwind": "^3.0.0",
30 | "@expressive-code/plugin-line-numbers": "^0.38.3"
31 | },
32 | "web-types": [
33 | "../../node_modules/angular-three/web-types.json",
34 | "../../node_modules/angular-three-soba/web-types.json"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/fxaa.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { FXAAEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ FXAAEffect });
7 |
8 | export type FXAAEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-fxaa',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpFXAA {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/smaa.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { SMAAEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ SMAAEffect });
7 |
8 | export type SMAAEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-smaa',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpSMAA {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/cannon/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-three-cannon",
3 | "version": "0.0.0-replace",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/angular-threejs/angular-three/tree/main/libs/cannon"
10 | },
11 | "author": {
12 | "name": "Chau Tran",
13 | "email": "nartc7789@gmail.com",
14 | "url": "https://nartc.me"
15 | },
16 | "description": "Physics Cannon for Angular Three",
17 | "keywords": [
18 | "angular",
19 | "threejs",
20 | "renderer",
21 | "cannon",
22 | "physics"
23 | ],
24 | "license": "MIT",
25 | "peerDependencies": {
26 | "@angular/common": ">=19.0.0 <20.0.0",
27 | "@angular/core": ">=19.0.0 <20.0.0",
28 | "@pmndrs/cannon-worker-api": "^2.0.0",
29 | "cannon-es": ">=0.20.0 <0.21.0",
30 | "cannon-es-debugger": "^1.0.0",
31 | "three": ">=0.148.0 <0.173.0"
32 | },
33 | "peerDependenciesMeta": {
34 | "cannon-es-debugger": {
35 | "optional": true
36 | }
37 | },
38 | "dependencies": {
39 | "tslib": "^2.7.0"
40 | },
41 | "sideEffects": false
42 | }
43 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/depth.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { DepthEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ DepthEffect });
7 |
8 | export type DepthEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-depth',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpDepth {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/sepia.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { SepiaEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ SepiaEffect });
7 |
8 | export type SepiaEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-sepia',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpSepia {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/cz.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('cz-git').UserConfig['prompt']} */
2 | module.exports = {
3 | types: [
4 | { value: 'feat', name: 'feat: A new feature', emoji: ':sparkles:' },
5 | { value: 'fix', name: 'fix: A bug fix', emoji: ':bug:' },
6 | { value: 'docs', name: 'docs: Documentation only changes', emoji: ':memo:' },
7 | {
8 | value: 'refactor',
9 | name: 'refactor: A code change that neither fixes a bug nor adds a feature',
10 | emoji: ':recycle:',
11 | },
12 | { value: 'perf', name: 'perf: A code change that improves performance', emoji: ':zap:' },
13 | {
14 | value: 'testing',
15 | name: 'testing: Adding missing tests or correcting existing tests',
16 | emoji: ':white_check_mark:',
17 | },
18 | { value: 'chore', name: "chore: Other changes that don't modify src or test files", emoji: ':hammer:' },
19 | ],
20 | scopes: [
21 | { name: 'core' },
22 | { name: 'soba' },
23 | { name: 'cannon' },
24 | { name: 'postprocessing' },
25 | { name: 'plugin' },
26 | { name: 'docs' },
27 | { name: 'testing' },
28 | ],
29 | };
30 |
--------------------------------------------------------------------------------
/libs/soba/abstractions/src/lib/prism-geometry.spec.ts:
--------------------------------------------------------------------------------
1 | import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { NgtTestBed } from 'angular-three/testing';
3 | import { ExtrudeGeometry, Mesh } from 'three';
4 | import { NgtsPrismGeometry } from './prism-geometry';
5 |
6 | describe(NgtsPrismGeometry.name, () => {
7 | @Component({
8 | template: `
9 |
10 |
18 |
19 | `,
20 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
21 | imports: [NgtsPrismGeometry],
22 | })
23 | class SceneGraph {}
24 |
25 | it('should render properly', async () => {
26 | const { scene, fixture, toGraph } = NgtTestBed.create(SceneGraph);
27 | fixture.detectChanges();
28 |
29 | expect(scene.children.length).toEqual(1);
30 | const mesh = scene.children[0] as Mesh;
31 |
32 | expect(mesh.geometry.type).toEqual('ExtrudeGeometry');
33 | expect(toGraph()).toMatchSnapshot();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/libs/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/canvas';
2 | export * from './lib/directives/args';
3 | export * from './lib/directives/parent';
4 | export * from './lib/directives/selection';
5 | export * from './lib/html';
6 | export * from './lib/instance';
7 | export * from './lib/loader';
8 | export * from './lib/loop';
9 | export * from './lib/pipes/hexify';
10 | export * from './lib/portal';
11 | export * from './lib/renderer';
12 | export * from './lib/roots';
13 | export * from './lib/routed-scene';
14 | export * from './lib/store';
15 | export * from './lib/utils/apply-props';
16 | export * from './lib/utils/attach';
17 | export * from './lib/utils/before-render';
18 | export * from './lib/utils/is';
19 | export * from './lib/utils/make';
20 | export * from './lib/utils/object-events';
21 | export * from './lib/utils/output-ref';
22 | export * from './lib/utils/parameters';
23 | export * from './lib/utils/resolve-ref';
24 | export * from './lib/utils/signal-store';
25 | export * from './lib/utils/update';
26 |
27 | export type * from './lib/three-types';
28 | export type * from './lib/types';
29 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/pixelation.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, computed, effect, input } from '@angular/core';
2 | import { NgtArgs, pick } from 'angular-three';
3 | import { mergeInputs } from 'ngxtension/inject-inputs';
4 | import { PixelationEffect } from 'postprocessing';
5 |
6 | export interface PixelationOptions {
7 | granularity: number;
8 | }
9 | @Component({
10 | selector: 'ngtp-pixelation',
11 | template: `
12 |
13 | `,
14 | changeDetection: ChangeDetectionStrategy.OnPush,
15 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
16 | imports: [NgtArgs],
17 | })
18 | export class NgtpPixelation {
19 | options = input({ granularity: 5 } as PixelationOptions, { transform: mergeInputs({ granularity: 5 }) });
20 | private granularity = pick(this.options, 'granularity');
21 |
22 | effect = computed(() => new PixelationEffect(this.granularity()));
23 |
24 | constructor() {
25 | effect((onCleanup) => {
26 | const effect = this.effect();
27 | onCleanup(() => effect.dispose());
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/vignette.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { VignetteEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ VignetteEffect });
7 |
8 | export type VignetteEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-vignette',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpVignette {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/plugin/eslint.config.js:
--------------------------------------------------------------------------------
1 | const { FlatCompat } = require('@eslint/eslintrc');
2 | const js = require('@eslint/js');
3 | const baseConfig = require('../../eslint.config.js');
4 |
5 | const compat = new FlatCompat({
6 | baseDirectory: __dirname,
7 | recommendedConfig: js.configs.recommended,
8 | });
9 |
10 | module.exports = [
11 | ...baseConfig,
12 | {
13 | files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
14 | // Override or add rules here
15 | rules: {},
16 | },
17 | {
18 | files: ['**/*.ts', '**/*.tsx'],
19 | // Override or add rules here
20 | rules: {},
21 | },
22 | {
23 | files: ['**/*.js', '**/*.jsx'],
24 | // Override or add rules here
25 | rules: {},
26 | },
27 | {
28 | files: ['**/*.json'],
29 | rules: {
30 | '@nx/dependency-checks': 'error',
31 | },
32 | languageOptions: {
33 | parser: require('jsonc-eslint-parser'),
34 | },
35 | },
36 | {
37 | files: ['./package.json', './generators.json'],
38 | rules: {
39 | '@nx/nx-plugin-checks': 'error',
40 | },
41 | languageOptions: {
42 | parser: require('jsonc-eslint-parser'),
43 | },
44 | },
45 | ];
46 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/bloom.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { BlendFunction, BloomEffect, BloomEffectOptions } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode, provideDefaultEffectOptions } from '../effect';
5 |
6 | extend({ BloomEffect });
7 |
8 | @Component({
9 | selector: 'ngtp-bloom',
10 | template: `
11 |
12 |
13 |
14 |
15 | `,
16 | imports: [NgtArgs, NgtpEffectBlendMode],
17 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
18 | changeDetection: ChangeDetectionStrategy.OnPush,
19 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
20 | providers: [provideDefaultEffectOptions({ blendFunction: BlendFunction.ADD })],
21 | })
22 | export class NgtpBloom {
23 | effect = inject(NgtpEffect, { host: true });
24 | options = input({} as Omit);
25 | }
26 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/dot-screen.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { DotScreenEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ DotScreenEffect });
7 |
8 | export type DotScreenEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-dot-screen',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpDotScreen {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/shock-wave.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { ShockWaveEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ ShockWaveEffect });
7 |
8 | export type ShockWaveEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-shock-wave',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpShockWave {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/apps/astro-docs/src/content/docs/core/testing/to-graph.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: toGraph
3 | description: Details about the Angular Three Testing `toGraph` function
4 | ---
5 |
6 | `toGraph` is a function that allows you to convert a THREE.js object into a simple `NgtTestGraphObject`.
7 |
8 | This is useful for snapshot testing as it _at least_ provides the scene graph structure.
9 |
10 | :::caution
11 |
12 | In many cases, we can use `toJSON` on the THREE.js object to better structure. However, this is not always possible
13 |
14 | - The `uuid` property will always be different for each test run making it difficult to do snapshot testing
15 | - Some THREE.objects do not have `toJSON` method and will throw an error when we try to call `scene.toJSON()` from the root
16 |
17 | :::
18 |
19 | ```ts
20 | const { fixture, toGraph } = NgtTestBed.create(SceneGraph);
21 | fixture.detectChanges();
22 |
23 | expect(toGraph()).toMatchSnapshot();
24 |
25 | /**
26 | * For a scene with a single mesh
27 | *
28 | * [
29 | * {
30 | * "type": "Mesh",
31 | * "name": "",
32 | * "children": []
33 | * }
34 | * ]
35 | */
36 | ```
37 |
--------------------------------------------------------------------------------
/apps/ionic-app/ios/App/App/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/color-depth.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { ColorDepthEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ ColorDepthEffect });
7 |
8 | export type ColorDepthEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-color-depth',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpColorDepth {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/tone-mapping.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { ToneMappingEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ ToneMappingEffect });
7 |
8 | export type ToneMappingEffectOptions = NonNullable[0]>;
9 |
10 | @Component({
11 | selector: 'ngtp-tone-mapping',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | imports: [NgtArgs, NgtpEffectBlendMode],
19 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
20 | changeDetection: ChangeDetectionStrategy.OnPush,
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpToneMapping {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Angular Three
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/soba/stars/stars.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { NgtCanvas } from 'angular-three';
3 | import { Experience } from './experience';
4 |
5 | @Component({
6 | template: `
7 |
8 |
11 | angular three
12 |
13 |
14 |
15 | `,
16 | changeDetection: ChangeDetectionStrategy.OnPush,
17 | host: { class: 'stars-soba' },
18 | styles: `
19 | :host {
20 | display: block;
21 | height: 100%;
22 | width: 100%;
23 | background: #12071f;
24 | }
25 |
26 | h1 {
27 | background: linear-gradient(30deg, #c850c0, #ffcc70);
28 | -webkit-background-clip: text;
29 | -webkit-text-fill-color: transparent;
30 | }
31 | `,
32 | imports: [NgtCanvas],
33 | })
34 | export default class Stars {
35 | protected sceneGraph = Experience;
36 | }
37 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/hue-saturation.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { HueSaturationEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ HueSaturationEffect });
7 |
8 | export type HueSaturationEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-hue-saturation',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpHueSaturation {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/soba/.storybook/manager-head.html:
--------------------------------------------------------------------------------
1 | Angular Three Documentation
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/libs/soba/src/cameras/perspective-camera.stories.ts:
--------------------------------------------------------------------------------
1 | import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { NgtArgs } from 'angular-three';
3 | import { NgtsPerspectiveCamera } from 'angular-three-soba/cameras';
4 | import { makeDecorators, makeStoryObject } from '../setup-canvas';
5 | import { positions } from './positions';
6 |
7 | @Component({
8 | template: `
9 |
10 |
11 |
12 | @for (position of positions(); track position.id) {
13 |
14 |
15 |
16 |
17 | }
18 |
19 | `,
20 | imports: [NgtsPerspectiveCamera, NgtArgs],
21 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
22 | })
23 | class DefaultPerspectiveCameraStory {
24 | positions = positions;
25 | }
26 |
27 | export default {
28 | title: 'Camera/PerspectiveCamera',
29 | decorators: makeDecorators(),
30 | };
31 |
32 | export const Default = makeStoryObject(DefaultPerspectiveCameraStory);
33 |
--------------------------------------------------------------------------------
/apps/ionic-app/src/assets/shapes.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/astro-docs/src/components/first-scene/scene-graph-step-three.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChangeDetectionStrategy,
3 | Component,
4 | CUSTOM_ELEMENTS_SCHEMA,
5 | type ElementRef,
6 | signal,
7 | viewChild,
8 | } from '@angular/core';
9 | import { injectBeforeRender } from 'angular-three';
10 | import type { Mesh } from 'three';
11 |
12 | @Component({
13 | template: `
14 |
21 |
22 |
23 |
24 | `,
25 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
26 | changeDetection: ChangeDetectionStrategy.OnPush,
27 | })
28 | export class SceneGraphStepThree {
29 | meshRef = viewChild.required>('mesh');
30 |
31 | hovered = signal(false);
32 | clicked = signal(false);
33 |
34 | constructor() {
35 | injectBeforeRender(({ delta }) => {
36 | const mesh = this.meshRef().nativeElement;
37 | mesh.rotation.x += delta;
38 | mesh.rotation.y += delta;
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/apps/kitchen-sink/src/app/routed/bomb.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { injectGLTF } from 'angular-three-soba/loaders';
3 | import { NgtsMeshTransmissionMaterial } from 'angular-three-soba/materials';
4 | import { Mesh } from 'three';
5 | import { GLTF } from 'three-stdlib';
6 |
7 | import bombUrl from './bomb-gp.glb';
8 |
9 | interface BombGLTF extends GLTF {
10 | nodes: {
11 | Little_Boy_Little_Boy_Material_0: Mesh;
12 | };
13 | }
14 |
15 | @Component({
16 | selector: 'app-bomb',
17 | template: `
18 | @if (gltf(); as gltf) {
19 |
25 |
26 |
27 | }
28 | `,
29 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
30 | changeDetection: ChangeDetectionStrategy.OnPush,
31 | imports: [NgtsMeshTransmissionMaterial],
32 | })
33 | export default class Bomb {
34 | protected gltf = injectGLTF(() => bombUrl);
35 | }
36 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/brightness-contrast.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { BrightnessContrastEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ BrightnessContrastEffect });
7 |
8 | export type BrightnessEffectOptions = NonNullable[0]>;
9 |
10 | @Component({
11 | selector: 'ngtp-brightness-contrast',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | imports: [NgtArgs, NgtpEffectBlendMode],
19 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
20 | changeDetection: ChangeDetectionStrategy.OnPush,
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | })
23 | export class NgtpBrightnessContrast {
24 | effect = inject(NgtpEffect, { host: true });
25 | options = input({} as Omit);
26 | }
27 |
--------------------------------------------------------------------------------
/libs/soba/src/cameras/orthographic-camera.stories.ts:
--------------------------------------------------------------------------------
1 | import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { NgtArgs } from 'angular-three';
3 | import { NgtsOrthographicCamera } from 'angular-three-soba/cameras';
4 | import { makeDecorators, makeStoryObject } from '../setup-canvas';
5 | import { positions } from './positions';
6 |
7 | @Component({
8 | template: `
9 |
10 |
11 |
12 | @for (position of positions(); track position.id) {
13 |
14 |
15 |
16 |
17 | }
18 |
19 | `,
20 | imports: [NgtsOrthographicCamera, NgtArgs],
21 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
22 | })
23 | class DefaultOrthographicCameraStory {
24 | positions = positions;
25 | }
26 |
27 | export default {
28 | title: 'Camera/OrthographicCamera',
29 | decorators: makeDecorators(),
30 | };
31 |
32 | export const Default = makeStoryObject(DefaultOrthographicCameraStory);
33 |
--------------------------------------------------------------------------------
/apps/astro-docs/src/content/docs/cannon/introduction.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: Introduction to the Angular Three Cannon package
4 | ---
5 |
6 | Angular Three Cannon is an integration of the [Cannon.js](https://github.com/pmndrs/cannon-es) physics engine for use with Angular Three.
7 |
8 | This implementation is based on the [@react-three/cannon](https://github.com/pmndrs/use-cannon) library.
9 |
10 | - [x] Doesn't block the main thread, runs in a web worker
11 | - [x] Supports all the features of cannon-es
12 |
13 | Examples are available at [angular-three-cannon](https://demo.angularthree.org/cannon)
14 |
15 | ## Installation
16 |
17 | ```sh
18 | npm install angular-three-cannon cannon-es @pmndrs/cannon-worker-api
19 | # yarn add angular-three-cannon cannon-es @pmndrs/cannon-worker-api
20 | # pnpm add angular-three-cannon cannon-es @pmndrs/cannon-worker-api
21 | ```
22 |
23 | ## Compatibility Matrix
24 |
25 | | Angular Three Cannon Version | cannon-es version | @pmndrs/cannon-worker-api version |
26 | | ---------------------------- | ----------------- | --------------------------------- |
27 | | 2.0.0 | ^0.20.0 | ^1.0.0 |
28 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/noise.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { BlendFunction, NoiseEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode, provideDefaultEffectOptions } from '../effect';
5 |
6 | extend({ NoiseEffect });
7 |
8 | export type NoiseEffectOptions = Partial[0]>>;
9 |
10 | @Component({
11 | selector: 'ngtp-noise',
12 | template: `
13 |
14 |
15 |
16 |
17 | `,
18 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
19 | changeDetection: ChangeDetectionStrategy.OnPush,
20 | imports: [NgtArgs, NgtpEffectBlendMode],
21 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
22 | providers: [provideDefaultEffectOptions({ blendFunction: BlendFunction.COLOR_DODGE })],
23 | })
24 | export class NgtpNoise {
25 | effect = inject(NgtpEffect, { host: true });
26 | options = input({} as Omit);
27 | }
28 |
--------------------------------------------------------------------------------
/libs/soba/loaders/src/lib/loader.css:
--------------------------------------------------------------------------------
1 | .ngts-loader-container {
2 | --ngts-loader-container-opacity: 0;
3 | position: absolute;
4 | top: 0;
5 | left: 0;
6 | width: 100%;
7 | height: 100%;
8 | background: #171717;
9 | display: flex;
10 | align-items: center;
11 | justify-content: center;
12 | transition: opacity 300ms ease;
13 | z-index: 1000;
14 | opacity: var(--ngts-loader-container-opacity);
15 | }
16 |
17 | .ngts-loader-inner {
18 | width: 100px;
19 | height: 3px;
20 | background: #272727;
21 | text-align: center;
22 | }
23 |
24 | .ngts-loader-bar {
25 | --ngts-loader-bar-scale: 0;
26 | height: 3px;
27 | width: 100px;
28 | background: white;
29 | transition: transform 200ms;
30 | transform-origin: left center;
31 | transform: scaleX(var(--ngts-loader-bar-scale));
32 | }
33 |
34 | .ngts-loader-data {
35 | display: inline-block;
36 | position: relative;
37 | font-variant-numeric: tabular-nums;
38 | margin-top: 0.8em;
39 | color: #f0f0f0;
40 | font-size: 0.6em;
41 | font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, Roboto,
42 | Ubuntu, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
43 | white-space: nowrap;
44 | }
45 |
--------------------------------------------------------------------------------
/libs/soba/abstractions/src/lib/line.spec.ts:
--------------------------------------------------------------------------------
1 | import { NgtTestBed } from 'angular-three/testing';
2 | import { Line2, LineGeometry, LineSegments2, LineSegmentsGeometry } from 'three-stdlib';
3 | import { NgtsLine } from './line';
4 |
5 | describe(NgtsLine.name, () => {
6 | it('should render properly', async () => {
7 | const { scene, fixture, toGraph } = NgtTestBed.create(NgtsLine);
8 | fixture.detectChanges();
9 |
10 | expect(scene.children.length).toEqual(1);
11 | const line = scene.children[0] as Line2;
12 | expect(line).toBeInstanceOf(Line2);
13 | expect(line.geometry).toBeInstanceOf(LineGeometry);
14 | expect(toGraph()).toMatchSnapshot();
15 | });
16 |
17 | it('should render properly with segments', async () => {
18 | const { scene, fixture, toGraph } = NgtTestBed.create(NgtsLine);
19 | fixture.componentRef.setInput('segments', true);
20 | fixture.detectChanges();
21 |
22 | expect(scene.children.length).toEqual(1);
23 | const line = scene.children[0] as LineSegments2;
24 | expect(line).toBeInstanceOf(LineSegments2);
25 | expect(line.isLineSegments2).toEqual(true);
26 | expect(line.geometry).toBeInstanceOf(LineSegmentsGeometry);
27 | expect(toGraph()).toMatchSnapshot();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/libs/postprocessing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-three-postprocessing",
3 | "version": "0.0.0-replace",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/angular-threejs/angular-three/tree/main/libs/postprocessing"
10 | },
11 | "author": {
12 | "name": "Chau Tran",
13 | "email": "nartc7789@gmail.com",
14 | "url": "https://nartc.me"
15 | },
16 | "description": "Postprocessing for Angular Three",
17 | "keywords": [
18 | "angular",
19 | "threejs",
20 | "renderer",
21 | "postprocessing"
22 | ],
23 | "license": "MIT",
24 | "peerDependencies": {
25 | "@angular/common": ">=19.0.0 <20.0.0",
26 | "@angular/core": ">=19.0.0 <20.0.0",
27 | "maath": ">=0.10.0 <0.11.0",
28 | "n8ao": ">=1.9.4 <2.0.0",
29 | "postprocessing": "^6.0.0",
30 | "three": ">=0.148.0 <0.173.0",
31 | "three-stdlib": "^2.0.0"
32 | },
33 | "peerDependenciesMeta": {
34 | "n8ao": {
35 | "optional": true
36 | }
37 | },
38 | "dependencies": {
39 | "tslib": "^2.7.0"
40 | },
41 | "sideEffects": false,
42 | "web-types": [
43 | "../../node_modules/angular-three/web-types.json",
44 | "node_modules/angular-three/web-types.json"
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/chromatic-abberation.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
2 | import { NgtArgs, extend } from 'angular-three';
3 | import { ChromaticAberrationEffect } from 'postprocessing';
4 | import { NgtpEffect, NgtpEffectBlendMode } from '../effect';
5 |
6 | extend({ ChromaticAberrationEffect });
7 |
8 | export type ChromaticAberrationEffectOptions = Partial<
9 | NonNullable[0]>
10 | >;
11 |
12 | @Component({
13 | selector: 'ngtp-chromatic-aberration',
14 | template: `
15 |
16 |
17 |
18 |
19 | `,
20 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
21 | changeDetection: ChangeDetectionStrategy.OnPush,
22 | imports: [NgtArgs, NgtpEffectBlendMode],
23 | hostDirectives: [{ directive: NgtpEffect, inputs: ['blendFunction', 'opacity'] }],
24 | })
25 | export class NgtpChromaticAberration {
26 | effect = inject(NgtpEffect, { host: true });
27 | options = input({} as Omit);
28 | }
29 |
--------------------------------------------------------------------------------
/apps/astro-docs/src/content/docs/core/getting-started/editor-setup.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Code Editor Setup
3 | description: Set up your code editor for Angular Three
4 | ---
5 |
6 | import { Tabs, TabItem } from '@astrojs/starlight/components';
7 |
8 | Angular Three uses a Custom Renderer and works with THREE.js via a collection of Custom Elements. Hence,
9 | we need to set up our code editor to recognize these Custom Elements.
10 |
11 |
12 |
13 | Angular Three provides and sets up a `web-types.json` file that can be used with Jetbrains IDEs out of the box.
14 |
15 | In the case where manual setup is required, `web-types.json` can be found in the `node_modules/angular-three/web-types.json`
16 |
17 |
18 |
19 | Angular Three provides a `metadata.json` file that can be used with VSCode `html.customData` setting.
20 | Please refer to the [VSCode documentation](https://code.visualstudio.com/docs/languages/html#_html-custom-data) for more information.
21 |
22 | The `metadata.json` file can be found in the `node_modules/angular-three/metadata.json`
23 |
24 |
25 |
26 | TBD
27 |
28 |
29 |
--------------------------------------------------------------------------------
/apps/astro-docs/src/content/docs/core/utilities/before-render.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: injectBeforeRender
3 | description: Details about the Angular Three `injectBeforeRender` function
4 | ---
5 |
6 | `injectBeforeRender` is a function that allows the consumers to register a callback to be run in the animation loop.
7 |
8 | It takes a callback that has access to the state of the canvas and the delta time since the last frame.
9 |
10 | `injectBeforeRender` cleans up the callback when the component is destroyed. Alternatively, `injectBeforeRender` returns a clean up function that can be called manually.
11 |
12 | ```angular-ts
13 | import { Component, ElementRef, viewChild } from '@angular/core';
14 | import { injectBeforeRender } from 'angular-three';
15 | import { Mesh } from 'three';
16 |
17 | @Component({
18 | template: `
19 |
20 |
21 |
22 | `
23 | })
24 | export class Experience {
25 | meshRef = viewChild.required>('mesh');
26 |
27 | constructor() {
28 | injectBeforeRender(({ delta }) => {
29 | const mesh = this.meshRef().nativeElement;
30 | mesh.rotation.x += delta;
31 | mesh.rotation.y += delta;
32 | });
33 | }
34 | }
35 | ```
36 |
--------------------------------------------------------------------------------
/libs/postprocessing/src/lib/effects/grid.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, computed, effect, input } from '@angular/core';
2 | import { NgtArgs, pick } from 'angular-three';
3 | import { GridEffect } from 'postprocessing';
4 |
5 | type GridOptions = NonNullable[0]> &
6 | Partial<{ size: { width: number; height: number } }>;
7 |
8 | @Component({
9 | selector: 'ngtp-grid',
10 | template: `
11 |
12 | `,
13 | imports: [NgtArgs],
14 | changeDetection: ChangeDetectionStrategy.OnPush,
15 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
16 | })
17 | export class NgtpGrid {
18 | options = input({} as GridOptions);
19 | private size = pick(this.options, 'size');
20 |
21 | effect = computed(() => {
22 | const { size: _, ...options } = this.options();
23 | return new GridEffect(options);
24 | });
25 |
26 | constructor() {
27 | effect(() => {
28 | const [size, effect] = [this.size(), this.effect()];
29 | if (size) {
30 | effect.setSize(size.width, size.height);
31 | }
32 | });
33 |
34 | effect((onCleanup) => {
35 | const effect = this.effect();
36 | onCleanup(() => effect.dispose());
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------