matrix-engine-wgpu
Version:
+HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.
309 lines (214 loc) • 7.5 kB
Markdown
# matrix-engine-wgpu
**Author:** Nikola Lukić
📧 [zlatnaspirala@gmail.com](mailto:zlatnaspirala@gmail.com)
📅 2025
## Logo
<img width="320" height="320" src="https://github.com/zlatnaspirala/matrix-engine-wgpu/blob/main/public/res/icons/512.png?raw=true" />
> Logo includes the official WebGPU logo.
> **WebGPU logo by [W3C](https://www.w3.org/)**
> Licensed under [Creative Commons Attribution 4.0](https://www.w3.org/2023/02/webgpu-logos.html)
## Description
This project is a work-in-progress WebGPU engine inspired by the original **matrix-engine** for WebGL.
It uses the `wgpu-matrix` npm package as a modern replacement for `gl-matrix` to handle model-view-projection matrices.
Published on npm as: **`matrix-engine-wgpu`**
## Goals
* ✔️ Support for 3D objects and scene transformations
* 🎯 Replicate matrix-engine (WebGL) features
* 📦 Based on the `shadowMapping` sample from [webgpu-samples](https://webgpu.github.io/webgpu-samples/?sample=shadowMapping)
* ✔️ Ammo.js physics integration (basic cube)
## Features
### Scene Management
* Canvas is dynamically created in JavaScript—no `<canvas>` element needed in HTML.
* Access the main scene objects:
```js
app.mainRenderBundle[0];
```
* Add meshes with `.addMeshObj()`, supporting `.obj` loading, unlit textures, cubes, spheres, etc.
* Cleanly destroy the scene:
```js
app.destroyProgram();
```
### Camera Options
Supported types: `WASD`, `arcball`
```js
mainCameraParams: {
type: 'WASD',
responseCoef: 1000
}
```
### Object Positioning
Control object position:
```js
app.mainRenderBundle[0].position.translateByX(12);
```
Teleport / set directly:
```js
app.mainRenderBundle[0].position.SetX(-2);
```
Adjust movement speed:
```js
app.mainRenderBundle[0].position.thrust = 0.1;
```
> ⚠️ For physics-enabled objects, use Ammo.js functions — `.position` and `.rotation` are not visually applied but can be read.
Example:
```js
app.matrixAmmo.rigidBodies[0].setAngularVelocity(new Ammo.btVector3(0, 2, 0));
app.matrixAmmo.rigidBodies[0].setLinearVelocity(new Ammo.btVector3(0, 7, 0));
```
### Object Rotation
Manual rotation:
```js
app.mainRenderBundle[0].rotation.x = 45;
```
Auto-rotate:
```js
app.mainRenderBundle[0].rotation.rotationSpeed.y = 10;
```
Stop rotation:
```js
app.mainRenderBundle[0].rotation.rotationSpeed.y = 0;
```
> ⚠️ For physics-enabled objects, use Ammo.js methods (e.g., `.setLinearVelocity()`).
### Camera Example
Manipulate WASD camera:
```js
app.cameras.WASD.pitch = 0.2;
```
## Object Interaction (Raycasting)
The raycast returns:
```js
{
rayOrigin: [x, y, z],
rayDirection: [x, y, z] // normalized
}
```
Manual raycast example:
```js
window.addEventListener('click', (event) => {
let canvas = document.querySelector('canvas');
let camera = app.cameras.WASD;
const { rayOrigin, rayDirection } = getRayFromMouse(event, canvas, camera);
for (const object of app.mainRenderBundle) {
if (rayIntersectsSphere(rayOrigin, rayDirection, object.position, object.raycast.radius)) {
console.log('Object clicked:', object.name);
}
}
});
```
Automatic raycast listener:
```js
addRaycastListener();
window.addEventListener('ray.hit.event', (event) => {
console.log('Ray hit:', event.detail.hitObject);
});
```
## How to Load `.obj` Models
```js
import MatrixEngineWGPU from "./src/world.js";
import { downloadMeshes } from './src/engine/loader-obj.js';
export let application = new MatrixEngineWGPU({
useSingleRenderPass: true,
canvasSize: 'fullscreen',
mainCameraParams: {
type: 'WASD',
responseCoef: 1000
}
}, () => {
addEventListener('AmmoReady', () => {
downloadMeshes({
welcomeText: "./res/meshes/blender/piramyd.obj",
armor: "./res/meshes/obj/armor.obj",
sphere: "./res/meshes/blender/sphere.obj",
cube: "./res/meshes/blender/cube.obj",
}, onLoadObj);
});
function onLoadObj(meshes) {
application.myLoadedMeshes = meshes;
for (const key in meshes) {
console.log(`%c Loaded obj: ${key} `, LOG_MATRIX);
}
application.addMeshObj({
position: {x: 0, y: 2, z: -10},
rotation: {x: 0, y: 0, z: 0},
rotationSpeed: {x: 0, y: 0, z: 0},
texturesPaths: ['./res/meshes/blender/cube.png'],
name: 'CubePhysics',
mesh: meshes.cube,
physics: {
enabled: true,
geometry: "Cube"
}
});
application.addMeshObj({
position: {x: 0, y: 2, z: -10},
rotation: {x: 0, y: 0, z: 0},
rotationSpeed: {x: 0, y: 0, z: 0},
texturesPaths: ['./res/meshes/blender/cube.png'],
name: 'SpherePhysics',
mesh: meshes.sphere,
physics: {
enabled: true,
geometry: "Sphere"
}
});
}
});
window.app = application;
```
## About `main.js`
`main.js` is the main instance for the Ultimate Yahtzee game template.
It contains the game context, e.g., `dices`.
For a clean startup without extra logic, use `empty.js`.
This minimal build is ideal for online editors like CodePen or StackOverflow snippets.
## NPM Scripts
Uses `watchify` to bundle JavaScript.
```json
"main-worker": "watchify app-worker.js -p [esmify --noImplicitAny] -o public/app-worker.js",
"examples": "watchify examples.js -p [esmify --noImplicitAny] -o public/examples.js",
"main": "watchify main.js -p [esmify --noImplicitAny] -o public/app.js",
"empty": "watchify empty.js -p [esmify --noImplicitAny] -o public/empty.js",
"build-all": "npm run main-worker && npm run examples && npm run main && npm run build-empty"
```
## Resources
All resources and output go into the `./public` folder — everything you need in one place.
## Proof of Concept
🎲 The first full app example will be a WebGPU-powered **Ultimate Yahtzee** game.
## Live Demos & Dev Links
* [Jamb WebGPU Demo (WIP)](https://maximumroulette.com/apps/webgpu/)
* [CodePen Demo](https://codepen.io/zlatnaspirala/pen/VwNKMar?editors=0011)
→ Uses `empty.js` build from:
[https://maximumroulette.com/apps/megpu/empty.js](https://maximumroulette.com/apps/megpu/empty.js)
* [CodeSandbox Implementation](https://codesandbox.io/p/github/zlatnaspirala/matrix-engine-wgpu/main?file=%2Fpackage.json%3A14%2C16)
* 📘 Learning Resource: [WebGPU Ray Tracing](https://maierfelix.github.io/2020-01-13-webgpu-ray-tracing/)
## License
### Usage Note
You may use, modify, and sell projects based on this code — just keep this notice and included references intact.
### Attribution & Credits
* Engine design and scene structure inspired by:
[WebGPU Samples](https://webgpu.github.io/webgpu-samples/?sample=shadowMapping)
* OBJ Loader adapted from:
[http://math.hws.edu/graphicsbook/source/webgl/cube-camera.html](http://math.hws.edu/graphicsbook/source/webgl/cube-camera.html)
* Dice roll sound `roll1.wav` sourced from:
[https://wavbvkery.com/dice-rolling-sound/](https://wavbvkery.com/dice-rolling-sound/)
* Raycasting logic assisted by ChatGPT
### BSD 3-Clause License (from WebGPU Samples)
[Full License Text](https://github.com/webgpu/webgpu-samples/blob/main/LICENSE.txt)