Added NodeJS Demo.
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
python/__pycache__/
|
||||||
|
models/
|
||||||
|
*.pyc
|
||||||
|
package-lock.json
|
||||||
844040
Demos/Texture2/demo.json
844040
Demos/Texture3/demo.json
844040
Demos/TextureArray/demo.json
844040
Demos/Triangles/demo.json
51
README.md
@@ -43,6 +43,40 @@ Just GPU work.
|
|||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
#### Browser demos
|
||||||
|
|
||||||
|
node server.js
|
||||||
|
|
||||||
|
then browse to localhost:3030/demos/
|
||||||
|
|
||||||
|
#### Nodejs demo
|
||||||
|
|
||||||
|
first install. in this project root.
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
npm i
|
||||||
|
```
|
||||||
|
|
||||||
|
To start the demo:
|
||||||
|
|
||||||
|
Open the directory
|
||||||
|
|
||||||
|
```command
|
||||||
|
cd ./demos/NodeJS
|
||||||
|
```
|
||||||
|
|
||||||
|
then execute:
|
||||||
|
|
||||||
|
```command
|
||||||
|
node index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### How to use the code
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Browser:
|
Browser:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
@@ -52,7 +86,7 @@ Browser:
|
|||||||
Node.js (requires headless or windowed WebGPU runtime):
|
Node.js (requires headless or windowed WebGPU runtime):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install webgpu-framework
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -207,18 +241,3 @@ Leeuwarden, Netherlands
|
|||||||
Portfolio: [https://kajdijkstra.com](https://kajdijkstra.com)
|
Portfolio: [https://kajdijkstra.com](https://kajdijkstra.com)
|
||||||
Email: [kajdijkstra@protonmail.com](mailto:kajdijkstra@protonmail.com)
|
Email: [kajdijkstra@protonmail.com](mailto:kajdijkstra@protonmail.com)
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Would you like me to:
|
|
||||||
|
|
||||||
✔ Add diagrams illustrating compute → sort → grid hashing pipeline
|
|
||||||
✔ Add screenshots from your particle simulation
|
|
||||||
✔ Add performance benchmarks
|
|
||||||
✔ Add a “Why use this instead of raw WebGPU?” section
|
|
||||||
✔ Publish this to npm with clean package layout
|
|
||||||
|
|
||||||
Also — should we link this repo from your CV under **Projects** with a short bullet like:
|
|
||||||
|
|
||||||
> *High-level WebGPU framework enabling real-time GPU compute + rendering in browser and native runtimes with a minimal API*
|
|
||||||
|
|
||||||
If you're ready, I can push this README into your Gitea repository automatically.
|
|
||||||
|
|||||||
304
demos/NodeJS/index.js
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import sdl from '@kmamal/sdl'
|
||||||
|
|
||||||
|
import gpu from '@kmamal/gpu'
|
||||||
|
|
||||||
|
import Shader from "../../framework/WebGpu_node.js"
|
||||||
|
|
||||||
|
import Matrix4 from "../../framework/Matrix4.js"
|
||||||
|
|
||||||
|
import Vector3 from "../../framework/Vector3.js"
|
||||||
|
|
||||||
|
import Camera from "../../framework/Camera.js";
|
||||||
|
|
||||||
|
import EventManager from "../../framework/eventManager_node.js";
|
||||||
|
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
|
|
||||||
|
const window = sdl.video.createWindow({ webgpu: true })
|
||||||
|
|
||||||
|
var canvas = window;
|
||||||
|
|
||||||
|
const instance = gpu.create([ 'verbose=1' ])
|
||||||
|
|
||||||
|
console.log("devices", gpu)
|
||||||
|
|
||||||
|
const adapter = await instance.requestAdapter()
|
||||||
|
|
||||||
|
const device = await adapter.requestDevice()
|
||||||
|
|
||||||
|
|
||||||
|
const renderer = gpu.renderGPUDeviceToWindow({ device, window })
|
||||||
|
|
||||||
|
|
||||||
|
canvas.getContext = function() {
|
||||||
|
|
||||||
|
return renderer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var renderShader = new Shader( device );
|
||||||
|
|
||||||
|
|
||||||
|
renderShader.setCanvas( canvas );
|
||||||
|
|
||||||
|
renderShader.topology = "triangle-list";
|
||||||
|
|
||||||
|
await renderShader.setup( "../../shaders/triangle-list.wgsl");
|
||||||
|
|
||||||
|
async function loadJSON( pathName ) {
|
||||||
|
|
||||||
|
const json = await readFileSync( pathName, 'utf8' )
|
||||||
|
|
||||||
|
return JSON.parse( json );
|
||||||
|
}
|
||||||
|
|
||||||
|
var camera = new Camera( [0, 0, 1115], [0, -.3, 0], [0, 1, 0] );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var eventManager = new EventManager( canvas );
|
||||||
|
|
||||||
|
eventManager.setup( canvas, camera );
|
||||||
|
|
||||||
|
eventManager.registerEventListenersNode();
|
||||||
|
|
||||||
|
var frameCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var model = await loadJSON("../../models/demo.json");
|
||||||
|
|
||||||
|
var mesh = model.meshes[0];
|
||||||
|
|
||||||
|
|
||||||
|
const instanceCount = 100;
|
||||||
|
const instancePositions = new Float32Array(instanceCount * 4); // vec4 per instance
|
||||||
|
|
||||||
|
for (let i = 0; i < instanceCount; i++) {
|
||||||
|
|
||||||
|
const x = (i % 10) * 300.0;
|
||||||
|
const y = Math.floor(i / 10) * 350.0;
|
||||||
|
|
||||||
|
instancePositions[i * 4 + 0] = x - 500;
|
||||||
|
instancePositions[i * 4 + 1] = 0;
|
||||||
|
instancePositions[i * 4 + 2] = y - 500;
|
||||||
|
instancePositions[i * 4 + 3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
renderShader.setAttribute( "position", mesh.vertices );
|
||||||
|
|
||||||
|
renderShader.setAttribute( "normal", mesh.normals );
|
||||||
|
|
||||||
|
renderShader.setVariable( "instancePositions", instancePositions );
|
||||||
|
|
||||||
|
|
||||||
|
var faces = mesh.faces;
|
||||||
|
|
||||||
|
const indexArray = new Uint32Array(faces.length * 3);
|
||||||
|
|
||||||
|
for (let i = 0; i < faces.length; i++) {
|
||||||
|
|
||||||
|
indexArray[i * 3 + 0] = faces[i][0];
|
||||||
|
|
||||||
|
indexArray[i * 3 + 1] = faces[i][1];
|
||||||
|
|
||||||
|
indexArray[i * 3 + 2] = faces[i][2];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
renderShader.setIndices( indexArray );
|
||||||
|
|
||||||
|
var lastFrameTime = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function updateTimeDelta() {
|
||||||
|
|
||||||
|
const now = performance.now();
|
||||||
|
|
||||||
|
deltaTimeValue = ( now - lastFrameTime ) / 1000;
|
||||||
|
|
||||||
|
lastFrameTime = now;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameCount = 0;
|
||||||
|
|
||||||
|
var deltaTimeValue = 0;
|
||||||
|
|
||||||
|
var vertexCount = 1;
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
|
||||||
|
if (window.destroyed) { return }
|
||||||
|
|
||||||
|
updateTimeDelta();
|
||||||
|
|
||||||
|
const viewMatrixData = camera.getViewMatrix();
|
||||||
|
|
||||||
|
const projectionMatrixData = Matrix4.createProjectionMatrix( camera, canvas )
|
||||||
|
|
||||||
|
const viewProjectionMatrix = Matrix4.multiply( projectionMatrixData, viewMatrixData );
|
||||||
|
|
||||||
|
const cameraWorldMatrix = Matrix4.invert( viewMatrixData );
|
||||||
|
|
||||||
|
const cameraPosition = Matrix4.getColumn( cameraWorldMatrix, 3 );
|
||||||
|
|
||||||
|
renderShader.setVariable( "viewProjectionMatrix", viewProjectionMatrix );
|
||||||
|
|
||||||
|
renderShader.setVariable( "cameraPosition", cameraPosition );
|
||||||
|
|
||||||
|
frameCount++;
|
||||||
|
|
||||||
|
|
||||||
|
renderShader.renderToCanvas( vertexCount, 60, 0, frameCount )
|
||||||
|
|
||||||
|
|
||||||
|
renderer.swap()
|
||||||
|
|
||||||
|
|
||||||
|
frameCount++;
|
||||||
|
|
||||||
|
setTimeout(render, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
|
||||||
|
|
||||||
|
window.on('close', () => {
|
||||||
|
device.destroy()
|
||||||
|
gpu.destroy(instance)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//console.log(model);
|
||||||
|
|
||||||
|
/*
|
||||||
|
const renderer = gpu.renderGPUDeviceToWindow({ device, window })
|
||||||
|
|
||||||
|
const positions = new Float32Array([
|
||||||
|
...[ 1.0, -1.0, 0.0 ],
|
||||||
|
...[ -1.0, -1.0, 0.0 ],
|
||||||
|
...[ 0.0, 1.0, 0.0 ],
|
||||||
|
])
|
||||||
|
|
||||||
|
const colors = new Float32Array([
|
||||||
|
...[ 1.0, 0.0, 0.0 ],
|
||||||
|
...[ 0.0, 1.0, 0.0 ],
|
||||||
|
...[ 0.0, 0.0, 1.0 ],
|
||||||
|
])
|
||||||
|
|
||||||
|
const indices = new Uint16Array([ 0, 1, 2 ])
|
||||||
|
|
||||||
|
const createBuffer = (arr, usage) => {
|
||||||
|
const buffer = device.createBuffer({
|
||||||
|
size: (arr.byteLength + 3) & ~3,
|
||||||
|
usage,
|
||||||
|
mappedAtCreation: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const writeArray = arr instanceof Uint16Array
|
||||||
|
? new Uint16Array(buffer.getMappedRange())
|
||||||
|
: new Float32Array(buffer.getMappedRange())
|
||||||
|
writeArray.set(arr)
|
||||||
|
buffer.unmap()
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionBuffer = createBuffer(positions, gpu.GPUBufferUsage.VERTEX)
|
||||||
|
const colorBuffer = createBuffer(colors, gpu.GPUBufferUsage.VERTEX)
|
||||||
|
const indexBuffer = createBuffer(indices, gpu.GPUBufferUsage.INDEX)
|
||||||
|
|
||||||
|
const vertexShaderFile = path.join(__dirname, 'vertex.wgsl')
|
||||||
|
const vertexShaderCode = await fs.promises.readFile(vertexShaderFile, 'utf8')
|
||||||
|
|
||||||
|
const fragmentShaderFile = path.join(__dirname, 'fragment.wgsl')
|
||||||
|
const fragmentShaderCode = await fs.promises.readFile(fragmentShaderFile, 'utf8')
|
||||||
|
|
||||||
|
const pipeline = device.createRenderPipeline({
|
||||||
|
layout: 'auto',
|
||||||
|
vertex: {
|
||||||
|
module: device.createShaderModule({ code: vertexShaderCode }),
|
||||||
|
entryPoint: 'main',
|
||||||
|
buffers: [
|
||||||
|
{
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
shaderLocation: 0,
|
||||||
|
offset: 0,
|
||||||
|
format: 'float32x3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT,
|
||||||
|
stepMode: 'vertex',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
shaderLocation: 1,
|
||||||
|
offset: 0,
|
||||||
|
format: 'float32x3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT,
|
||||||
|
stepMode: 'vertex',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fragment: {
|
||||||
|
module: device.createShaderModule({ code: fragmentShaderCode }),
|
||||||
|
entryPoint: 'main',
|
||||||
|
targets: [ { format: renderer.getPreferredFormat() } ],
|
||||||
|
},
|
||||||
|
primitive: {
|
||||||
|
topology: 'triangle-list',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
if (window.destroyed) { return }
|
||||||
|
|
||||||
|
const commandEncoder = device.createCommandEncoder()
|
||||||
|
|
||||||
|
const renderPass = commandEncoder.beginRenderPass({
|
||||||
|
colorAttachments: [
|
||||||
|
{
|
||||||
|
view: renderer.getCurrentTextureView(),
|
||||||
|
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
|
||||||
|
loadOp: 'clear',
|
||||||
|
storeOp: 'store',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
renderPass.setPipeline(pipeline)
|
||||||
|
renderPass.setViewport(0, 0, width, height, 0, 1)
|
||||||
|
renderPass.setScissorRect(0, 0, width, height)
|
||||||
|
renderPass.setVertexBuffer(0, positionBuffer)
|
||||||
|
renderPass.setVertexBuffer(1, colorBuffer)
|
||||||
|
renderPass.setIndexBuffer(indexBuffer, 'uint16')
|
||||||
|
renderPass.drawIndexed(3)
|
||||||
|
renderPass.end()
|
||||||
|
|
||||||
|
device.queue.submit([ commandEncoder.finish() ])
|
||||||
|
|
||||||
|
renderer.swap()
|
||||||
|
|
||||||
|
setTimeout(render, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
|
|
||||||
|
window.on('close', () => {
|
||||||
|
device.destroy()
|
||||||
|
gpu.destroy(instance)
|
||||||
|
})
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -173,7 +173,7 @@ export class ParticleSimulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var model = await this.loadJSON("demo.json");
|
var model = await this.loadJSON("../../models/demo.json");
|
||||||
|
|
||||||
var mesh = model.meshes[0];
|
var mesh = model.meshes[0];
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -173,7 +173,7 @@ export class ParticleSimulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var model = await this.loadJSON("demo.json");
|
var model = await this.loadJSON("../../models/demo.json");
|
||||||
|
|
||||||
var mesh = model.meshes[0];
|
var mesh = model.meshes[0];
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -173,7 +173,7 @@ export class ParticleSimulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var model = await this.loadJSON("demo.json");
|
var model = await this.loadJSON("../../models/demo.json");
|
||||||
|
|
||||||
var mesh = model.meshes[0];
|
var mesh = model.meshes[0];
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -222,7 +222,7 @@ export class ParticleSimulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var model = await this.loadJSON("demo.json");
|
var model = await this.loadJSON("../../models/demo.json");
|
||||||
|
|
||||||
var mesh = model.meshes[0];
|
var mesh = model.meshes[0];
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -69,7 +69,7 @@ export class ParticleSimulation {
|
|||||||
alphaMode: "opaque"
|
alphaMode: "opaque"
|
||||||
});
|
});
|
||||||
|
|
||||||
var model = await this.loadJSON("demo.json");
|
var model = await this.loadJSON("../../models/demo.json");
|
||||||
|
|
||||||
var mesh = model.meshes[0];
|
var mesh = model.meshes[0];
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
1960
framework/WebGpu_node.js
Normal file
214
framework/eventManager_node.js
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
// eventManager.js
|
||||||
|
|
||||||
|
import sdl from '@kmamal/sdl'
|
||||||
|
|
||||||
|
var isNode = true;
|
||||||
|
|
||||||
|
if ( typeof window === 'undefined' ) {
|
||||||
|
|
||||||
|
//isNode = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("isNode", isNode);
|
||||||
|
|
||||||
|
export default class EventManager {
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
|
||||||
|
lastX = 0;
|
||||||
|
|
||||||
|
lastY = 0;
|
||||||
|
|
||||||
|
camera;
|
||||||
|
|
||||||
|
canvas;
|
||||||
|
|
||||||
|
setCanvas( canvas ) {
|
||||||
|
|
||||||
|
this.canvas = canvas;
|
||||||
|
|
||||||
|
|
||||||
|
//this.registerEventListeners();
|
||||||
|
|
||||||
|
//this.handleResize();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setup( canvas, camera ) {
|
||||||
|
|
||||||
|
this.canvas = canvas;
|
||||||
|
|
||||||
|
this.camera = camera;
|
||||||
|
|
||||||
|
//this.registerEventListeners();
|
||||||
|
|
||||||
|
//this.handleResize();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
|
||||||
|
this.canvas.addEventListener( "mousedown", this.onMouseDown.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.addEventListener( "mouseup", this.onMouseUp.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.addEventListener( "mouseleave", this.onMouseLeave.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.addEventListener( "mousemove", this.onMouseMove.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.addEventListener( "wheel", this.onWheel.bind(this), { passive: false } );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEventListenersNode() {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this.canvas.on('mouseMove', function( event ) {
|
||||||
|
|
||||||
|
that.mousemove( event )
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
this.canvas.on('mouseButtonDown', function( event ) {
|
||||||
|
|
||||||
|
that.mousedown( event )
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
this.canvas.on('mouseButtonUp', function( event ) {
|
||||||
|
|
||||||
|
that.mouseup( event )
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
this.canvas.on( "mouseButtonDown", this.onMouseDown.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.on( "mouseButtonUp", this.onMouseUp.bind(this) );
|
||||||
|
|
||||||
|
//this.canvas.on( "mouseleave", this.onMouseLeave.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.on( "mouseMove", this.onMouseMove.bind(this) );
|
||||||
|
|
||||||
|
this.canvas.on( "mouseWheel", this.onWheel.bind(this), { passive: false } );
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
resize( event ) {
|
||||||
|
|
||||||
|
this.canvas.width = event.width;
|
||||||
|
|
||||||
|
this.canvas.height = event.height;
|
||||||
|
|
||||||
|
//this.canvas.width = window.innerWidth;
|
||||||
|
|
||||||
|
//this.canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mousedown( event ) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.isDragging = true;
|
||||||
|
|
||||||
|
if( isNode ) {
|
||||||
|
|
||||||
|
var mouseX = event.x;
|
||||||
|
|
||||||
|
var mouseY = event.y;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var mouseX = event.clientX;
|
||||||
|
|
||||||
|
var mouseY = event.clientY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("mouseDownHandler", mouseX, mouseY);
|
||||||
|
|
||||||
|
if( isNode ) {
|
||||||
|
|
||||||
|
this.lastX = mouseX;
|
||||||
|
|
||||||
|
this.lastY = mouseY;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.lastX = mouseX;
|
||||||
|
|
||||||
|
this.lastY = mouseY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseup( event ) {
|
||||||
|
|
||||||
|
this.isDragging = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseleave( event ) {
|
||||||
|
|
||||||
|
this.isDragging = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mousemove( event ) {
|
||||||
|
|
||||||
|
if( isNode ) {
|
||||||
|
|
||||||
|
var mouseX = event.x;
|
||||||
|
|
||||||
|
var mouseY = event.y;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var mouseX = event.clientX;
|
||||||
|
|
||||||
|
var mouseY = event.clientY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ( !this.isDragging ) return;
|
||||||
|
|
||||||
|
const deltaX = ( mouseX - this.lastX ) * 0.005;
|
||||||
|
|
||||||
|
const deltaY = ( mouseY - this.lastY ) * 0.005;
|
||||||
|
|
||||||
|
//console.log("mousemove", mouseX, mouseY);
|
||||||
|
|
||||||
|
this.camera.rotate( deltaX, -deltaY );
|
||||||
|
|
||||||
|
this.lastX = mouseX;
|
||||||
|
|
||||||
|
this.lastY = mouseY;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
wheel( event ) {
|
||||||
|
|
||||||
|
|
||||||
|
const delta = event.deltaY * 0.01;
|
||||||
|
|
||||||
|
this.camera.zoom( delta );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1 +1,5 @@
|
|||||||
{"type": "module" }
|
{"type":"module","dependencies":{
|
||||||
|
|
||||||
|
"@kmamal/gpu": "^0.2.0",
|
||||||
|
"@kmamal/sdl": "^0.11.12"
|
||||||
|
}}
|
||||||
@@ -28,7 +28,9 @@ class App
|
|||||||
|
|
||||||
this.httpServer = http.createServer( this.handleRequest.bind( this ) );
|
this.httpServer = http.createServer( this.handleRequest.bind( this ) );
|
||||||
|
|
||||||
this.httpServer.listen( 3000 );
|
this.httpServer.listen( 3030 );
|
||||||
|
|
||||||
|
console.log("Server started on port http://localhost:3030/");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||