From c85adf5e7370f34991b1eaabefe10e335310383e Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 11:13:28 +0530 Subject: [PATCH 1/9] Update readme --- README.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 85539cb4..1659637b 100644 --- a/README.md +++ b/README.md @@ -2,61 +2,107 @@ [![test](https://github.com/react18-tools/esbuild-plugin-webgl/actions/workflows/test.yml/badge.svg)](https://github.com/react18-tools/esbuild-plugin-webgl/actions/workflows/test.yml) [![Maintainability](https://api.codeclimate.com/v1/badges/c2532ae011e53b1bf011/maintainability)](https://codeclimate.com/github/react18-tools/esbuild-plugin-webgl/maintainability) [![codecov](https://codecov.io/gh/react18-tools/esbuild-plugin-webgl/graph/badge.svg)](https://codecov.io/gh/react18-tools/esbuild-plugin-webgl) [![Version](https://img.shields.io/npm/v/esbuild-plugin-webgl.svg?colorB=green)](https://www.npmjs.com/package/esbuild-plugin-webgl) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/d18m/esbuild-plugin-webgl.svg)](https://www.npmjs.com/package/esbuild-plugin-webgl) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/esbuild-plugin-webgl) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/from-referrer/) -ESBuild plugin to load webGL shaders from `.glsl` files. +ESBuild plugin to load WebGL shaders from `.glsl` files. > Please consider starring [this repository](https://github.com/react18-tools/esbuild-plugin-webgl) and sharing it with your friends. +## Overview + +This ESBuild plugin streamlines the process of loading WebGL shaders in your JavaScript or TypeScript projects. By allowing you to import GLSL shader files directly into your project, it ensures a seamless development experience for WebGL applications. This approach promotes better separation of concerns by eliminating the need to hard code GLSL shader code into your JS or TS files. Additionally, it compresses the shader code, helping you to deliver a smaller minified bundle for your library. + +### Key Features + +- **Easy Integration**: Easily integrate GLSL shaders into your ESBuild workflow. +- **TypeScript Support**: Full support for TypeScript, making it easier to work with shaders in a type-safe environment. +- **Lightweight**: Minimal footprint, ensuring your build times remain fast. +- **Flexible Configuration**: Compatible with various build tools and configurations, including `tsup` and standard `esbuild` setups. + ## Getting Started +Follow these steps to get started with the ESBuild Plugin WebGL: + ### Installation +You can install the plugin using your preferred package manager: + +Using `pnpm`: + ```bash $ pnpm add esbuild-plugin-webgl ``` -**_or_** +Using `npm`: ```bash $ npm install esbuild-plugin-webgl ``` -**_or_** +Using `yarn`: ```bash $ yarn add esbuild-plugin-webgl ``` -> If you are using `monorepo` or `workspaces`, you can install this plugin to the root using `-w` or to a specific workspace using `--filter your-package` or `--scope your-package` for `pnpm` or `yarn` workspaces, respectively. +> **Note**: If you are using a monorepo or workspaces, you can install this plugin at the root using the `-w` option or to a specific workspace using `--filter your-package` or `--scope your-package` for `pnpm` or `yarn` workspaces, respectively. + +## Usage + +### With `tsup` -## Use with `tsup` +To use the plugin with `tsup`, add it to your `tsup.config.ts` or `tsup.config.js` file: ```ts // tsup.config.ts or tsup.config.js import { defineConfig } from "tsup"; import { webglPlugin } from "esbuild-plugin-webgl"; -export default defineConfig(options => ({ +export default defineConfig((options) => ({ ... - esbuildPlugins:[webglPlugin()] + esbuildPlugins: [webglPlugin()], })); ``` -## Use with `esbuild` +### With `esbuild` + +To use the plugin directly with `esbuild`, include it in your build configuration: ```ts import { webglPlugin } from "esbuild-plugin-webgl"; esbuild.build({ - ... - plugins: [webglPlugin()], + ... + plugins: [webglPlugin()], }); ``` +### Example Usage + +Here's a quick example of how you can import and use a GLSL shader in your project: + +```ts +import vertexShader from "./shaders/vertex.glsl"; +import fragmentShader from "./shaders/fragment.glsl"; + +// Initialize WebGL context and use the imported shaders +const gl = canvas.getContext("webgl"); +const vertexShader = gl.createShader(gl.VERTEX_SHADER); +gl.shaderSource(vertexShader, vertexShaderSource); +gl.compileShader(vertexShader); + +const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); +gl.shaderSource(fragmentShader, fragmentShaderSource); +gl.compileShader(fragmentShader); +``` + ![Alt](https://repobeats.axiom.co/api/embed/a1fadcf8aa3054acff5d430c970af9e61254da5c.svg "Repobeats analytics image") +## Contributing + +Contributions are welcome! If you find a bug or have a feature request, please open an issue. For major changes, please open a discussion first to discuss what you would like to change. + ## License -This library is licensed under the MPL-2.0 open-source license. +This library is licensed under the MPL-2.0 open-source license. See the [LICENSE](LICENSE) file for more details. > Please consider enrolling in [our courses](https://mayank-chaudhari.vercel.app/courses) or [sponsoring](https://github.com/sponsors/mayank1513) our work. From 9e4c8e1a4b8c822b74ffc7ace46bc5467179802b Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 11:28:46 +0530 Subject: [PATCH 2/9] Update operators --- .vscode/settings.json | 5 ++++- lib/__tests__/shaders/render-vert.glsl | 4 ++-- lib/src/index.ts | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 36db13c4..7a0fc8ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,5 +13,8 @@ "editor.formatOnSave": true, "editor.formatOnPaste": true, "editor.formatOnSaveMode": "file", - "mayank1513.trello-kanban.Workspace.filePath": ".tkb" + "mayank1513.trello-kanban.Workspace.filePath": ".tkb", + "[glsl]": { + "editor.defaultFormatter": "raczzalan.webgl-glsl-editor" + } } diff --git a/lib/__tests__/shaders/render-vert.glsl b/lib/__tests__/shaders/render-vert.glsl index 1918db55..5d795718 100644 --- a/lib/__tests__/shaders/render-vert.glsl +++ b/lib/__tests__/shaders/render-vert.glsl @@ -4,6 +4,6 @@ precision mediump float; in vec2 p; void main() { - gl_PointSize = 1.0; - gl_Position = vec4(p, 0.0, 1.0); + gl_PointSize = 1.f; + gl_Position = vec4(p, 0.f, 1.f); } diff --git a/lib/src/index.ts b/lib/src/index.ts index a4f96fc1..55dc936a 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -18,13 +18,13 @@ export const webglPlugin: () => Plugin = () => ({ .replace(/\/\*.*\*\//gm, "") // remove line comments .replace(/\/\/.*/g, "") - // remove white spaces around = - .replace(/ [=+*-/] /g, m => m.trim()) + // remove white spaces around operators + .replace(/ ([=+*-/%><&^|]|[=!><+*-/]=|&&|\|\||\^\^) /g, m => m.trim()) .split("\n") .map(line => line.trim()) .filter(Boolean); - const contents = `export default \`${lines[0]}\n${lines.slice(1).join("")}\``; - return { contents, loader: "ts" }; + const contents = `${lines[0]}\n${lines.slice(1).join("")}`; + return { contents, loader: "text" }; }); }, }); From dcf38c79ab8151c77dc88d8580913824563077af Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 11:29:25 +0530 Subject: [PATCH 3/9] docs(changeset): Update operators --- .changeset/curly-radios-press.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/curly-radios-press.md diff --git a/.changeset/curly-radios-press.md b/.changeset/curly-radios-press.md new file mode 100644 index 00000000..8075754b --- /dev/null +++ b/.changeset/curly-radios-press.md @@ -0,0 +1,5 @@ +--- +"esbuild-plugin-webgl": patch +--- + +Update operators From 7899ee7d5b1bf4b7ee8f50b79c3401d05344f118 Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 11:29:43 +0530 Subject: [PATCH 4/9] docs(changeset): Use text loader instead --- .changeset/wise-carrots-nail.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wise-carrots-nail.md diff --git a/.changeset/wise-carrots-nail.md b/.changeset/wise-carrots-nail.md new file mode 100644 index 00000000..c518e4c5 --- /dev/null +++ b/.changeset/wise-carrots-nail.md @@ -0,0 +1,5 @@ +--- +"esbuild-plugin-webgl": patch +--- + +Use text loader instead From b425a281b8a0f401c4a1e0f4a1e5569de457e768 Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 11:32:37 +0530 Subject: [PATCH 5/9] Update build location for tests --- lib/__tests__/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/__tests__/index.test.ts b/lib/__tests__/index.test.ts index 811042a2..af45f458 100644 --- a/lib/__tests__/index.test.ts +++ b/lib/__tests__/index.test.ts @@ -12,7 +12,7 @@ describe("WebGL plugins", () => { bundle: true, minify: true, entryPoints: [path.resolve(__dirname, "shaders/render-frag.glsl")], - outdir: "dist", + outdir: "__tests__/dist", treeShaking: true, plugins: [webglPlugin()], }); From a98356a8d4a5c98b6828e9d1a558c979c0ef6bfd Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 11:32:54 +0530 Subject: [PATCH 6/9] scaffold rotating cube --- packages/shared/src/client/index.ts | 1 + .../shared/src/client/rotating-cube/index.ts | 4 +++ .../rotating-cube/rotating-cube.module.scss | 3 +++ .../rotating-cube/rotating-cube.test.tsx | 13 ++++++++++ .../client/rotating-cube/rotating-cube.tsx | 25 +++++++++++++++++++ 5 files changed, 46 insertions(+) create mode 100644 packages/shared/src/client/rotating-cube/index.ts create mode 100644 packages/shared/src/client/rotating-cube/rotating-cube.module.scss create mode 100644 packages/shared/src/client/rotating-cube/rotating-cube.test.tsx create mode 100644 packages/shared/src/client/rotating-cube/rotating-cube.tsx diff --git a/packages/shared/src/client/index.ts b/packages/shared/src/client/index.ts index 9ae49861..dfbcb380 100644 --- a/packages/shared/src/client/index.ts +++ b/packages/shared/src/client/index.ts @@ -7,6 +7,7 @@ */ // client component exports +export * from "./rotating-cube"; export * from "./demo"; export * from "./header"; export * from "./global-loader"; diff --git a/packages/shared/src/client/rotating-cube/index.ts b/packages/shared/src/client/rotating-cube/index.ts new file mode 100644 index 00000000..4ef5aea3 --- /dev/null +++ b/packages/shared/src/client/rotating-cube/index.ts @@ -0,0 +1,4 @@ +"use client"; + +// component exports +export * from "./rotating-cube"; diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.module.scss b/packages/shared/src/client/rotating-cube/rotating-cube.module.scss new file mode 100644 index 00000000..fd43b16d --- /dev/null +++ b/packages/shared/src/client/rotating-cube/rotating-cube.module.scss @@ -0,0 +1,3 @@ +.rotating-cube { + /* create your container styles here */ +} \ No newline at end of file diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.test.tsx b/packages/shared/src/client/rotating-cube/rotating-cube.test.tsx new file mode 100644 index 00000000..efae4431 --- /dev/null +++ b/packages/shared/src/client/rotating-cube/rotating-cube.test.tsx @@ -0,0 +1,13 @@ +import { cleanup, render, screen } from "@testing-library/react"; +import { afterEach, describe, test } from "vitest"; +import { RotatingCube } from "./rotating-cube"; + +describe.concurrent("rotating-cube", () => { + afterEach(cleanup); + + test("Dummy test - test if renders without errors", ({ expect }) => { + const clx = "my-class"; + render(); + expect(screen.getByTestId("rotating-cube").classList).toContain(clx); + }); +}); diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.tsx b/packages/shared/src/client/rotating-cube/rotating-cube.tsx new file mode 100644 index 00000000..17f8e0f6 --- /dev/null +++ b/packages/shared/src/client/rotating-cube/rotating-cube.tsx @@ -0,0 +1,25 @@ +import { HTMLProps, ReactNode } from "react"; +import styles from "./rotating-cube.module.scss"; + +export interface RotatingCubeProps extends HTMLProps { + children?: ReactNode; +} + +/** + * + * + * @example + * ```tsx + * + * ``` + * + * @source - Source code + */ +export const RotatingCube = ({ children, ...props }: RotatingCubeProps) => { + const className = [props.className, styles["rotating-cube"]].filter(Boolean).join(" "); + return ( +
+ {children} +
+ ); +} From 9ec54564c18ea0a78a74b808697c99aa3c576f0c Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 12:05:01 +0530 Subject: [PATCH 7/9] Add rotating cube example --- packages/shared/package.json | 4 +- .../src/client/rotating-cube/fragment.glsl | 6 + .../rotating-cube/rotating-cube.module.scss | 6 +- .../client/rotating-cube/rotating-cube.tsx | 33 ++-- .../shared/src/client/rotating-cube/utils.ts | 174 ++++++++++++++++++ .../src/client/rotating-cube/vertex.glsl | 13 ++ packages/shared/src/declaration.d.ts | 8 + packages/shared/tsup.config.ts | 7 +- pnpm-lock.yaml | 46 +++-- 9 files changed, 259 insertions(+), 38 deletions(-) create mode 100644 packages/shared/src/client/rotating-cube/fragment.glsl create mode 100644 packages/shared/src/client/rotating-cube/utils.ts create mode 100644 packages/shared/src/client/rotating-cube/vertex.glsl diff --git a/packages/shared/package.json b/packages/shared/package.json index c718e409..3ee5a21e 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -20,6 +20,7 @@ "devDependencies": { "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "esbuild-plugin-webgl": "workspace:*", "@testing-library/react": "^16.0.0", "@types/node": "^20.14.9", "@types/react": "^18.3.3", @@ -40,6 +41,7 @@ "@mayank1513/fork-me": "^2.1.2", "@repo/scripts": "workspace:*", "esbuild-plugin-webgl": "workspace:*", + "gl-matrix": "^3.4.3", "nextjs-darkmode": "^1.0.4", "nextjs-themes": "^4.0.3", "r18gs": "^1.1.3", @@ -56,4 +58,4 @@ "optional": true } } -} \ No newline at end of file +} diff --git a/packages/shared/src/client/rotating-cube/fragment.glsl b/packages/shared/src/client/rotating-cube/fragment.glsl new file mode 100644 index 00000000..f0f63595 --- /dev/null +++ b/packages/shared/src/client/rotating-cube/fragment.glsl @@ -0,0 +1,6 @@ +precision mediump float; + +varying vec3 fragColor; +void main() { + gl_FragColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.module.scss b/packages/shared/src/client/rotating-cube/rotating-cube.module.scss index fd43b16d..829803f8 100644 --- a/packages/shared/src/client/rotating-cube/rotating-cube.module.scss +++ b/packages/shared/src/client/rotating-cube/rotating-cube.module.scss @@ -1,3 +1,5 @@ -.rotating-cube { +.cube { /* create your container styles here */ -} \ No newline at end of file + height: 400px; + width: 600px; +} diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.tsx b/packages/shared/src/client/rotating-cube/rotating-cube.tsx index 17f8e0f6..a30fe975 100644 --- a/packages/shared/src/client/rotating-cube/rotating-cube.tsx +++ b/packages/shared/src/client/rotating-cube/rotating-cube.tsx @@ -1,25 +1,28 @@ -import { HTMLProps, ReactNode } from "react"; +import { HTMLProps, useEffect, useRef } from "react"; import styles from "./rotating-cube.module.scss"; - -export interface RotatingCubeProps extends HTMLProps { - children?: ReactNode; -} +import { createCube } from "./utils"; /** - * + * * * @example * ```tsx * * ``` - * + * * @source - Source code */ -export const RotatingCube = ({ children, ...props }: RotatingCubeProps) => { - const className = [props.className, styles["rotating-cube"]].filter(Boolean).join(" "); - return ( -
- {children} -
- ); -} +export const RotatingCube = ({ className, ...props }: HTMLProps) => { + const canvasRef = useRef(null); + useEffect(() => { + if (!canvasRef.current) return; + const gl = canvasRef.current.getContext("webgl"); + if (!gl) { + // skipcq: JS-0052 -- Alert required + alert("Your browser does not support WebGL"); + return; + } + createCube(canvasRef.current, gl); + }, []); + return ; +}; diff --git a/packages/shared/src/client/rotating-cube/utils.ts b/packages/shared/src/client/rotating-cube/utils.ts new file mode 100644 index 00000000..29852b0b --- /dev/null +++ b/packages/shared/src/client/rotating-cube/utils.ts @@ -0,0 +1,174 @@ +import vertexShaderText from "./vertex.glsl?raw"; +import fragmentShaderText from "./fragment.glsl?raw"; +import { mat4, glMatrix } from "gl-matrix"; + +/** Create rotating Cube */ +export const createCube = (canvas: HTMLCanvasElement, gl: WebGLRenderingContext) => { + gl.clearColor(2.55, 2.55, 2.55, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.enable(gl.DEPTH_TEST); + gl.enable(gl.CULL_FACE); + gl.frontFace(gl.CCW); + gl.cullFace(gl.BACK); + + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + + /** Create shader */ + const createShader = (type: number, source: string): WebGLShader => { + const shader = gl.createShader(type); + if (!shader) throw new Error("Failed to create shader"); + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + /* v8 ignore next */ + const info = gl.getShaderInfoLog(shader); + /* v8 ignore next */ + gl.deleteShader(shader); + /* v8 ignore next */ + throw new Error("Could not compile WebGL shader. \n\n" + info); // skipcq: JS-0246 + /* v8 ignore next */ + } + return shader; + }; + + /** Create webGL program */ + const createProgram = ( + vertexShaderSource: string, + fragmentShaderSource: string, + ): WebGLProgram => { + const vertexShader = createShader(gl.VERTEX_SHADER, vertexShaderSource); + const fragmentShader = createShader(gl.FRAGMENT_SHADER, fragmentShaderSource); + const program = gl.createProgram(); + if (!program) throw new Error("Failed to create program"); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + /* v8 ignore next */ + // eslint-disable-next-line no-console -- error handling + console.error(gl.getProgramInfoLog(program)); + /* v8 ignore next */ + throw new Error("Failed to link program"); + /* v8 ignore next */ + } + return program; + }; + + const rotatingCubeProgram = createProgram(vertexShaderText, fragmentShaderText); + + //Buffers + + const boxVertices = [ + //top + -1.0, 1.0, -1.0, 0.5, 0.5, 0.5, -1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, + 1.0, 1.0, -1.0, 0.5, 0.5, 0.5, + //left + -1.0, 1.0, 1.0, 0.75, 0.25, 0.5, -1.0, -1.0, 1.0, 0.75, 0.25, 0.5, -1.0, -1.0, -1.0, 0.75, 0.25, + 0.5, -1.0, 1.0, -1.0, 0.75, 0.25, 0.5, + //right + 1.0, 1.0, 1.0, 0.25, 0.25, 0.75, 1.0, -1.0, 1.0, 0.25, 0.25, 0.75, 1.0, -1.0, -1.0, 0.25, 0.25, + 0.75, 1.0, 1.0, -1.0, 0.25, 0.25, 0.75, + //front + 1.0, 1.0, 1.0, 1.0, 0.0, 0.15, 1.0, -1.0, 1.0, 1.0, 0.0, 0.15, -1.0, -1.0, 1.0, 1.0, 0.0, 0.15, + -1.0, 1.0, 1.0, 1.0, 0.0, 0.15, + //back + 1.0, 1.0, -1.0, 0.0, 1.0, 0.15, 1.0, -1.0, -1.0, 0.0, 1.0, 0.15, -1.0, -1.0, -1.0, 0.0, 1.0, + 0.15, -1.0, 1.0, -1.0, 0.0, 1.0, 0.15, + //bottom + -1.0, -1.0, -1.0, 0.5, 0.5, 1.0, -1.0, -1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.0, 1.0, 0.5, 0.5, 1.0, + 1.0, -1.0, -1.0, 0.5, 0.5, 1.0, + ]; + + const boxIndices = [ + //top + 0, 1, 2, 0, 2, 3, + //left + 5, 4, 6, 6, 4, 7, + // right + 8, 9, 10, 8, 10, 11, + //front + 13, 12, 14, 15, 14, 12, + //back + 16, 17, 18, 16, 18, 19, + //bottom + 21, 20, 22, 22, 20, 23, + ]; + + const boxVertexBufferObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, boxVertexBufferObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(boxVertices), gl.STATIC_DRAW); + + const boxIndexBufferObject = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boxIndexBufferObject); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(boxIndices), gl.STATIC_DRAW); + + const positionAttribLocation = gl.getAttribLocation(rotatingCubeProgram, "vertPosition"); + const colorAttribLocation = gl.getAttribLocation(rotatingCubeProgram, "vertColor"); + + gl.vertexAttribPointer( + positionAttribLocation, + 3, + gl.FLOAT, + false, + 6 * Float32Array.BYTES_PER_ELEMENT, + 0, + ); + gl.vertexAttribPointer( + colorAttribLocation, + 3, + gl.FLOAT, + false, + 6 * Float32Array.BYTES_PER_ELEMENT, + 3 * Float32Array.BYTES_PER_ELEMENT, + ); + gl.enableVertexAttribArray(positionAttribLocation); + gl.enableVertexAttribArray(colorAttribLocation); + + //Program used + gl.useProgram(rotatingCubeProgram); + + const matWorldUniformLocation = gl.getUniformLocation(rotatingCubeProgram, "mWorld"); + const matViewUniformLocation = gl.getUniformLocation(rotatingCubeProgram, "mView"); + const matProjUniformLocation = gl.getUniformLocation(rotatingCubeProgram, "mProj"); + + const worldMatrix = new Float32Array(16); + const viewMatrix = new Float32Array(16); + const projMatrix = new Float32Array(16); + + mat4.identity(worldMatrix); + mat4.lookAt(viewMatrix, [0, 0, -8], [0, 0, 0], [0, 1, 0]); + mat4.perspective(projMatrix, glMatrix.toRadian(45), canvas.width / canvas.height, 0.1, 1000.0); + + gl.uniformMatrix4fv(matWorldUniformLocation, false, worldMatrix); + gl.uniformMatrix4fv(matViewUniformLocation, false, viewMatrix); + gl.uniformMatrix4fv(matProjUniformLocation, false, projMatrix); + + const xRotationMatrix = new Float32Array(16); + const yRotationMatrix = new Float32Array(16); + + //Render loop + + const identityMatrix = new Float32Array(16); + mat4.identity(identityMatrix); + + let angle = 0; + + /** Render loop */ + const loop = function () { + angle = (performance.now() / 1000 / 6) * 2 * Math.PI; + mat4.rotate(yRotationMatrix, identityMatrix, angle, [0, 1, 0]); + mat4.rotate(xRotationMatrix, identityMatrix, angle / 4, [1, 0, 0]); + mat4.mul(worldMatrix, yRotationMatrix, xRotationMatrix); + gl.uniformMatrix4fv(matWorldUniformLocation, false, worldMatrix); + + gl.clearColor(2.55, 2.55, 2.55, 1.0); + gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT); + + gl.drawElements(gl.TRIANGLES, boxIndices.length, gl.UNSIGNED_SHORT, 0); + + requestAnimationFrame(loop); + }; + + requestAnimationFrame(loop); +}; diff --git a/packages/shared/src/client/rotating-cube/vertex.glsl b/packages/shared/src/client/rotating-cube/vertex.glsl new file mode 100644 index 00000000..3e61aaab --- /dev/null +++ b/packages/shared/src/client/rotating-cube/vertex.glsl @@ -0,0 +1,13 @@ +precision mediump float; + +attribute vec3 vertPosition; +attribute vec3 vertColor; +varying vec3 fragColor; +uniform mat4 mWorld; +uniform mat4 mView; +uniform mat4 mProj; + +void main() { + fragColor = vertColor; + gl_Position = mProj * mView * mWorld * vec4(vertPosition, 1.0); +} diff --git a/packages/shared/src/declaration.d.ts b/packages/shared/src/declaration.d.ts index e6c7b7b2..42affc27 100644 --- a/packages/shared/src/declaration.d.ts +++ b/packages/shared/src/declaration.d.ts @@ -1,2 +1,10 @@ declare module "*.module.css"; declare module "*.module.scss"; +declare module "*.glsl" { + const value: string; + export default value; +} +declare module "*.glsl?raw" { + const value: string; + export default value; +} diff --git a/packages/shared/tsup.config.ts b/packages/shared/tsup.config.ts index 562de8d6..8d18fe09 100644 --- a/packages/shared/tsup.config.ts +++ b/packages/shared/tsup.config.ts @@ -1,6 +1,7 @@ import { defineConfig, type Options } from "tsup"; import react18Plugin from "esbuild-plugin-react18"; import cssPlugin from "esbuild-plugin-react18-css"; +import { webglPlugin } from "esbuild-plugin-webgl"; export default defineConfig( (options: Options) => @@ -12,7 +13,11 @@ export default defineConfig( clean: !options.watch, bundle: true, minify: !options.watch, - esbuildPlugins: [react18Plugin(), cssPlugin({ generateScopedName: "[folder]__[local]" })], + esbuildPlugins: [ + webglPlugin(), + react18Plugin(), + cssPlugin({ generateScopedName: "[folder]__[local]" }), + ], external: ["react"], ...options, }) as Options, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4c993ff..5990cc62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,7 +67,7 @@ importers: version: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6) nextjs-darkmode: specifier: ^1.0.4 - version: 1.0.4(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.0.4(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) nextjs-themes: specifier: ^4.0.3 version: 4.0.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) @@ -82,7 +82,7 @@ importers: version: 0.0.4(@types/react@18.3.3)(react@18.3.1) react18-loaders: specifier: ^1.1.3 - version: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) devDependencies: '@next/eslint-plugin-next': specifier: ^14.2.4 @@ -116,7 +116,7 @@ importers: version: link:../../lib nextjs-darkmode: specifier: ^1.0.4 - version: 1.0.4(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.0.4(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -125,10 +125,10 @@ importers: version: 18.3.1(react@18.3.1) react18-loaders: specifier: ^1.1.3 - version: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) react18-themes: specifier: ^3.2.0 - version: 3.2.0(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 3.2.0(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -206,31 +206,34 @@ importers: dependencies: '@mayank1513/fork-me': specifier: ^2.1.2 - version: 2.1.2(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 2.1.2(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) '@repo/scripts': specifier: workspace:* version: link:../../scripts esbuild-plugin-webgl: specifier: workspace:* version: link:../../lib + gl-matrix: + specifier: ^3.4.3 + version: 3.4.3 next: specifier: 10 - 14 version: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6) nextjs-darkmode: specifier: ^1.0.4 - version: 1.0.4(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.0.4(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) nextjs-themes: specifier: ^4.0.3 version: 4.0.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) r18gs: specifier: ^1.1.3 - version: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) react-live: specifier: ^4.1.7 version: 4.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react18-loaders: specifier: ^1.1.3 - version: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -258,7 +261,7 @@ importers: version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(jsdom@24.1.0)(sass@1.77.6)) esbuild-plugin-react18: specifier: ^0.2.4 - version: 0.2.4(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + version: 0.2.4(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) esbuild-plugin-react18-css: specifier: ^0.0.4 version: 0.0.4 @@ -2555,6 +2558,9 @@ packages: git-hooks-list@3.1.0: resolution: {integrity: sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==} + gl-matrix@3.4.3: + resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5952,7 +5958,7 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@mayank1513/fork-me@2.1.2(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1)': + '@mayank1513/fork-me@2.1.2(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1)': dependencies: '@types/react': 18.3.3 react: 18.3.1 @@ -7293,7 +7299,7 @@ snapshots: postcss-modules: 6.0.0(postcss@8.4.39) sass: 1.77.6 - esbuild-plugin-react18@0.2.4(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): + esbuild-plugin-react18@0.2.4(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): dependencies: '@types/react': 18.3.3 react: 18.3.1 @@ -7922,6 +7928,8 @@ snapshots: git-hooks-list@3.1.0: {} + gl-matrix@3.4.3: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -9382,10 +9390,10 @@ snapshots: - '@babel/core' - babel-plugin-macros - nextjs-darkmode@1.0.4(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): + nextjs-darkmode@1.0.4(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): dependencies: '@types/react': 18.3.3 - r18gs: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + r18gs: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) react: 18.3.1 transitivePeerDependencies: - next @@ -9851,7 +9859,7 @@ snapshots: queue-microtask@1.2.3: {} - r18gs@1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): + r18gs@1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): dependencies: '@types/react': 18.3.3 react: 18.3.1 @@ -9892,18 +9900,18 @@ snapshots: react-refresh@0.14.2: {} - react18-loaders@1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): + react18-loaders@1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): dependencies: '@types/react': 18.3.3 - r18gs: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + r18gs: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) react: 18.3.1 optionalDependencies: next: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6) - react18-themes@3.2.0(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): + react18-themes@3.2.0(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1): dependencies: '@types/react': 18.3.3 - r18gs: 1.1.3(@types/react@18.3.3)(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) + r18gs: 1.1.3(@types/react@18.3.3)(next@14.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1) react: 18.3.1 optionalDependencies: next: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6) From 2f70e0bb7300788144409709fecb1b0f3b3f10d7 Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 12:12:33 +0530 Subject: [PATCH 8/9] Add example of using the plugin --- examples/nextjs/src/app/page.tsx | 3 ++- .../client/rotating-cube/rotating-cube.module.scss | 6 ++++-- .../src/client/rotating-cube/rotating-cube.test.tsx | 13 ------------- packages/shared/src/client/rotating-cube/utils.ts | 3 +-- packages/shared/src/server/cards/cards.module.scss | 1 + 5 files changed, 8 insertions(+), 18 deletions(-) delete mode 100644 packages/shared/src/client/rotating-cube/rotating-cube.test.tsx diff --git a/examples/nextjs/src/app/page.tsx b/examples/nextjs/src/app/page.tsx index dc615fb1..10e3dc20 100644 --- a/examples/nextjs/src/app/page.tsx +++ b/examples/nextjs/src/app/page.tsx @@ -1,6 +1,6 @@ import MyButton from "./button"; import { LandingPage } from "@repo/shared/dist/server"; -import { Demo } from "@repo/shared"; +import { Demo, RotatingCube } from "@repo/shared"; export const metadata = { title: "Esbuild Plugin Webgl", @@ -10,6 +10,7 @@ export const metadata = { export default function Page(): JSX.Element { return ( + diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.module.scss b/packages/shared/src/client/rotating-cube/rotating-cube.module.scss index 829803f8..8c6428a6 100644 --- a/packages/shared/src/client/rotating-cube/rotating-cube.module.scss +++ b/packages/shared/src/client/rotating-cube/rotating-cube.module.scss @@ -1,5 +1,7 @@ .cube { /* create your container styles here */ - height: 400px; - width: 600px; + height: 480px; + width: 640px; + margin: auto; + display: block; } diff --git a/packages/shared/src/client/rotating-cube/rotating-cube.test.tsx b/packages/shared/src/client/rotating-cube/rotating-cube.test.tsx deleted file mode 100644 index efae4431..00000000 --- a/packages/shared/src/client/rotating-cube/rotating-cube.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { cleanup, render, screen } from "@testing-library/react"; -import { afterEach, describe, test } from "vitest"; -import { RotatingCube } from "./rotating-cube"; - -describe.concurrent("rotating-cube", () => { - afterEach(cleanup); - - test("Dummy test - test if renders without errors", ({ expect }) => { - const clx = "my-class"; - render(); - expect(screen.getByTestId("rotating-cube").classList).toContain(clx); - }); -}); diff --git a/packages/shared/src/client/rotating-cube/utils.ts b/packages/shared/src/client/rotating-cube/utils.ts index 29852b0b..0ec6fa83 100644 --- a/packages/shared/src/client/rotating-cube/utils.ts +++ b/packages/shared/src/client/rotating-cube/utils.ts @@ -4,7 +4,7 @@ import { mat4, glMatrix } from "gl-matrix"; /** Create rotating Cube */ export const createCube = (canvas: HTMLCanvasElement, gl: WebGLRenderingContext) => { - gl.clearColor(2.55, 2.55, 2.55, 1); + gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); @@ -162,7 +162,6 @@ export const createCube = (canvas: HTMLCanvasElement, gl: WebGLRenderingContext) mat4.mul(worldMatrix, yRotationMatrix, xRotationMatrix); gl.uniformMatrix4fv(matWorldUniformLocation, false, worldMatrix); - gl.clearColor(2.55, 2.55, 2.55, 1.0); gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT); gl.drawElements(gl.TRIANGLES, boxIndices.length, gl.UNSIGNED_SHORT, 0); diff --git a/packages/shared/src/server/cards/cards.module.scss b/packages/shared/src/server/cards/cards.module.scss index 5a19cfb8..2320b237 100644 --- a/packages/shared/src/server/cards/cards.module.scss +++ b/packages/shared/src/server/cards/cards.module.scss @@ -17,6 +17,7 @@ background: rgba(var(--card-rgb), 0); border: 1px solid rgba(var(--card-border-rgb), 0.1); cursor: pointer; + max-width: 300px; transition: background 200ms, border 200ms; From 5eef57bf38fb5ee010f4f0dbb203b5bbced192bd Mon Sep 17 00:00:00 2001 From: Mayank Date: Mon, 1 Jul 2024 12:19:55 +0530 Subject: [PATCH 9/9] RELEASING: Releasing 2 package(s) Releases: esbuild-plugin-webgl@0.0.3 @repo/shared@0.0.3 --- .changeset/curly-radios-press.md | 5 ----- .changeset/wise-carrots-nail.md | 5 ----- lib/CHANGELOG.md | 7 +++++++ lib/package.json | 2 +- packages/shared/CHANGELOG.md | 8 ++++++++ packages/shared/package.json | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) delete mode 100644 .changeset/curly-radios-press.md delete mode 100644 .changeset/wise-carrots-nail.md diff --git a/.changeset/curly-radios-press.md b/.changeset/curly-radios-press.md deleted file mode 100644 index 8075754b..00000000 --- a/.changeset/curly-radios-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"esbuild-plugin-webgl": patch ---- - -Update operators diff --git a/.changeset/wise-carrots-nail.md b/.changeset/wise-carrots-nail.md deleted file mode 100644 index c518e4c5..00000000 --- a/.changeset/wise-carrots-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"esbuild-plugin-webgl": patch ---- - -Use text loader instead diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 66a92fad..00620d82 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,12 @@ # esbuild-plugin-webgl +## 0.0.3 + +### Patch Changes + +- dcf38c7: Update operators +- 7899ee7: Use text loader instead + ## 0.0.2 ### Patch Changes diff --git a/lib/package.json b/lib/package.json index 2e3bb671..2e33ff5b 100644 --- a/lib/package.json +++ b/lib/package.json @@ -2,7 +2,7 @@ "name": "esbuild-plugin-webgl", "author": "Mayank Kumar Chaudhari ", "private": false, - "version": "0.0.2", + "version": "0.0.3", "description": "ESBuild plugin to load webGL shaders from .glsl files.", "license": "MPL-2.0", "main": "./dist/index.js", diff --git a/packages/shared/CHANGELOG.md b/packages/shared/CHANGELOG.md index 7cd9f676..322d01ab 100644 --- a/packages/shared/CHANGELOG.md +++ b/packages/shared/CHANGELOG.md @@ -1,5 +1,13 @@ # @repo/shared +## 0.0.3 + +### Patch Changes + +- Updated dependencies [dcf38c7] +- Updated dependencies [7899ee7] + - esbuild-plugin-webgl@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/packages/shared/package.json b/packages/shared/package.json index 3ee5a21e..b695c08f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@repo/shared", - "version": "0.0.2", + "version": "0.0.3", "private": true, "sideEffects": false, "main": "./dist/index.js",