├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── README.md ├── app ├── index.js └── vanilla │ ├── GL.js │ ├── Loader.js │ ├── Material.js │ ├── Mesh.js │ ├── Object3D.js │ ├── OrbitControl.js │ ├── Scene.js │ ├── Shader.js │ ├── State.js │ ├── Texture.js │ ├── cameras │ ├── Camera.js │ └── PerspectiveCamera.js │ ├── geometry │ ├── BoxGeometry.js │ ├── Geometries.js │ ├── Geometry.js │ ├── PlaneGeometry.js │ └── QuadGeometry.js │ ├── index.js │ └── shaders │ ├── basic.frag │ └── basic.vert ├── example.js ├── examples ├── assets │ ├── brick.jpg │ ├── grid.jpg │ └── normal.png ├── cube │ ├── bundle.js │ └── index.html ├── index.html └── src │ ├── cube.js │ └── index.js ├── package.json └── public ├── assets ├── brick.jpg ├── grid.jpg └── normal.png ├── build └── main.js └── index.html /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015" ], 3 | "plugins": [ 4 | "transform-es2015-modules-commonjs", 5 | ] 6 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb/base", 3 | "env": { 4 | "browser": true, 5 | }, 6 | 7 | "globals": { 8 | "_gaq": true, 9 | "_pa": true, 10 | "$": true, 11 | "app": true, 12 | "alert": true, 13 | "canvg": true, 14 | "console": true, 15 | "escape": true, 16 | "WAGNER": true, 17 | "browser": true, 18 | "module": true, 19 | "require": true, 20 | "System": true, 21 | "user": true, 22 | "TweenMax": true, 23 | "TimelineMax": true, 24 | "Power1": true, 25 | "Power2": true, 26 | "Power3": true, 27 | "Power4": true, 28 | "gl": true 29 | }, 30 | 31 | "rules": { 32 | "max-len":["error", {"code":150}], 33 | "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], 34 | "no-unused-vars": 0, 35 | "no-plusplus": 0, 36 | "no-new": 0, 37 | "no-bitwise": 0, 38 | "guard-for-in": 0, 39 | "no-restricted-syntax":0, 40 | "no-new-func": 0, 41 | "no-param-reassign": ["error", { "props": false }], 42 | "class-methods-use-this": 0, 43 | "no-underscore-dangle": 0, 44 | "no-console": 0, 45 | "padded-blocks": 0, 46 | "id-length": 0, 47 | "no-use-before-define": 0 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vanilla GL 2 | 3 | 4 | CURRENTLY SET ASIDE I NEED TO [ LEARN WORK LEARN WORK LEARN WORK ](https://youtu.be/Qp9psZmAM_Y?t=19s) 5 | 6 | 7 | Little 3D framework. 8 | 9 | The goal of this project is to learn webgl & glsl stuff by making my own framework (big inspiration from framework below) 10 | 11 | 12 | [examples](https://jojo.ninja/vanillagl/examples/) 13 | 14 | # Installation 15 | ```bash 16 | npm i 17 | ``` 18 | 19 | # Start developement 20 | 21 | It starts a developement server at localhost:9966 with live reload. 22 | 23 | ```bash 24 | npm start 25 | ``` 26 | 27 | # Build 28 | 29 | ```bash 30 | npm run build 31 | ``` 32 | 33 | # Examples 34 | 35 | All the examples are [THERE](https://jojo.ninja/vanillagl/examples/) 36 | 37 | 38 | # Examples (WIP) 39 | 40 | ## Run example in developement 41 | ```bash 42 | npm run example {exampleName} 43 | ``` 44 | 45 | ## Build example 46 | ```bash 47 | npm run example {exampleName} build 48 | ``` 49 | 50 | 51 | # Inspiration & Ressources 52 | 53 | * [Leonardo](https://github.com/amelierosser/leonardo/) by [@amelierosser](https://twitter.com/ixviii_io?lang=en) 54 | 55 | * [Alfrid](https://github.com/yiwenl/Alfrid) by [@yiwen_lin](https://twitter.com/yiwen_lin?lang=en) 56 | 57 | * [THREE.js](https://github.com/mrdoob/three.js/) by a lot of people ^^ 58 | 59 | * [webglfundamentals](https://webglfundamentals.org/) by [greggman](https://github.com/greggman) 60 | 61 | * [learningwebgl](http://learningwebgl.com/blog/?page_id=1217) 62 | 63 | * [budo](https://github.com/mattdesl/budo) by [@mattdesl](https://twitter.com/mattdesl?lang=en) 64 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import raf from 'raf'; 2 | import matrix from 'gl-matrix'; 3 | import listener from 'mini-listener'; 4 | import * as Vanilla from './vanilla'; 5 | 6 | const glslify = require('glslify'); 7 | 8 | 9 | const loader = window.loader = new Vanilla.Loader(); 10 | loader.load(['assets/brick.jpg', 'assets/normal.png'], () => { 11 | mesh.material.uniforms.map.value = loader.textures.brick; 12 | }); 13 | 14 | 15 | const gl = new Vanilla.GL(); 16 | const scene = new Vanilla.Scene(); 17 | const camera = new Vanilla.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); 18 | camera.z = -200; 19 | const controls = new Vanilla.OrbitControl(camera); 20 | let time = 0; 21 | const mesh = new Vanilla.Mesh( 22 | new Vanilla.BoxGeometry(2, 2, 2), 23 | new Vanilla.Material({ 24 | uniforms: { 25 | time: { 26 | type: 'uniform1f', 27 | value: 1, 28 | }, 29 | map: { 30 | type: 'uniform1i', 31 | value: new Vanilla.Texture(), 32 | }, 33 | }, 34 | }), 35 | ); 36 | 37 | scene.add(mesh); 38 | 39 | document.body.appendChild(gl.canvas); 40 | 41 | draw(); 42 | 43 | listener.add('resize', () => { 44 | gl.resize(); 45 | camera.aspect = window.innerWidth / window.innerHeight; 46 | }); 47 | 48 | function draw() { 49 | raf(draw); 50 | time += 0.1; 51 | mesh.material.uniforms.time.value = time; 52 | // mesh2.material.uniforms.time.value = time; 53 | // scene.ry += 0.02; 54 | // mesh.rz += 0.02; 55 | // mesh2.rx -= 0.02; 56 | controls.update(); 57 | 58 | gl.render(camera, scene); 59 | } 60 | -------------------------------------------------------------------------------- /app/vanilla/GL.js: -------------------------------------------------------------------------------- 1 | import glm from 'gl-matrix'; 2 | import State from './State'; 3 | 4 | export default class GL { 5 | constructor({ 6 | canvas = null, 7 | width = window.innerWidth, 8 | height = window.innerWidth, 9 | contextOptions = {}, 10 | } = {}) { 11 | 12 | this.canvas = this.canvas ? canvas : document.createElement('canvas'); 13 | try { 14 | window.gl = this.gl = this.canvas.getContext('webgl', contextOptions) || this.canvas.getContext('experimental-webgl', contextOptions); 15 | } catch (e) { 16 | console.warn('Webgl not supported'); 17 | } 18 | 19 | this.gl.clearColor(0, 0, 0, 1); 20 | this.resize(width, height); 21 | 22 | this.normalMatrix = glm.mat4.create(); 23 | this.currentMesh = null; 24 | } 25 | resize() { 26 | this.canvas.width = window.innerWidth; 27 | this.canvas.height = window.innerHeight; 28 | this.gl.viewport(0, 0, window.innerWidth, window.innerHeight); 29 | } 30 | render(camera, scene) { 31 | this.gl.clear(this.gl.COLOR_BUFFER_BIT); 32 | for (let i = 0; i < scene.children.length; i += 1) { 33 | this.currentMesh = scene.children[i]; 34 | if (this.currentMesh.material.depthTest) { 35 | State.enable(gl.DEPTH_TEST); 36 | } else { 37 | State.disable(gl.DEPTH_TEST); 38 | } 39 | this.useShader(this.currentMesh.material.shader); 40 | this.computeNormalMatrix(); 41 | this.setDefaultUniforms(camera); 42 | if (this.currentMesh.material.uniforms) { 43 | this.setUniforms(); 44 | } 45 | 46 | this.bindBuffer(this.currentMesh); 47 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.currentMesh.geometry.indices.buffer); 48 | switch (this.currentMesh.material.drawType) { 49 | case 'triangles': 50 | gl.drawElements(gl.TRIANGLES, this.currentMesh.geometry.indices.length, gl.UNSIGNED_SHORT, 0); 51 | break; 52 | case 'lines': 53 | gl.drawElements(gl.LINES, this.currentMesh.geometry.indices.length, gl.UNSIGNED_SHORT, 0); 54 | break; 55 | case 'points': 56 | gl.drawElements(gl.POINTS, this.currentMesh.geometry.indices.length, gl.UNSIGNED_SHORT, 0); 57 | break; 58 | default: 59 | 60 | } 61 | 62 | 63 | } 64 | 65 | } 66 | computeNormalMatrix() { 67 | glm.mat4.identity(this.normalMatrix); 68 | glm.mat4.transpose(this.normalMatrix, this.normalMatrix); 69 | glm.mat4.invert(this.normalMatrix, this.currentMesh.matrix); 70 | } 71 | setDefaultUniforms(camera) { 72 | this.shader.uniform('worldMatrix', 'uniformMatrix4fv', this.currentMesh.matrixWorld); 73 | this.shader.uniform('normalMatrix', 'uniformMatrix4fv', this.normalMatrix); 74 | this.shader.uniform('viewMatrix', 'uniformMatrix4fv', camera.matrix); 75 | this.shader.uniform('projectionMatrix', 'uniformMatrix4fv', camera.projection); 76 | } 77 | setUniforms() { 78 | if (this.currentMesh.material.uniforms) { 79 | const uniforms = this.currentMesh.material.uniforms; 80 | // console.log(mesh.material.uniforms); 81 | for (const key in uniforms) { 82 | const _uniform = uniforms[key]; 83 | if (_uniform.type === 'uniform1i') { 84 | // console.log(_uniform.value.bind); 85 | this.shader.uniform(key, _uniform.type, _uniform.value.index); 86 | _uniform.value.bind(); 87 | 88 | } else { 89 | this.shader.uniform(key, _uniform.type, _uniform.value); 90 | } 91 | } 92 | } 93 | } 94 | useShader(shader) { 95 | if (this.shader === shader) return; 96 | this.shader = shader; 97 | this.gl.useProgram(shader.program); 98 | } 99 | bindBuffer(mesh) { 100 | for (const key in mesh.geometry.attributes) { 101 | const attribute = mesh.geometry.attributes[key]; 102 | gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer); 103 | const attrPosition = this.getAttribLoc(this.shader.program, attribute.name); 104 | gl.vertexAttribPointer(attrPosition, attribute.itemSize, gl.FLOAT, false, 0, 0); 105 | State.enableAttribute(attrPosition); 106 | } 107 | } 108 | getAttribLoc(program, name) { 109 | return gl.getAttribLocation(program, name); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/vanilla/Loader.js: -------------------------------------------------------------------------------- 1 | import Texture from './Texture'; 2 | 3 | let index = 0; 4 | export default class TextureLoader { 5 | constructor() { 6 | this.textures = {}; 7 | index = 0; 8 | } 9 | load(src, cb) { 10 | if (typeof src === 'object') { 11 | const imagesToLoad = src.length; 12 | let imagesLoaded = 0; 13 | for (let i = 0; i < src.length; i += 1) { 14 | const img = new Image(); 15 | img.addEventListener('load', () => { 16 | const tex = new Texture(img); 17 | const name = this.getTextureName(src[i]); 18 | this.textures[name] = tex; 19 | imagesLoaded++; 20 | if (imagesLoaded === imagesToLoad && cb) { 21 | cb(); 22 | } 23 | }); 24 | img.src = src[i]; 25 | } 26 | } else { 27 | const img = new Image(); 28 | img.addEventListener('load', () => { 29 | const tex = new Texture(img); 30 | const name = this.getTextureName(src); 31 | this.textures[name] = tex; 32 | if (cb) cb(tex); 33 | }); 34 | img.src = src; 35 | } 36 | } 37 | getTextureName(src) { 38 | const ary = src.split('/'); 39 | let str = ary[ary.length - 1]; 40 | const lastIndex = str.lastIndexOf('.'); 41 | str = str.substring(0, lastIndex); 42 | return str; 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/vanilla/Material.js: -------------------------------------------------------------------------------- 1 | import Shader from './Shader'; 2 | const glslify = require('glslify'); 3 | 4 | export default class Material { 5 | constructor({ 6 | fog = false, 7 | lights = false, 8 | depthTest = true, 9 | uniforms = null, 10 | drawType = 'triangles', 11 | side = 'double', 12 | vertexShader = glslify('./shaders/basic.vert'), 13 | fragmentShader = glslify('./shaders/basic.frag'), 14 | } = {}) { 15 | this.depthTest = depthTest; 16 | this.drawType = drawType; 17 | this.side = side; 18 | this.defines = []; 19 | this.uniforms = uniforms; 20 | this.shader = new Shader(vertexShader, fragmentShader, this); 21 | 22 | for (const key in this.uniforms) { 23 | const item = this.uniforms[key]; 24 | console.log(item); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/vanilla/Mesh.js: -------------------------------------------------------------------------------- 1 | import Object3D from './Object3D'; 2 | 3 | export default class Mesh extends Object3D { 4 | constructor(geometry, material) { 5 | super(); 6 | this.geometry = geometry; 7 | this.material = material; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/vanilla/Object3D.js: -------------------------------------------------------------------------------- 1 | import glm from 'gl-matrix'; 2 | // TODO need to find how to update the matrix of parents without calling update matrix for each position etc... 3 | export default class Object3D { 4 | constructor() { 5 | 6 | this._needUpdate = true; 7 | 8 | this._x = 0; 9 | this._y = 0; 10 | this._z = 0; 11 | 12 | this._rx = 0; 13 | this._ry = 0; 14 | this._rz = 0; 15 | 16 | this._sx = 1; 17 | this._sy = 1; 18 | this._sz = 1; 19 | 20 | this._position = glm.vec3.create(); 21 | this._rotation = glm.vec3.create(); 22 | this._scale = glm.vec3.fromValues(1, 1, 1); 23 | 24 | this._matrix = glm.mat4.create(); 25 | this._matrixWorld = glm.mat4.create(); 26 | this._matrixRotation = glm.mat4.create(); 27 | this._matrixScale = glm.mat4.create(); 28 | this._matrixTranslation = glm.mat4.create(); 29 | 30 | this.parent = null; 31 | this.children = []; 32 | } 33 | _updateMatrix() { 34 | glm.vec3.set(this._scale, this._sx, this._sy, this._sz); 35 | glm.vec3.set(this._rotation, this._rx, this._ry, this._rz); 36 | glm.vec3.set(this._position, this._x, this._y, this._z); 37 | 38 | glm.mat4.identity(this._matrixTranslation, this._matrixTranslation); 39 | glm.mat4.identity(this._matrixScale, this._matrixScale); 40 | glm.mat4.identity(this._matrixRotation, this._matrixRotation); 41 | 42 | glm.mat4.rotateX(this._matrixRotation, this._matrixRotation, this._rx); 43 | glm.mat4.rotateY(this._matrixRotation, this._matrixRotation, this._ry); 44 | glm.mat4.rotateZ(this._matrixRotation, this._matrixRotation, this._rz); 45 | 46 | glm.mat4.scale(this._matrixScale, this._matrixScale, this._scale); 47 | glm.mat4.translate(this._matrixTranslation, this._matrixTranslation, this._position); 48 | 49 | glm.mat4.mul(this._matrix, this._matrixTranslation, this._matrixRotation); 50 | glm.mat4.mul(this._matrix, this._matrix, this._matrixScale); 51 | 52 | this._updateMatrixWorld(); 53 | 54 | this._needUpdate = false; 55 | 56 | 57 | } 58 | _updateMatrixWorld() { 59 | 60 | if (this.parent) { 61 | glm.mat4.multiply(this._matrixWorld, this.parent.matrixWorld, this._matrix); 62 | } else { 63 | glm.mat4.copy(this._matrixWorld, this._matrix); 64 | } 65 | 66 | for (let i = 0, l = this.children.length; i < l; i += 1) { 67 | this.children[i]._updateMatrixWorld(); 68 | } 69 | } 70 | set x(value) { 71 | this._needUpdate = true; 72 | this._x = value; 73 | } 74 | get x() { 75 | return this._x; 76 | } 77 | set y(value) { 78 | this._needUpdate = true; 79 | this._y = value; 80 | } 81 | get y() { 82 | return this._y; 83 | } 84 | set z(value) { 85 | this._needUpdate = true; 86 | this._z = value; 87 | } 88 | get z() { 89 | return this._z; 90 | } 91 | set position(value) { 92 | this._needUpdate = true; 93 | this._position = value; 94 | } 95 | get position() { 96 | return this._position; 97 | } 98 | set rx(value) { 99 | this._needUpdate = true; 100 | this._rx = value; 101 | } 102 | get rx() { 103 | return this._rx; 104 | } 105 | set ry(value) { 106 | this._needUpdate = true; 107 | if (this._needUpdate) this._updateMatrix(); 108 | 109 | this._ry = value; 110 | } 111 | get ry() { 112 | return this._ry; 113 | } 114 | set rz(value) { 115 | this._needUpdate = true; 116 | if (this._needUpdate) this._updateMatrix(); 117 | this._rz = value; 118 | } 119 | get rz() { 120 | return this._rz; 121 | } 122 | set rotation(value) { 123 | this._needUpdate = true; 124 | if (this._needUpdate) this._updateMatrix(); 125 | this._rotation = value; 126 | } 127 | get rotation() { 128 | return this._rotation; 129 | } 130 | set sx(value) { 131 | this._needUpdate = true; 132 | if (this._needUpdate) this._updateMatrix(); 133 | this._sx = value; 134 | } 135 | get sx() { 136 | return this._sx; 137 | } 138 | set sy(value) { 139 | this._needUpdate = true; 140 | if (this._needUpdate) this._updateMatrix(); 141 | this._sy = value; 142 | } 143 | get sy() { 144 | return this._sy; 145 | } 146 | set sz(value) { 147 | this._needUpdate = true; 148 | if (this._needUpdate) this._updateMatrix(); 149 | this._sz = value; 150 | } 151 | get sz() { 152 | return this._sz; 153 | } 154 | set scale(value) { 155 | this._needUpdate = true; 156 | this._sx = value[0]; 157 | this._sy = value[1]; 158 | this._sz = value[2]; 159 | this._scale = value; 160 | if (this._needUpdate) this._updateMatrix(); 161 | } 162 | get scale() { 163 | return this._scale; 164 | } 165 | 166 | get matrix() { 167 | if (this._needUpdate) this._updateMatrix(); 168 | return this._matrix; 169 | } 170 | get matrixWorld() { 171 | return this._matrixWorld; 172 | } 173 | add(object) { 174 | // todo check if he has already a parent 175 | object.parent = this; 176 | this.children.push(object); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /app/vanilla/OrbitControl.js: -------------------------------------------------------------------------------- 1 | import listener from 'mini-listener'; 2 | 3 | function clamp(num, min, max) { 4 | return num <= min ? min : num >= max ? max : num; 5 | } 6 | export default class OrbitControl { 7 | constructor(target) { 8 | this.target = target; 9 | listener.add('mouseup', this.mouseup.bind(this)); 10 | listener.add('mousemove', this.mousemove.bind(this)); 11 | listener.add('mousedown', this.mousedown.bind(this)); 12 | this.dragging = false; 13 | this.radius = 50; 14 | this.start = { 15 | x: 0, 16 | y: 0, 17 | }; 18 | this.distance = { 19 | x: 0, 20 | y: 0, 21 | }; 22 | this.position = [0, 0, 0]; 23 | this.rx = 0; 24 | this.ry = 0; 25 | this.prevRx = 0; 26 | this.prevRy = 0; 27 | 28 | } 29 | mousedown(e) { 30 | this.dragging = true; 31 | this.start = { 32 | x: e.clientX / window.innerWidth, 33 | y: e.clientY / window.innerHeight, 34 | }; 35 | this.prevRx = this.rx; 36 | this.prevRy = this.ry; 37 | } 38 | mouseup() { 39 | this.dragging = false; 40 | this.distance.x = 0; 41 | this.distance.y = 0; 42 | this.start = { 43 | x: 0, 44 | y: 0, 45 | }; 46 | } 47 | 48 | mousemove(e) { 49 | if (!this.dragging) return; 50 | const current = { 51 | x: e.clientX / window.innerWidth, 52 | y: e.clientY / window.innerHeight, 53 | }; 54 | this.distance.x = current.x - this.start.x; 55 | this.distance.y = current.y - this.start.y; 56 | this.rx = -(this.prevRx - this.distance.y) * 5; 57 | this.ry = (this.prevRy - this.distance.x) * 5; 58 | this.rx = clamp(this.rx, -Math.PI / 2, Math.PI / 2); 59 | 60 | } 61 | update() { 62 | this.position[1] = Math.sin(this.rx) * this.radius; 63 | const r = this.radius * Math.cos(this.rx); 64 | this.position[0] = Math.sin(this.ry) * r; 65 | this.position[2] = Math.cos(this.ry) * r; 66 | this.target.lookAt(this.position, [0, 0, 0]); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/vanilla/Scene.js: -------------------------------------------------------------------------------- 1 | import Object3D from './Object3D'; 2 | 3 | export default class Scene extends Object3D { 4 | constructor() { 5 | super(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/vanilla/Shader.js: -------------------------------------------------------------------------------- 1 | const glslify = require('glslify'); 2 | 3 | const uniformsDictionnary = { 4 | float: 'uniform1f', 5 | } 6 | 7 | export default class Shader { 8 | constructor(strVs = glslify('./shaders/basic.vert'), strFs = glslify('./shaders/basic.frag')) { 9 | 10 | this.vs = this.createShader(strVs, true); 11 | this.fs = this.createShader(strFs, false); 12 | this.program = this.createProgram(this.vs, this.fs); 13 | } 14 | 15 | createShader(source, isShaderVextex) { 16 | const type = isShaderVextex ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER; 17 | const shader = gl.createShader(type); 18 | gl.shaderSource(shader, source); 19 | gl.compileShader(shader); 20 | const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 21 | if (success) return shader; 22 | 23 | console.log(gl.getShaderInfoLog(shader)); 24 | gl.deleteShader(shader); 25 | return null; 26 | } 27 | createProgram(vs, fs) { 28 | const program = gl.createProgram(); 29 | gl.attachShader(program, vs); 30 | gl.attachShader(program, fs); 31 | gl.linkProgram(program); 32 | const success = gl.getProgramParameter(program, gl.LINK_STATUS); 33 | if (success) return program; 34 | 35 | console.log(gl.getProgramInfoLog(program)); 36 | gl.deleteProgram(program); 37 | return null; 38 | } 39 | uniform(name, type, value) { 40 | 41 | this.program[name] = gl.getUniformLocation(this.program, name); 42 | 43 | if (type.indexOf('Matrix') === -1) { 44 | gl[type](this.program[name], value); 45 | } else { 46 | gl[type](this.program[name], gl.FALSE, value); 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/vanilla/State.js: -------------------------------------------------------------------------------- 1 | class State { 2 | constructor() { 3 | // all state 4 | this.capabilities = {}; 5 | this.enabledAttributes = {}; 6 | } 7 | enableAttribute(attribute) { 8 | if (this.enabledAttributes[attribute] !== true) { 9 | gl.enableVertexAttribArray(attribute); 10 | this.enabledAttributes[attribute] = true; 11 | 12 | } 13 | } 14 | disableAttribute(attribute) { 15 | if (this.enabledAttributes[attribute] !== false) { 16 | console.log('not enable yet'); 17 | gl.disableVertexAttribArray(attribute); 18 | } 19 | } 20 | enable(id) { 21 | if (this.capabilities[id] !== true) { 22 | gl.enable(id); 23 | this.capabilities[id] = true; 24 | } 25 | } 26 | disable(id) { 27 | if (this.capabilities[id] !== false) { 28 | gl.disable(id); 29 | this.capabilities[id] = false; 30 | } 31 | } 32 | } 33 | 34 | const state = new State(); 35 | export default state; 36 | -------------------------------------------------------------------------------- /app/vanilla/Texture.js: -------------------------------------------------------------------------------- 1 | let index = 0; 2 | 3 | export default class Texture { 4 | constructor(image) { 5 | index++; 6 | if (!image) { 7 | image = document.createElement('canvas'); 8 | image.width = 1; 9 | image.height = 1; 10 | } 11 | 12 | this.texture = gl.createTexture(); 13 | console.log('this.texture',this.texture); 14 | 15 | gl.bindTexture(gl.TEXTURE_2D, this.texture); 16 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 17 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 18 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 19 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 20 | gl.bindTexture(gl.TEXTURE_2D, null); 21 | 22 | // this.texture.image = image; 23 | this.index = index; 24 | // gl.activeTexture(gl.TEXTURE0 + index); 25 | 26 | } 27 | bind(tindex = 0) { 28 | gl.activeTexture(gl.TEXTURE0 + this.index); 29 | gl.bindTexture(gl.TEXTURE_2D, this.texture); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/vanilla/cameras/Camera.js: -------------------------------------------------------------------------------- 1 | import glm from 'gl-matrix'; 2 | import Object3D from '../Object3D'; 3 | 4 | export default class Camera extends Object3D { 5 | constructor() { 6 | super(); 7 | this.projection = glm.mat4.create(); 8 | glm.mat4.identity(this.projection); 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/vanilla/cameras/PerspectiveCamera.js: -------------------------------------------------------------------------------- 1 | import glm from 'gl-matrix'; 2 | import Camera from './Camera'; 3 | 4 | export default class PerspectiveCamera extends Camera { 5 | constructor(fieldOfViewDegree, aspect, zNear, zFar) { 6 | super(); 7 | this.fieldOfView = fieldOfViewDegree; 8 | this._apsect = aspect; 9 | this.zNear = zNear; 10 | this.zFar = zFar; 11 | 12 | this.projection = glm.mat4.perspective(this.projection, 13 | glm.glMatrix.toRadian(fieldOfViewDegree), 14 | aspect, 15 | zNear, 16 | zFar, 17 | ); 18 | } 19 | lookAt(aEye, aCenter, aUp = [0, 1, 0]) { 20 | glm.vec3.copy(this.position, aEye); 21 | glm.mat4.identity(this.matrix); 22 | glm.mat4.lookAt(this.matrix, aEye, aCenter, aUp); 23 | } 24 | updateProjectionMatrix() { 25 | this.projection = glm.mat4.perspective(this.projection, 26 | glm.glMatrix.toRadian(this.fieldOfView), 27 | this._apsect, 28 | this.zNear, 29 | this.zFar, 30 | ); 31 | } 32 | get aspect() { 33 | return this._apsect; 34 | } 35 | set aspect(value) { 36 | this._apsect = value; 37 | this.updateProjectionMatrix(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/vanilla/geometry/BoxGeometry.js: -------------------------------------------------------------------------------- 1 | import Geometry from './Geometry'; 2 | 3 | export default class Box extends Geometry { 4 | constructor(width = 1, height = 1, depth = 1) { 5 | const vertices = [ 6 | // Front face 7 | -1.0, -1.0, 1.0, 8 | 1.0, -1.0, 1.0, 9 | 1.0, 1.0, 1.0, 10 | -1.0, 1.0, 1.0, 11 | 12 | // Back face 13 | -1.0, -1.0, -1.0, 14 | -1.0, 1.0, -1.0, 15 | 1.0, 1.0, -1.0, 16 | 1.0, -1.0, -1.0, 17 | 18 | // Top face 19 | -1.0, 1.0, -1.0, 20 | -1.0, 1.0, 1.0, 21 | 1.0, 1.0, 1.0, 22 | 1.0, 1.0, -1.0, 23 | 24 | // Bottom face 25 | -1.0, -1.0, -1.0, 26 | 1.0, -1.0, -1.0, 27 | 1.0, -1.0, 1.0, 28 | -1.0, -1.0, 1.0, 29 | 30 | // Right face 31 | 1.0, -1.0, -1.0, 32 | 1.0, 1.0, -1.0, 33 | 1.0, 1.0, 1.0, 34 | 1.0, -1.0, 1.0, 35 | 36 | // Left face 37 | -1.0, -1.0, -1.0, 38 | -1.0, -1.0, 1.0, 39 | -1.0, 1.0, 1.0, 40 | -1.0, 1.0, -1.0, 41 | ]; 42 | for (let i = 0; i < vertices.length; i += 3) { 43 | vertices[i] *= width; 44 | vertices[i + 1] *= height; 45 | vertices[i + 2] *= depth; 46 | } 47 | const indices = [ 48 | 0, 1, 2, 0, 2, 3, // front 49 | 4, 5, 6, 4, 6, 7, // back 50 | 8, 9, 10, 8, 10, 11, // top 51 | 12, 13, 14, 12, 14, 15, // bottom 52 | 16, 17, 18, 16, 18, 19, // right 53 | 20, 21, 22, 20, 22, 23, // left 54 | ]; 55 | const normals = [ 56 | // Front face 57 | 0.0, 0.0, 1.0, 58 | 0.0, 0.0, 1.0, 59 | 0.0, 0.0, 1.0, 60 | 0.0, 0.0, 1.0, 61 | 62 | // Back face 63 | 0.0, 0.0, -1.0, 64 | 0.0, 0.0, -1.0, 65 | 0.0, 0.0, -1.0, 66 | 0.0, 0.0, -1.0, 67 | 68 | // Top face 69 | 0.0, 1.0, 0.0, 70 | 0.0, 1.0, 0.0, 71 | 0.0, 1.0, 0.0, 72 | 0.0, 1.0, 0.0, 73 | 74 | // Bottom face 75 | 0.0, -1.0, 0.0, 76 | 0.0, -1.0, 0.0, 77 | 0.0, -1.0, 0.0, 78 | 0.0, -1.0, 0.0, 79 | 80 | // Right face 81 | 1.0, 0.0, 0.0, 82 | 1.0, 0.0, 0.0, 83 | 1.0, 0.0, 0.0, 84 | 1.0, 0.0, 0.0, 85 | 86 | // Left face 87 | -1.0, 0.0, 0.0, 88 | -1.0, 0.0, 0.0, 89 | -1.0, 0.0, 0.0, 90 | -1.0, 0.0, 0.0, 91 | ]; 92 | const uvs = [ 93 | // Front face 94 | 0.0, 0.0, 95 | 1.0, 0.0, 96 | 1.0, 1.0, 97 | 0.0, 1.0, 98 | 99 | // Back face 100 | 1.0, 0.0, 101 | 1.0, 1.0, 102 | 0.0, 1.0, 103 | 0.0, 0.0, 104 | 105 | // Top face 106 | 0.0, 1.0, 107 | 0.0, 0.0, 108 | 1.0, 0.0, 109 | 1.0, 1.0, 110 | 111 | // Bottom face 112 | 1.0, 1.0, 113 | 0.0, 1.0, 114 | 0.0, 0.0, 115 | 1.0, 0.0, 116 | 117 | // Right face 118 | 1.0, 0.0, 119 | 1.0, 1.0, 120 | 0.0, 1.0, 121 | 0.0, 0.0, 122 | 123 | // Left face 124 | 0.0, 0.0, 125 | 1.0, 0.0, 126 | 1.0, 1.0, 127 | 0.0, 1.0, 128 | ]; 129 | 130 | super(vertices, indices, normals, uvs); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /app/vanilla/geometry/Geometries.js: -------------------------------------------------------------------------------- 1 | import Geometry from './Geometry'; 2 | import QuadGeometry from './QuadGeometry'; 3 | import BoxGeometry from './BoxGeometry'; 4 | 5 | export { 6 | Geometry, 7 | QuadGeometry, 8 | BoxGeometry, 9 | }; 10 | -------------------------------------------------------------------------------- /app/vanilla/geometry/Geometry.js: -------------------------------------------------------------------------------- 1 | export default class Geometry { 2 | constructor(vertices, indices, normals, uvs) { 3 | this.attributes = {}; 4 | if (vertices) { 5 | this.addAttribute('position', vertices, 3); 6 | } 7 | if (indices) { 8 | this.addIndices(indices); 9 | } 10 | if (normals) { 11 | this.addAttribute('normal', normals, 3); 12 | } 13 | if (uvs) { 14 | this.addAttribute('uv', uvs, 2); 15 | } 16 | } 17 | addAttribute(name, data, size) { 18 | const buffer = gl.createBuffer(); 19 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 20 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); 21 | this.attributes[name] = { name, itemSize: size, buffer }; 22 | return this; 23 | } 24 | addIndices(data) { 25 | const buffer = gl.createBuffer(); 26 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); 27 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(data), gl.STATIC_DRAW); 28 | this.indices = { 29 | length: data.length, 30 | buffer, 31 | }; 32 | return this; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/vanilla/geometry/PlaneGeometry.js: -------------------------------------------------------------------------------- 1 | import Geometry from './Geometry'; 2 | 3 | export default class PlaneGeometry extends Geometry { 4 | constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { 5 | 6 | const halfWidth = width / 2; 7 | const halfHeight = height / 2; 8 | 9 | 10 | const vertices = [ 11 | -0.5, 0.5, 0.0, 12 | -0.5, -0.5, 0.0, 13 | 0.5, -0.5, 0.0, 14 | 0.5, 0.5, 0.0, 15 | ]; 16 | for (let i = 0; i < vertices.length; i += 3) { 17 | vertices[i] *= width; 18 | vertices[i + 1] *= height; 19 | } 20 | const indices = [3, 2, 1, 3, 1, 0]; 21 | const normals = [ 22 | 0.0, 0.0, 1.0, 23 | 0.0, 0.0, 1.0, 24 | 0.0, 0.0, 1.0, 25 | 0.0, 0.0, 1.0, 26 | ]; 27 | const uvs = [ 28 | 0.0, 0.0, 29 | 1.0, 0.0, 30 | 1.0, 1.0, 31 | 0.0, 1.0, 32 | ]; 33 | 34 | super(vertices, indices, normals, uvs); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/vanilla/geometry/QuadGeometry.js: -------------------------------------------------------------------------------- 1 | import Geometry from './Geometry'; 2 | 3 | export default class Quad extends Geometry { 4 | constructor(width = 1, height = 1) { 5 | const vertices = [ 6 | -0.5, 0.5, 0.0, 7 | -0.5, -0.5, 0.0, 8 | 0.5, -0.5, 0.0, 9 | 0.5, 0.5, 0.0, 10 | ]; 11 | for (let i = 0; i < vertices.length; i += 3) { 12 | vertices[i] *= width; 13 | vertices[i + 1] *= height; 14 | } 15 | const indices = [3, 2, 1, 3, 1, 0]; 16 | const normals = [ 17 | 0.0, 0.0, 1.0, 18 | 0.0, 0.0, 1.0, 19 | 0.0, 0.0, 1.0, 20 | 0.0, 0.0, 1.0, 21 | ]; 22 | const uvs = [ 23 | 0.0, 0.0, 24 | 1.0, 0.0, 25 | 1.0, 1.0, 26 | 0.0, 1.0, 27 | ]; 28 | 29 | super(vertices, indices, normals, uvs); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/vanilla/index.js: -------------------------------------------------------------------------------- 1 | import GL from './GL'; 2 | import Shader from './Shader'; 3 | import { Geometry, QuadGeometry, BoxGeometry } from './geometry/Geometries'; 4 | import Material from './Material'; 5 | import Mesh from './Mesh'; 6 | import Scene from './Scene'; 7 | import PerspectiveCamera from './cameras/PerspectiveCamera'; 8 | import OrbitControl from './OrbitControl'; 9 | import Loader from './Loader'; 10 | import Texture from './Texture'; 11 | 12 | console.log('%c VANILLA (ノ◕ヮ◕)ノ ', 'background: #282828; color: #fffbf4'); 13 | export { 14 | GL, 15 | Shader, 16 | Geometry, 17 | QuadGeometry, 18 | BoxGeometry, 19 | Mesh, 20 | Scene, 21 | Loader, 22 | OrbitControl, 23 | Texture, 24 | Material, 25 | PerspectiveCamera, 26 | }; 27 | -------------------------------------------------------------------------------- /app/vanilla/shaders/basic.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | varying vec2 vUv; 3 | uniform float time; 4 | uniform sampler2D map; 5 | 6 | void main() { 7 | vec4 tex = texture2D(map, vUv); 8 | gl_FragColor = vec4(vUv, cos(time), 1.0); 9 | gl_FragColor = vec4(tex); 10 | } 11 | -------------------------------------------------------------------------------- /app/vanilla/shaders/basic.vert: -------------------------------------------------------------------------------- 1 | 2 | attribute vec3 position; 3 | attribute vec2 uv; 4 | attribute vec3 normal; 5 | 6 | uniform mat4 worldMatrix; 7 | uniform mat4 normalMatrix; 8 | uniform mat4 viewMatrix; 9 | uniform mat4 projectionMatrix; 10 | 11 | varying vec2 vUv; 12 | varying vec3 vNormal; 13 | 14 | 15 | void main() { 16 | 17 | vec4 pos = vec4(position, 1.0); 18 | vec4 worldPos = worldMatrix * pos; 19 | vec4 transformedNormal = vec4(normal, 1.0) * normalMatrix; 20 | vNormal = transformedNormal.xyz; 21 | vUv = uv; 22 | 23 | 24 | gl_PointSize = 10.0; 25 | gl_Position = projectionMatrix * viewMatrix * worldPos; 26 | } 27 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const budo = require('budo'); 2 | const argv = require('minimist')(process.argv.slice(2)); 3 | const babelify = require('babelify'); 4 | const path = require('path'); 5 | const browserify = require('browserify'); 6 | const fs = require('fs') 7 | const glslify = require('glslify'); 8 | 9 | const entry = argv._[0]; 10 | const examplesDir = path.resolve(__dirname, 'examples'); 11 | const fileName = entry ? `${entry}.js` : 'index.js'; 12 | const entryFile = path.resolve(__dirname, `${examplesDir}/src`, fileName); 13 | 14 | if (argv._[1] === 'build') { 15 | build(); 16 | } else { 17 | startDev(); 18 | } 19 | function startDev() { 20 | // console.log(path.join(entry, 'index.js')); 21 | budo(entryFile, { 22 | serve: `${entry}/bundle.js`, 23 | open: true, 24 | live: true, // live reload 25 | dir: examplesDir, 26 | browserify: { 27 | transform: [babelify, glslify], 28 | }, 29 | }); 30 | } 31 | 32 | function build() { 33 | console.log('Build ...'); 34 | const b = browserify(entryFile, { 35 | debug: false, 36 | }); 37 | b.transform(babelify) 38 | b.transform(glslify) 39 | b.bundle((err, src) => { 40 | fs.writeFile(`${examplesDir}/${entry}/bundle.js`, src, (err) => { 41 | console.log('Done :D'); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /examples/assets/brick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/examples/assets/brick.jpg -------------------------------------------------------------------------------- /examples/assets/grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/examples/assets/grid.jpg -------------------------------------------------------------------------------- /examples/assets/normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/examples/assets/normal.png -------------------------------------------------------------------------------- /examples/cube/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cube 6 | 31 | 32 | 33 | sources 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vanilla GL examples 6 | 22 | 23 | 24 |

Vanilla examples

25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/src/cube.js: -------------------------------------------------------------------------------- 1 | import listener from 'mini-listener'; 2 | import raf from 'raf'; 3 | import * as Vanilla from '../../app/vanilla'; 4 | 5 | console.log('This is an example.'); 6 | console.log('Source code: https://github.com/JordanMachado/vanillagl/blob/master/examples/src/cube.js'); 7 | const glslify = require('glslify'); 8 | 9 | const loader = new Vanilla.Loader(); 10 | loader.load('../assets/brick.jpg', () => { 11 | mesh.material.uniforms.map.value = loader.textures.brick; 12 | }); 13 | 14 | const gl = new Vanilla.GL(); 15 | const scene = new Vanilla.Scene(); 16 | const camera = new Vanilla.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); 17 | const controls = new Vanilla.OrbitControl(camera); 18 | 19 | const mesh = new Vanilla.Mesh( 20 | new Vanilla.BoxGeometry(2, 2, 2), 21 | new Vanilla.Material({ 22 | uniforms: { 23 | time: { 24 | type: 'uniform1f', 25 | value: 1, 26 | }, 27 | map: { 28 | type: 'uniform1i', 29 | value: new Vanilla.Texture(), 30 | }, 31 | }, 32 | }), 33 | ); 34 | 35 | scene.add(mesh); 36 | 37 | let time = 0; 38 | draw(); 39 | 40 | function draw() { 41 | raf(draw); 42 | time += 0.1; 43 | mesh.material.uniforms.time.value = time; 44 | mesh.ry += 0.02; 45 | mesh.rz += 0.02; 46 | controls.update(); 47 | 48 | gl.render(camera, scene); 49 | } 50 | 51 | listener.add('resize', () => { 52 | gl.resize(); 53 | camera.aspect = window.innerWidth / window.innerHeight; 54 | }); 55 | 56 | document.body.appendChild(gl.canvas); 57 | -------------------------------------------------------------------------------- /examples/src/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/examples/src/index.js -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vanillagl", 3 | "version": "1.0.0", 4 | "description": "Little 3D framework", 5 | "main": "app/index.js", 6 | "scripts": { 7 | "start": "npm run dev", 8 | "dev": "budo ./app/index.js:build/main.js --dir ./public --live --open -- -t babelify -t glslify", 9 | "build": "browserify -t babelify -g glslify app/index.js -o public/build/main.js", 10 | "example": "node example.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+ssh://git@github.com:JordanMachado/raymarching-starter-kit.git" 15 | }, 16 | "author": "Jordan Machado", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "babel-eslint": "^7.1.0", 20 | "babel-plugin-transform-es2015-modules-commonjs": "^6.2.0", 21 | "babel-preset-es2015": "^6.1.18", 22 | "babelify": "^7.2.0", 23 | "browserify": "^12.0.1", 24 | "budo": "^6.0.4", 25 | "eslint": "^3.10.0", 26 | "eslint-config-airbnb": "^13.0.0", 27 | "eslint-plugin-babel": "^3.3.0", 28 | "eslint-plugin-import": "^2.2.0", 29 | "glslify": "^2.3.1", 30 | "minimist": "^1.2.0", 31 | "stripify": "^3.0.0", 32 | "uglifyjs": "^2.4.10" 33 | }, 34 | "babel": { 35 | "presets": [ 36 | "es2015" 37 | ], 38 | "plugins": [ 39 | "transform-es2015-modules-commonjs" 40 | ] 41 | }, 42 | "dependencies": { 43 | "events": "^1.1.1", 44 | "gl-matrix": "^2.3.2", 45 | "hex-rgb": "^1.0.0", 46 | "mini-listener": "^1.0.4", 47 | "raf": "^3.1.0", 48 | "ua-device-type": "0.0.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /public/assets/brick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/public/assets/brick.jpg -------------------------------------------------------------------------------- /public/assets/grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/public/assets/grid.jpg -------------------------------------------------------------------------------- /public/assets/normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JordanMachado/vanillagl/84defdb26729e58d1b835e348506f9acaf520bd7/public/assets/normal.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vanilla 6 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------