summaryrefslogtreecommitdiff
path: root/public/gl/core
diff options
context:
space:
mode:
Diffstat (limited to 'public/gl/core')
-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
7 files changed, 352 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