Skip to content

Commit 0c14c0c

Browse files
committed
backdrop, decal, sampler, computed attribute (non doc)
1 parent ff616c9 commit 0c14c0c

File tree

13 files changed

+821
-4
lines changed

13 files changed

+821
-4
lines changed

libs/core/src/lib/renderer/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ export class NgtRenderer implements Renderer2 {
401401
? value.map((v) => v.toString())
402402
: typeof value === 'function'
403403
? value
404-
: [value];
404+
: typeof value === 'string'
405+
? value.split('.')
406+
: [value];
405407
if (parent) attachThreeChild(parent, el);
406408
return;
407409
}
24.6 KB
Loading
18.7 KB
Loading

libs/soba/materials/src/lib/mesh-wobble-material.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ChangeDetectionStrategy, Component, computed, CUSTOM_ELEMENTS_SCHEMA, input } from '@angular/core';
2-
import { injectBeforeRender, NgtArgs, omit } from 'angular-three';
2+
import { injectBeforeRender, NgtArgs, NgtMeshStandardMaterial, omit } from 'angular-three';
33
import { MeshWobbleMaterial, MeshWobbleMaterialParameters } from 'angular-three-soba/vanilla-exports';
44
import { mergeInputs } from 'ngxtension/inject-inputs';
55

6-
export interface NgtsMeshWobbleMaterialOptions extends MeshWobbleMaterialParameters {
6+
export interface NgtsMeshWobbleMaterialOptions extends MeshWobbleMaterialParameters, Partial<NgtMeshStandardMaterial> {
77
speed: number;
88
}
99

libs/soba/misc/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
export * from './lib/animations';
22
export * from './lib/bake-shadows';
3+
export * from './lib/computed-attribute';
34
export * from './lib/constants';
5+
export * from './lib/decal';
46
export * from './lib/deprecated';
57
export * from './lib/depth-buffer';
68
export * from './lib/fbo';
9+
export * from './lib/sampler';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
CUSTOM_ELEMENTS_SCHEMA,
3+
ChangeDetectionStrategy,
4+
Component,
5+
ElementRef,
6+
afterNextRender,
7+
input,
8+
viewChild,
9+
} from '@angular/core';
10+
import { NgtArgs, NgtBufferAttribute, getLocalState } from 'angular-three';
11+
import { injectAutoEffect } from 'ngxtension/auto-effect';
12+
import { BufferAttribute, BufferGeometry } from 'three';
13+
14+
@Component({
15+
selector: 'ngts-computed-attribute',
16+
standalone: true,
17+
template: `
18+
<ngt-primitive #primitive *args="[bufferAttribute]" [attach]="['attributes', name()]" [parameters]="options()">
19+
<ng-content />
20+
</ngt-primitive>
21+
`,
22+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
23+
changeDetection: ChangeDetectionStrategy.OnPush,
24+
imports: [NgtArgs],
25+
})
26+
/**
27+
* Used exclusively as a child of a BufferGeometry.
28+
* Computes the BufferAttribute by calling the `compute` function
29+
* and attaches the attribute to the geometry.
30+
*/
31+
export class NgtsComputedAttribute {
32+
compute = input.required<(geometry: BufferGeometry) => BufferAttribute>();
33+
name = input.required<string>();
34+
options = input({} as Partial<NgtBufferAttribute>);
35+
36+
bufferAttribute = new BufferAttribute(new Float32Array(0), 1);
37+
primitive = viewChild<ElementRef<BufferAttribute>>('primitive');
38+
39+
constructor() {
40+
const autoEffect = injectAutoEffect();
41+
42+
afterNextRender(() => {
43+
autoEffect(() => {
44+
const primitive = this.primitive()?.nativeElement;
45+
if (!primitive) return;
46+
47+
const localState = getLocalState(primitive);
48+
if (!localState) return;
49+
50+
const geometry = ((primitive as any).parent as BufferGeometry) ?? localState.parent();
51+
console.log(geometry);
52+
53+
const attribute = this.compute()(geometry);
54+
primitive.copy(attribute);
55+
});
56+
});
57+
}
58+
}

libs/soba/misc/src/lib/decal.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import {
2+
afterNextRender,
3+
ChangeDetectionStrategy,
4+
Component,
5+
CUSTOM_ELEMENTS_SCHEMA,
6+
ElementRef,
7+
input,
8+
viewChild,
9+
} from '@angular/core';
10+
import { applyProps, extend, getLocalState, NgtMesh, omit, pick, resolveRef } from 'angular-three';
11+
import { injectAutoEffect } from 'ngxtension/auto-effect';
12+
import { mergeInputs } from 'ngxtension/inject-inputs';
13+
import { AxesHelper, BoxGeometry, Euler, Mesh, MeshNormalMaterial, Object3D, Texture, Vector3 } from 'three';
14+
import { DecalGeometry } from 'three-stdlib';
15+
16+
export interface NgtsDecalOptions extends Partial<NgtMesh> {
17+
polygonOffsetFactor: number;
18+
depthTest: boolean;
19+
debug: boolean;
20+
map?: Texture;
21+
}
22+
23+
const defaultOptions: NgtsDecalOptions = {
24+
polygonOffsetFactor: -10,
25+
debug: false,
26+
depthTest: false,
27+
};
28+
29+
@Component({
30+
selector: 'ngts-decal',
31+
standalone: true,
32+
template: `
33+
<ngt-mesh #mesh [parameters]="parameters()">
34+
<ng-content />
35+
36+
@if (debug()) {
37+
<ngt-mesh #helper>
38+
<ngt-box-geometry />
39+
<ngt-mesh-normal-material [wireframe]="true" />
40+
<ngt-axes-helper />
41+
</ngt-mesh>
42+
}
43+
44+
<ngt-value [rawValue]="true" attach="material.transparent" />
45+
<ngt-value [rawValue]="true" attach="material.polygonOffset" />
46+
<ngt-value [rawValue]="polygonOffsetFactor()" attach="material.polygonOffsetFactor" />
47+
<ngt-value [rawValue]="depthTest()" attach="material.depthTest" />
48+
<ngt-value [rawValue]="map()" attach="material.map" />
49+
</ngt-mesh>
50+
`,
51+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
52+
changeDetection: ChangeDetectionStrategy.OnPush,
53+
})
54+
export class NgtsDecal {
55+
mesh = input<ElementRef<Mesh> | Mesh | null>();
56+
options = input(defaultOptions, { transform: mergeInputs(defaultOptions) });
57+
parameters = omit(this.options, [
58+
'debug',
59+
'map',
60+
'depthTest',
61+
'polygonOffsetFactor',
62+
'position',
63+
'scale',
64+
'rotation',
65+
]);
66+
67+
meshRef = viewChild.required<ElementRef<Mesh>>('mesh');
68+
helperRef = viewChild<ElementRef<Mesh>>('helper');
69+
70+
map = pick(this.options, 'map');
71+
depthTest = pick(this.options, 'depthTest');
72+
polygonOffsetFactor = pick(this.options, 'polygonOffsetFactor');
73+
debug = pick(this.options, 'debug');
74+
private position = pick(this.options, 'position');
75+
private rotation = pick(this.options, 'rotation');
76+
private scale = pick(this.options, 'scale');
77+
78+
constructor() {
79+
extend({ Mesh, BoxGeometry, MeshNormalMaterial, AxesHelper });
80+
const autoEffect = injectAutoEffect();
81+
82+
afterNextRender(() => {
83+
autoEffect(() => {
84+
const thisMesh = this.meshRef().nativeElement;
85+
const localState = getLocalState(thisMesh);
86+
if (!localState) return;
87+
88+
const mesh = resolveRef(this.mesh());
89+
const parent = mesh || localState.parent();
90+
91+
if (parent && !(parent as Mesh).isMesh) {
92+
throw new Error('<ngts-decal> must have a Mesh as parent or specify its "mesh" input');
93+
}
94+
95+
if (!parent) return;
96+
97+
const [position, rotation, scale] = [this.position(), this.rotation(), this.scale()];
98+
const state = {
99+
position: new Vector3(),
100+
rotation: new Euler(),
101+
scale: new Vector3(1, 1, 1),
102+
};
103+
104+
applyProps(state, { position, scale });
105+
106+
// zero out the parents matrix world for this operation
107+
const matrixWorld = parent.matrixWorld.clone();
108+
parent.matrixWorld.identity();
109+
110+
if (!rotation || typeof rotation === 'number') {
111+
const o = new Object3D();
112+
o.position.copy(state.position);
113+
o.lookAt(parent.position);
114+
if (typeof rotation === 'number') o.rotateZ(rotation);
115+
applyProps(state, { rotation: o.rotation });
116+
} else {
117+
applyProps(state, { rotation });
118+
}
119+
120+
thisMesh.geometry = new DecalGeometry(parent, state.position, state.rotation, state.scale);
121+
const helper = this.helperRef()?.nativeElement;
122+
if (helper) {
123+
applyProps(helper, state);
124+
// Prevent the helpers from blocking rays
125+
helper.traverse((child) => (child.raycast = () => null));
126+
}
127+
128+
// Reset parents matrix-world
129+
parent.matrixWorld = matrixWorld;
130+
131+
return () => {
132+
thisMesh.geometry.dispose();
133+
};
134+
});
135+
});
136+
}
137+
}

0 commit comments

Comments
 (0)