summaryrefslogtreecommitdiff
path: root/public/gl
diff options
context:
space:
mode:
Diffstat (limited to 'public/gl')
-rw-r--r--public/gl/core/Camera.js18
-rw-r--r--public/gl/core/Entity.js25
-rw-r--r--public/gl/core/Material.js46
-rw-r--r--public/gl/core/Mesh.js48
-rw-r--r--public/gl/core/Renderer.js79
-rw-r--r--public/gl/core/Scene.js38
-rw-r--r--public/gl/core/Shader.js98
-rw-r--r--public/gl/gl.js20
-rw-r--r--public/gl/io/file.js11
-rw-r--r--public/gl/math/Mat4.js157
-rw-r--r--public/gl/math/Vec2.js78
-rw-r--r--public/gl/math/Vec3.js88
12 files changed, 706 insertions, 0 deletions
diff --git a/public/gl/core/Camera.js b/public/gl/core/Camera.js
new file mode 100644
index 0000000..870c13f
--- /dev/null
+++ b/public/gl/core/Camera.js
@@ -0,0 +1,18 @@
+import { Vec3 } from '../math/Vec3.js'
+import { Mat4 } from '../math/Mat4.js'
+
+export class Camera {
+
+ constructor() {
+ this.position = new Vec3()
+ this.rotation = new Vec3()
+ }
+
+ view() {
+ return new Mat4()
+ .identity()
+ .rot(this.rotation.clone().multS(Math.PI/180))
+ .pos(this.position.clone().multS(-1))
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/core/Entity.js b/public/gl/core/Entity.js
new file mode 100644
index 0000000..d0d6e4a
--- /dev/null
+++ b/public/gl/core/Entity.js
@@ -0,0 +1,25 @@
+import { Vec3 } from '../math/Vec3.js'
+import { Mat4 } from '../math/Mat4.js'
+
+export class Entity {
+
+ static #id = 0
+
+ constructor(mesh) {
+ this.mesh = mesh
+ this.position = new Vec3()
+ this.rotation = new Vec3()
+ this.scale = new Vec3(1, 1, 1)
+ this.id = Entity.#id
+ Entity.#id++
+ }
+
+ tran() {
+ return new Mat4()
+ .identity()
+ .pos(this.position)
+ .rot(this.rotation.clone().multS(Math.PI/180))
+ .scale(this.scale)
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/core/Material.js b/public/gl/core/Material.js
new file mode 100644
index 0000000..c99b9e8
--- /dev/null
+++ b/public/gl/core/Material.js
@@ -0,0 +1,46 @@
+export class Material {
+
+ static #id = 0
+
+ constructor(shader, data = {}) {
+ this.shader = shader
+ this.data = data
+ this.id = Material.#id
+ Material.#id++
+ }
+
+ set(data) {
+ this.data = data
+ return this
+ }
+
+ bind() {
+ this.shader.start()
+ for (const key in this.data) {
+ const value = this.data[key]
+ if (typeof value == 'number' && !isNaN(x)) {
+ if (Number.isInteger(value)) {
+ this.shader.loadInt(key, value)
+ } else {
+ this.shader.loadFloat(key, value)
+ }
+ } else if (typeof value == 'boolean') {
+ this.shader.loadBool(key, value)
+ } else if (typeof value == 'object') {
+ const name = value.constructor.name
+ if (name == 'Vec2') {
+ this.shader.loadVec2(key, value)
+ } else if (name == 'Vec3') {
+ this.shader.loadVec3(key,value)
+ } else if (name == 'Mat4') {
+ this.shader.loadMat4(key, value)
+ }
+ }
+ }
+ }
+
+ unbind() {
+ this.shader.stop()
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/core/Mesh.js b/public/gl/core/Mesh.js
new file mode 100644
index 0000000..b35c13c
--- /dev/null
+++ b/public/gl/core/Mesh.js
@@ -0,0 +1,48 @@
+import { gl, ext } from './Renderer.js'
+
+export class Mesh {
+
+ static #vaos = []
+ static #vbos = []
+
+ #vertexCount
+ #count
+ #id
+
+ constructor(vertexCount) {
+ this.#id = ext.createVertexArrayOES()
+ ext.bindVertexArrayOES(this.#id)
+ Mesh.#vaos.push(this.#id)
+ this.#vertexCount = vertexCount
+ this.#count = 0
+ }
+
+ store(data, dim) {
+ let id = gl.createBuffer()
+ Mesh.#vbos.push(id)
+ gl.bindBuffer(gl.ARRAY_BUFFER, id)
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
+ gl.vertexAttribPointer(this.#count, dim, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * dim, 0)
+ gl.enableVertexAttribArray(this.#count)
+ this.#count++;
+ return this
+ }
+
+ finish() {
+ this.unbind()
+ return this
+ }
+
+ bind() {
+ ext.bindVertexArrayOES(this.#id)
+ }
+
+ unbind() {
+ ext.bindVertexArrayOES(null)
+ }
+
+ draw() {
+ gl.drawArrays(gl.TRIANGLES, 0, this.#vertexCount)
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/core/Renderer.js b/public/gl/core/Renderer.js
new file mode 100644
index 0000000..32f5e9c
--- /dev/null
+++ b/public/gl/core/Renderer.js
@@ -0,0 +1,79 @@
+import { Mat4 } from '../math/Mat4.js'
+
+export var gl
+export var ext
+export var canvas
+
+export class Renderer {
+ constructor() {
+ canvas = document.createElement("canvas")
+ canvas.id = "canvas"
+ canvas.width = window.innerWidth
+ canvas.height = window.innerHeight
+ canvas.style = "display: block;"
+ document.body.appendChild(canvas)
+ document.body.style.margin = 0
+
+ gl = canvas.getContext('webgl')
+
+ if(!gl) {
+ console.log("Your browser does not support webgl")
+ return
+ }
+
+ ext = gl.getExtension('OES_vertex_array_object');
+ if(!ext) {
+ console.log("Your browser does not support webgl 2")
+ return
+ }
+
+ window.onresize = (event) => {
+ canvas.width = window.innerWidth
+ canvas.height = window.innerHeight
+ gl.viewport(0, 0, canvas.width, canvas.height)
+ }
+ }
+
+ draw(scene, camera) {
+ for (const material_id in scene.map) {
+ const material = scene.materials[material_id]
+ gl.enable(gl.DEPTH_TEST);
+ gl.enable(gl.CULL_FACE);
+ gl.cullFace(gl.BACK);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ gl.clearColor(0, 0, 0, 1);
+ material.bind()
+ material.shader.loadMat4("proj", this.proj())
+ material.shader.loadMat4("view", camera.view())
+ scene.map[material_id].forEach((entity_id) => {
+ const entity = scene.entities[entity_id]
+ material.shader.loadMat4("tran", entity.tran())
+ entity.mesh.bind()
+ entity.mesh.draw()
+ entity.mesh.unbind()
+ })
+ material.unbind()
+ }
+ }
+
+ proj() {
+ const fov = 90
+ const far = 100
+ const near = 0.1
+
+ const aspect = canvas.width / canvas.height
+ const f = 1.0 / Math.tan((fov * (Math.PI/180)) / 2)
+ const fa = f / aspect
+ const nf = 1.0 / (near - far)
+
+ const c1 = (near + far) * nf
+ const c2 = near * far * nf * 2
+
+ return new Mat4().set(
+ fa, 0, 0, 0,
+ 0, f, 0, 0,
+ 0, 0, c1, c2,
+ 0, 0, -1, 0
+ )
+ }
+} \ No newline at end of file
diff --git a/public/gl/core/Scene.js b/public/gl/core/Scene.js
new file mode 100644
index 0000000..568fe37
--- /dev/null
+++ b/public/gl/core/Scene.js
@@ -0,0 +1,38 @@
+export class Scene {
+
+ constructor() {
+ this.map = {}
+ this.entities = {}
+ this.materials = {}
+ }
+
+ add(material, entity) {
+ var arr = this.map[material.id]
+ if (arr == undefined) {
+ arr = []
+ this.materials[material.id] = material
+ }
+ if (!arr.includes(entity.id)) {
+ arr.push(entity.id)
+ this.entities[entity.id] = entity
+ }
+ this.map[material.id] = arr
+ }
+
+ remove(material, entity) {
+ var arr = this.objects[material.id]
+ if (arr == undefined) {
+ return
+ }
+ const i = arr.indexOf(entity.id)
+ if(i > -1) {
+ arr = arr.splice(i, 1)
+ delete this.entities[entity.id]
+ if(arr.length < 1) {
+ delete this.map[material.id]
+ delete this.materials[material.id]
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/core/Shader.js b/public/gl/core/Shader.js
new file mode 100644
index 0000000..f5e0a88
--- /dev/null
+++ b/public/gl/core/Shader.js
@@ -0,0 +1,98 @@
+import { gl } from './Renderer.js'
+
+export class Shader {
+
+ #vertexShader;
+ #fragmentShader;
+ #program;
+ #attributes;
+ #uniforms;
+
+ constructor(vertexCode, fragmentCode) {
+
+ this.#vertexShader = gl.createShader(gl.VERTEX_SHADER)
+ this.#fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
+
+ gl.shaderSource(this.#vertexShader, vertexCode)
+ gl.shaderSource(this.#fragmentShader, fragmentCode)
+
+ gl.compileShader(this.#vertexShader)
+ if (!gl.getShaderParameter(this.#vertexShader, gl.COMPILE_STATUS)) {
+ console.error('Failed to compile vertex shader!', gl.getShaderInfoLog(this.#vertexShader))
+ return
+ }
+
+ gl.compileShader(this.#fragmentShader)
+ if (!gl.getShaderParameter(this.#fragmentShader, gl.COMPILE_STATUS)) {
+ console.error('Failed to compile fragment shader!', gl.getShaderInfoLog(this.#fragmentShader))
+ return
+ }
+
+ this.#program = gl.createProgram()
+ gl.attachShader(this.#program, this.#vertexShader)
+ gl.attachShader(this.#program, this.#fragmentShader)
+ gl.linkProgram(this.#program)
+ if (!gl.getProgramParameter(this.#program, gl.LINK_STATUS)) {
+ console.error("Failed to link shader program!", gl.getProgramInfoLog(this.#program));
+ return
+ }
+
+ gl.validateProgram(this.#program);
+ if (!gl.getProgramParameter(this.#program, gl.VALIDATE_STATUS)) {
+ console.error("Failed to validate shader program!", gl.getProgramInfoLog(this.#program));
+ return
+ }
+
+ this.#attributes = []
+ this.#uniforms = {}
+
+ vertexCode.split('\n').forEach((line) => {
+ const tokens = line.split(" ")
+ if (tokens.length != 3) return
+ if (tokens[0] === "attribute") {
+ this.#attributes.push(tokens[2].replace(/\s+/g, '').split(';')[0])
+ }
+ if (tokens[0] == "uniform") {
+ const loc = gl.getUniformLocation(this.#program, tokens[2].replace(/\s+/g, '').split(';')[0])
+ this.#uniforms[tokens[2].replace(/\s+/g, '').split(';')[0]] = loc
+ }
+ })
+
+ }
+
+ start() {
+ gl.useProgram(this.#program)
+ for (let i = 0; i < this.#attributes.length; i++) {
+ gl.bindAttribLocation(this.#program, i, this.#attributes[i])
+ }
+ }
+
+ stop() {
+ gl.useProgram(null)
+ }
+
+ loadFloat(name, float) {
+ gl.uniform1f(this.#uniforms[name], float)
+ }
+
+ loadInt(name, int) {
+ gl.uniform1i(this.#uniforms[name], int)
+ }
+
+ loadVec2(name, v) {
+ gl.uniform2f(this.#uniforms[name], v.x, v.y)
+ }
+
+ loadVec3(name, v) {
+ gl.uniform3f(this.#uniforms[name], v.x, v.y, v.z)
+ }
+
+ loadMat4(name, m) {
+ gl.uniformMatrix4fv(this.#uniforms[name], gl.FALSE, new Float32Array(m.get()))
+ }
+
+ loadBool(name, bool) {
+ gl.uniform1f(this.#uniforms[name], bool ? 1 : 0)
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/gl.js b/public/gl/gl.js
new file mode 100644
index 0000000..20b6225
--- /dev/null
+++ b/public/gl/gl.js
@@ -0,0 +1,20 @@
+export { Camera } from './core/Camera.js'
+export { Entity } from './core/Entity.js'
+export { Material } from './core/Material.js'
+export { Mesh } from './core/Mesh.js'
+export { Renderer } from './core/Renderer.js'
+export { Scene } from './core/Scene.js'
+export { Shader } from './core/Shader.js'
+export { readFileAsync } from './io/file.js'
+export { Mat4 } from './math/Mat4.js'
+export { Vec2 } from './math/Vec2.js'
+export { Vec3 } from './math/Vec3.js'
+export { Loop }
+
+const Loop = (fn) => {
+ const callback = () => {
+ fn()
+ window.requestAnimationFrame(callback)
+ }
+ callback()
+} \ No newline at end of file
diff --git a/public/gl/io/file.js b/public/gl/io/file.js
new file mode 100644
index 0000000..f89565c
--- /dev/null
+++ b/public/gl/io/file.js
@@ -0,0 +1,11 @@
+export { readFileAsync }
+
+const readFileAsync = async (path) => {
+ try {
+ let data = await fetch(path)
+ let text = await data.text()
+ return text
+ } catch (err) {
+ return undefined
+ }
+} \ No newline at end of file
diff --git a/public/gl/math/Mat4.js b/public/gl/math/Mat4.js
new file mode 100644
index 0000000..a342c0a
--- /dev/null
+++ b/public/gl/math/Mat4.js
@@ -0,0 +1,157 @@
+export class Mat4 {
+
+ constructor() {
+ this.data = [
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ ]
+ }
+
+ set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
+ const d = this.data
+ d[0] = m00; d[1] = m01; d[2] = m02; d[3] = m03;
+ d[4] = m10; d[5] = m11; d[6] = m12; d[7] = m13;
+ d[8] = m20; d[9] = m21; d[10] = m22; d[11] = m23;
+ d[12] = m30; d[13] = m31; d[14] = m32; d[15] = m33;
+ return this
+ }
+
+ clone() {
+ return new Mat4().arr(this.da)
+ }
+
+ arr(arr, offset = 0) {
+ for(let i = 0; i < 16; i++) {
+ this.data[i] = arr[i + offset]
+ }
+ return this
+ }
+
+ identity() {
+ this.set(
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ )
+ return this
+ }
+
+ copy(m) {
+ const d = this.data
+ const o = m.data
+ d[0] = o[0]; d[1] = o[1]; d[2] = o[2]; d[3] = o[3];
+ d[4] = o[4]; d[5] = o[5]; d[6] = o[6]; d[7] = o[7];
+ d[8] = o[8]; d[9] = o[9]; d[10] = o[10]; d[11] = o[11];
+ d[12] = o[12]; d[13] = o[13]; d[14] = o[14]; d[15] = o[15];
+ return this
+ }
+
+ mult(v) {
+ return this.multmat(this.clone(), v)
+ }
+
+ premult(v) {
+ return this.multmat(v, this.clone())
+ }
+
+ multmat(ma, mb) {
+ const a = ma.data;
+ const b = mb.data
+ const d = this.data;
+
+ const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+ const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+ const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+ const a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+ const b00 = b[0], b01 = b[1], b02 = b[2], b03 = a[3];
+ const b10 = b[4], b11 = b[5], b12 = b[6], b13 = a[7];
+ const b20 = b[8], b21 = b[9], b22 = b[10], b23 = a[11];
+ const b30 = b[12], b31 = b[13], b32 = b[14], b33 = a[15];
+
+ d[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
+ d[1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
+ d[2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
+ d[3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
+
+ d[4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
+ d[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
+ d[6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
+ d[7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
+
+ d[8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
+ d[9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
+ d[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
+ d[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
+
+ d[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
+ d[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
+ d[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
+ d[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
+
+ return this;
+ }
+
+ pos(v) {
+ const d = this.data
+
+ const m30 = d[0] * v.x + d[4] * v.y + d[8] * v.z;
+ const m31 = d[1] * v.x + d[5] * v.y + d[9] * v.z;
+ const m32 = d[2] * v.x + d[6] * v.y + d[10] * v.z;
+ const m33 = d[3] * v.x + d[7] * v.y + d[11] * v.z;
+
+ d[3] += m30;
+ d[7] += m31;
+ d[11] += m32;
+ d[15] += m33;
+ return this;
+ }
+
+ rot(v) {
+ const da = this.data;
+ const c3 = Math.cos(v.z)
+ const s3 = Math.sin(v.z)
+ const c2 = Math.cos(v.x)
+ const s2 = Math.sin(v.x)
+ const c1 = Math.cos(v.y)
+ const s1 = Math.sin(v.y)
+
+ da[0] = c1 * c3 + s1 * s2 * s3
+ da[4] = c2 * s3
+ da[8] = c1 * s2 * s3 - c3 * s1
+
+ da[1] = c3 * s1 * s2 - c1 * s3
+ da[5] = c2 * c3
+ da[9] = c1 * c3 * s2 + s1 * s3
+
+ da[2] = c2 * s1
+ da[6] = -s2
+ da[10] = c1 * c2
+
+ return this
+ }
+
+ scale( v ) {
+ const d = this.data;
+
+ d[0] *= v.x; d[1] *= v.y; d[2] *= v.z;
+ d[4] *= v.x; d[5] *= v.y; d[6] *= v.z;
+ d[8] *= v.x; d[9] *= v.y; d[10] *= v.z;
+ d[12] *= v.x; d[13] *= v.y; d[14] *= v.z;
+
+ return this;
+ }
+
+ get() {
+ const d = this.data
+ return [
+ d[0], d[4], d[8], d[12],
+ d[1], d[5], d[9], d[13],
+ d[2], d[6], d[10], d[14],
+ d[3], d[7], d[11], d[15]
+ ]
+ }
+} \ No newline at end of file
diff --git a/public/gl/math/Vec2.js b/public/gl/math/Vec2.js
new file mode 100644
index 0000000..6156e40
--- /dev/null
+++ b/public/gl/math/Vec2.js
@@ -0,0 +1,78 @@
+export class Vec2 {
+
+ constructor(x = 0, y = 0) {
+ this.x = x
+ this.y = y
+ }
+
+ set(x,y) {
+ this.x = x
+ this.y = y
+ return this
+ }
+
+ clone() {
+ return new Vec2(this.x, this.y)
+ }
+
+ add(v) {
+ this.x += v.x
+ this.y += v.y
+ return this
+ }
+
+ sub(v) {
+ this.x -= v.x
+ this.y -= v.y
+ return this
+ }
+
+ copy(v) {
+ this.x = v.x
+ this.y = v.y
+ return this
+ }
+
+ multV(v) {
+ this.x *= v.x
+ this.y *= v.y
+ return this
+ }
+
+ multS(s) {
+ this.x *= s
+ this.y *= s
+ return this
+ }
+
+ divV(v) {
+ this.x *= (1 / v.x)
+ this.y *= (1 / v.y)
+ return this
+ }
+
+ divS(s) {
+ this.x *= (1 / s)
+ this.y *= (1 / s)
+ return this
+ }
+
+ invert() {
+ this.x = this.x == 0 ? 0 : (1 / this.x)
+ this.y = this.y == 0 ? 0 : (1 / this.y)
+ return this
+ }
+
+ normalize() {
+ return this.divS(this.length() || 1)
+ }
+
+ dot(v) {
+ return this.x * v.x + this.y * v.y;
+ }
+
+ length() {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ }
+
+} \ No newline at end of file
diff --git a/public/gl/math/Vec3.js b/public/gl/math/Vec3.js
new file mode 100644
index 0000000..d77d49d
--- /dev/null
+++ b/public/gl/math/Vec3.js
@@ -0,0 +1,88 @@
+export class Vec3 {
+
+ constructor(x = 0, y = 0, z = 0) {
+ this.x = x
+ this.y = y
+ this.z = z
+ }
+
+ set(x,y,z) {
+ this.x = x
+ this.y = y
+ this.z = z
+ return this
+ }
+
+ clone() {
+ return new Vec3(this.x, this.y, this.z)
+ }
+
+ add(v) {
+ this.x += v.x
+ this.y += v.y
+ this.x += v.x
+ return this
+ }
+
+ sub(v) {
+ this.x -= v.x
+ this.y -= v.y
+ this.x -= v.z
+ return this
+ }
+
+ copy(v) {
+ this.x = v.x
+ this.y = v.y
+ this.z = v.z
+ return this
+ }
+
+ multV(v) {
+ this.x *= v.x
+ this.y *= v.y
+ this.z *= v.z
+ return this
+ }
+
+ multS(s) {
+ this.x *= s
+ this.y *= s
+ this.z *= s
+ return this
+ }
+
+ divV(v) {
+ this.x *= (1 / v.x)
+ this.y *= (1 / v.y)
+ this.z *= (1 / v.z)
+ return this
+ }
+
+ divS(s) {
+ this.x *= (1 / s)
+ this.y *= (1 / s)
+ this.z *= (1 / s)
+ return this
+ }
+
+ invert() {
+ this.x = this.x == 0 ? 0 : (1 / this.x)
+ this.y = this.y == 0 ? 0 : (1 / this.y)
+ this.z = this.z == 0 ? 0 : (1 / this.z)
+ return this
+ }
+
+ normalize() {
+ return this.divS(this.length() || 1)
+ }
+
+ dot(v) {
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+ }
+
+ length() {
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
+ }
+
+} \ No newline at end of file