diff --git a/package.json b/package.json new file mode 100644 index 0000000..7500e5f --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "bspview", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "stats.js": "^0.17.0", + "three": "^0.114.0" + }, + "devDependencies": { + "@types/stats.js": "^0.17.0", + "@types/three": "^0.103.2", + "ts-loader": "^6.2.1", + "typescript": "^3.7.5", + "webpack": "^4.41.5", + "webpack-cli": "^3.3.10", + "webpack-dev-server": "^3.10.3" + }, + "scripts": { + "build": "webpack", + "start": "webpack-dev-server --open", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/public/bsp/c1a0.bsp b/public/bsp/c1a0.bsp new file mode 100644 index 0000000..449beae Binary files /dev/null and b/public/bsp/c1a0.bsp differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..f9a7f20 --- /dev/null +++ b/public/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/bsp.ts b/src/bsp.ts new file mode 100644 index 0000000..c9a9435 --- /dev/null +++ b/src/bsp.ts @@ -0,0 +1,101 @@ + +// #define LUMP_ENTITIES 0 +// #define LUMP_PLANES 1 +// #define LUMP_TEXTURES 2 +// #define LUMP_VERTICES 3 +// #define LUMP_VISIBILITY 4 +// #define LUMP_NODES 5 +// #define LUMP_TEXINFO 6 +// #define LUMP_FACES 7 +// #define LUMP_LIGHTING 8 +// #define LUMP_CLIPNODES 9 +// #define LUMP_LEAVES 10 +// #define LUMP_MARKSURFACES 11 +// #define LUMP_EDGES 12 +// #define LUMP_SURFEDGES 13 +// #define LUMP_MODELS 14 +// #define HEADER_LUMPS 15 + +// typedef struct _VECTOR3D +// { +// float x, y, z; +// } VECTOR3D; + +const lumps = [ + "LUMP_ENTITIES", + "LUMP_PLANES", + "LUMP_TEXTURES", + "LUMP_VERTICES", + "LUMP_VISIBILITY", + "LUMP_NODES", + "LUMP_TEXINFO", + "LUMP_FACES", + "LUMP_LIGHTING", + "LUMP_CLIPNODES", + "LUMP_LEAVES", + "LUMP_MARKSURFACES", + "LUMP_EDGES", + "LUMP_SURFEDGES", + "LUMP_MODELS", + "HEADER_LUMPS" +] + +interface Vector3D { + x: number; + y: number; + z: number; +} + +interface BSP { + vertices: Vector3D[]; + edges: number[][]; +} + +export function parseBSP(buffer: ArrayBuffer): BSP { + const view = new DataView(buffer); + console.log(view.getUint32(0, true)); + + const lumpData: { [key: string]: any } = {}; + + for (let i = 0; i < lumps.length; i++) { + let lumpType = lumps[i]; + const offset = view.getUint32((i * 8) + 4, true); + const lumpLength = view.getUint32((i * 8) + 8, true); + lumpData[lumpType] = { offset, lumpLength }; + } + + console.table(lumpData); + + // Parse vertices + const vertexView = new DataView(buffer, lumpData["LUMP_VERTICES"].offset, lumpData["LUMP_VERTICES"].lumpLength); + const vertices = []; + for (let offset = 0; offset < vertexView.byteLength; offset += 12) { + const x = vertexView.getFloat32(offset, true); + const y = vertexView.getFloat32(offset + 4, true); + const z = vertexView.getFloat32(offset + 8, true); + vertices.push({ + x, y, z + }); + } + + const edgeView = new DataView(buffer, lumpData["LUMP_EDGES"].offset, lumpData["LUMP_EDGES"].lumpLength); + const edges = []; + for (let offset = 0; offset < edgeView.byteLength; offset += 4) { + const a = edgeView.getUint16(offset, true); + const b = edgeView.getUint16(offset + 2, true); + edges.push([a, b]); + } + + const bsp: BSP = { + vertices, + edges + }; + + return bsp; +} + +// function parseVector3D(buffer: ArrayBuffer) { +// return { +// x: buffer. +// } +// } \ No newline at end of file diff --git a/src/entry.ts b/src/entry.ts new file mode 100644 index 0000000..c981ed1 --- /dev/null +++ b/src/entry.ts @@ -0,0 +1,63 @@ +import { main } from "./webgl"; +import { parseBSP } from "./bsp"; +import * as THREE from "three"; + +import * as Stats from "stats.js"; +import { FlyControls } from "./flyControls"; + + +var scene = new THREE.Scene(); +var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 500); +var renderer = new THREE.WebGLRenderer(); +renderer.setSize(window.innerWidth, window.innerHeight); +document.body.appendChild(renderer.domElement); + +const clock = new THREE.Clock(); + + +fetch("/bsp/c1a0.bsp").then(async (response) => { + const buffer = await response.arrayBuffer(); + const bsp = parseBSP(buffer); + bsp.edges.forEach(edge => { + var geometry = new THREE.Geometry(); + const v1 = bsp.vertices[edge[0]]; + const v2 = bsp.vertices[edge[1]]; + geometry.vertices.push( + new THREE.Vector3(v1.x, v1.z, v1.y), + new THREE.Vector3(v2.x, v2.z, v2.y), + ); + var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); + var seg = new THREE.LineSegments(geometry, material); + scene.add(seg); + }); + + var stats = new Stats(); + stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom + document.body.appendChild(stats.dom); + + const controls = new FlyControls(camera, renderer.domElement); + + controls.movementSpeed = 1000; + controls.domElement = renderer.domElement; + controls.rollSpeed = Math.PI / 24; + controls.autoForward = false; + controls.dragToLook = false; + + + const render = function () { + var delta = clock.getDelta(); + + stats.begin(); + renderer.render(scene, camera); + stats.end(); + + controls.update(delta); + + requestAnimationFrame(render); + }; + + render(); + + +}); + diff --git a/src/flyControls.ts b/src/flyControls.ts new file mode 100644 index 0000000..5caef8e --- /dev/null +++ b/src/flyControls.ts @@ -0,0 +1,314 @@ +/** + * @author James Baicoianu / http://www.baicoianu.com/ + */ + +import * as THREE from "three"; + +const Vector3 = THREE.Vector3; +const Quaternion = THREE.Quaternion; + +export class FlyControls { + object: any; + domElement: any; + movementSpeed: number; + rollSpeed: number; + dragToLook: boolean; + autoForward: boolean; + tmpQuaternion: any; + + mouseStatus: number; + + moveState: any; + moveVector: any; + rotationVector: any; + movementSpeedMultiplier: number; + + _mousemove: any; + _mousedown: any; + _mouseup: any; + _keydown: any; + _keyup: any; + + lastX: number; + lastY: number; + + constructor(object: any, domElement: any) { + this.object = object; + this.domElement = domElement; + if (domElement) this.domElement.setAttribute('tabindex', - 1); + + // API + + this.movementSpeed = 1.0; + this.rollSpeed = 0.005; + + this.dragToLook = false; + this.autoForward = false; + + // disable default target object behavior + + // internals + + this.tmpQuaternion = new Quaternion(); + + this.mouseStatus = 0; + + this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 }; + this.moveVector = new Vector3(0, 0, 0); + this.rotationVector = new Vector3(0, 0, 0); + this.movementSpeedMultiplier = 1; + + this._mousemove = this.bind(this, this.mousemove); + this._mousedown = this.bind(this, this.mousedown); + this._mouseup = this.bind(this, this.mouseup); + this._keydown = this.bind(this, this.keydown); + this._keyup = this.bind(this, this.keyup); + + this.domElement.addEventListener('contextmenu', this.contextmenu, false); + this.domElement.addEventListener('mousemove', this._mousemove, false); + this.domElement.addEventListener('mousedown', this._mousedown, false); + this.domElement.addEventListener('mouseup', this._mouseup, false); + + window.addEventListener('keydown', this._keydown, false); + window.addEventListener('keyup', this._keyup, false); + + this.updateMovementVector(); + this.updateRotationVector(); + } + + keydown(event: any) { + + if (event.altKey) { + + return; + + } + + //event.preventDefault(); + + switch (event.keyCode) { + + case 16: /* shift */ this.movementSpeedMultiplier = .1; break; + + case 87: /*W*/ this.moveState.forward = 1; break; + case 83: /*S*/ this.moveState.back = 1; break; + + case 65: /*A*/ this.moveState.left = 1; break; + case 68: /*D*/ this.moveState.right = 1; break; + + case 82: /*R*/ this.moveState.up = 1; break; + case 70: /*F*/ this.moveState.down = 1; break; + + case 38: /*up*/ this.moveState.pitchUp = 1; break; + case 40: /*down*/ this.moveState.pitchDown = 1; break; + + case 37: /*left*/ this.moveState.yawLeft = 1; break; + case 39: /*right*/ this.moveState.yawRight = 1; break; + + case 81: /*Q*/ this.moveState.rollLeft = 1; break; + case 69: /*E*/ this.moveState.rollRight = 1; break; + + } + + this.updateMovementVector(); + this.updateRotationVector(); + + }; + + keyup(event: any) { + + switch (event.keyCode) { + + case 16: /* shift */ this.movementSpeedMultiplier = 1; break; + + case 87: /*W*/ this.moveState.forward = 0; break; + case 83: /*S*/ this.moveState.back = 0; break; + + case 65: /*A*/ this.moveState.left = 0; break; + case 68: /*D*/ this.moveState.right = 0; break; + + case 82: /*R*/ this.moveState.up = 0; break; + case 70: /*F*/ this.moveState.down = 0; break; + + case 38: /*up*/ this.moveState.pitchUp = 0; break; + case 40: /*down*/ this.moveState.pitchDown = 0; break; + + case 37: /*left*/ this.moveState.yawLeft = 0; break; + case 39: /*right*/ this.moveState.yawRight = 0; break; + + case 81: /*Q*/ this.moveState.rollLeft = 0; break; + case 69: /*E*/ this.moveState.rollRight = 0; break; + + } + + this.updateMovementVector(); + this.updateRotationVector(); + + }; + + mousedown(event: any) { + + if (this.domElement !== document) { + + this.domElement.focus(); + + } + + event.preventDefault(); + event.stopPropagation(); + + if (this.dragToLook) { + + this.mouseStatus++; + + } else { + + switch (event.button) { + + case 0: this.moveState.forward = 1; break; + case 2: this.moveState.back = 1; break; + + } + + this.updateMovementVector(); + + } + + }; + + mousemove(event: any) { + + if (!this.dragToLook || this.mouseStatus > 0) { + + var container = this.getContainerDimensions(); + var halfWidth = container.size[0] / 2; + var halfHeight = container.size[1] / 2; + + console.log(event); + + this.moveState.yawLeft = - ((event.pageX - container.offset[0]) - halfWidth) / halfWidth; + this.moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight; + + this.updateRotationVector(); + + } + + }; + + mouseup = function (event: any) { + + event.preventDefault(); + event.stopPropagation(); + + if (this.dragToLook) { + + this.mouseStatus--; + + this.moveState.yawLeft = this.moveState.pitchDown = 0; + + } else { + + switch (event.button) { + + case 0: this.moveState.forward = 0; break; + case 2: this.moveState.back = 0; break; + + } + + this.updateMovementVector(); + + } + + this.updateRotationVector(); + + }; + + update(delta: any) { + + var moveMult = delta * this.movementSpeed; + var rotMult = delta * this.rollSpeed; + + this.object.translateX(this.moveVector.x * moveMult); + this.object.translateY(this.moveVector.y * moveMult); + this.object.translateZ(this.moveVector.z * moveMult); + + this.tmpQuaternion.set(this.rotationVector.x * rotMult, this.rotationVector.y * rotMult, this.rotationVector.z * rotMult, 1).normalize(); + this.object.quaternion.multiply(this.tmpQuaternion); + + // expose the rotation vector for convenience + this.object.rotation.setFromQuaternion(this.object.quaternion, this.object.rotation.order); + + + }; + + updateMovementVector() { + + var forward = (this.moveState.forward || (this.autoForward && !this.moveState.back)) ? 1 : 0; + + this.moveVector.x = (- this.moveState.left + this.moveState.right); + this.moveVector.y = (- this.moveState.down + this.moveState.up); + this.moveVector.z = (- forward + this.moveState.back); + + //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] ); + + }; + + updateRotationVector() { + + this.rotationVector.x = (- this.moveState.pitchDown + this.moveState.pitchUp); + this.rotationVector.y = (- this.moveState.yawRight + this.moveState.yawLeft); + this.rotationVector.z = (- this.moveState.rollRight + this.moveState.rollLeft); + + //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] ); + + }; + + getContainerDimensions() { + + if (this.domElement != document) { + + return { + size: [this.domElement.offsetWidth, this.domElement.offsetHeight], + offset: [this.domElement.offsetLeft, this.domElement.offsetTop] + }; + + } else { + + return { + size: [window.innerWidth, window.innerHeight], + offset: [0, 0] + }; + + } + + }; + + bind(scope: any, fn: any) { + + return function () { + + fn.apply(scope, arguments); + + }; + + } + + contextmenu(event: any) { + + event.preventDefault(); + + } + + dispose() { + + this.domElement.removeEventListener('contextmenu', this.contextmenu, false); + this.domElement.removeEventListener('mousedown', this._mousedown, false); + this.domElement.removeEventListener('mousemove', this._mousemove, false); + this.domElement.removeEventListener('mouseup', this._mouseup, false); + + window.removeEventListener('keydown', this._keydown, false); + window.removeEventListener('keyup', this._keyup, false); + + }; +} + diff --git a/src/m4.ts b/src/m4.ts new file mode 100644 index 0000000..f0e75ae --- /dev/null +++ b/src/m4.ts @@ -0,0 +1,248 @@ +export const m4 = { + + perspective: function (fieldOfViewInRadians: number, aspect: number, near: number, far: number) { + var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians); + var rangeInv = 1.0 / (near - far); + + return [ + f / aspect, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (near + far) * rangeInv, -1, + 0, 0, near * far * rangeInv * 2, 0 + ]; + }, + + projection: function (width: number, height: number, depth: number) { + // Note: This matrix flips the Y axis so 0 is at the top. + return [ + 2 / width, 0, 0, 0, + 0, -2 / height, 0, 0, + 0, 0, 2 / depth, 0, + -1, 1, 0, 1, + ]; + }, + + multiply: function (a: any, b: any) { + var a00 = a[0 * 4 + 0]; + var a01 = a[0 * 4 + 1]; + var a02 = a[0 * 4 + 2]; + var a03 = a[0 * 4 + 3]; + var a10 = a[1 * 4 + 0]; + var a11 = a[1 * 4 + 1]; + var a12 = a[1 * 4 + 2]; + var a13 = a[1 * 4 + 3]; + var a20 = a[2 * 4 + 0]; + var a21 = a[2 * 4 + 1]; + var a22 = a[2 * 4 + 2]; + var a23 = a[2 * 4 + 3]; + var a30 = a[3 * 4 + 0]; + var a31 = a[3 * 4 + 1]; + var a32 = a[3 * 4 + 2]; + var a33 = a[3 * 4 + 3]; + var b00 = b[0 * 4 + 0]; + var b01 = b[0 * 4 + 1]; + var b02 = b[0 * 4 + 2]; + var b03 = b[0 * 4 + 3]; + var b10 = b[1 * 4 + 0]; + var b11 = b[1 * 4 + 1]; + var b12 = b[1 * 4 + 2]; + var b13 = b[1 * 4 + 3]; + var b20 = b[2 * 4 + 0]; + var b21 = b[2 * 4 + 1]; + var b22 = b[2 * 4 + 2]; + var b23 = b[2 * 4 + 3]; + var b30 = b[3 * 4 + 0]; + var b31 = b[3 * 4 + 1]; + var b32 = b[3 * 4 + 2]; + var b33 = b[3 * 4 + 3]; + return [ + b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, + b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, + b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, + b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, + b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, + b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, + b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, + b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, + b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, + b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, + b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, + b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, + b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, + b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, + b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, + b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33, + ]; + }, + + translation: function (tx: number, ty: number, tz: number) { + return [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + tx, ty, tz, 1, + ]; + }, + + xRotation: function (angleInRadians: number) { + var c = Math.cos(angleInRadians); + var s = Math.sin(angleInRadians); + + return [ + 1, 0, 0, 0, + 0, c, s, 0, + 0, -s, c, 0, + 0, 0, 0, 1, + ]; + }, + + yRotation: function (angleInRadians: number) { + var c = Math.cos(angleInRadians); + var s = Math.sin(angleInRadians); + + return [ + c, 0, -s, 0, + 0, 1, 0, 0, + s, 0, c, 0, + 0, 0, 0, 1, + ]; + }, + + zRotation: function (angleInRadians: number) { + var c = Math.cos(angleInRadians); + var s = Math.sin(angleInRadians); + + return [ + c, s, 0, 0, + -s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + ]; + }, + + scaling: function (sx: number, sy: number, sz: number) { + return [ + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 0, + 0, 0, 0, 1, + ]; + }, + + translate: function (m: any, tx: number, ty: number, tz: number) { + return m4.multiply(m, m4.translation(tx, ty, tz)); + }, + + xRotate: function (m: any, angleInRadians: number) { + return m4.multiply(m, m4.xRotation(angleInRadians)); + }, + + yRotate: function (m: any, angleInRadians: number) { + return m4.multiply(m, m4.yRotation(angleInRadians)); + }, + + zRotate: function (m: any, angleInRadians: number) { + return m4.multiply(m, m4.zRotation(angleInRadians)); + }, + + scale: function (m: any, sx: number, sy: number, sz: number) { + return m4.multiply(m, m4.scaling(sx, sy, sz)); + }, + + inverse: function (m: any) { + var m00 = m[0 * 4 + 0]; + var m01 = m[0 * 4 + 1]; + var m02 = m[0 * 4 + 2]; + var m03 = m[0 * 4 + 3]; + var m10 = m[1 * 4 + 0]; + var m11 = m[1 * 4 + 1]; + var m12 = m[1 * 4 + 2]; + var m13 = m[1 * 4 + 3]; + var m20 = m[2 * 4 + 0]; + var m21 = m[2 * 4 + 1]; + var m22 = m[2 * 4 + 2]; + var m23 = m[2 * 4 + 3]; + var m30 = m[3 * 4 + 0]; + var m31 = m[3 * 4 + 1]; + var m32 = m[3 * 4 + 2]; + var m33 = m[3 * 4 + 3]; + var tmp_0 = m22 * m33; + var tmp_1 = m32 * m23; + var tmp_2 = m12 * m33; + var tmp_3 = m32 * m13; + var tmp_4 = m12 * m23; + var tmp_5 = m22 * m13; + var tmp_6 = m02 * m33; + var tmp_7 = m32 * m03; + var tmp_8 = m02 * m23; + var tmp_9 = m22 * m03; + var tmp_10 = m02 * m13; + var tmp_11 = m12 * m03; + var tmp_12 = m20 * m31; + var tmp_13 = m30 * m21; + var tmp_14 = m10 * m31; + var tmp_15 = m30 * m11; + var tmp_16 = m10 * m21; + var tmp_17 = m20 * m11; + var tmp_18 = m00 * m31; + var tmp_19 = m30 * m01; + var tmp_20 = m00 * m21; + var tmp_21 = m20 * m01; + var tmp_22 = m00 * m11; + var tmp_23 = m10 * m01; + + var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - + (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); + var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - + (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); + var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - + (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); + var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - + (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); + + var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); + + return [ + d * t0, + d * t1, + d * t2, + d * t3, + d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - + (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)), + d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - + (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)), + d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - + (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)), + d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - + (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)), + d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - + (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)), + d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - + (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)), + d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - + (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)), + d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - + (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)), + d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - + (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)), + d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - + (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)), + d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - + (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)), + d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - + (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)) + ]; + }, + + vectorMultiply: function (v: any, m: any) { + var dst = []; + for (var i = 0; i < 4; ++i) { + dst[i] = 0.0; + for (var j = 0; j < 4; ++j) { + dst[i] += v[j] * m[j * 4 + i]; + } + } + return dst; + }, + +}; \ No newline at end of file diff --git a/src/webgl.ts b/src/webgl.ts new file mode 100644 index 0000000..6fc2fd5 --- /dev/null +++ b/src/webgl.ts @@ -0,0 +1,134 @@ +import { m4 } from "./m4"; + + +export function main(vertices: number[]) { + + + const canvas = document.getElementById('canvas') as HTMLCanvasElement; + const gl = canvas.getContext('webgl'); + + + // Create an empty buffer object to store the vertex buffer + var vertex_buffer = gl.createBuffer(); + + //Bind appropriate array buffer to it + gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); + + // Pass the vertex data to the buffer + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + + // Unbind the buffer + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + /*=========================Shaders========================*/ + + // vertex shader source code + var vertCode = + 'attribute vec3 coordinates;' + + + 'void main(void) {' + + ' gl_Position = vec4(coordinates, 1.0);' + + 'gl_PointSize = 10.0;' + + '}'; + + // Create a vertex shader object + var vertShader = gl.createShader(gl.VERTEX_SHADER); + + // Attach vertex shader source code + gl.shaderSource(vertShader, vertCode); + + // Compile the vertex shader + gl.compileShader(vertShader); + + // fragment shader source code + var fragCode = + 'void main(void) {' + + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + + '}'; + + // Create fragment shader object + var fragShader = gl.createShader(gl.FRAGMENT_SHADER); + + // Attach fragment shader source code + gl.shaderSource(fragShader, fragCode); + + // Compile the fragmentt shader + gl.compileShader(fragShader); + + // Create a shader program object to store + // the combined shader program + var shaderProgram = gl.createProgram(); + + // Attach a vertex shader + gl.attachShader(shaderProgram, vertShader); + + // Attach a fragment shader + gl.attachShader(shaderProgram, fragShader); + + // Link both programs + gl.linkProgram(shaderProgram); + + // Use the combined shader program object + gl.useProgram(shaderProgram); + + /*======== Associating shaders to buffer objects ========*/ + + // Bind vertex buffer object + gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); + + // Get the attribute location + var coord = gl.getAttribLocation(shaderProgram, "coordinates"); + + var matrixLocation = gl.getUniformLocation(shaderProgram, "u_matrix"); + + // Point an attribute to the currently bound VBO + gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); + + // Enable the attribute + gl.enableVertexAttribArray(coord); + + /*============= Drawing the primitive ===============*/ + + // Clear the canvas + gl.clearColor(0.0, 0.0, 0.0, 1.0); + + // Enable the depth test + gl.enable(gl.DEPTH_TEST); + + // Clear the color buffer bit + gl.clear(gl.COLOR_BUFFER_BIT); + + // Set the view port + gl.viewport(0, 0, canvas.width, canvas.height); + + var aspect = gl.canvas.width / gl.canvas.height; + var zNear = 1; + var zFar = 2000; + var projectionMatrix = m4.perspective(1.5708, aspect, zNear, zFar); + var radius = 500; + + // Compute a matrix for the camera + var cameraMatrix = m4.yRotation(1); + cameraMatrix = m4.translate(cameraMatrix, 0, 0, radius * 1.5); + // Make a view matrix from the camera matrix. + var viewMatrix = m4.inverse(cameraMatrix); + // Compute a view projection matrix + var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix); + + // starting with the view projection matrix + // compute a matrix for the F + var matrix = m4.translate(viewProjectionMatrix, 0, 0, 0); + + + // Set the matrix. + gl.uniformMatrix4fv(matrixLocation, false, matrix); + + console.log(vertices); + + // Draw the triangle + gl.drawArrays(gl.POINTS, 0, vertices.length / 3); + + + + +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ca11873 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + // "module": "system", + "module":"ES6", + "target": "ES6", + "moduleResolution": "node", + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "outDir": "dist/", + "sourceMap": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "**/*.spec.ts" + ] +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..b39a73e --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,25 @@ +const path = require('path'); + +module.exports = { + entry: './src/entry.ts', + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, + + devServer: { + contentBase: './public', + }, + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, 'public'), + }, +}; \ No newline at end of file