Skip to content

Commit 9e1ad4c

Browse files
committed
shadow
1 parent c6c0977 commit 9e1ad4c

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

libs/soba/misc/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from './depth-buffer/depth-buffer';
55
export * from './example/example';
66
export * from './fbo/fbo';
77
export * from './sampler/sampler';
8+
export * from './shadow/shadow';

libs/soba/misc/src/shadow/shadow.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input, computed } from '@angular/core';
2+
import { NgtArgs, NgtMesh, extend, injectNgtRef, signalStore } from 'angular-three';
3+
import * as THREE from 'three';
4+
import { CanvasTexture, Mesh, MeshBasicMaterial, PlaneGeometry } from 'three';
5+
6+
export type NgtsShadowState = {
7+
colorStop: number;
8+
fog: boolean;
9+
color: THREE.ColorRepresentation;
10+
opacity: number;
11+
depthWrite: boolean;
12+
};
13+
14+
declare global {
15+
interface HTMLElementTagNameMap {
16+
/**
17+
* @extends ngt-mesh
18+
*/
19+
'ngts-shadow': NgtsShadowState & NgtMesh;
20+
}
21+
}
22+
23+
extend({ Mesh, CanvasTexture, PlaneGeometry, MeshBasicMaterial });
24+
25+
@Component({
26+
selector: 'ngts-shadow',
27+
standalone: true,
28+
template: `
29+
<ngt-mesh ngtCompound [ref]="shadowRef" [rotation]="[-Math.PI / 2, 0, 0]">
30+
<ngt-plane-geometry />
31+
<ngt-mesh-basic-material
32+
[transparent]="true"
33+
[opacity]="opacity()"
34+
[fog]="fog()"
35+
[depthWrite]="depthWrite()"
36+
[side]="DoubleSide"
37+
>
38+
<ngt-canvas-texture *args="[canvas()]" attach="map" />
39+
</ngt-mesh-basic-material>
40+
<ng-content />
41+
</ngt-mesh>
42+
`,
43+
imports: [NgtArgs],
44+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
45+
})
46+
export class NgtsShadow {
47+
Math = Math;
48+
DoubleSide = THREE.DoubleSide;
49+
50+
private inputs = signalStore<NgtsShadowState>({
51+
fog: false,
52+
depthWrite: false,
53+
colorStop: 0.0,
54+
color: 'black',
55+
opacity: 0.5,
56+
});
57+
58+
@Input() shadowRef = injectNgtRef<Mesh>();
59+
60+
@Input({ alias: 'colorStop' }) set _colorStop(colorStop: number) {
61+
this.inputs.set({ colorStop });
62+
}
63+
64+
@Input({ alias: 'fog' }) set _fog(fog: boolean) {
65+
this.inputs.set({ fog });
66+
}
67+
68+
@Input({ alias: 'color' }) set _color(color: THREE.ColorRepresentation) {
69+
this.inputs.set({ color });
70+
}
71+
72+
@Input({ alias: 'opacity' }) set _opacity(opacity: number) {
73+
this.inputs.set({ opacity });
74+
}
75+
76+
@Input({ alias: 'depthWrite' }) set _depthWrite(depthWrite: boolean) {
77+
this.inputs.set({ depthWrite });
78+
}
79+
80+
private colorStop = this.inputs.select('colorStop');
81+
private color = this.inputs.select('color');
82+
83+
opacity = this.inputs.select('opacity');
84+
fog = this.inputs.select('fog');
85+
depthWrite = this.inputs.select('depthWrite');
86+
87+
canvas = computed(() => {
88+
const [colorStop, color] = [this.colorStop(), this.color()];
89+
const canvas = document.createElement('canvas');
90+
canvas.width = 128;
91+
canvas.height = 128;
92+
const context = canvas.getContext('2d') as CanvasRenderingContext2D;
93+
const gradient = context.createRadialGradient(
94+
canvas.width / 2,
95+
canvas.height / 2,
96+
0,
97+
canvas.width / 2,
98+
canvas.height / 2,
99+
canvas.width / 2,
100+
);
101+
gradient.addColorStop(colorStop, new THREE.Color(color).getStyle());
102+
gradient.addColorStop(1, 'rgba(0,0,0,0)');
103+
context.fillStyle = gradient;
104+
context.fillRect(0, 0, canvas.width, canvas.height);
105+
return canvas;
106+
});
107+
}
108+
109+
//
110+
//
111+
// type Props = JSX.IntrinsicElements['mesh'] & {
112+
// colorStop?: number
113+
// fog?: boolean
114+
// color?: Color | number | string
115+
// opacity?: number
116+
// depthWrite?: boolean
117+
// }
118+
//
119+
// export const Shadow = React.forwardRef(
120+
// (
121+
// { fog = false, renderOrder, depthWrite = false, colorStop = 0.0, color = 'black', opacity = 0.5, ...props }: Props,
122+
// ref
123+
// ) => {
124+
// const canvas = React.useMemo(() => {
125+
// const canvas = document.createElement('canvas')
126+
// canvas.width = 128
127+
// canvas.height = 128
128+
// const context = canvas.getContext('2d') as CanvasRenderingContext2D
129+
// const gradient = context.createRadialGradient(
130+
// canvas.width / 2,
131+
// canvas.height / 2,
132+
// 0,
133+
// canvas.width / 2,
134+
// canvas.height / 2,
135+
// canvas.width / 2
136+
// )
137+
// gradient.addColorStop(colorStop, new Color(color).getStyle())
138+
// gradient.addColorStop(1, 'rgba(0,0,0,0)')
139+
// context.fillStyle = gradient
140+
// context.fillRect(0, 0, canvas.width, canvas.height)
141+
// return canvas
142+
// }, [color, colorStop])
143+
// return (
144+
// <mesh renderOrder={renderOrder} ref={ref as React.MutableRefObject<Mesh>} rotation-x={-Math.PI / 2} {...props}>
145+
// <planeGeometry />
146+
// <meshBasicMaterial transparent opacity={opacity} fog={fog} depthWrite={depthWrite} side={DoubleSide}>
147+
// <canvasTexture attach="map" args={[canvas]} />
148+
// </meshBasicMaterial>
149+
// </mesh>
150+
// )
151+
// }
152+
// )

libs/soba/src/misc/shadow.stories.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component, ElementRef, ViewChild } from '@angular/core';
2+
import { NgtArgs, injectBeforeRender } from 'angular-three';
3+
import { NgtsShadow } from 'angular-three-soba/misc';
4+
import { Mesh } from 'three';
5+
import { makeDecorators, makeStoryFunction } from '../setup-canvas';
6+
7+
@Component({
8+
standalone: true,
9+
template: `
10+
<ngt-mesh #icosahedron [position]="[0, 2, 0]">
11+
<ngt-icosahedron-geometry *args="[1, 2]" />
12+
<ngt-mesh-basic-material color="lightblue" [wireframe]="true" />
13+
</ngt-mesh>
14+
15+
<ngts-shadow #shadow [scale]="2" [position]="[0, 0.1, 0]" [rotation]="[-Math.PI / 2, 0, 0]" />
16+
17+
<ngt-mesh [rotation]="[-Math.PI / 2, 0, 0]">
18+
<ngt-plane-geometry *args="[4, 4]" />
19+
<ngt-mesh-basic-material color="white" />
20+
</ngt-mesh>
21+
`,
22+
imports: [NgtsShadow, NgtArgs],
23+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
24+
})
25+
class DefaultShadowStory {
26+
Math = Math;
27+
28+
@ViewChild('icosahedron', { static: true }) icosahedron!: ElementRef<Mesh>;
29+
@ViewChild('shadow', { static: true }) shadow!: NgtsShadow;
30+
31+
constructor() {
32+
injectBeforeRender(({ clock }) => {
33+
const icosahedron = this.icosahedron.nativeElement;
34+
const shadow = this.shadow.shadowRef.nativeElement;
35+
36+
if (icosahedron && shadow) {
37+
shadow.scale.x = Math.sin(clock.getElapsedTime()) + 3;
38+
shadow.scale.y = Math.sin(clock.getElapsedTime()) + 3;
39+
40+
icosahedron.position.y = Math.sin(clock.getElapsedTime()) + 2.5;
41+
}
42+
});
43+
}
44+
}
45+
46+
export default {
47+
title: 'Misc/Shadow',
48+
decorators: makeDecorators(),
49+
};
50+
51+
export const Default = makeStoryFunction(DefaultShadowStory);

0 commit comments

Comments
 (0)