NgtsDecal
import { ChangeDetectionStrategy, Component } from '@angular/core';import { SobaWrapper } from '@soba/wrapper.ts';import { NgtCanvas, provideNgtRenderer } from 'angular-three/dom';import { SceneGraph } from './scene-graph';
@Component({ selector: 'app-decal', template: ` <ngt-canvas [camera]="{ position: [0, 0, 3], fov: 50 }"> <app-soba-wrapper *canvasContent> <app-scene-graph /> </app-soba-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'decal-demo relative block h-full' }, imports: [NgtCanvas, SobaWrapper, SceneGraph],})export default class Decal { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import { NgtArgs } from 'angular-three';import { textureResource } from 'angular-three-soba/loaders';import { NgtsDecal } from 'angular-three-soba/misc';
@Component({ selector: 'app-scene-graph', template: ` <!-- Sphere with decal --> <ngt-mesh> <ngt-sphere-geometry *args="[1, 64, 64]" /> <ngt-mesh-standard-material color="#444" [roughness]="0.4" [metalness]="0.1" />
<!-- Front decal --> <ngts-decal [options]="{ position: [0, 0, 1], scale: 0.75 }"> <ngt-mesh-basic-material [map]="texture.value()" transparent [polygonOffset]="true" [polygonOffsetFactor]="-10" /> </ngts-decal>
<!-- Back decal --> <ngts-decal [options]="{ position: [0, 0, -1], rotation: [0, Math.PI, 0], scale: 0.75 }"> <ngt-mesh-basic-material [map]="texture.value()" transparent [polygonOffset]="true" [polygonOffsetFactor]="-10" /> </ngts-decal>
<!-- Side decal --> <ngts-decal [options]="{ position: [1, 0, 0], rotation: [0, Math.PI / 2, 0], scale: 0.5 }"> <ngt-mesh-basic-material [map]="texture.value()" transparent [polygonOffset]="true" [polygonOffsetFactor]="-10" /> </ngts-decal> </ngt-mesh> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtsDecal],})export class SceneGraph { protected readonly Math = Math;
// Using a simple data URL for a circular decal pattern texture = textureResource( () => 'data:image/svg+xml,' + encodeURIComponent(` <svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128"> <circle cx="64" cy="64" r="60" fill="#ff6b6b"/> <circle cx="64" cy="64" r="40" fill="#4ecdc4"/> <circle cx="64" cy="64" r="20" fill="#fff"/> </svg> `), );}NgtsDecal is an abstraction around THREE.js DecalGeometry. It projects textures onto mesh surfaces for effects like stickers, logos, or damage marks.
The decal box must intersect the surface to be visible. If you don’t specify a rotation, it will orient toward the parent’s center point.
Usage
With Custom Material
<ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-basic-material /> <ngts-decal [options]="{ position: [0, 0, 0], rotation: [0, 0, 0], scale: 1, debug: true }"> <ngt-mesh-basic-material [map]="texture()" [polygonOffset]="true" [polygonOffsetFactor]="-1" /> </ngts-decal></ngt-mesh>With Default Material
If you don’t specify a material, a transparent MeshBasicMaterial with polygonOffsetFactor: -10 is created automatically:
<ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-basic-material /> <ngts-decal [options]="{ map: texture() }" /></ngt-mesh>With External Mesh Reference
<ngts-decal [mesh]="meshRef()" [options]="{ map: texture() }"> <ngt-mesh-basic-material [map]="texture()" [polygonOffset]="true" [polygonOffsetFactor]="-1" /></ngts-decal>Options
Properties
| name | type | description |
|---|---|---|
| map | THREE.Texture | The texture to use for the decal |
| position | [number, number, number] | Position of the decal. Default: [0, 0, 0] |
| rotation | [number, number, number] | number | Rotation of the decal. A single number spins around the normal |
| scale | number | [number, number, number] | Scale of the decal. Default: 1 |
| debug | boolean | Makes the bounding box of the decal visible. Default: false |
| depthTest | boolean | Whether to enable depth testing. Default: false |
| polygonOffsetFactor | number | The factor by which the polygon offset is multiplied. Default: -10 |
Abstraction around THREE.js DecalGeometry. Projects textures onto mesh surfaces for effects like stickers, logos, or damage marks.
The decal box must intersect the surface to be visible. If you don’t specify a rotation, it will orient toward the parent’s center point. You can also pass a single number as the rotation to spin the decal.
With Custom Material
Section titled “With Custom Material”<ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-basic-material /> <ngts-decal [options]="{ position: [0, 0, 0], rotation: [0, 0, 0], scale: 1, debug: true }"> <ngt-mesh-basic-material [map]="texture()" [polygonOffset]="true" [polygonOffsetFactor]="-1" /> </ngts-decal></ngt-mesh>With Default Material
Section titled “With Default Material”If you don’t specify a material, a transparent MeshBasicMaterial with polygonOffsetFactor: -10 is created automatically:
<ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-basic-material /> <ngts-decal [options]="{ map: texture() }" /></ngt-mesh>With External Mesh Reference
Section titled “With External Mesh Reference”When declarative composition isn’t possible, use the mesh input:
<ngts-decal [mesh]="meshRef()" [options]="{ map: texture() }"> <ngt-mesh-basic-material [map]="texture()" [polygonOffset]="true" [polygonOffsetFactor]="-1" /></ngts-decal>Inputs
Section titled “Inputs”| Property | Description | Default |
|---|---|---|
mesh | External mesh to attach the decal to | Parent |
options | Decal configuration object (see NgtsDecalOptions) | - |
NgtsDecalOptions
Section titled “NgtsDecalOptions”| Property | Description | Default |
|---|---|---|
map | The texture to use for the decal | undefined |
debug | Makes the “bounding box” of the decal visible | false |
depthTest | Whether to enable depth testing | false |
polygonOffsetFactor | The factor by which the polygon offset is multiplied | -10 |
position | Position of the decal [x, y, z] | [0, 0, 0] |
rotation | Rotation of the decal [x, y, z] or single number | - |
scale | Scale of the decal (uniform or [x, y, z]) | 1 |
Example: Multiple Decals on a Character
Section titled “Example: Multiple Decals on a Character”@Component({ template: ` <ngt-mesh> <ngt-capsule-geometry *args="[0.5, 1, 16, 32]" /> <ngt-mesh-standard-material color="white" />
<!-- Badge decal --> <ngts-decal [options]="{ position: [0, 0.5, 0.5], scale: 0.3, map: badgeTexture() }" />
<!-- Number decal --> <ngts-decal [options]="{ position: [0, -0.2, 0.5], scale: 0.2, map: numberTexture() }" /> </ngt-mesh> `,})class Character { badgeTexture = textureResource(() => 'badge.png'); numberTexture = textureResource(() => 'number.png');}Debug Mode
Section titled “Debug Mode”Set debug: true to visualize the decal’s bounding box:
<ngts-decal [options]="{ debug: true, position: [0, 0, 1], scale: 0.5 }"> <ngt-mesh-basic-material color="red" /></ngts-decal>