Reusing Resources
Each Geometry and Material consumes the GPU’s resources. If you know certain geometries and/or materials will repeat, you can reuse them
Imperative
You can have static geometries and materials as Component’s properties
@Component({ template: ` <ngt-mesh [geometry]="sphere" [material]="redMaterial" [position]="[1, 1, 1]" /> <ngt-mesh [geometry]="sphere" [material]="redMaterial" [position]="[2, 2, 2]" /> `,})export class SceneGraph { protected readonly sphere = new THREE.SphereGeometry(1, 32, 32); protected readonly redMaterial = new THREE.MeshBasicMaterial({ color: 'red' });}
We can also store these static objects in a Service to reuse across the application.
Declarative
If you want your static objects to react to changes, you can also put them on the template declaratively.
<ngt-sphere-geometry #sphere *args="[1, 32, 32]" attach="none" /><ngt-mesh-basic-material #redMaterial [color]="color()" attach="none" />
<ngt-mesh [geometry]="sphere" [material]="redMaterial" [position]="[1, 1, 1]" /><ngt-mesh [geometry]="sphere" [material]="redMaterial" [position]="[2, 2, 2]" />
Imperative or Declarative, each has its own set of trade-offs so you should pick what works for you in various scenarios.
Using angular-three-plugin:gltf
Another performance optimization that you can achieve is to optimize your GLTF models by using angular-three-plugin:gltf
. angular-three-plugin:gltf
turns a GLTF/GLB model file into an Angular component which reuse the materials and geometries from the model by default. Then, you can reuse the component itself to render multiple instances of the same model without worrying about geometries/materials bloat.
Shoe GLTF generated component
/**Auto-generated by: https://github.com/angular-threejs/gltfCommand: npx angular-three-gltf@2.0.5 src/components/scenes/reuse-gltf-demo/shoe.gltf -o src/components/scenes/reuse-gltf-demo/shoe.ts --selector app-shoe --name Shoe --shadows --importattribute**/
import type * as THREE from "three";import { Group, Mesh } from "three";import { extend, type NgtThreeElements, NgtObjectEvents } from "angular-three";import { Component, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, input, viewChild, ElementRef, inject, effect } from "@angular/core";import { injectGLTF } from "angular-three-soba/loaders";import type { GLTF } from "three-stdlib";
// @ts-expect-error - import .glb/.gltf fileimport ShoeGLTF from "./shoe.gltf" with { loader: "file" };
export type ShoeGLTFGLTFResult = GLTF & { nodes: { shoe: THREE.Mesh; shoe_1: THREE.Mesh; shoe_2: THREE.Mesh; shoe_3: THREE.Mesh; shoe_4: THREE.Mesh; shoe_5: THREE.Mesh; shoe_6: THREE.Mesh; shoe_7: THREE.Mesh; }; materials: { laces: THREE.MeshStandardMaterial; mesh: THREE.MeshStandardMaterial; caps: THREE.MeshStandardMaterial; inner: THREE.MeshStandardMaterial; sole: THREE.MeshStandardMaterial; stripes: THREE.MeshStandardMaterial; band: THREE.MeshStandardMaterial; patch: THREE.MeshStandardMaterial; };};
@Component({ selector: "app-shoe", template: ` @if (gltf(); as gltf) { <ngt-group #model [parameters]="options()" [dispose]="null"> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe.geometry" [material]="gltf.materials.laces" /> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_1.geometry" > <!-- this was modified so we can change color --> <ngt-mesh-standard-material [parameters]="gltf.materials.mesh" [color]="color()" /> </ngt-mesh> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_2.geometry" [material]="gltf.materials.caps" /> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_3.geometry" [material]="gltf.materials.inner" /> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_4.geometry" [material]="gltf.materials.sole" /> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_5.geometry" [material]="gltf.materials.stripes" /> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_6.geometry" [material]="gltf.materials.band" /> <ngt-mesh castShadow receiveShadow [geometry]="gltf.nodes.shoe_7.geometry" [material]="gltf.materials.patch" />
<ng-content /> </ngt-group> } `, hostDirectives: [ { directive: NgtObjectEvents, outputs: ["click", "dblclick", "contextmenu", "pointerup", "pointerdown", "pointerover", "pointerout", "pointerenter", "pointerleave", "pointermove", "pointermissed", "pointercancel", "wheel"], }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush,})export class Shoe { protected readonly Math = Math;
options = input({} as Partial<NgtThreeElements["ngt-group"]>); color = input.required<string>();
modelRef = viewChild<ElementRef<Group>>("model");
protected gltf = injectGLTF<ShoeGLTFGLTFResult>(() => ShoeGLTF);
constructor() { extend({ Group, Mesh });
const objectEvents = inject(NgtObjectEvents, { host: true }); effect(() => { const model = this.modelRef()?.nativeElement; if (!model) return;
objectEvents.ngtObjectEvents.set(model); }); }}