Skip to content

Commit c1ddf9a

Browse files
committed
cameras
1 parent 6aa6f29 commit c1ddf9a

File tree

11 files changed

+437
-137
lines changed

11 files changed

+437
-137
lines changed

libs/soba/cameras/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# angular-three-soba/cameras
2+
3+
Secondary entry point of `angular-three-soba`. It can be used by importing from `angular-three-soba/cameras`.

libs/soba/cameras/ng-package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"lib": {
3+
"entryFile": "src/index.ts"
4+
}
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Directive, inject, Input, TemplateRef } from '@angular/core';
2+
import * as THREE from 'three';
3+
4+
@Directive({ selector: 'ng-template[ngtsCameraContent]', standalone: true })
5+
export class NgtsCameraContent {
6+
template = inject(TemplateRef);
7+
@Input() ngtsCameraContent: true | '' = '';
8+
9+
static ngTemplateContextGuard(
10+
_: NgtsCameraContent,
11+
ctx: unknown,
12+
): ctx is { fbo: THREE.WebGLRenderTarget; group?: THREE.Group } {
13+
return true;
14+
}
15+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Directive, effect, Input } from '@angular/core';
2+
import { injectNgtRef, injectNgtStore, signalStore, type NgtCamera } from 'angular-three';
3+
import { injectNgtsFBO } from 'angular-three-soba/misc';
4+
5+
export type NgtsCameraState = {
6+
makeDefault: boolean;
7+
manual: boolean;
8+
frames: number;
9+
resolution: number;
10+
envMap?: THREE.Texture;
11+
};
12+
13+
@Directive()
14+
export abstract class NgtsCamera<TCamera extends NgtCamera> {
15+
protected inputs = signalStore<NgtsCameraState>({
16+
resolution: 256,
17+
frames: Infinity,
18+
makeDefault: false,
19+
manual: false,
20+
});
21+
22+
@Input() set makeDefault(makeDefault: boolean) {
23+
this.inputs.set({ makeDefault });
24+
}
25+
26+
@Input() set manual(manual: boolean) {
27+
this.inputs.set({ manual });
28+
}
29+
30+
@Input() set frames(frames: number) {
31+
this.inputs.set({ frames });
32+
}
33+
34+
@Input() set resolution(resolution: number) {
35+
this.inputs.set({ resolution });
36+
}
37+
38+
@Input() set envMap(envMap: THREE.Texture) {
39+
this.inputs.set({ envMap });
40+
}
41+
42+
@Input() cameraRef = injectNgtRef<TCamera>();
43+
44+
protected store = injectNgtStore();
45+
private cameraResolution = this.inputs.select('resolution');
46+
protected fboRef = injectNgtsFBO(() => ({ width: this.cameraResolution() }));
47+
48+
constructor() {
49+
this.setDefaultCamera();
50+
this.updateProjectionMatrix();
51+
}
52+
53+
private setDefaultCamera() {
54+
const makeDefault = this.inputs.select('makeDefault');
55+
effect((onCleanup) => {
56+
const camera = this.cameraRef.nativeElement;
57+
if (camera && makeDefault()) {
58+
const { camera: oldCamera } = this.store.get();
59+
this.store.set({ camera });
60+
onCleanup(() => this.store.set({ camera: oldCamera }));
61+
}
62+
});
63+
}
64+
65+
private updateProjectionMatrix() {
66+
const manual = this.inputs.select('manual');
67+
effect(() => {
68+
const camera = this.cameraRef.nativeElement;
69+
if (!manual() && camera) camera.updateProjectionMatrix();
70+
});
71+
}
72+
}

libs/soba/cameras/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './orthographic-camera/orthographic-camera';
2+
export * from './perspective-camera/perspective-camera';
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { NgIf, NgTemplateOutlet } from '@angular/common';
2+
import { CUSTOM_ELEMENTS_SCHEMA, Component, ContentChild, Input, computed } from '@angular/core';
3+
import { extend, signalStore, type NgtOrthographicCamera } from 'angular-three';
4+
import { Group, OrthographicCamera } from 'three';
5+
import { NgtsCamera, type NgtsCameraState } from '../camera/camera';
6+
import { NgtsCameraContent } from '../camera/camera-content';
7+
8+
extend({ OrthographicCamera, Group });
9+
10+
export type NgtsOrthographicCameraState = {
11+
left: number;
12+
top: number;
13+
right: number;
14+
bottom: number;
15+
};
16+
17+
declare global {
18+
interface HTMLElementTagNameMap {
19+
/**
20+
* @extends ngt-orthographic-camera
21+
*/
22+
'ngts-orthographic-camera': NgtsCameraState & NgtsOrthographicCameraState & NgtOrthographicCamera;
23+
}
24+
}
25+
26+
@Component({
27+
selector: 'ngts-orthographic-camera',
28+
standalone: true,
29+
template: `
30+
<ngt-orthographic-camera
31+
ngtCompound
32+
[ref]="cameraRef"
33+
[left]="cameraLeft()"
34+
[right]="cameraRight()"
35+
[top]="cameraTop()"
36+
[bottom]="cameraBottom()"
37+
>
38+
<ng-container
39+
*ngIf="cameraContent && !cameraContent.ngtsCameraContent"
40+
[ngTemplateOutlet]="cameraContent.template"
41+
/>
42+
</ngt-orthographic-camera>
43+
<ngt-group #group *ngIf="cameraContent && cameraContent.ngtsCameraContent">
44+
<ng-container *ngTemplateOutlet="cameraContent.template; context: { fbo: fboRef(), group }" />
45+
</ngt-group>
46+
`,
47+
imports: [NgIf, NgTemplateOutlet],
48+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
49+
})
50+
export class NgtsOrthographicCamera extends NgtsCamera<THREE.OrthographicCamera> {
51+
private orthographicInputs = signalStore<NgtsOrthographicCameraState>({
52+
left: 0,
53+
right: 0,
54+
top: 0,
55+
bottom: 0,
56+
});
57+
58+
@ContentChild(NgtsCameraContent) cameraContent?: NgtsCameraContent;
59+
60+
@Input() set left(left: number) {
61+
this.orthographicInputs.set({ left });
62+
}
63+
64+
@Input() set right(right: number) {
65+
this.orthographicInputs.set({ right });
66+
}
67+
68+
@Input() set top(top: number) {
69+
this.orthographicInputs.set({ top });
70+
}
71+
72+
@Input() set bottom(bottom: number) {
73+
this.orthographicInputs.set({ bottom });
74+
}
75+
76+
private _left = this.orthographicInputs.select('left');
77+
private _right = this.orthographicInputs.select('right');
78+
private _top = this.orthographicInputs.select('top');
79+
private _bottom = this.orthographicInputs.select('bottom');
80+
private size = this.store.select('size');
81+
82+
cameraLeft = computed(() => this._left() || this.size().width / -2);
83+
cameraRight = computed(() => this._right() || this.size().width / 2);
84+
cameraTop = computed(() => this._top() || this.size().height / 2);
85+
cameraBottom = computed(() => this._bottom() || this.size().height / -2);
86+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { NgIf, NgTemplateOutlet } from '@angular/common';
2+
import { CUSTOM_ELEMENTS_SCHEMA, Component, ContentChild } from '@angular/core';
3+
import { extend, type NgtPerspectiveCamera } from 'angular-three';
4+
import { Group, PerspectiveCamera } from 'three';
5+
import { NgtsCamera, type NgtsCameraState } from '../camera/camera';
6+
import { NgtsCameraContent } from '../camera/camera-content';
7+
8+
extend({ PerspectiveCamera, Group });
9+
10+
declare global {
11+
interface HTMLElementTagNameMap {
12+
/**
13+
* @extends ngt-perspective-camera
14+
*/
15+
'ngts-perspective-camera': NgtsCameraState & NgtPerspectiveCamera;
16+
}
17+
}
18+
19+
@Component({
20+
selector: 'ngts-perspective-camera',
21+
standalone: true,
22+
template: `
23+
<ngt-perspective-camera [ref]="cameraRef" ngtCompound>
24+
<ng-container
25+
*ngIf="cameraContent && !cameraContent.ngtsCameraContent"
26+
[ngTemplateOutlet]="cameraContent.template"
27+
/>
28+
</ngt-perspective-camera>
29+
<ngt-group #group *ngIf="cameraContent && cameraContent.ngtsCameraContent">
30+
<ng-container *ngTemplateOutlet="cameraContent.template; context: { fbo: fboRef.nativeElement, group }" />
31+
</ngt-group>
32+
`,
33+
imports: [NgIf, NgTemplateOutlet],
34+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
35+
})
36+
export class NgtsPerspectiveCamera extends NgtsCamera<PerspectiveCamera> {
37+
@ContentChild(NgtsCameraContent) cameraContent?: NgtsCameraContent;
38+
}

0 commit comments

Comments
 (0)