diff --git a/packages/asset-manager/src/file.ts b/packages/asset-manager/src/file.ts index 849456f1..94ebd9ec 100644 --- a/packages/asset-manager/src/file.ts +++ b/packages/asset-manager/src/file.ts @@ -1,6 +1,4 @@ -import { type IFile } from "@nanoforge/common"; - -export class NfFile implements IFile { +export class NfFile { private readonly _path: string; constructor(path: string) { diff --git a/packages/asset-manager/test/asset-manager.library.spec.ts b/packages/asset-manager/test/asset-manager.library.spec.ts index 9c246c78..52c420f0 100644 --- a/packages/asset-manager/test/asset-manager.library.spec.ts +++ b/packages/asset-manager/test/asset-manager.library.spec.ts @@ -18,7 +18,7 @@ describe("Asset Manager Library", () => { library.init(context); it("Should get asset", async () => { - await expect(library.getAsset("test.png")).resolves.toEqual( + expect((await library.getAsset("test.png")).path).toEqual( "blob:http://localhost:3000/test.png", ); }); @@ -28,7 +28,7 @@ describe("Asset Manager Library", () => { }); it("Should get wasm", async () => { - await expect(library.getWasm("test.wasm")).resolves.toEqual( + expect((await library.getWasm("test.wasm")).path).toEqual( "blob:http://localhost:3000/test.wasm", ); }); @@ -38,7 +38,7 @@ describe("Asset Manager Library", () => { }); it("Should get wgsl", async () => { - await expect(library.getWgsl("test.wgsl")).resolves.toEqual( + expect((await library.getWgsl("test.wgsl")).path).toEqual( "blob:http://localhost:3000/test.wgsl", ); }); diff --git a/packages/common/src/library/libraries/interfaces/index.ts b/packages/common/src/library/libraries/interfaces/index.ts index 19028c01..1f1aea24 100644 --- a/packages/common/src/library/libraries/interfaces/index.ts +++ b/packages/common/src/library/libraries/interfaces/index.ts @@ -1,6 +1,6 @@ export { IExposedLibrary } from "./bases/exposed.library.type"; export { IRunnerLibrary } from "./bases/runner.library.type"; -export { IAssetManagerLibrary, IFile } from "./finals/asset-manager.library.type"; +export { IAssetManagerLibrary } from "./finals/asset-manager.library.type"; export { IComponentSystemLibrary } from "./finals/component-system.library.type"; export { IGraphicsLibrary } from "./finals/graphics.library.type"; export { INetworkLibrary } from "./finals/network.library.type"; diff --git a/packages/graphics-2d/src/components/component.ts b/packages/graphics-2d/src/components/component.ts new file mode 100644 index 00000000..bec886e5 --- /dev/null +++ b/packages/graphics-2d/src/components/component.ts @@ -0,0 +1,115 @@ +import type { GraphicsCore } from "../core"; +import type { ShaderManager } from "../shader/shader.manager"; + +export abstract class NfgComponent { + private readonly _core: GraphicsCore; + protected readonly _shaderManager: ShaderManager; + private _vertices: Float32Array; + protected _vertexBuffer: GPUBuffer; + protected abstract _vertexLength: number; + protected abstract readonly _vertexBufferLayout: GPUVertexBufferLayout; + private _uniformBuffer: GPUBuffer; + protected abstract _shader: GPUShaderModule; + private _pipeline: GPURenderPipeline; + private readonly _pipelineLayout: GPUPipelineLayout; + private readonly _label: string; + private _bindGroup: GPUBindGroup; + + constructor(core: GraphicsCore) { + this._core = core; + this._shaderManager = core.shaderManager; + this._label = `${this.constructor.name} - ${Date.now()}`; + + const bindGroupLayout = this._core.device.createBindGroupLayout({ + label: `${this._label} Bind Group Layout`, + entries: [ + { + binding: 0, + visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT, + buffer: {}, + }, + ], + }); + + this._pipelineLayout = this._core.device.createPipelineLayout({ + label: `${this._label} Pipeline Layout`, + bindGroupLayouts: [bindGroupLayout], + }); + } + + public async init(): Promise { + await this._init(); + this._updateUniforms(); + this._updatePipeline(); + return this; + } + + public draw(pass: GPURenderPassEncoder): void { + pass.setPipeline(this._pipeline); + pass.setBindGroup(0, this._bindGroup); + pass.setVertexBuffer(0, this._vertexBuffer); + pass.draw(this._vertices.length / this._vertexLength); + } + + protected abstract _init(): Promise; + + protected _setVertices(raw: number[]): void { + this._vertices = new Float32Array(raw); + this._updateVertexBuffer(); + } + + protected _updateVertexBuffer(): void { + this._core.device.queue.writeBuffer(this._vertexBuffer, 0, this._vertices); + } + + protected _updatePipeline(): void { + this._pipeline = this._core.device.createRenderPipeline({ + label: `${this._label} pipeline`, + layout: this._pipelineLayout, + vertex: { + module: this._shader, + entryPoint: "vertex_main", + buffers: [this._vertexBufferLayout], + }, + fragment: { + module: this._shader, + entryPoint: "fragment_main", + targets: [ + { + format: this._core.render.canvasFormat, + }, + ], + }, + }); + this._updateBindGroup(); + } + + protected _updateUniforms(): void { + const uniformArray = new Float32Array([ + 0, + 0, + 1, + this._core.initContext.canvas.width, + this._core.initContext.canvas.height, + ]); + this._uniformBuffer = this._core.device.createBuffer({ + label: "View Uniforms", + size: uniformArray.byteLength, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, // Une uniform est une valeur constante pour le gpu + }); + this._core.device.queue.writeBuffer(this._uniformBuffer, 0, uniformArray); + } + + protected _updateBindGroup(): void { + this._bindGroup = this._core.device.createBindGroup({ + label: `${this._label} renderer bind group`, + layout: this._pipeline.getBindGroupLayout(0), + entries: [ + { + binding: 0, + resource: { buffer: this._uniformBuffer }, + }, + ], + }); + } +} diff --git a/packages/graphics-2d/src/components/shape/common/shape.ts b/packages/graphics-2d/src/components/shape/common/shape.ts index 1e3ba8c1..f136e974 100644 --- a/packages/graphics-2d/src/components/shape/common/shape.ts +++ b/packages/graphics-2d/src/components/shape/common/shape.ts @@ -1,10 +1,3 @@ -import { type GraphicsCore } from "../../../core"; -import { type ShaderManager } from "../../../shader/shader.manager"; +import { NfgComponent } from "../../component"; -export class NfShape { - private readonly _shaderManager: ShaderManager; - - constructor(core: GraphicsCore) { - this._shaderManager = core.shaderManager; - } -} +export abstract class NfgShape extends NfgComponent {} diff --git a/packages/graphics-2d/src/components/shape/shapes/circle.shape.ts b/packages/graphics-2d/src/components/shape/shapes/circle.shape.ts index b0b7fa59..72b216fa 100644 --- a/packages/graphics-2d/src/components/shape/shapes/circle.shape.ts +++ b/packages/graphics-2d/src/components/shape/shapes/circle.shape.ts @@ -1,8 +1,13 @@ import { type GraphicsCore } from "../../../core"; +import { ShadersEnum } from "../../../shader/shaders.enum"; import { type ICircleOptions, type IColor, type IVertex2D } from "../../../types"; -import { NfShape } from "../common/shape"; +import { NfgShape } from "../common/shape"; + +export class NfgCircle extends NfgShape { + protected _shader: GPUShaderModule; + protected readonly _vertexBufferLayout: GPUVertexBufferLayout; + protected _vertexLength: number; -export class NfCircle extends NfShape { private _pos: IVertex2D; private _radius: number; private _color: IColor; @@ -10,6 +15,28 @@ export class NfCircle extends NfShape { constructor(core: GraphicsCore, options?: Partial) { super(core); + this._vertexBufferLayout = { + arrayStride: 28, + attributes: [ + { + format: "float32x2", + offset: 0, + shaderLocation: 0, + }, + { + format: "float32", + offset: 8, + shaderLocation: 1, + }, + { + format: "float32x4", + offset: 12, + shaderLocation: 2, + }, + ], + }; + this._vertexLength = 7; + this._pos = options?.pos ?? { x: 0, y: 0 }; this._radius = options?.radius ?? 1; this._color = options?.color ?? { r: 0, g: 0, b: 0, a: 1 }; @@ -17,13 +44,32 @@ export class NfCircle extends NfShape { public setPosition(pos: IVertex2D): void { this._pos = pos; + this._updateVertices(); } public setRadius(radius: number): void { this._radius = radius; + this._updateVertices(); } public setColor(color: IColor): void { this._color = color; + this._updateVertices(); + } + + protected async _init(): Promise { + this._shader = await this._shaderManager.get(ShadersEnum.CIRCLE); + } + + protected _updateVertices(): void { + this._setVertices([ + this._pos.x, + this._pos.y, + this._radius, + this._color.r, + this._color.g, + this._color.b, + this._color.a, + ]); } } diff --git a/packages/graphics-2d/src/core.ts b/packages/graphics-2d/src/core.ts index 2a19e936..02aa7400 100644 --- a/packages/graphics-2d/src/core.ts +++ b/packages/graphics-2d/src/core.ts @@ -34,6 +34,10 @@ export class GraphicsCore { return this._shaderManager; } + get render(): GraphicsRender { + return this._render; + } + public async init(): Promise { if (!navigator.gpu) { throw new Error("WebGPU not supported on this browser."); diff --git a/packages/graphics-2d/src/graphics-2d.library.ts b/packages/graphics-2d/src/graphics-2d.library.ts index 207639ce..c3f92861 100644 --- a/packages/graphics-2d/src/graphics-2d.library.ts +++ b/packages/graphics-2d/src/graphics-2d.library.ts @@ -1,7 +1,5 @@ import { BaseGraphicsLibrary, type ExecutionContext, type InitContext } from "@nanoforge/common"; -import { type ICircleOptions } from "./types"; - export class Graphics2DLibrary extends BaseGraphicsLibrary { get name(): string { return "Graphics2DLibrary"; @@ -13,8 +11,6 @@ export class Graphics2DLibrary extends BaseGraphicsLibrary { } } - public createCircle(options: ICircleOptions): void {} - public async run(context: ExecutionContext): Promise { console.log(context); } diff --git a/packages/graphics-2d/src/render.ts b/packages/graphics-2d/src/render.ts index 01625050..be44aa65 100644 --- a/packages/graphics-2d/src/render.ts +++ b/packages/graphics-2d/src/render.ts @@ -26,4 +26,12 @@ export class GraphicsRender { format: this._canvasFormat, }); } + + get canvasContext(): GPUCanvasContext { + return this._canvasContext; + } + + get canvasFormat(): GPUTextureFormat { + return this._canvasFormat; + } } diff --git a/packages/graphics-2d/src/components/window.ts b/packages/graphics-2d/src/render/window.ts similarity index 100% rename from packages/graphics-2d/src/components/window.ts rename to packages/graphics-2d/src/render/window.ts diff --git a/packages/graphics-2d/test/graphics-2d.library.spec.ts b/packages/graphics-2d/test/graphics-2d.library.spec.ts index f9175281..0230d16c 100644 --- a/packages/graphics-2d/test/graphics-2d.library.spec.ts +++ b/packages/graphics-2d/test/graphics-2d.library.spec.ts @@ -11,7 +11,8 @@ describe("Graphics 2D Library", () => { canvas: null, files: { assets: new Map([["/test.png", "blob:http://localhost:3000/test.png"]]), - scripts: new Map([["/test.wasm", "blob:http://localhost:3000/test.wasm"]]), + wasm: new Map([["/test.wasm", "blob:http://localhost:3000/test.wasm"]]), + wgsl: new Map([["/test.wgsl", "blob:http://localhost:3000/test.wgsl"]]), }, });