Skip to content

Commit 204bb49

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
feat(soba): add Grid
1 parent 774fc7b commit 204bb49

File tree

5 files changed

+232
-0
lines changed

5 files changed

+232
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core';
2+
import { NgtArgs, NgtSignalStore, extend, injectNgtRef } from 'angular-three';
3+
import { GridMaterial } from 'angular-three-soba/shaders';
4+
import * as THREE from 'three';
5+
import { Mesh, PlaneGeometry } from 'three';
6+
7+
extend({ Mesh, GridMaterial, PlaneGeometry });
8+
9+
export interface NgtsGridState {
10+
/** Cell size, default: 0.5 */
11+
cellSize: number;
12+
/** Cell thickness, default: 0.5 */
13+
cellThickness: number;
14+
/** Cell color, default: black */
15+
cellColor: THREE.ColorRepresentation;
16+
/** Section size, default: 1 */
17+
sectionSize: number;
18+
/** Section thickness, default: 1 */
19+
sectionThickness: number;
20+
/** Section color, default: #2080ff */
21+
sectionColor: THREE.ColorRepresentation;
22+
/** Follow camera, default: false */
23+
followCamera: boolean;
24+
/** Display the grid infinitely, default: false */
25+
infiniteGrid: boolean;
26+
/** Fade distance, default: 100 */
27+
fadeDistance: number;
28+
/** Fade strength, default: 1 */
29+
fadeStrength: number;
30+
/** Material side, default: THREE.BackSide */
31+
side: THREE.Side;
32+
/** Default plane-geometry arguments */
33+
args?: ConstructorParameters<typeof THREE.PlaneGeometry>;
34+
}
35+
36+
@Component({
37+
selector: 'ngts-grid',
38+
standalone: true,
39+
template: `
40+
<ngt-mesh ngtCompound [ref]="gridRef" [frustumCulled]="false">
41+
<ngt-grid-material
42+
[transparent]="true"
43+
[side]="gridSide()"
44+
[cellSize]="gridCellSize()"
45+
[sectionSize]="gridSectionSize()"
46+
[cellColor]="gridCellColor()"
47+
[sectionColor]="gridSectionColor()"
48+
[cellThickness]="gridCellThickness()"
49+
[sectionThickness]="gridSectionThickness()"
50+
[fadeDistance]="gridFadeDistance()"
51+
[fadeStrength]="gridFadeStrength()"
52+
[infiniteGrid]="gridInfiniteGrid()"
53+
[followCamera]="gridFollowCamera()"
54+
/>
55+
<ngt-plane-geometry *args="gridArgs()" />
56+
</ngt-mesh>
57+
`,
58+
imports: [NgtArgs],
59+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
60+
})
61+
export class NgtsGrid extends NgtSignalStore<NgtsGridState> {
62+
@Input() gridRef = injectNgtRef<THREE.Mesh>();
63+
@Input() set cellSize(cellSize: NgtsGridState['cellSize']) {
64+
this.set({ cellSize });
65+
}
66+
67+
@Input() set cellThickness(cellThickness: NgtsGridState['cellThickness']) {
68+
this.set({ cellThickness });
69+
}
70+
71+
@Input() set cellColor(cellColor: NgtsGridState['cellColor']) {
72+
this.set({ cellColor });
73+
}
74+
75+
@Input() set sectionSize(sectionSize: NgtsGridState['sectionSize']) {
76+
this.set({ sectionSize });
77+
}
78+
79+
@Input() set sectionThickness(sectionThickness: NgtsGridState['sectionThickness']) {
80+
this.set({ sectionThickness });
81+
}
82+
83+
@Input() set sectionColor(sectionColor: NgtsGridState['sectionColor']) {
84+
this.set({ sectionColor });
85+
}
86+
87+
@Input() set followCamera(followCamera: NgtsGridState['followCamera']) {
88+
this.set({ followCamera });
89+
}
90+
91+
@Input() set infiniteGrid(infiniteGrid: NgtsGridState['infiniteGrid']) {
92+
this.set({ infiniteGrid });
93+
}
94+
95+
@Input() set fadeDistance(fadeDistance: NgtsGridState['fadeDistance']) {
96+
this.set({ fadeDistance });
97+
}
98+
99+
@Input() set fadeStrength(fadeStrength: NgtsGridState['fadeStrength']) {
100+
this.set({ fadeStrength });
101+
}
102+
103+
@Input() set side(side: NgtsGridState['side']) {
104+
this.set({ side });
105+
}
106+
107+
@Input() set args(args: NgtsGridState['args']) {
108+
this.set({ args });
109+
}
110+
111+
readonly gridCellSize = this.select('cellSize');
112+
readonly gridSectionSize = this.select('sectionSize');
113+
readonly gridFadeDistance = this.select('fadeDistance');
114+
readonly gridFadeStrength = this.select('fadeStrength');
115+
readonly gridCellThickness = this.select('cellThickness');
116+
readonly gridSectionThickness = this.select('sectionThickness');
117+
readonly gridInfiniteGrid = this.select('infiniteGrid');
118+
readonly gridFollowCamera = this.select('followCamera');
119+
readonly gridCellColor = this.select('cellColor');
120+
readonly gridSectionColor = this.select('sectionColor');
121+
readonly gridSide = this.select('side');
122+
readonly gridArgs = this.select('args');
123+
124+
constructor() {
125+
super({
126+
cellSize: 0.5,
127+
sectionSize: 1,
128+
fadeDistance: 100,
129+
fadeStrength: 1,
130+
cellThickness: 0.5,
131+
sectionThickness: 1,
132+
infiniteGrid: false,
133+
followCamera: false,
134+
cellColor: '#000000',
135+
sectionColor: '#2080ff',
136+
side: THREE.BackSide,
137+
args: [1, 1, 1],
138+
});
139+
}
140+
}

libs/soba/abstractions/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './edges/edges';
55
export * from './gizmo-helper/gizmo-helper';
66
export * from './gizmo-helper/gizmo-viewcube/gizmo-viewcube';
77
export * from './gizmo-helper/gizmo-viewport/gizmo-viewport';
8+
export * from './grid/grid';
89
export * from './line/line';
910
export * from './quadratic-bezier-line/quadratic-bezier-line';
1011
export * from './text-3d/text-3d';
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as THREE from 'three';
2+
import { shaderMaterial } from '../shader-material/shader-material';
3+
4+
export const GridMaterial = shaderMaterial(
5+
{
6+
cellSize: 0.5,
7+
sectionSize: 1,
8+
fadeDistance: 100,
9+
fadeStrength: 1,
10+
cellThickness: 0.5,
11+
sectionThickness: 1,
12+
cellColor: new THREE.Color(),
13+
sectionColor: new THREE.Color(),
14+
infiniteGrid: false,
15+
followCamera: false,
16+
},
17+
/* glsl */ `
18+
varying vec3 worldPosition;
19+
uniform float fadeDistance;
20+
uniform bool infiniteGrid;
21+
uniform bool followCamera;
22+
23+
void main() {
24+
worldPosition = position.xzy;
25+
if (infiniteGrid) worldPosition *= 1.0 + fadeDistance;
26+
if (followCamera) worldPosition.xz +=cameraPosition.xz;
27+
28+
gl_Position = projectionMatrix * modelViewMatrix * vec4(worldPosition, 1.0);
29+
}
30+
`,
31+
/* glsl */ `
32+
varying vec3 worldPosition;
33+
uniform float cellSize;
34+
uniform float sectionSize;
35+
uniform vec3 cellColor;
36+
uniform vec3 sectionColor;
37+
uniform float fadeDistance;
38+
uniform float fadeStrength;
39+
uniform float cellThickness;
40+
uniform float sectionThickness;
41+
42+
float getGrid(float size, float thickness) {
43+
vec2 r = worldPosition.xz / size;
44+
vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r);
45+
float line = min(grid.x, grid.y) + 1. - thickness;
46+
return 1.0 - min(line, 1.);
47+
}
48+
49+
void main() {
50+
float g1 = getGrid(cellSize, cellThickness);
51+
float g2 = getGrid(sectionSize, sectionThickness);
52+
53+
float d = 1.0 - min(distance(cameraPosition.xz, worldPosition.xz) / fadeDistance, 1.);
54+
vec3 color = mix(cellColor, sectionColor, min(1.,sectionThickness * g2));
55+
56+
gl_FragColor = vec4(color, (g1 + g2) * pow(d,fadeStrength));
57+
gl_FragColor.a = mix(0.75 * gl_FragColor.a, gl_FragColor.a, g2);
58+
if (gl_FragColor.a <= 0.0) discard;
59+
60+
#include <tonemapping_fragment>
61+
#include <encodings_fragment>
62+
}
63+
`
64+
);

libs/soba/shaders/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './caustics-material/caustics-material';
33
export * from './caustics-material/caustics-projection-material';
44
export * from './convolution-material/convolution-material';
55
export * from './discard-material/discard-material';
6+
export * from './grid-material/grid-material';
67
export * from './mesh-distort-material/mesh-distort-material';
78
export * from './mesh-reflector-material/mesh-reflector-material';
89
export * from './mesh-refraction-material/mesh-refraction-material';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
2+
import { Meta, moduleMetadata } from '@storybook/angular';
3+
import { NgtsGrid } from 'angular-three-soba/abstractions';
4+
import { StorybookSetup, makeStoryFunction } from '../setup-canvas';
5+
6+
@Component({
7+
standalone: true,
8+
template: `
9+
<ngts-grid cellColor="white" [args]="[10, 10]" />
10+
<ngt-mesh [position]="[0, 0.5, 0]">
11+
<ngt-box-geometry />
12+
<ngt-mesh-standard-material />
13+
</ngt-mesh>
14+
<ngt-directional-light [position]="[10, 10, 5]" />
15+
`,
16+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
17+
imports: [NgtsGrid],
18+
})
19+
class DefaultGridStory {}
20+
21+
export default {
22+
title: 'Gizmo/Grid',
23+
decorators: [moduleMetadata({ imports: [StorybookSetup] })],
24+
} as Meta;
25+
26+
export const Default = makeStoryFunction(DefaultGridStory, { camera: { position: [-5, 5, 10] } });

0 commit comments

Comments
 (0)