Skip to content

Commit e31a003

Browse files
authored
Merge pull request #12 from NanoForge-dev/feat/graphics/add-graphics
feat(graphics): add base component and circle component
2 parents 0dd55ed + fa11fc9 commit e31a003

File tree

11 files changed

+184
-23
lines changed

11 files changed

+184
-23
lines changed

packages/asset-manager/src/file.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { type IFile } from "@nanoforge/common";
2-
3-
export class NfFile implements IFile {
1+
export class NfFile {
42
private readonly _path: string;
53

64
constructor(path: string) {

packages/asset-manager/test/asset-manager.library.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe("Asset Manager Library", () => {
1818
library.init(context);
1919

2020
it("Should get asset", async () => {
21-
await expect(library.getAsset("test.png")).resolves.toEqual(
21+
expect((await library.getAsset("test.png")).path).toEqual(
2222
"blob:http://localhost:3000/test.png",
2323
);
2424
});
@@ -28,7 +28,7 @@ describe("Asset Manager Library", () => {
2828
});
2929

3030
it("Should get wasm", async () => {
31-
await expect(library.getWasm("test.wasm")).resolves.toEqual(
31+
expect((await library.getWasm("test.wasm")).path).toEqual(
3232
"blob:http://localhost:3000/test.wasm",
3333
);
3434
});
@@ -38,7 +38,7 @@ describe("Asset Manager Library", () => {
3838
});
3939

4040
it("Should get wgsl", async () => {
41-
await expect(library.getWgsl("test.wgsl")).resolves.toEqual(
41+
expect((await library.getWgsl("test.wgsl")).path).toEqual(
4242
"blob:http://localhost:3000/test.wgsl",
4343
);
4444
});
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { IExposedLibrary } from "./bases/exposed.library.type";
22
export { IRunnerLibrary } from "./bases/runner.library.type";
3-
export { IAssetManagerLibrary, IFile } from "./finals/asset-manager.library.type";
3+
export { IAssetManagerLibrary } from "./finals/asset-manager.library.type";
44
export { IComponentSystemLibrary } from "./finals/component-system.library.type";
55
export { IGraphicsLibrary } from "./finals/graphics.library.type";
66
export { INetworkLibrary } from "./finals/network.library.type";
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import type { GraphicsCore } from "../core";
2+
import type { ShaderManager } from "../shader/shader.manager";
3+
4+
export abstract class NfgComponent {
5+
private readonly _core: GraphicsCore;
6+
protected readonly _shaderManager: ShaderManager;
7+
private _vertices: Float32Array;
8+
protected _vertexBuffer: GPUBuffer;
9+
protected abstract _vertexLength: number;
10+
protected abstract readonly _vertexBufferLayout: GPUVertexBufferLayout;
11+
private _uniformBuffer: GPUBuffer;
12+
protected abstract _shader: GPUShaderModule;
13+
private _pipeline: GPURenderPipeline;
14+
private readonly _pipelineLayout: GPUPipelineLayout;
15+
private readonly _label: string;
16+
private _bindGroup: GPUBindGroup;
17+
18+
constructor(core: GraphicsCore) {
19+
this._core = core;
20+
this._shaderManager = core.shaderManager;
21+
this._label = `${this.constructor.name} - ${Date.now()}`;
22+
23+
const bindGroupLayout = this._core.device.createBindGroupLayout({
24+
label: `${this._label} Bind Group Layout`,
25+
entries: [
26+
{
27+
binding: 0,
28+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,
29+
buffer: {},
30+
},
31+
],
32+
});
33+
34+
this._pipelineLayout = this._core.device.createPipelineLayout({
35+
label: `${this._label} Pipeline Layout`,
36+
bindGroupLayouts: [bindGroupLayout],
37+
});
38+
}
39+
40+
public async init(): Promise<NfgComponent> {
41+
await this._init();
42+
this._updateUniforms();
43+
this._updatePipeline();
44+
return this;
45+
}
46+
47+
public draw(pass: GPURenderPassEncoder): void {
48+
pass.setPipeline(this._pipeline);
49+
pass.setBindGroup(0, this._bindGroup);
50+
pass.setVertexBuffer(0, this._vertexBuffer);
51+
pass.draw(this._vertices.length / this._vertexLength);
52+
}
53+
54+
protected abstract _init(): Promise<void>;
55+
56+
protected _setVertices(raw: number[]): void {
57+
this._vertices = new Float32Array(raw);
58+
this._updateVertexBuffer();
59+
}
60+
61+
protected _updateVertexBuffer(): void {
62+
this._core.device.queue.writeBuffer(this._vertexBuffer, 0, this._vertices);
63+
}
64+
65+
protected _updatePipeline(): void {
66+
this._pipeline = this._core.device.createRenderPipeline({
67+
label: `${this._label} pipeline`,
68+
layout: this._pipelineLayout,
69+
vertex: {
70+
module: this._shader,
71+
entryPoint: "vertex_main",
72+
buffers: [this._vertexBufferLayout],
73+
},
74+
fragment: {
75+
module: this._shader,
76+
entryPoint: "fragment_main",
77+
targets: [
78+
{
79+
format: this._core.render.canvasFormat,
80+
},
81+
],
82+
},
83+
});
84+
this._updateBindGroup();
85+
}
86+
87+
protected _updateUniforms(): void {
88+
const uniformArray = new Float32Array([
89+
0,
90+
0,
91+
1,
92+
this._core.initContext.canvas.width,
93+
this._core.initContext.canvas.height,
94+
]);
95+
this._uniformBuffer = this._core.device.createBuffer({
96+
label: "View Uniforms",
97+
size: uniformArray.byteLength,
98+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, // Une uniform est une valeur constante pour le gpu
99+
});
100+
this._core.device.queue.writeBuffer(this._uniformBuffer, 0, uniformArray);
101+
}
102+
103+
protected _updateBindGroup(): void {
104+
this._bindGroup = this._core.device.createBindGroup({
105+
label: `${this._label} renderer bind group`,
106+
layout: this._pipeline.getBindGroupLayout(0),
107+
entries: [
108+
{
109+
binding: 0,
110+
resource: { buffer: this._uniformBuffer },
111+
},
112+
],
113+
});
114+
}
115+
}
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
import { type GraphicsCore } from "../../../core";
2-
import { type ShaderManager } from "../../../shader/shader.manager";
1+
import { NfgComponent } from "../../component";
32

4-
export class NfShape {
5-
private readonly _shaderManager: ShaderManager;
6-
7-
constructor(core: GraphicsCore) {
8-
this._shaderManager = core.shaderManager;
9-
}
10-
}
3+
export abstract class NfgShape extends NfgComponent {}
Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,75 @@
11
import { type GraphicsCore } from "../../../core";
2+
import { ShadersEnum } from "../../../shader/shaders.enum";
23
import { type ICircleOptions, type IColor, type IVertex2D } from "../../../types";
3-
import { NfShape } from "../common/shape";
4+
import { NfgShape } from "../common/shape";
5+
6+
export class NfgCircle extends NfgShape {
7+
protected _shader: GPUShaderModule;
8+
protected readonly _vertexBufferLayout: GPUVertexBufferLayout;
9+
protected _vertexLength: number;
410

5-
export class NfCircle extends NfShape {
611
private _pos: IVertex2D;
712
private _radius: number;
813
private _color: IColor;
914

1015
constructor(core: GraphicsCore, options?: Partial<ICircleOptions>) {
1116
super(core);
1217

18+
this._vertexBufferLayout = {
19+
arrayStride: 28,
20+
attributes: [
21+
{
22+
format: "float32x2",
23+
offset: 0,
24+
shaderLocation: 0,
25+
},
26+
{
27+
format: "float32",
28+
offset: 8,
29+
shaderLocation: 1,
30+
},
31+
{
32+
format: "float32x4",
33+
offset: 12,
34+
shaderLocation: 2,
35+
},
36+
],
37+
};
38+
this._vertexLength = 7;
39+
1340
this._pos = options?.pos ?? { x: 0, y: 0 };
1441
this._radius = options?.radius ?? 1;
1542
this._color = options?.color ?? { r: 0, g: 0, b: 0, a: 1 };
1643
}
1744

1845
public setPosition(pos: IVertex2D): void {
1946
this._pos = pos;
47+
this._updateVertices();
2048
}
2149

2250
public setRadius(radius: number): void {
2351
this._radius = radius;
52+
this._updateVertices();
2453
}
2554

2655
public setColor(color: IColor): void {
2756
this._color = color;
57+
this._updateVertices();
58+
}
59+
60+
protected async _init(): Promise<void> {
61+
this._shader = await this._shaderManager.get(ShadersEnum.CIRCLE);
62+
}
63+
64+
protected _updateVertices(): void {
65+
this._setVertices([
66+
this._pos.x,
67+
this._pos.y,
68+
this._radius,
69+
this._color.r,
70+
this._color.g,
71+
this._color.b,
72+
this._color.a,
73+
]);
2874
}
2975
}

packages/graphics-2d/src/core.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export class GraphicsCore {
3434
return this._shaderManager;
3535
}
3636

37+
get render(): GraphicsRender {
38+
return this._render;
39+
}
40+
3741
public async init(): Promise<void> {
3842
if (!navigator.gpu) {
3943
throw new Error("WebGPU not supported on this browser.");

packages/graphics-2d/src/graphics-2d.library.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { BaseGraphicsLibrary, type ExecutionContext, type InitContext } from "@nanoforge/common";
22

3-
import { type ICircleOptions } from "./types";
4-
53
export class Graphics2DLibrary extends BaseGraphicsLibrary {
64
get name(): string {
75
return "Graphics2DLibrary";
@@ -13,8 +11,6 @@ export class Graphics2DLibrary extends BaseGraphicsLibrary {
1311
}
1412
}
1513

16-
public createCircle(options: ICircleOptions): void {}
17-
1814
public async run(context: ExecutionContext): Promise<void> {
1915
console.log(context);
2016
}

packages/graphics-2d/src/render.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,12 @@ export class GraphicsRender {
2626
format: this._canvasFormat,
2727
});
2828
}
29+
30+
get canvasContext(): GPUCanvasContext {
31+
return this._canvasContext;
32+
}
33+
34+
get canvasFormat(): GPUTextureFormat {
35+
return this._canvasFormat;
36+
}
2937
}
File renamed without changes.

0 commit comments

Comments
 (0)