Custom Renderer
Catalogue
Angular Three Custom Renderer maintains a single catalogue of entities to render. By default, the catalogue is empty.
extend
In order to populate the catalogue, call the extend
function and pass in a Record
of entities. Angular Three then maps the catalogue to Custom Elements tags with the following naming convention:
1extend({2 Mesh, // makes ngt-mesh available3 BoxGeometry, // makes ngt-box-geometry available4 /* ... */,5 MyMesh: Mesh, // makes ngt-my-mesh available6})
CUSTOM_ELEMENTS_SCHEMA
The CUSTOM_ELEMENTS_SCHEMA
is required to use Angular Three elements as Angular does not support custom schemas at the moment.
1@Component({2 standalone: true,3 template: `<!-- contains custom ngt elements -->`,4 schemas: [CUSTOM_ELEMENTS_SCHEMA],5})6export class Experience {}
Property Bindings
We can use property bindings to pass data to any of the NGT elements’ properties.
1<ngt-mesh2 [position]="[0, 1, 0]"3 [rotation]="[Math.PI / 2, 0, 0]"4 [scale]="1.5"5>6 <ngt-box-geometry />7 <ngt-mesh-standard-material8 color="hotpink"9 />10</ngt-mesh>
Shortcuts
Some THREE.js entities have different methods to update their values due to performance and how WebGL works. For example, THREE.Vector3
has a set
method that accepts a tuple of [x, y, z]
instead.
On the other hand, if we pass in a THREE.Vector3
instance, the current THREE.Vector3
will call copy
and pass in the new instance instead.
This characteristic is baked into the Angular Three Custom Renderer so it is more intuitive to pass values to these properties.
NgtVector*
NgtVector2
, NgtVector3
, and NgtVector4
are shortcuts types for THREE.Vector2
, THREE.Vector3
, and THREE.Vector4
respectively.
1<ngt-mesh2
3 [position]="[0, 1, 0]"4
5 [scale]="1.5"6
7 [rotation]="myRotation"8></ngt-mesh>
NgtEuler
and NgtQuaternion
Similar to NgtVector*
, these types are shortcuts for THREE.Euler
and THREE.Quaternion
respectively.
ColorRepresentation
Similar to NgtVector*
, any elements that accept a color
property can accept a ColorRepresentation
type. This is
a THREE.js type and Angular Three Custom Renderer will apply the value properly.
1<!-- different ways to pass in color -->2
3<ngt-mesh-basic-material color="hotpink" />4
5
6<ngt-mesh-basic-material color="#ff00ff" />7
8
9<ngt-mesh-basic-material color="rgb(255, 0, 255)" />10
11
12<ngt-mesh-basic-material [color]="myColor" />13
14
15<ngt-mesh-basic-material [color]="myHexadecimalColor" />
NGT Properties
Aside from the elements’ own properties, there are a few properties that are specific to the Angular Three Custom Renderer.
parameters
All custom elements accept a parameters
property that accepts an object of properties to pass to the underlying entity.
1<ngt-mesh-basic-material2 [parameters]="{ color: 'hotpink', side: BackSide, transparent: true }"3/>
attach
This property is used to specify a property on the parent that this element should be attached to. Attaching takes into account the life-cycle of the elements and will automatically detach when the elements are destroyed.
Static Value
If the property on the parent is a static value, use Attribute Binding to bind a static string
to the attach
property.
1<ngt-mesh>2 <ngt-mesh-basic-material attach="material" />3</ngt-mesh>
This is equivalent to:
1const mesh = new THREE.Mesh();2const material = new THREE.MeshBasicMaterial();3
4mesh.material = material;
Nested Path
We can attach to a nested property on the parent by using a dot-separated path.
1<ngt-spot-light [castShadow]="true">2 <ngt-vector2 attach="shadow.mapSize" />3</ngt-spot-light>
This is equivalent to:
1const spotLight = new THREE.SpotLight();2spotLight.castShadow = true;3
4const vector2 = new THREE.Vector2();5// shortcut is still applied automatically6spotLight.shadow.mapSize.copy(vector2);
Dynamic Value
We can pass a dynamic value to attach
property by using Property Binding syntax [attach]
. When this is the case, attach
accepts Array<string | number>
as well as string
1@Component({2 template: `3 <ngt-mesh>4 <ngt-box-geometry />5 @for (color of colors; track $index) {6 <ngt-mesh-basic-material7 [attach]="['material', $index]"8 [color]="color"9 />10 }11 </ngt-mesh>12 `13})14export class MyCube {15 colors = [16 'red',17 'green',18 'blue',19 'yellow',20 'orange',21 'purple',22 ]; // cube has 6 faces23}
This is equivalent to:
1const mesh = new THREE.Mesh();2const geometry = new THREE.BoxGeometry();3
4mesh.geometry = geometry;5mesh.material = [];6
7const colors = ['red', 'green', 'blue', 'yellow', 'orange', 'purple'];8
9for (let i = 0; i < colors.length; i++) {10 const material = new THREE.MeshLambertMaterial();11 material.color.set(colors[i]);12 mesh.material[i] = material;13}
NgtAttachFunction
Optionally, we can pass a NgtAttachFunction
to attach
property. We are responsible for attaching and detaching the elements.
1import { createAttachFunction } from 'angular-three';2
3@Component({4 template: `5 <ngt-mesh>6 <ngt-mesh-basic-material [attach]="attachFn" />7 </ngt-mesh>8 `9})10export class Experience {11 attachFn = createAttachFunction<MeshBasicMaterial, Mesh>(({ parent, child }) => {12 const oldMaterial = parent.material;13 parent.material = child;14
15 // return a clean-up function that will be called when `ngt-mesh-basic-material` is destroyed16 return () => {17 parent.material = oldMaterial;18 }19 })20}
priority
See Render Priority for more information.
Event Bindings
Pointer Events
Event Name | Description |
---|---|
click | If observed, emits when the object is clicked. |
contextmenu | If observed, emits when the object is right-clicked. |
dblclick | If observed, emits when the object is double clicked. |
pointerup | If observed, emits when the pointer moves up while on the object. |
pointerdown | If observed, emits when the pointer moves down while on the object. |
pointerover | If observed, emits when the pointer is over the object. |
pointerout | If observed, emits when the pointer gets on then out of the object. |
pointerenter | If observed, emits when the pointer gets on the object. |
pointerleave | If observed, emits when the pointer gets on then out of the object. |
pointermove | If observed, emits when the pointer moves while on the object. |
pointermissed | If observed, emits when the pointer misses the object. |
pointercancel | If observed, emits when the current pointer event gets cancelled. |
wheel | If observed, emits when the wheel is acted on when on the object. |
beforeRender
To register a callback in the animation loop, listen to the beforeRender
event on a NGT element.
1@Component({2 template: `3 <ngt-mesh4 (beforeRender)="onBeforeRender($any($event))"5 ></ngt-mesh>6 `7})8export class Experience {9 onBeforeRender(event: NgtBeforeRenderEvent<Mesh>) {10 const { object, state } = event;11 // runs on every frame12 }13}
When the element is destroyed, the callback will be removed automatically.
Render Priority
By default, NGT renders the scene on every frame.
If we need to control this process, we can pass priority
as Attribute Binding with number-string values
to any object whose (beforeRender)
is being listened to.
When a priority
is set, we are responsible to render our scene.
1@Component({2 template: `3 <ngt-mesh4 priority="1"5 (beforeRender)="onBeforeRender($any($event))"6 />7 <ngt-mesh8 priority="2"9 (beforeRender)="onOtherBeforeRender($any($event))"10 />11 `,12})13export class SceneGraph {14 onBeforeRender(event: NgtBeforeRenderEvent<Mesh>) {15 const { gl, scene, camera } = event.state;16 // do something17 gl.render(scene, camera);18 // do something else19 }20
21 onOtherBeforeRender(event: NgtBeforeRenderEvent<Mesh>) {22 // this runs after the above beforeRender23 }24}
attached
This event is emitted when the element is attached or added to the parent.
1@Component({2 template: `3 <ngt-mesh>4 <ngt-mesh-basic-material (attached)="onAttached($any($event))" />5 </ngt-mesh>6 `7})8export class Experience {9 onAttached(event: NgtAfterAttach<Mesh, MeshBasicMaterial>) {10 const { parent, node } = event;11 // ^? Mesh ^? MeshBasicMaterial12 }13}
updated
This event is emitted when the element is updated.
1@Component({2 template: `3 <ngt-mesh4 [position]="[1, 1, 1]"5 (updated)="onUpdated($any($event)"6 ></ngt-mesh>7 `8})9export class Experience {10 onUpdated(event: Mesh) { }11}