Skip to content

Commit 774c70f

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
docs: migrate cube heap
1 parent ab7a619 commit 774c70f

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<ngt-canvas
2+
[sceneGraph]="scene"
3+
[gl]="{alpha: false}"
4+
[camera]="{fov: 50, position: [-1, 1, 2.5]}"
5+
[shadows]="true"
6+
(pointerMissed)="toggle()"
7+
/>
8+
<ngts-stats [right]="true" />
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { RouteMeta } from '@analogjs/router';
2+
import { NgIf } from '@angular/common';
3+
import { ChangeDetectionStrategy, Component, computed, CUSTOM_ELEMENTS_SCHEMA, Input, signal } from '@angular/core';
4+
import { Triplet } from '@pmndrs/cannon-worker-api';
5+
import { injectBeforeRender, NgtArgs, NgtCanvas } from 'angular-three';
6+
import { NgtcPhysics } from 'angular-three-cannon';
7+
import { injectBox, injectPlane, injectSphere } from 'angular-three-cannon/services';
8+
import { NgtsStats } from 'angular-three-soba/performance';
9+
import * as THREE from 'three';
10+
// @ts-ignore
11+
import niceColors from 'nice-color-palettes';
12+
13+
export const routeMeta: RouteMeta = {
14+
title: 'Cube Heap',
15+
};
16+
17+
@Component({
18+
selector: 'heap-plane',
19+
standalone: true,
20+
template: `
21+
<ngt-mesh [ref]="plane.ref" [receiveShadow]="true">
22+
<ngt-plane-geometry *args="[5, 5]" />
23+
<ngt-shadow-material color="#171717" />
24+
</ngt-mesh>
25+
`,
26+
imports: [NgtArgs],
27+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
28+
})
29+
class HeapPlane {
30+
readonly rotation = [-Math.PI / 2, 0, 0] as Triplet;
31+
readonly plane = injectPlane<THREE.Mesh>(() => ({ rotation: this.rotation }));
32+
}
33+
34+
@Component({
35+
selector: 'heap-spheres',
36+
standalone: true,
37+
template: `
38+
<ngt-instanced-mesh
39+
*args="[undefined, undefined, count]"
40+
[ref]="sphere.ref"
41+
[castShadow]="true"
42+
[receiveShadow]="true"
43+
>
44+
<ngt-sphere-geometry *args="[size, 48]">
45+
<ngt-instanced-buffer-attribute attach="attributes.color" *args="[colors, 3]" />
46+
</ngt-sphere-geometry>
47+
<ngt-mesh-lambert-material [vertexColors]="true" />
48+
</ngt-instanced-mesh>
49+
`,
50+
imports: [NgtArgs],
51+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
52+
})
53+
class HeapSpheres {
54+
readonly size = 0.1;
55+
@Input({ required: true }) count!: number;
56+
@Input({ required: true }) colors!: Float32Array;
57+
58+
readonly sphere = injectSphere<THREE.InstancedMesh>(() => ({
59+
args: [this.size],
60+
mass: 1,
61+
position: [Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5],
62+
}));
63+
64+
constructor() {
65+
injectBeforeRender(() => {
66+
this.sphere
67+
.api()
68+
.at(Math.floor(Math.random() * this.count))
69+
.position.set(0, Math.random() * 2, 0);
70+
});
71+
}
72+
}
73+
74+
@Component({
75+
selector: 'heap-boxes',
76+
standalone: true,
77+
template: `
78+
<ngt-instanced-mesh
79+
*args="[undefined, undefined, count]"
80+
[ref]="box.ref"
81+
[castShadow]="true"
82+
[receiveShadow]="true"
83+
>
84+
<ngt-box-geometry *args="args">
85+
<ngt-instanced-buffer-attribute attach="attributes.color" *args="[colors, 3]" />
86+
</ngt-box-geometry>
87+
<ngt-mesh-lambert-material [vertexColors]="true" />
88+
</ngt-instanced-mesh>
89+
`,
90+
imports: [NgtArgs],
91+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
92+
})
93+
class HeapBoxes {
94+
readonly args = [0.1, 0.1, 0.1] as [number, number, number];
95+
@Input({ required: true }) count!: number;
96+
@Input({ required: true }) colors!: Float32Array;
97+
98+
readonly box = injectBox<THREE.Mesh>(() => ({
99+
args: this.args,
100+
mass: 1,
101+
position: [Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5],
102+
}));
103+
104+
constructor() {
105+
// NOTE: without requestAnimationFrame here, we will see an error of accessing position (in the Web Worker) too early
106+
// It is probably fine. But would love to dig deeper into the root cause. Same with Spheres above
107+
// requestAnimationInInjectionContext(() => {
108+
injectBeforeRender(() => {
109+
this.box
110+
.api()
111+
.at(Math.floor(Math.random() * this.count))
112+
.position.set(0, Math.random() * 2, 0);
113+
});
114+
// });
115+
}
116+
}
117+
118+
const currentGeometry = signal<'box' | 'sphere'>('box');
119+
120+
@Component({
121+
standalone: true,
122+
templateUrl: 'scene.html',
123+
imports: [HeapPlane, HeapSpheres, HeapBoxes, NgtArgs, NgIf, NgtcPhysics],
124+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
125+
})
126+
class SceneGraph {
127+
readonly isBox = computed(() => currentGeometry() === 'box');
128+
readonly count = 200;
129+
readonly colors = new Float32Array(this.count * 3);
130+
131+
ngOnInit() {
132+
const color = new THREE.Color();
133+
for (let i = 0; i < this.count; i++) {
134+
color
135+
.set(niceColors[17][Math.floor(Math.random() * 5)])
136+
.convertSRGBToLinear()
137+
.toArray(this.colors, i * 3);
138+
}
139+
}
140+
}
141+
142+
@Component({
143+
standalone: true,
144+
templateUrl: 'index.html',
145+
imports: [NgtCanvas, NgtsStats],
146+
changeDetection: ChangeDetectionStrategy.OnPush,
147+
})
148+
export default class CubeHeap {
149+
readonly scene = SceneGraph;
150+
151+
toggle() {
152+
currentGeometry.update((prev) => (prev === 'box' ? 'sphere' : 'box'));
153+
}
154+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<ngt-color *args="['lightblue']" attach="background" />
2+
<ngt-hemisphere-light [intensity]="0.35" />
3+
<ngt-spot-light [position]="5" [angle]="0.3" [penumbra]="1" [intensity]="2" [castShadow]="true">
4+
<ngt-vector2 *args="[256, 256]" attach="shadow.mapSize" />
5+
</ngt-spot-light>
6+
7+
<ngtc-physics broadphase="SAP">
8+
<heap-plane />
9+
<heap-boxes *ngIf="isBox(); else spheres" [count]="count" [colors]="colors" />
10+
<ng-template #spheres>
11+
<heap-spheres [count]="count" [colors]="colors" />
12+
</ng-template>
13+
</ngtc-physics>

0 commit comments

Comments
 (0)