first commit
This commit is contained in:
76
framework/Block.js
Normal file
76
framework/Block.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Memory } from "./Memory.js";
|
||||
|
||||
export class Block {
|
||||
|
||||
parameters = {};
|
||||
|
||||
constructor(name, pipeline) {
|
||||
this.name = name;
|
||||
this.pipeline = pipeline;
|
||||
this.id = -1;
|
||||
|
||||
this.passes = [];
|
||||
this.passMap = new Map();
|
||||
|
||||
this.memory = new Memory("block");
|
||||
}
|
||||
|
||||
getPreviousBlock() {
|
||||
if (this.id === 0) return null;
|
||||
return this.pipeline.blocks[this.id - 1];
|
||||
}
|
||||
|
||||
addPass( name, passInstance ) {
|
||||
|
||||
if (this.passMap.has(name)) {
|
||||
throw new Error(`Pass '${name}' already exists in block '${this.name}'.`);
|
||||
}
|
||||
|
||||
passInstance.passName = name;
|
||||
passInstance.indexInBlock = this.passes.length;
|
||||
|
||||
passInstance.pipeline = this.pipeline;
|
||||
passInstance.block = this;
|
||||
passInstance.device = this.pipeline.device;
|
||||
|
||||
if( this.layerIndex !== undefined ) {
|
||||
|
||||
passInstance.layerIndex = this.layerIndex;
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.passes.push(passInstance);
|
||||
this.passMap.set(name, passInstance);
|
||||
|
||||
return passInstance;
|
||||
}
|
||||
|
||||
getPass(name) {
|
||||
return this.passMap.get(name);
|
||||
}
|
||||
|
||||
getAllPasses() {
|
||||
return this.passes;
|
||||
}
|
||||
|
||||
getPreviousPass(passInstance) {
|
||||
const idx = passInstance.indexInBlock;
|
||||
if (idx <= 0) return null;
|
||||
return this.passes[idx - 1];
|
||||
}
|
||||
|
||||
setLayerIndex( layerIndex ) {
|
||||
|
||||
this.layerIndex = layerIndex;
|
||||
|
||||
for (var i = 0; i < this.passes.length; i++) {
|
||||
|
||||
this.passes[i].layerIndex = layerIndex;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
83
framework/Camera.js
Normal file
83
framework/Camera.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import Vector3 from "./Vector3.js";
|
||||
import Matrix4 from "./Matrix4.js";
|
||||
|
||||
export default class Camera {
|
||||
|
||||
eye = new Vector3();
|
||||
target = new Vector3();
|
||||
up = new Vector3( 0, 1, 0 );
|
||||
|
||||
yaw = 0;
|
||||
pitch = 0;
|
||||
fovRadians = Math.PI / 4;
|
||||
near = 0.1;
|
||||
far = 3000.0;
|
||||
distance = 10;
|
||||
viewMatrix = new Float32Array( 16 );
|
||||
|
||||
constructor( eye = [0, 0, 5], target = [0, 0, 0], up = [0, 1, 0] ) {
|
||||
|
||||
this.eye = new Vector3( ...eye );
|
||||
this.target = new Vector3( ...target );
|
||||
this.up = new Vector3( ...up );
|
||||
|
||||
this.distance = Vector3.subtract( this.eye, this.target ).length();
|
||||
|
||||
this.viewMatrix = Matrix4.lookAt( this.eye, this.target, this.up );
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const x = this.distance * Math.cos( this.pitch ) * Math.sin( this.yaw );
|
||||
const y = this.distance * Math.sin( this.pitch );
|
||||
const z = this.distance * Math.cos( this.pitch ) * Math.cos( this.yaw );
|
||||
|
||||
this.eye = new Vector3(
|
||||
x + this.target.x,
|
||||
y + this.target.y,
|
||||
z + this.target.z
|
||||
);
|
||||
|
||||
this.viewMatrix = Matrix4.lookAt( this.eye, this.target, this.up );
|
||||
|
||||
}
|
||||
|
||||
getViewMatrix() {
|
||||
|
||||
return this.viewMatrix;
|
||||
|
||||
}
|
||||
|
||||
rotate( deltaYaw, deltaPitch ) {
|
||||
|
||||
this.yaw += deltaYaw;
|
||||
this.pitch -= deltaPitch;
|
||||
|
||||
const maxPitch = Math.PI / 2 - 0.01;
|
||||
|
||||
if ( this.pitch > maxPitch ) this.pitch = maxPitch;
|
||||
if ( this.pitch < -maxPitch ) this.pitch = -maxPitch;
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
zoom( delta ) {
|
||||
|
||||
this.distance += delta * 1;
|
||||
|
||||
if ( this.distance < 0.1 ) this.distance = 0.1;
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
setTarget( target ) {
|
||||
|
||||
this.target = new Vector3( ...target );
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
}
|
||||
31
framework/Engine.js
Normal file
31
framework/Engine.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Memory } from "./Memory.js";
|
||||
|
||||
import { RenderSystem } from "./RenderSystem.js";
|
||||
|
||||
export class Engine {
|
||||
|
||||
constructor( device ) {
|
||||
|
||||
this.device = device;
|
||||
this.memory = new Memory( "engine" );
|
||||
this.pipelines = [];
|
||||
|
||||
this.renderSystem = null;
|
||||
|
||||
}
|
||||
|
||||
addPipeline( p ) {
|
||||
|
||||
this.pipelines.push( p );
|
||||
|
||||
}
|
||||
|
||||
|
||||
createRenderSystem( canvas ) {
|
||||
|
||||
this.renderSystem = new RenderSystem( this.device, canvas );
|
||||
|
||||
return this.renderSystem;
|
||||
|
||||
}
|
||||
}
|
||||
125
framework/Matrix4.js
Normal file
125
framework/Matrix4.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import Vector3 from "./Vector3.js";
|
||||
|
||||
export default class Matrix4 {
|
||||
|
||||
static lookAt( eye, target, up ) {
|
||||
|
||||
const zAxis = Vector3.normalize( Vector3.subtract( eye, target ) );
|
||||
|
||||
const xAxis = Vector3.normalize( Vector3.cross( up, zAxis ) );
|
||||
|
||||
const yAxis = Vector3.cross( zAxis, xAxis );
|
||||
|
||||
return new Float32Array([
|
||||
xAxis.x, yAxis.x, zAxis.x, 0,
|
||||
xAxis.y, yAxis.y, zAxis.y, 0,
|
||||
xAxis.z, yAxis.z, zAxis.z, 0,
|
||||
-Vector3.dot( xAxis, eye ), -Vector3.dot( yAxis, eye ), -Vector3.dot( zAxis, eye ), 1,
|
||||
]);
|
||||
}
|
||||
|
||||
static getColumn( matrix, index ) {
|
||||
const i = index * 4;
|
||||
|
||||
return new Vector3(
|
||||
matrix[ i + 0 ],
|
||||
matrix[ i + 1 ],
|
||||
matrix[ i + 2 ]
|
||||
);
|
||||
}
|
||||
|
||||
static createProjectionMatrix( camera, canvas ) {
|
||||
return Matrix4.perspective(
|
||||
camera.fovRadians,
|
||||
canvas.width / canvas.height,
|
||||
camera.near,
|
||||
camera.far
|
||||
);
|
||||
}
|
||||
|
||||
static invert( m ) {
|
||||
const out = new Float32Array(16);
|
||||
|
||||
const m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
|
||||
const m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
|
||||
const m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
|
||||
const m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
|
||||
|
||||
const a0 = m00 * m11 - m01 * m10;
|
||||
const a1 = m00 * m12 - m02 * m10;
|
||||
const a2 = m00 * m13 - m03 * m10;
|
||||
const a3 = m01 * m12 - m02 * m11;
|
||||
const a4 = m01 * m13 - m03 * m11;
|
||||
const a5 = m02 * m13 - m03 * m12;
|
||||
|
||||
const b0 = m20 * m31 - m21 * m30;
|
||||
const b1 = m20 * m32 - m22 * m30;
|
||||
const b2 = m20 * m33 - m23 * m30;
|
||||
const b3 = m21 * m32 - m22 * m31;
|
||||
const b4 = m21 * m33 - m23 * m31;
|
||||
const b5 = m22 * m33 - m23 * m32;
|
||||
|
||||
const det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
|
||||
|
||||
if (det === 0) return null;
|
||||
|
||||
const invDet = 1 / det;
|
||||
|
||||
out[0] = ( m11 * b5 - m12 * b4 + m13 * b3) * invDet;
|
||||
out[1] = (-m01 * b5 + m02 * b4 - m03 * b3) * invDet;
|
||||
out[2] = ( m31 * a5 - m32 * a4 + m33 * a3) * invDet;
|
||||
out[3] = (-m21 * a5 + m22 * a4 - m23 * a3) * invDet;
|
||||
|
||||
out[4] = (-m10 * b5 + m12 * b2 - m13 * b1) * invDet;
|
||||
out[5] = ( m00 * b5 - m02 * b2 + m03 * b1) * invDet;
|
||||
out[6] = (-m30 * a5 + m32 * a2 - m33 * a1) * invDet;
|
||||
out[7] = ( m20 * a5 - m22 * a2 + m23 * a1) * invDet;
|
||||
|
||||
out[8] = ( m10 * b4 - m11 * b2 + m13 * b0) * invDet;
|
||||
out[9] = (-m00 * b4 + m01 * b2 - m03 * b0) * invDet;
|
||||
out[10] = ( m30 * a4 - m31 * a2 + m33 * a0) * invDet;
|
||||
out[11] = (-m20 * a4 + m21 * a2 - m23 * a0) * invDet;
|
||||
|
||||
out[12] = (-m10 * b3 + m11 * b1 - m12 * b0) * invDet;
|
||||
out[13] = ( m00 * b3 - m01 * b1 + m02 * b0) * invDet;
|
||||
out[14] = (-m30 * a3 + m31 * a1 - m32 * a0) * invDet;
|
||||
out[15] = ( m20 * a3 - m21 * a1 + m22 * a0) * invDet;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static perspective( fovRadians, aspect, near, far ) {
|
||||
|
||||
const f = 1.0 / Math.tan( fovRadians / 2 );
|
||||
|
||||
const nf = 1 / ( near - far );
|
||||
|
||||
return new Float32Array([
|
||||
f / aspect, 0, 0, 0,
|
||||
0, f, 0, 0,
|
||||
0, 0, (far + near) * nf, -1,
|
||||
0, 0, (2 * far * near) * nf, 0,
|
||||
]);
|
||||
}
|
||||
|
||||
static multiply( a, b ) {
|
||||
const out = new Float32Array(16);
|
||||
|
||||
for ( let col = 0; col < 4; col++ ) {
|
||||
for ( let row = 0; row < 4; row++ ) {
|
||||
let sum = 0;
|
||||
|
||||
for ( let k = 0; k < 4; k++ ) {
|
||||
// a is column-major: element at col k, row row => a[k*4 + row]
|
||||
// b is column-major: element at col col, row k => b[col*4 + k]
|
||||
sum += a[k * 4 + row] * b[col * 4 + k];
|
||||
}
|
||||
|
||||
out[col * 4 + row] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
49
framework/Measure.js
Normal file
49
framework/Measure.js
Normal file
@@ -0,0 +1,49 @@
|
||||
export default class Measure {
|
||||
|
||||
startTimes = {};
|
||||
|
||||
endTimes = {};
|
||||
|
||||
writeToPage = false;
|
||||
|
||||
element = false;
|
||||
|
||||
start ( label ) {
|
||||
|
||||
this.startTimes[ label ] = performance.now();
|
||||
}
|
||||
|
||||
end ( label ) {
|
||||
|
||||
this.endTimes[ label ] = performance.now();
|
||||
|
||||
this.log( label );
|
||||
}
|
||||
|
||||
getElapsed ( label ) {
|
||||
|
||||
if ( this.startTimes[ label ] === undefined || this.endTimes[ label ] === undefined ) {
|
||||
|
||||
throw new Error( "Start or end time missing for label: " + label );
|
||||
}
|
||||
|
||||
return this.endTimes[ label ] - this.startTimes[ label ];
|
||||
}
|
||||
|
||||
log ( label ) {
|
||||
|
||||
const elapsed = this.getElapsed( label );
|
||||
|
||||
if( this.writeToPage ) {
|
||||
|
||||
var p = document.createElement("p")
|
||||
|
||||
p.innerText = label + " took " + elapsed.toFixed(3) + " ms";
|
||||
|
||||
this.element.appendChild( p );
|
||||
|
||||
}
|
||||
|
||||
console.log( label + " took " + elapsed.toFixed(3) + " ms" );
|
||||
}
|
||||
}
|
||||
25
framework/Memory.js
Normal file
25
framework/Memory.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export class Memory {
|
||||
|
||||
constructor(scopeName = "") {
|
||||
this.scopeName = scopeName;
|
||||
this._map = new Map();
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
this._map.set(name, value);
|
||||
this[name] = value; // property access
|
||||
}
|
||||
|
||||
get(name) {
|
||||
return this._map.get(name);
|
||||
}
|
||||
|
||||
has(name) {
|
||||
return this._map.has(name);
|
||||
}
|
||||
|
||||
delete(name) {
|
||||
this._map.delete(name);
|
||||
delete this[name];
|
||||
}
|
||||
}
|
||||
59
framework/Mesh.js
Normal file
59
framework/Mesh.js
Normal file
@@ -0,0 +1,59 @@
|
||||
export class Mesh {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
this.shaders = [];
|
||||
|
||||
this.instanceCount = 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
addShader( shader, options = { } ) {
|
||||
|
||||
this.shaders.push( {
|
||||
shader,
|
||||
options
|
||||
} );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
setInstanceCount( count ) {
|
||||
|
||||
this.instanceCount = count;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
draw( passEncoder ) {
|
||||
|
||||
for ( const entry of this.shaders ) {
|
||||
|
||||
const shader = entry.shader;
|
||||
|
||||
const opts = entry.options || { };
|
||||
|
||||
const instances = opts.instances != null ? opts.instances : this.instanceCount;
|
||||
|
||||
// When no explicit vertex count is provided, rely on the shader's indexCount
|
||||
const vertexCount = opts.vertexCount != null ? opts.vertexCount : ( shader.indexCount || 0 );
|
||||
|
||||
if ( vertexCount <= 0 ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
shader.encodeRender( passEncoder, vertexCount, instances );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
18
framework/ModelState.js
Normal file
18
framework/ModelState.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export class ModelState {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.config = null;
|
||||
|
||||
// embeddings
|
||||
this.tokenEmbedding = null;
|
||||
this.positionEmbedding = null;
|
||||
|
||||
// transformer layers
|
||||
this.layers = []; // each layer: { Wq,Wk,Wv,bq,bk,bv, Wout,bout, Wfc1,bfc1, Wfc2,bfc2 }
|
||||
|
||||
// tied output projection
|
||||
this.Wlogits = null;
|
||||
}
|
||||
|
||||
}
|
||||
37
framework/RenderPass.js
Normal file
37
framework/RenderPass.js
Normal file
@@ -0,0 +1,37 @@
|
||||
export class RenderPass {
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
attach(device, block, pipeline) {
|
||||
this.device = device;
|
||||
this.block = block;
|
||||
this.pipeline = pipeline;
|
||||
}
|
||||
|
||||
async create() {
|
||||
// override — create Shader, load WGSL, create local buffers
|
||||
}
|
||||
|
||||
async bindBuffers() {
|
||||
// override — wire connections to other passes
|
||||
}
|
||||
|
||||
async execute() {
|
||||
// override — dispatch shader
|
||||
}
|
||||
|
||||
async test() {
|
||||
// override — CPU-vs-GPU correctness test
|
||||
}
|
||||
|
||||
setLayerIndex(index) {
|
||||
this.layerIndex = index;
|
||||
}
|
||||
|
||||
setLayerWeightArray(array) {
|
||||
this.layerWeightArray = array;
|
||||
}
|
||||
|
||||
}
|
||||
70
framework/RenderPipeline.js
Normal file
70
framework/RenderPipeline.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Memory } from "./Memory.js";
|
||||
|
||||
export class RenderPipeline {
|
||||
|
||||
constructor(engine) {
|
||||
this.engine = engine;
|
||||
this.device = engine.device;
|
||||
|
||||
this.blocks = [];
|
||||
this.memory = new Memory("pipeline");
|
||||
|
||||
engine.addPipeline(this);
|
||||
}
|
||||
|
||||
addBlock(block) {
|
||||
block.id = this.blocks.length;
|
||||
block.pipeline = this;
|
||||
|
||||
this.blocks.push(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
getBlock(id) {
|
||||
return this.blocks[id];
|
||||
}
|
||||
|
||||
getBlockByName(name) {
|
||||
return this.blocks.find(block => block.name === name) || null;
|
||||
}
|
||||
|
||||
getPreviousBlock(block) {
|
||||
if (block.id === 0) return null;
|
||||
return this.blocks[block.id - 1];
|
||||
}
|
||||
|
||||
async create() {
|
||||
for (const block of this.blocks) {
|
||||
for (const pass of block.getAllPasses()) {
|
||||
await pass.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async bindBuffers() {
|
||||
for (const block of this.blocks) {
|
||||
for (const pass of block.getAllPasses()) {
|
||||
await pass.bindBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async execute() {
|
||||
for (const block of this.blocks) {
|
||||
for (const pass of block.getAllPasses()) {
|
||||
await pass.execute();
|
||||
await this.device.queue.onSubmittedWorkDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async test() {
|
||||
for (const block of this.blocks) {
|
||||
for (const pass of block.getAllPasses()) {
|
||||
if (typeof pass.test === "function") {
|
||||
await pass.test();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
framework/RenderSystem.js
Normal file
83
framework/RenderSystem.js
Normal file
@@ -0,0 +1,83 @@
|
||||
export class RenderSystem {
|
||||
|
||||
constructor( device, canvas ) {
|
||||
|
||||
this.device = device;
|
||||
this.canvas = canvas;
|
||||
|
||||
// simple per-system depth texture; recreated on resize by user as needed
|
||||
this.depthTexture = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
_beginFrame( clearColor = { r: 0.3, g: 0.3, b: 0.3, a: 1 } ) {
|
||||
|
||||
const canvas = this.canvas;
|
||||
|
||||
const context = canvas.getContext( "webgpu" );
|
||||
|
||||
const format = navigator.gpu.getPreferredCanvasFormat();
|
||||
|
||||
if ( !this.depthTexture ||
|
||||
this.depthTexture.width !== canvas.width ||
|
||||
this.depthTexture.height !== canvas.height ) {
|
||||
|
||||
this.depthTexture = this.device.createTexture( {
|
||||
size: [ canvas.width, canvas.height, 1 ],
|
||||
sampleCount: 1,
|
||||
format: "depth24plus",
|
||||
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
const encoder = this.device.createCommandEncoder();
|
||||
|
||||
const view = context.getCurrentTexture().createView();
|
||||
|
||||
const depthView = this.depthTexture.createView();
|
||||
|
||||
const renderPassDescriptor = {
|
||||
colorAttachments: [ {
|
||||
view: view,
|
||||
loadOp: "clear",
|
||||
storeOp: "store",
|
||||
clearValue: clearColor
|
||||
} ],
|
||||
depthStencilAttachment: {
|
||||
view: depthView,
|
||||
depthLoadOp: "clear",
|
||||
depthStoreOp: "store",
|
||||
depthClearValue: 1.0
|
||||
}
|
||||
};
|
||||
|
||||
const passEncoder = encoder.beginRenderPass( renderPassDescriptor );
|
||||
|
||||
return { encoder, passEncoder };
|
||||
|
||||
}
|
||||
|
||||
|
||||
_endFrame( frame ) {
|
||||
|
||||
frame.passEncoder.end();
|
||||
|
||||
this.device.queue.submit( [ frame.encoder.finish() ] );
|
||||
|
||||
}
|
||||
|
||||
|
||||
render( scene, clearColor ) {
|
||||
|
||||
const frame = this._beginFrame( clearColor );
|
||||
|
||||
scene.draw( frame.passEncoder );
|
||||
|
||||
this._endFrame( frame );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
framework/Request.js
Normal file
11
framework/Request.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export class Request {
|
||||
|
||||
constructor( method, payload = {} ) {
|
||||
|
||||
this.method = method; // method name to call on Controller, e.g. "Ping"
|
||||
|
||||
this.payload = payload; // any data for the method
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
34
framework/Scene.js
Normal file
34
framework/Scene.js
Normal file
@@ -0,0 +1,34 @@
|
||||
export class Scene {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
this.meshes = [];
|
||||
|
||||
}
|
||||
|
||||
|
||||
addMesh( mesh ) {
|
||||
|
||||
this.meshes.push( mesh );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
draw( passEncoder ) {
|
||||
|
||||
for ( const mesh of this.meshes ) {
|
||||
|
||||
if ( typeof mesh.draw === "function" ) {
|
||||
|
||||
mesh.draw( passEncoder );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
67
framework/ShaderInpector.js
Normal file
67
framework/ShaderInpector.js
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
class shaderDebugger{
|
||||
|
||||
setup() {
|
||||
|
||||
var shaders = document.shaders;
|
||||
|
||||
var select = document.querySelector(".selectDebugShader");
|
||||
|
||||
for (var i = 0; i < shaders.length; i++) {
|
||||
|
||||
var currentShader = shaders[i];
|
||||
|
||||
var option = document.createElement("option");
|
||||
|
||||
option.innerText = currentShader.path;
|
||||
|
||||
option.id = i;
|
||||
|
||||
select.appendChild( option );
|
||||
|
||||
}
|
||||
|
||||
document.querySelector( "#showBuffers" ).addEventListener( "click", async function() {
|
||||
|
||||
var select = document.querySelector(".selectDebugShader");
|
||||
|
||||
var selectedIndex = select.selectedIndex;
|
||||
|
||||
var selectedShader = document.shaders[ selectedIndex ]
|
||||
|
||||
const keysArray = Array.from( selectedShader.buffers );
|
||||
|
||||
console.log("\n\n\n\n -------------------- Debugging Shader --------------- \n\n\n\n");
|
||||
|
||||
console.log( "Shader Path: ", selectedShader.path );
|
||||
|
||||
console.log( selectedShader );
|
||||
|
||||
for (var i = 0; i < keysArray.length; i++) {
|
||||
|
||||
const bindingInfo = selectedShader.bindings.find( b => b.varName === keysArray[i][0] );
|
||||
|
||||
|
||||
if( bindingInfo ) {
|
||||
|
||||
if( bindingInfo.type == "storage" ) {
|
||||
|
||||
await selectedShader.debugBuffer( keysArray[i][0] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.log("this is a Uniform", keysArray, selectedShader.bindings);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default shaderDebugger;
|
||||
205
framework/Tools.js
Normal file
205
framework/Tools.js
Normal file
@@ -0,0 +1,205 @@
|
||||
// ============================================================================
|
||||
// Tools.js — now loads real GPT-2 vocab.json + merges.txt into fake model
|
||||
// ============================================================================
|
||||
|
||||
export class Tools {
|
||||
|
||||
static async generateFakeModel() {
|
||||
|
||||
console.log("[Tools] Loading vocab.json + merges.txt…");
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Load vocabulary
|
||||
// ------------------------------------------------------------
|
||||
const vocabResponse =
|
||||
await fetch("model/vocab.json"); // <-- adjust path
|
||||
const vocabularyList =
|
||||
await vocabResponse.json();
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Load merges
|
||||
// ------------------------------------------------------------
|
||||
const mergesResponse =
|
||||
await fetch("model/merges.txt"); // <-- adjust path
|
||||
const mergeRuleText =
|
||||
await mergesResponse.text();
|
||||
|
||||
const mergeRuleList =
|
||||
mergeRuleText
|
||||
.split("\n")
|
||||
.filter(line => line.trim().length > 0 && !line.startsWith("#"));
|
||||
|
||||
console.log("[Tools] ✓ Loaded",
|
||||
Object.keys(vocabularyList).length, "vocab tokens,",
|
||||
mergeRuleList.length, "merge rules."
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Fake GPT-2 weights (hidden size = 8)
|
||||
// ------------------------------------------------------------
|
||||
const fakeHiddenSize = 8;
|
||||
const fakeIntermediateSize = fakeHiddenSize * 4;
|
||||
const fakeSequenceLength = 4;
|
||||
const fakeNumberOfLayers = 2;
|
||||
|
||||
function createFakeArray(size) {
|
||||
let arr = new Float32Array(size);
|
||||
for (let i = 0; i < size; i++) arr[i] = (i % 7) * 0.1;
|
||||
return arr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Build fake model
|
||||
// ------------------------------------------------------------
|
||||
let model = {
|
||||
|
||||
configuration : {
|
||||
hiddenSize: fakeHiddenSize,
|
||||
numberOfTransformerLayers: fakeNumberOfLayers,
|
||||
numberOfAttentionHeads: 2,
|
||||
maximumSequenceLength: fakeSequenceLength,
|
||||
vocabularySize: Object.keys(vocabularyList).length,
|
||||
maximumPositionCount: 2048
|
||||
},
|
||||
|
||||
// real tokenizer data:
|
||||
vocabularyList: vocabularyList,
|
||||
mergeRuleList: mergeRuleList,
|
||||
|
||||
// fake embeddings:
|
||||
tokenEmbeddingTensor:
|
||||
createFakeArray(Object.keys(vocabularyList).length * fakeHiddenSize),
|
||||
|
||||
positionEmbeddingTensor:
|
||||
createFakeArray(2048 * fakeHiddenSize),
|
||||
|
||||
transformerLayerList: [],
|
||||
layerWeightFlatList: [],
|
||||
tokenIndexArray: null
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Create fake transformer layers
|
||||
// ------------------------------------------------------------
|
||||
for (let layerIndex = 0; layerIndex < fakeNumberOfLayers; layerIndex++) {
|
||||
|
||||
let layer = {
|
||||
|
||||
firstNormalizationWeightTensor: createFakeArray(fakeHiddenSize),
|
||||
firstNormalizationBiasTensor: createFakeArray(fakeHiddenSize),
|
||||
|
||||
queryWeightTensor: createFakeArray(fakeHiddenSize * fakeHiddenSize),
|
||||
keyWeightTensor: createFakeArray(fakeHiddenSize * fakeHiddenSize),
|
||||
valueWeightTensor: createFakeArray(fakeHiddenSize * fakeHiddenSize),
|
||||
|
||||
queryBiasTensor: createFakeArray(fakeHiddenSize),
|
||||
keyBiasTensor: createFakeArray(fakeHiddenSize),
|
||||
valueBiasTensor: createFakeArray(fakeHiddenSize),
|
||||
|
||||
attentionOutputProjectionWeightTensor:
|
||||
createFakeArray(fakeHiddenSize * fakeHiddenSize),
|
||||
|
||||
attentionOutputProjectionBiasTensor:
|
||||
createFakeArray(fakeHiddenSize),
|
||||
|
||||
secondNormalizationWeightTensor:
|
||||
createFakeArray(fakeHiddenSize),
|
||||
|
||||
secondNormalizationBiasTensor:
|
||||
createFakeArray(fakeHiddenSize),
|
||||
|
||||
feedForwardLayerOneWeightTensor:
|
||||
createFakeArray(fakeHiddenSize * fakeIntermediateSize),
|
||||
|
||||
feedForwardLayerOneBiasTensor:
|
||||
createFakeArray(fakeIntermediateSize),
|
||||
|
||||
feedForwardLayerTwoWeightTensor:
|
||||
createFakeArray(fakeIntermediateSize * fakeHiddenSize),
|
||||
|
||||
feedForwardLayerTwoBiasTensor:
|
||||
createFakeArray(fakeHiddenSize)
|
||||
};
|
||||
|
||||
model.transformerLayerList.push(layer);
|
||||
}
|
||||
|
||||
console.log("[Tools] ✓ Fake model ready.");
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Packs a 1D typed array into a 2D RGBA Float texture.
|
||||
*
|
||||
* @param {TypedArray} data - Input values (Float32Array, Uint32Array, etc.)
|
||||
* @param {number} texWidth - Maximum texture width (e.g., 8192)
|
||||
* @returns {{texData: Float32Array, width: number, height: number, totalPixels: number}}
|
||||
*/
|
||||
static packIntoTextureRGBA(sourceData, texWidth = 8192) {
|
||||
|
||||
const totalValues = sourceData.length;
|
||||
const totalPixels = Math.ceil(totalValues / 4); // 4 channels/pixel
|
||||
|
||||
const width = texWidth;
|
||||
const height = Math.ceil(totalPixels / width);
|
||||
|
||||
const data = new Float32Array(width * height * 4);
|
||||
|
||||
for (let i = 0; i < totalValues; i++) {
|
||||
|
||||
const pixelIndex = Math.floor(i / 4);
|
||||
const channel = i % 4;
|
||||
|
||||
const x = pixelIndex % width;
|
||||
const y = Math.floor(pixelIndex / width);
|
||||
|
||||
data[(y * width + x) * 4 + channel] = sourceData[i];
|
||||
}
|
||||
|
||||
return { data, width, height, totalPixels };
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Packs a 2D tensor (rows × cols) into a 2D RGBA float texture.
|
||||
*
|
||||
* @param {TypedArray} tensor - 1D row-major data
|
||||
* @param {number} rows - number of rows
|
||||
* @param {number} cols - number of columns
|
||||
* @param {number} texWidth - max texture width (default 8192)
|
||||
*/
|
||||
static pack2DTensorIntoTexture( tensor, rows, cols, texWidth = 8192 ) {
|
||||
|
||||
const flatLength = rows * cols;
|
||||
if (tensor.length !== flatLength)
|
||||
throw new Error("Tensor length does not match rows*cols");
|
||||
|
||||
const totalPixels = Math.ceil(flatLength / 4);
|
||||
const width = texWidth;
|
||||
const height = Math.ceil(totalPixels / width);
|
||||
|
||||
const data = new Float32Array(width * height * 4);
|
||||
|
||||
for (let index = 0; index < flatLength; index++) {
|
||||
|
||||
const pixelIndex = index >> 2; // /4
|
||||
const channel = index & 3; // %4
|
||||
|
||||
const x = pixelIndex % width;
|
||||
const y = (pixelIndex / width) | 0;
|
||||
|
||||
data[(y * width + x) * 4 + channel] = tensor[index];
|
||||
}
|
||||
|
||||
return { data, width, height, totalPixels };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
60
framework/Vector3.js
Normal file
60
framework/Vector3.js
Normal file
@@ -0,0 +1,60 @@
|
||||
export default class Vector3 {
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
|
||||
constructor( x = 0, y = 0, z = 0 ) {
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
|
||||
}
|
||||
|
||||
static subtract( a, b ) {
|
||||
|
||||
return new Vector3( a.x - b.x, a.y - b.y, a.z - b.z );
|
||||
|
||||
}
|
||||
|
||||
|
||||
length() {
|
||||
|
||||
return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
|
||||
|
||||
}
|
||||
|
||||
static cross( a, b ) {
|
||||
|
||||
return new Vector3(
|
||||
|
||||
a.y * b.z - a.z * b.y,
|
||||
a.z * b.x - a.x * b.z,
|
||||
a.x * b.y - a.y * b.x
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
static dot( a, b ) {
|
||||
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
|
||||
}
|
||||
|
||||
static normalize( v ) {
|
||||
|
||||
const length = Math.sqrt( v.x * v.x + v.y * v.y + v.z * v.z );
|
||||
|
||||
if ( length > 0.00001 ) {
|
||||
|
||||
return new Vector3( v.x / length, v.y / length, v.z / length );
|
||||
|
||||
} else {
|
||||
|
||||
return new Vector3( 0, 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
143
framework/WGSLReflection.js
Normal file
143
framework/WGSLReflection.js
Normal file
@@ -0,0 +1,143 @@
|
||||
export class WGSLReflection {
|
||||
|
||||
constructor(wgslSource) {
|
||||
|
||||
this.src = wgslSource;
|
||||
this.structs = {};
|
||||
this.bindings = [];
|
||||
|
||||
this.parseStructs();
|
||||
this.parseBindings();
|
||||
}
|
||||
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
// STRUCT PARSING
|
||||
// ------------------------------------------------------------
|
||||
//
|
||||
parseStructs() {
|
||||
|
||||
const structRegex =
|
||||
/struct\s+(\w+)\s*\{([^}]+)\}/g;
|
||||
|
||||
let match;
|
||||
while ((match = structRegex.exec(this.src))) {
|
||||
|
||||
const structName = match[1];
|
||||
const body = match[2].trim();
|
||||
|
||||
const fields = this.parseStructFields(body);
|
||||
const size = fields.reduce((sum, f) => sum + f.size, 0);
|
||||
|
||||
this.structs[structName] = {
|
||||
name: structName,
|
||||
fields,
|
||||
size
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
parseStructFields(body) {
|
||||
|
||||
const lines = body.split("\n");
|
||||
const fields = [];
|
||||
|
||||
for (let line of lines) {
|
||||
|
||||
line = line.trim();
|
||||
if (!line) continue;
|
||||
|
||||
const m = line.match(/(\w+)\s*:\s*([^;]+);/);
|
||||
if (!m) continue;
|
||||
|
||||
const fieldName = m[1];
|
||||
const wgslType = m[2].trim();
|
||||
const size = this.computeTypeSize(wgslType);
|
||||
|
||||
fields.push({ fieldName, wgslType, size });
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
// BINDING PARSING
|
||||
// ------------------------------------------------------------
|
||||
//
|
||||
parseBindings() {
|
||||
|
||||
const varRegex =
|
||||
/@group\((\d+)\)\s*@binding\((\d+)\)\s*var<([^>]+)>\s+(\w+)\s*:\s*([^;]+);/g;
|
||||
|
||||
let match;
|
||||
while ((match = varRegex.exec(this.src))) {
|
||||
|
||||
const group = parseInt(match[1]);
|
||||
const binding = parseInt(match[2]);
|
||||
const type = match[3].trim();
|
||||
const varName = match[4].trim();
|
||||
const varType = match[5].trim();
|
||||
|
||||
const size = this.computeTypeSize(varType);
|
||||
|
||||
this.bindings.push({
|
||||
group,
|
||||
binding,
|
||||
type,
|
||||
varName,
|
||||
varType,
|
||||
size
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
// TYPE SIZE COMPUTATION (Simplified)
|
||||
// ------------------------------------------------------------
|
||||
//
|
||||
computeTypeSize(wgslType) {
|
||||
|
||||
// scalar
|
||||
if (wgslType === "f32" || wgslType === "u32" || wgslType === "i32") {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// array<f32, N>
|
||||
const arrMatch = wgslType.match(/array<(\w+),\s*(\d+)>/);
|
||||
if (arrMatch) {
|
||||
const elementType = arrMatch[1];
|
||||
const count = parseInt(arrMatch[2]);
|
||||
return this.computeTypeSize(elementType) * count;
|
||||
}
|
||||
|
||||
// array<f32> runtime-sized → minimal
|
||||
const runtimeArr = wgslType.match(/array<(\w+)>/);
|
||||
if (runtimeArr) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// struct
|
||||
if (this.structs[wgslType]) {
|
||||
return this.structs[wgslType].size;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
// SUMMARY OUTPUT
|
||||
// ------------------------------------------------------------
|
||||
//
|
||||
reflect() {
|
||||
return {
|
||||
structs: this.structs,
|
||||
bindings: this.bindings
|
||||
};
|
||||
}
|
||||
}
|
||||
2256
framework/WebGpu.js
Normal file
2256
framework/WebGpu.js
Normal file
File diff suppressed because it is too large
Load Diff
143
framework/eventManager.js
Normal file
143
framework/eventManager.js
Normal file
@@ -0,0 +1,143 @@
|
||||
// eventManager.js
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
onMouseDown( event ) {
|
||||
|
||||
this.mousedown( event );
|
||||
|
||||
}
|
||||
|
||||
onMouseUp( event ) {
|
||||
|
||||
this.mouseup( event );
|
||||
|
||||
}
|
||||
|
||||
onMouseLeave( event ) {
|
||||
|
||||
this.mouseleave( event );
|
||||
|
||||
}
|
||||
|
||||
onMouseMove( event ) {
|
||||
|
||||
this.mousemove( event );
|
||||
|
||||
}
|
||||
|
||||
onWheel( event ) {
|
||||
|
||||
this.wheel( event );
|
||||
|
||||
}
|
||||
|
||||
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 } );
|
||||
|
||||
}
|
||||
|
||||
resize( event ) {
|
||||
|
||||
this.canvas.width = event.width;
|
||||
|
||||
this.canvas.height = event.height;
|
||||
|
||||
//this.canvas.width = window.innerWidth;
|
||||
|
||||
//this.canvas.height = window.innerHeight;
|
||||
|
||||
}
|
||||
|
||||
mousedown( event ) {
|
||||
|
||||
console.log("mouseDownHandler");
|
||||
|
||||
this.isDragging = true;
|
||||
|
||||
this.lastX = event.clientX;
|
||||
|
||||
this.lastY = event.clientY;
|
||||
|
||||
}
|
||||
|
||||
mouseup( event ) {
|
||||
|
||||
this.isDragging = false;
|
||||
|
||||
}
|
||||
|
||||
mouseleave( event ) {
|
||||
|
||||
this.isDragging = false;
|
||||
|
||||
}
|
||||
|
||||
mousemove( event ) {
|
||||
|
||||
if ( !this.isDragging ) return;
|
||||
|
||||
const deltaX = ( event.clientX - this.lastX ) * 0.005;
|
||||
|
||||
const deltaY = ( event.clientY - this.lastY ) * 0.005;
|
||||
|
||||
this.camera.rotate( deltaX, -deltaY );
|
||||
|
||||
this.lastX = event.clientX;
|
||||
|
||||
this.lastY = event.clientY;
|
||||
|
||||
}
|
||||
|
||||
wheel( event ) {
|
||||
|
||||
|
||||
const delta = event.deltaY * 0.01;
|
||||
|
||||
this.camera.zoom( delta );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
214
framework/eventManager_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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
framework/package.json
Normal file
6
framework/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user