Skip to content

Commit 513c9da

Browse files
committed
docs: add some advanced topics
1 parent 9c77d66 commit 513c9da

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
id: compound
3+
title: Compound Component
4+
sidebar_label: Compound Component
5+
---
6+
7+
Compound Component is a component that wraps at least one THREE.js entity in its template and all Inputs will be forwarded to that THREE.js entity.
8+
9+
There are two steps to create a Compound component
10+
11+
- Ensure that NGT Custom Renderer recognizes the Compound Component's selector as a `compound`. We can use `[compoundPrefixes]` Input on `<ngt-canvas>` to configure this
12+
- Add `ngtCompound` attribute on the THREE.js entity that we want our Compound Component to wrap
13+
14+
Suppose we have the following template
15+
16+
```html
17+
<ngt-mesh>
18+
<ngt-box-geometry *args="args" />
19+
<ngt-mesh-basic-material />
20+
</ngt-mesh>
21+
22+
<ngt-mesh>
23+
<ngt-box-geometry *args="argsTwo" />
24+
<ngt-mesh-standard-material />
25+
</ngt-mesh>
26+
```
27+
28+
We notice that we keep using `<ngt-mesh>` and `<ngt-box-geometry>`. This is a good candidate to make into a Compound Component. In this case,
29+
we'll make a `Box` component.
30+
31+
```ts
32+
extend({ Mesh, BoxGeometry });
33+
34+
@Component({
35+
selector: 'tutorial-box',
36+
standalone: true,
37+
template: `
38+
<ngt-mesh ngtCompound [ref]="boxRef">
39+
<ngt-box-geometry *args="boxArgs" />
40+
<ng-content />
41+
</ngt-mesh>
42+
`,
43+
imports: [NgtArgs],
44+
})
45+
export class Box {
46+
@Input() boxRef = injectNgtRef<Mesh>();
47+
@Input() boxArgs: ConstructorParameters<typeof BoxGeometry> = [1, 1, 1];
48+
}
49+
50+
@Component({
51+
template: `<ngt-canvas [compoundPrefixes]="['tutorial']" />`,
52+
})
53+
export class SomeFeature {}
54+
```
55+
56+
- We use `<ngt-mesh>` and `<ngt-box-geometry>` with `<ng-content>`. This allows the consumers to pass in any Material, or other objects as children to our `<ngt-mesh>`
57+
58+
- We use `ngtCompound` on the `<ngt-mesh>`. This lets NGT Custom Renderer knows that `Box` component will forward its Inputs to `<ngt-mesh>`
59+
:::note
60+
61+
Inputs that are defined like `boxRef` and `boxArgs` will **not** get forwarded.
62+
63+
:::
64+
65+
- We `extend({Mesh, BoxGeometry})` to ensure that NGT Custom Renderer knows about `Mesh` and `BoxGeometry` if they consume `Box` component
66+
- We let the NGT Custom Renderer knows that our `Box` component is a Compound Component by providing `['tutorial']` to `compoundPrefixes`
67+
68+
Now that we have `Box`, we can consume it like following
69+
70+
```html
71+
<tutorial-box>
72+
<ngt-mesh-basic-material />
73+
</tutorial-box>
74+
75+
<tutorial-box [position]="[2, 2, 2]">
76+
<ngt-mesh-standard-material />
77+
</tutorial-box>
78+
79+
<tutorial-box [position]="[-2, -2, -2]">
80+
<ngt-mesh-standard-material />
81+
<ngt-mesh>
82+
<ngt-plane-geometry />
83+
</ngt-mesh>
84+
</tutorial-box>
85+
```
86+
87+
Check out [TBD: angular-three-soba](#) for more examples on Compound Components.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
id: performance
3+
title: Performance
4+
sidebar_label: Performance
5+
---
6+
7+
## Re-use Geometries and Materials
8+
9+
Each Geometry and Material consumes the GPU's resources. If we know certain Geometries and/or Materials will repeat, we can reuse them
10+
11+
### Imperative
12+
13+
We can have static geometries and materials as Component's properties
14+
15+
```ts
16+
@Component({
17+
standalone: true,
18+
template: `
19+
<ngt-mesh [geometry]="sphere" [material]="redMaterial" [position]="[1, 1, 1]" />
20+
<ngt-mesh [geometry]="sphere" [material]="redMaterial" [position]="[2, 2, 2]" />
21+
`,
22+
})
23+
export class SceneGraph {
24+
readonly sphere = new THREE.SphereGeometry(1, 32, 32);
25+
readonly redMaterial = new THREE.MeshBasicMaterial({ color: 'red' });
26+
}
27+
```
28+
29+
We can also store these static objects in a Service to reuse across the application
30+
31+
### Declarative
32+
33+
We can put the Geometries and Materials declaratively on the template so they can react to Input changes; and still can reuse them
34+
35+
```ts
36+
@Component({
37+
standalone: true,
38+
template: `
39+
<ngt-sphere-geometry *args="sphereArgs" [ref]="sphereRef" />
40+
<ngt-mesh-basic-material #redMaterial [color]="color" />
41+
42+
<ngt-mesh [geometry]="sphereRef.nativeElement" [material]="redMaterial" [position]="[1, 1, 1]" />
43+
<ngt-mesh [geometry]="sphereRef.nativeElement" [material]="redMaterial" [position]="[2, 2, 2]" />
44+
`,
45+
imports: [NgtArgs],
46+
})
47+
export class SceneGraph {
48+
readonly sphereRef = injectNgtRef<THREE.SphereGeometry>();
49+
50+
sphereArgs = [1, 32, 32];
51+
color = 'red';
52+
}
53+
```
54+
55+
## On-demand Rendering
56+
57+
> Credit: [React Three Fiber](https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#on-demand-rendering)
58+
59+
The SceneGraph is usually rendered at 60 frames per second. This makes sense if the SceneGraph ontains _constantly_ moving parts (eg: game).
60+
Consequently, this drains the device's resources.
61+
62+
If the SceneGraph has static entities, or entities that are allowed to come to a rest, constantly rendering at 60fps would be wasteful.
63+
In those cases, we can opt into on-demand rendering, which will only render when necessary. All we have to do is to set `frameloop="demand"` on the `<ngt-canvas>`
64+
65+
```html
66+
<ngt-canvas frameloop="demand" />
67+
```
68+
69+
:::info
70+
71+
Check out [TBD: Color Grading Stackblitz example](#) to see on-demand rendering in action.
72+
73+
:::

apps/documentation/sidebars.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ const sidebars = {
4242
'api/additional-exports',
4343
],
4444
},
45+
{
46+
type: 'category',
47+
label: 'Advanced',
48+
items: ['advanced/compound', 'advanced/performance'],
49+
},
4550
],
4651
// By default, Docusaurus generates a sidebar from the docs folder structure
4752
// tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }],

0 commit comments

Comments
 (0)