├── .npmignore ├── dev ├── assets │ ├── img │ │ ├── test.jpg │ │ ├── studio.dds │ │ ├── test1.jpg │ │ ├── test2.jpg │ │ └── singleLight.hdr │ ├── video │ │ └── color1.mp4 │ └── gltf │ │ ├── deer │ │ ├── deer1.bin │ │ └── deer1.gltf │ │ ├── head │ │ ├── model.bin │ │ └── model.gltf │ │ └── microphone │ │ └── microphone.bin ├── index.html ├── test.frag ├── shaders │ ├── pass.vert │ ├── depth.frag │ ├── save.vert │ ├── save.frag │ ├── cube.frag │ ├── cube.vert │ ├── floor.vert │ ├── shadowMap300es.vert │ ├── shadowMap.vert │ ├── render.vert │ ├── shadowMap.frag │ ├── sim.frag │ ├── render.frag │ ├── shadowMap300es.frag │ └── floor.frag ├── global.scss ├── uv.frag ├── features │ ├── classes │ │ └── ViewCube.js │ ├── camera-ortho.js │ ├── scene-graph.js │ ├── multi-render-target.js │ ├── utils │ │ └── debugCamera.js │ ├── gltf-parser.js │ ├── webgl2.js │ └── webgl2-multitarget.js ├── instance.vert ├── bugs │ └── TextureMinFilter.js ├── main.js ├── utils │ ├── setupAlfrid.js │ └── quickSetup.js └── noise.frag ├── tasks ├── asset-template.js ├── getExtension.js ├── isDirectory.js ├── copy.frag ├── insertString.js ├── getFileName.js ├── getAllMatches.js ├── checkExtension.js ├── basic.vert ├── watch.js ├── copy-file.js ├── dev.js ├── find-folder-promise.js ├── html.js ├── find-folder.js ├── html-watch.js ├── test-shader.js ├── shader-watcher.js ├── watch-asset.js └── uniforms-checker.js ├── src ├── alfrid │ ├── shaders │ │ ├── simpleColor.frag │ │ ├── copy.frag │ │ ├── dotsPlane.frag │ │ ├── skybox.frag │ │ ├── basic.frag │ │ ├── axis.frag │ │ ├── bigTriangle.vert │ │ ├── axis.vert │ │ ├── basic.vert │ │ ├── sky.vert │ │ ├── dotsPlane.vert │ │ ├── skybox.vert │ │ ├── general.vert │ │ ├── blur5.frag │ │ ├── blur9.frag │ │ ├── blur13.frag │ │ ├── pbr.vert │ │ ├── fxaa.frag │ │ ├── pbrColor.frag │ │ └── pbrTexture.frag │ ├── post │ │ ├── PassMacro.js │ │ ├── PassHBlur.js │ │ ├── PassVBlur.js │ │ ├── PassFxaa.js │ │ ├── PassBloom.js │ │ ├── PassBlur.js │ │ ├── PassBlurBase.js │ │ ├── Pass.js │ │ └── EffectComposer.js │ ├── utils │ │ ├── getMouse.js │ │ ├── getAttribLoc.js │ │ ├── ExtensionsList.js │ │ ├── getAndApplyExtension.js │ │ ├── ShaderLibs.js │ │ ├── getFloat.js │ │ ├── getHalfFloat.js │ │ ├── exposeAttributes.js │ │ ├── getTextureParameters.js │ │ ├── EaseNumber.js │ │ ├── SpringNumber.js │ │ ├── TouchDetector.js │ │ ├── EventDispatcher.js │ │ ├── OrbitalControl.js │ │ ├── QuatRotation.js │ │ └── HDRParser.js │ ├── FboPingPong.js │ ├── helpers │ │ ├── View.js │ │ ├── View3D.js │ │ ├── BatchSky.js │ │ ├── BatchSkybox.js │ │ ├── BatchCopy.js │ │ ├── BatchFXAA.js │ │ ├── BatchBall.js │ │ ├── ShaderUtils.js │ │ ├── BatchLine.js │ │ ├── BatchAxis.js │ │ ├── BatchDotsPlane.js │ │ ├── Scene.js │ │ └── Draw.js │ ├── Batch.js │ ├── loaders │ │ ├── HDRLoader.js │ │ ├── BinaryLoader.js │ │ └── ColladaParser.js │ ├── FboArray.js │ ├── cameras │ │ ├── CameraOrtho.js │ │ ├── CameraCube.js │ │ ├── Camera.js │ │ └── CameraPerspective.js │ ├── TransformFeedbackObject.js │ ├── CubeFrameBuffer.js │ ├── math │ │ └── Ray.js │ ├── GLCubeTexture.js │ ├── objects │ │ └── Object3D.js │ ├── MultisampleFrameBuffer.js │ └── GLShader.js └── alfrid.js ├── .gitignore ├── README.md ├── LICENSE ├── package.json └── webpack.config.js /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | src/test/ 3 | test/ 4 | tutorials/ 5 | dev/ 6 | tasks/ -------------------------------------------------------------------------------- /dev/assets/img/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/img/test.jpg -------------------------------------------------------------------------------- /dev/assets/img/studio.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/img/studio.dds -------------------------------------------------------------------------------- /dev/assets/img/test1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/img/test1.jpg -------------------------------------------------------------------------------- /dev/assets/img/test2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/img/test2.jpg -------------------------------------------------------------------------------- /tasks/asset-template.js: -------------------------------------------------------------------------------- 1 | 2 | const assetsToLoad = {{ASSETS}}; 3 | 4 | 5 | export default assetsToLoad; -------------------------------------------------------------------------------- /dev/assets/video/color1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/video/color1.mp4 -------------------------------------------------------------------------------- /dev/assets/gltf/deer/deer1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/gltf/deer/deer1.bin -------------------------------------------------------------------------------- /dev/assets/gltf/head/model.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/gltf/head/model.bin -------------------------------------------------------------------------------- /dev/assets/img/singleLight.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/img/singleLight.hdr -------------------------------------------------------------------------------- /dev/assets/gltf/microphone/microphone.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanuys/Alfrid/master/dev/assets/gltf/microphone/microphone.bin -------------------------------------------------------------------------------- /tasks/getExtension.js: -------------------------------------------------------------------------------- 1 | // getExtension.js 2 | 3 | 'use strict'; 4 | 5 | module.exports = function(mFile) { 6 | const ary = mFile.split('.'); 7 | return ary[ary.length - 1]; 8 | } -------------------------------------------------------------------------------- /dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Alfrid 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /dev/test.frag: -------------------------------------------------------------------------------- 1 | // test.frag 2 | 3 | precision highp float; 4 | varying vec2 vTextureCoord; 5 | 6 | uniform sampler2D texture; 7 | 8 | void main(void) { 9 | gl_FragColor = texture2D(texture, vTextureCoord); 10 | } -------------------------------------------------------------------------------- /tasks/isDirectory.js: -------------------------------------------------------------------------------- 1 | // isDirectory.js 2 | const fs = require('fs'); 3 | 4 | module.exports = function isDirectory(mPath) { 5 | try { 6 | return fs.lstatSync(mPath).isDirectory(); 7 | } catch(e) { 8 | return false; 9 | } 10 | } -------------------------------------------------------------------------------- /dev/shaders/pass.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec2 aPosition; 5 | out vec2 vTextureCoord; 6 | 7 | void main(void) { 8 | gl_Position = vec4(aPosition, 0.0, 1.0); 9 | vTextureCoord = aPosition * .5 + .5; 10 | } -------------------------------------------------------------------------------- /tasks/copy.frag: -------------------------------------------------------------------------------- 1 | // copy.frag 2 | 3 | #define SHADER_NAME SIMPLE_TEXTURE 4 | 5 | precision highp float; 6 | varying vec2 vTextureCoord; 7 | uniform sampler2D texture; 8 | 9 | void main(void) { 10 | gl_FragColor = texture2D(texture, vTextureCoord); 11 | } -------------------------------------------------------------------------------- /src/alfrid/shaders/simpleColor.frag: -------------------------------------------------------------------------------- 1 | // simpleColor.frag 2 | 3 | #define SHADER_NAME SIMPLE_COLOR 4 | 5 | precision mediump float; 6 | 7 | uniform vec3 color; 8 | uniform float opacity; 9 | 10 | void main(void) { 11 | gl_FragColor = vec4(color, opacity); 12 | } -------------------------------------------------------------------------------- /dev/shaders/depth.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | out vec4 oColor; 5 | 6 | void main(void) { 7 | if(distance(gl_PointCoord, vec2(.5)) > .5) { 8 | discard; 9 | } 10 | 11 | oColor = vec4(gl_FragCoord.z, 0.0, 0.0, 1.0); 12 | } -------------------------------------------------------------------------------- /src/alfrid/shaders/copy.frag: -------------------------------------------------------------------------------- 1 | // copy.frag 2 | 3 | #define SHADER_NAME COPY_FRAGMENT 4 | 5 | precision mediump float; 6 | 7 | varying vec2 vTextureCoord; 8 | uniform sampler2D texture; 9 | 10 | void main(void) { 11 | gl_FragColor = texture2D(texture, vTextureCoord); 12 | } -------------------------------------------------------------------------------- /src/alfrid/shaders/dotsPlane.frag: -------------------------------------------------------------------------------- 1 | // dotsPlane.frag 2 | 3 | precision highp float; 4 | 5 | uniform vec3 color; 6 | uniform float opacity; 7 | 8 | void main(void) { 9 | if(distance(gl_PointCoord, vec2(.5)) > .5) { 10 | discard; 11 | } 12 | gl_FragColor = vec4(color, opacity); 13 | } -------------------------------------------------------------------------------- /tasks/insertString.js: -------------------------------------------------------------------------------- 1 | // insertString.js 2 | 3 | 'use strict'; 4 | 5 | module.exports = function insertString(mStr, mStrToInsert, mIndex) { 6 | const sBefore = mStr.substring(0, mIndex); 7 | const sAfter = mStr.substring(mIndex); 8 | 9 | return `${sBefore}${mStrToInsert}${sAfter}`; 10 | } -------------------------------------------------------------------------------- /src/alfrid/post/PassMacro.js: -------------------------------------------------------------------------------- 1 | // PassMacro.js 2 | 3 | class PassMacro { 4 | constructor() { 5 | this._passes = []; 6 | } 7 | 8 | addPass(pass) { 9 | this._passes.push(pass); 10 | } 11 | 12 | get passes() { 13 | return this._passes; 14 | } 15 | } 16 | 17 | export default PassMacro; -------------------------------------------------------------------------------- /dev/global.scss: -------------------------------------------------------------------------------- 1 | /*global.scss*/ 2 | 3 | html, body { 4 | background: black; 5 | 6 | padding: 0; 7 | margin: 0; 8 | 9 | width: 100%; 10 | height: 100%; 11 | } 12 | 13 | 14 | canvas { 15 | position: fixed; 16 | 17 | width: 100%; 18 | height: 100%; 19 | 20 | top: 0; 21 | left: 0; 22 | } -------------------------------------------------------------------------------- /src/alfrid/shaders/skybox.frag: -------------------------------------------------------------------------------- 1 | // basic.frag 2 | 3 | #define SHADER_NAME SKYBOX_FRAGMENT 4 | 5 | precision mediump float; 6 | uniform samplerCube texture; 7 | varying vec2 vTextureCoord; 8 | varying vec3 vVertex; 9 | 10 | void main(void) { 11 | gl_FragColor = textureCube(texture, vVertex); 12 | } -------------------------------------------------------------------------------- /src/alfrid/utils/getMouse.js: -------------------------------------------------------------------------------- 1 | // getMouse.js 2 | 3 | export default function (e) { 4 | let x, y; 5 | 6 | if(e.touches) { 7 | x = e.touches[0].pageX; 8 | y = e.touches[0].pageY; 9 | } else { 10 | x = e.clientX; 11 | y = e.clientY; 12 | } 13 | 14 | 15 | return { 16 | x, y 17 | }; 18 | } -------------------------------------------------------------------------------- /dev/uv.frag: -------------------------------------------------------------------------------- 1 | // uv.frag 2 | 3 | #define SHADER_NAME SIMPLE_TEXTURE 4 | 5 | precision highp float; 6 | varying vec2 vTextureCoord; 7 | uniform sampler2D texture; 8 | 9 | 10 | 11 | 12 | void main(void) { 13 | vec2 uv = (vTextureCoord - .5) * 5.0 + .5; 14 | gl_FragColor = texture2D(texture, uv); 15 | } -------------------------------------------------------------------------------- /src/alfrid/FboPingPong.js: -------------------------------------------------------------------------------- 1 | // FboPingPong.js 2 | 3 | import FboFarray from './FboArray' 4 | 5 | class FboPingPong extends FboFarray { 6 | constructor (width, height, params = {}, mNumTargets = 1) { 7 | super(2, width, height, params, mNumTargets) 8 | } 9 | } 10 | 11 | export default FboPingPong 12 | -------------------------------------------------------------------------------- /src/alfrid/shaders/basic.frag: -------------------------------------------------------------------------------- 1 | // basic.frag 2 | 3 | #define SHADER_NAME BASIC_FRAGMENT 4 | 5 | precision lowp float; 6 | varying vec2 vTextureCoord; 7 | uniform float time; 8 | // uniform sampler2D texture; 9 | 10 | void main(void) { 11 | gl_FragColor = vec4(vTextureCoord, sin(time) * .5 + .5, 1.0); 12 | } -------------------------------------------------------------------------------- /src/alfrid/post/PassHBlur.js: -------------------------------------------------------------------------------- 1 | // PassHBlur.js 2 | 3 | import PassBlurBase from './PassBlurBase'; 4 | 5 | class PassHBlur extends PassBlurBase { 6 | constructor(mQuality = 9, mWidth, mHeight, mParams) { 7 | super(mQuality, [1, 0], mWidth, mHeight, mParams); 8 | } 9 | } 10 | 11 | export default PassHBlur; 12 | -------------------------------------------------------------------------------- /src/alfrid/shaders/axis.frag: -------------------------------------------------------------------------------- 1 | // axis.frag 2 | 3 | #define SHADER_NAME SIMPLE_TEXTURE 4 | 5 | precision lowp float; 6 | varying vec3 vColor; 7 | varying vec3 vNormal; 8 | 9 | void main(void) { 10 | // vec3 color = vNormal; 11 | vec3 color = vColor + vNormal * 0.0001; 12 | gl_FragColor = vec4(color, 1.0); 13 | } -------------------------------------------------------------------------------- /src/alfrid/post/PassVBlur.js: -------------------------------------------------------------------------------- 1 | // PassVBlur.js 2 | 3 | import PassBlurBase from './PassBlurBase'; 4 | 5 | class PassVBlur extends PassBlurBase { 6 | constructor(mQuality = 9, mWidth, mHeight, mParams) { 7 | super(mQuality, [0, 1], mWidth, mHeight, mParams); 8 | } 9 | } 10 | 11 | export default PassVBlur; 12 | 13 | -------------------------------------------------------------------------------- /tasks/getFileName.js: -------------------------------------------------------------------------------- 1 | // getFileName.js 2 | 3 | 'use strict'; 4 | 5 | module.exports = function getFileName(mPath) { 6 | const ary = mPath.split('/'); 7 | let str = ary[ary.length-1]; 8 | const lastIndex = str.lastIndexOf('.'); 9 | str = str.substring(0, lastIndex); 10 | return str; 11 | } -------------------------------------------------------------------------------- /src/alfrid/shaders/bigTriangle.vert: -------------------------------------------------------------------------------- 1 | // bigTriangle.vert 2 | 3 | #define SHADER_NAME BIG_TRIANGLE_VERTEX 4 | 5 | precision mediump float; 6 | attribute vec2 aPosition; 7 | varying vec2 vTextureCoord; 8 | 9 | void main(void) { 10 | gl_Position = vec4(aPosition, 0.0, 1.0); 11 | vTextureCoord = aPosition * .5 + .5; 12 | } -------------------------------------------------------------------------------- /dev/shaders/save.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec3 aVertexPosition; 5 | in vec2 aTextureCoord; 6 | in vec3 aNormal; 7 | 8 | out vec3 vPosition; 9 | out vec3 vNormal; 10 | 11 | void main(void) { 12 | gl_Position = vec4(aTextureCoord, 0.0, 1.0); 13 | vPosition = aVertexPosition; 14 | vNormal = aNormal; 15 | } -------------------------------------------------------------------------------- /src/alfrid/post/PassFxaa.js: -------------------------------------------------------------------------------- 1 | // PassFxaa.js 2 | 3 | import GL from '../GLTool'; 4 | import Pass from './Pass'; 5 | import fsFxaa from '../shaders/fxaa.frag'; 6 | 7 | class PassFxaa extends Pass { 8 | constructor() { 9 | super(fsFxaa); 10 | this.uniform('uResolution', [1/GL.width, 1/GL.height]); 11 | } 12 | } 13 | 14 | export default PassFxaa; -------------------------------------------------------------------------------- /src/alfrid/post/PassBloom.js: -------------------------------------------------------------------------------- 1 | // PassBloom.js 2 | 3 | import GL from '../GLTool'; 4 | import Pass from './Pass'; 5 | import fsFxaa from '../shaders/fxaa.frag'; 6 | 7 | class PassBloom extends Pass { 8 | constructor() { 9 | super(fsFxaa); 10 | this.uniform('uResolution', [1/GL.width, 1/GL.height]); 11 | } 12 | } 13 | 14 | export default PassBloom; -------------------------------------------------------------------------------- /dev/features/classes/ViewCube.js: -------------------------------------------------------------------------------- 1 | // ViewCube.js 2 | 3 | import alfrid, { GL, View3D } from 'src/alfrid' 4 | 5 | class ViewCube extends View3D { 6 | _init () { 7 | this.mesh = alfrid.Geom.cube(0.1, 0.1, 0.1) 8 | } 9 | 10 | render () { 11 | GL.rotate(this.matrix) 12 | this.shader.bind() 13 | GL.draw(this.mesh) 14 | } 15 | } 16 | 17 | export default ViewCube 18 | -------------------------------------------------------------------------------- /src/alfrid/helpers/View.js: -------------------------------------------------------------------------------- 1 | // View.js 2 | 3 | import GLShader from '../GLShader'; 4 | 5 | class View { 6 | constructor(mStrVertex, mStrFrag) { 7 | this.shader = new GLShader(mStrVertex, mStrFrag); 8 | 9 | this._init(); 10 | } 11 | 12 | 13 | // PROTECTED METHODS 14 | 15 | _init() { 16 | 17 | } 18 | 19 | // PUBLIC METHODS 20 | 21 | render() { 22 | 23 | } 24 | } 25 | 26 | export default View; -------------------------------------------------------------------------------- /src/alfrid/utils/getAttribLoc.js: -------------------------------------------------------------------------------- 1 | // getAttribLoc.js 2 | 3 | export default function (gl, shaderProgram, name) { 4 | if(shaderProgram.cacheAttribLoc === undefined) { shaderProgram.cacheAttribLoc = {}; } 5 | if(shaderProgram.cacheAttribLoc[name] === undefined) { 6 | shaderProgram.cacheAttribLoc[name] = gl.getAttribLocation(shaderProgram, name); 7 | } 8 | 9 | return shaderProgram.cacheAttribLoc[name]; 10 | }; -------------------------------------------------------------------------------- /dev/shaders/save.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec3 vPosition; 5 | in vec3 vNormal; 6 | 7 | layout (location = 0) out vec4 oFragColor0; 8 | layout (location = 1) out vec4 oFragColor1; 9 | layout (location = 2) out vec4 oFragColor2; 10 | 11 | void main(void) { 12 | oFragColor0 = vec4(vPosition, 1.0); 13 | oFragColor1 = vec4(vec3(0.0), 1.0); 14 | oFragColor2 = vec4(vNormal, 1.0); 15 | } -------------------------------------------------------------------------------- /tasks/getAllMatches.js: -------------------------------------------------------------------------------- 1 | // getAllMatches.js 2 | 3 | 'use strict'; 4 | 5 | module.exports = function getAllMatches(mStr, mRegExp, mWithDetials) { 6 | mWithDetials = mWithDetials || false; 7 | let results = []; 8 | 9 | let match; 10 | while( match = mRegExp.exec(mStr)) { 11 | if(mWithDetials) { 12 | results.push(match); 13 | } else { 14 | results.push(match[0]); 15 | } 16 | 17 | } 18 | 19 | return results; 20 | } -------------------------------------------------------------------------------- /src/alfrid/Batch.js: -------------------------------------------------------------------------------- 1 | // Batch.js 2 | 3 | import GL from './GLTool'; 4 | 5 | class Batch { 6 | 7 | constructor(mMesh, mShader) { 8 | this._mesh = mMesh; 9 | this._shader = mShader; 10 | } 11 | 12 | 13 | // PUBLIC METHODS 14 | 15 | draw() { 16 | this._shader.bind(); 17 | GL.draw(this.mesh); 18 | } 19 | 20 | 21 | // GETTER AND SETTER 22 | 23 | get mesh() { return this._mesh; } 24 | 25 | get shader() { return this._shader; } 26 | } 27 | 28 | export default Batch; -------------------------------------------------------------------------------- /src/alfrid/utils/ExtensionsList.js: -------------------------------------------------------------------------------- 1 | // ExtensionsList.js 2 | 3 | export default [ 4 | 'EXT_shader_texture_lod', 5 | 'EXT_sRGB', 6 | 'EXT_frag_depth', 7 | 'OES_texture_float', 8 | 'OES_texture_half_float', 9 | 'OES_texture_float_linear', 10 | 'OES_texture_half_float_linear', 11 | 'OES_standard_derivatives', 12 | 'WEBGL_depth_texture', 13 | 'EXT_texture_filter_anisotropic', 14 | 'OES_vertex_array_object', 15 | 'ANGLE_instanced_arrays', 16 | 'WEBGL_draw_buffers' 17 | ]; -------------------------------------------------------------------------------- /tasks/checkExtension.js: -------------------------------------------------------------------------------- 1 | // checkExtension.js 2 | 3 | const path = require('path'); 4 | 5 | module.exports = function checkExtension(mFile, mExtensions) { 6 | if(mExtensions.length == 0) { 7 | return true; 8 | } 9 | 10 | 11 | let extensions; 12 | 13 | if(!mExtensions.concat) 14 | { 15 | extensions = [mExtensions]; 16 | } 17 | else 18 | { 19 | extensions = mExtensions.concat(); 20 | } 21 | 22 | const ext = path.extname(mFile).replace('.', ''); 23 | return mExtensions.indexOf(ext) > -1; 24 | } -------------------------------------------------------------------------------- /tasks/basic.vert: -------------------------------------------------------------------------------- 1 | // basic.vert 2 | 3 | precision highp float; 4 | attribute vec3 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | attribute vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | 12 | varying vec2 vTextureCoord; 13 | varying vec3 vNormal; 14 | 15 | void main(void) { 16 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0); 17 | vTextureCoord = aTextureCoord; 18 | vNormal = aNormal; 19 | } -------------------------------------------------------------------------------- /src/alfrid/post/PassBlur.js: -------------------------------------------------------------------------------- 1 | // PassBlur.js 2 | 3 | import PassVBlur from './PassVBlur'; 4 | import PassHBlur from './PassHBlur'; 5 | import PassMacro from './PassMacro'; 6 | 7 | class PassBlur extends PassMacro { 8 | constructor(mQuality = 9, mWidth, mHeight, mParams) { 9 | super(); 10 | const vBlur = new PassVBlur(mQuality, mWidth, mHeight, mParams); 11 | const hBlur = new PassHBlur(mQuality, mWidth, mHeight, mParams); 12 | 13 | this.addPass(vBlur); 14 | this.addPass(hBlur); 15 | } 16 | } 17 | 18 | export default PassBlur; 19 | -------------------------------------------------------------------------------- /src/alfrid/shaders/axis.vert: -------------------------------------------------------------------------------- 1 | // axis.vert 2 | 3 | #define SHADER_NAME BASIC_VERTEX 4 | 5 | precision highp float; 6 | attribute vec3 aVertexPosition; 7 | attribute vec3 aColor; 8 | attribute vec3 aNormal; 9 | 10 | uniform mat4 uModelMatrix; 11 | uniform mat4 uViewMatrix; 12 | uniform mat4 uProjectionMatrix; 13 | 14 | varying vec3 vColor; 15 | varying vec3 vNormal; 16 | 17 | void main(void) { 18 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0); 19 | vColor = aColor; 20 | vNormal = aNormal; 21 | } -------------------------------------------------------------------------------- /src/alfrid/helpers/View3D.js: -------------------------------------------------------------------------------- 1 | // View3D.js 2 | 3 | import Object3D from '../objects/Object3D'; 4 | import GLShader from '../GLShader'; 5 | import GL from '../GLTool'; 6 | 7 | class View3D extends Object3D { 8 | constructor(mStrVertex, mStrFrag) { 9 | super(); 10 | 11 | this._children = []; 12 | this.shader = new GLShader(mStrVertex, mStrFrag); 13 | this._init(); 14 | } 15 | 16 | 17 | // PROTECTED METHODS 18 | 19 | _init() { 20 | 21 | } 22 | 23 | // PUBLIC METHODS 24 | 25 | render() { 26 | } 27 | 28 | } 29 | 30 | 31 | export default View3D; -------------------------------------------------------------------------------- /src/alfrid/shaders/basic.vert: -------------------------------------------------------------------------------- 1 | // basic.vert 2 | 3 | #define SHADER_NAME BASIC_VERTEX 4 | 5 | precision highp float; 6 | attribute vec3 aVertexPosition; 7 | attribute vec2 aTextureCoord; 8 | attribute vec3 aNormal; 9 | 10 | uniform mat4 uModelMatrix; 11 | uniform mat4 uViewMatrix; 12 | uniform mat4 uProjectionMatrix; 13 | 14 | varying vec2 vTextureCoord; 15 | varying vec3 vNormal; 16 | 17 | void main(void) { 18 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0); 19 | vTextureCoord = aTextureCoord; 20 | vNormal = aNormal; 21 | } -------------------------------------------------------------------------------- /dev/shaders/cube.frag: -------------------------------------------------------------------------------- 1 | // cube.frag 2 | 3 | precision highp float; 4 | varying vec2 vTextureCoord; 5 | varying vec3 vNormal; 6 | varying vec3 vPosition; 7 | 8 | void main(void) { 9 | gl_FragData[0] = vec4(vPosition, 1.0); 10 | gl_FragData[1] = vec4(vTextureCoord, 0.0, 1.0); 11 | gl_FragData[2] = vec4(vNormal, 1.0); 12 | gl_FragData[3] = vec4(1.0, 1.0, 1.0, 1.0); 13 | gl_FragData[4] = vec4(1.0, 0.0, 0.0, 1.0); 14 | gl_FragData[5] = vec4(0.0, 1.0, 0.0, 1.0); 15 | gl_FragData[6] = vec4(0.0, 0.0, 1.0, 1.0); 16 | gl_FragData[7] = vec4(1.0, 1.0, 0.0, 1.0); 17 | } -------------------------------------------------------------------------------- /dev/shaders/cube.vert: -------------------------------------------------------------------------------- 1 | // cube.vert 2 | 3 | precision highp float; 4 | attribute vec3 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | attribute vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | 12 | varying vec2 vTextureCoord; 13 | varying vec3 vNormal; 14 | varying vec3 vPosition; 15 | 16 | void main(void) { 17 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0); 18 | vTextureCoord = aTextureCoord; 19 | vNormal = aNormal; 20 | vPosition = aVertexPosition; 21 | } -------------------------------------------------------------------------------- /dev/instance.vert: -------------------------------------------------------------------------------- 1 | // instance.vert 2 | 3 | precision highp float; 4 | attribute vec3 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | attribute vec3 aNormal; 7 | attribute vec3 aPosOffset; 8 | 9 | uniform mat4 uModelMatrix; 10 | uniform mat4 uViewMatrix; 11 | uniform mat4 uProjectionMatrix; 12 | 13 | varying vec2 vTextureCoord; 14 | varying vec3 vNormal; 15 | 16 | void main(void) { 17 | vec3 pos = aVertexPosition + aPosOffset; 18 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(pos, 1.0); 19 | vTextureCoord = aTextureCoord; 20 | vNormal = aNormal; 21 | } -------------------------------------------------------------------------------- /tasks/watch.js: -------------------------------------------------------------------------------- 1 | // watch.js 2 | 3 | const watch = require('chokidar').watch; 4 | const Emitter = require('events'); 5 | 6 | const ignores = [ 7 | 'node_modules/**', 8 | 'bower_components/**', 9 | '.git', 10 | '.DS_Store', 11 | ] 12 | 13 | module.exports = function(blob,opt) { 14 | 15 | opt = Object.assign({ 16 | ignored: ignores, 17 | ignoreInitial: true 18 | }, opt); 19 | 20 | const emitter = new Emitter(); 21 | const watcher = watch(blob,opt); 22 | watcher.on('all',(event, path) => { 23 | emitter.emit('all', event, path) 24 | }); 25 | return emitter; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /dev/bugs/TextureMinFilter.js: -------------------------------------------------------------------------------- 1 | // TextureMinFilter.js 2 | 3 | import alfrid from '../../src/alfrid'; 4 | import setupAlfrid from '../utils/setupAlfrid'; 5 | 6 | const { GL, BatchAxis, Geom } = alfrid; 7 | 8 | setupAlfrid({ignoreWebgl2:true}, {init, render, resize}).then((canvas)=> { 9 | console.log('canvas created:', canvas); 10 | }) 11 | .catch((err) => { 12 | console.log('Error :', err); 13 | }); 14 | 15 | 16 | 17 | function init(camera, orbitalControl) { 18 | // console.log('Init', GL); 19 | 20 | } 21 | 22 | function render() { 23 | // console.log('render', b); 24 | 25 | } 26 | 27 | function resize() { 28 | } -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchSky.js: -------------------------------------------------------------------------------- 1 | // BatchSky.js 2 | 3 | import Geom from '../Geom'; 4 | import GLShader from '../GLShader'; 5 | import Batch from '../Batch'; 6 | 7 | const vs = require('../shaders/sky.vert'); 8 | const fs = require('../shaders/copy.frag'); 9 | 10 | 11 | class BatchSky extends Batch { 12 | 13 | constructor(size = 50, seg = 24) { 14 | const mesh = Geom.sphere(size, seg, true); 15 | const shader = new GLShader(vs, fs); 16 | 17 | super(mesh, shader); 18 | } 19 | 20 | draw(texture) { 21 | this.shader.bind(); 22 | texture.bind(0); 23 | super.draw(); 24 | } 25 | } 26 | 27 | export default BatchSky; -------------------------------------------------------------------------------- /tasks/copy-file.js: -------------------------------------------------------------------------------- 1 | // copy-file.js 2 | 3 | 'use strict'; 4 | 5 | const fs = require('fs'); 6 | 7 | function copyFile(source, target, cb) { 8 | let cbCalled = false; 9 | 10 | const rd = fs.createReadStream(source); 11 | rd.on("error", function(err) { 12 | done(err); 13 | }); 14 | const wr = fs.createWriteStream(target); 15 | wr.on("error", function(err) { 16 | done(err); 17 | }); 18 | wr.on("close", function(ex) { 19 | done(); 20 | }); 21 | rd.pipe(wr); 22 | 23 | function done(err) { 24 | if (!cbCalled) { 25 | cb(err); 26 | cbCalled = true; 27 | } 28 | } 29 | } 30 | 31 | 32 | module.exports = copyFile; -------------------------------------------------------------------------------- /dev/shaders/floor.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec3 aVertexPosition; 5 | in vec2 aTextureCoord; 6 | in vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | uniform mat4 uShadowMatrix; 12 | 13 | out vec2 vTextureCoord; 14 | out vec3 vNormal; 15 | out vec4 vShadowCoord; 16 | 17 | void main(void) { 18 | vec4 wsPos = uModelMatrix * vec4(aVertexPosition, 1.0); 19 | gl_Position = uProjectionMatrix * uViewMatrix * wsPos; 20 | vTextureCoord = aTextureCoord; 21 | vNormal = aNormal; 22 | 23 | vShadowCoord = uShadowMatrix * wsPos; 24 | } -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchSkybox.js: -------------------------------------------------------------------------------- 1 | // BatchSkybox.js 2 | 3 | import Geom from '../Geom'; 4 | import GLShader from '../GLShader'; 5 | import Batch from '../Batch'; 6 | 7 | const vs = require('../shaders/skybox.vert'); 8 | const fs = require('../shaders/skybox.frag'); 9 | 10 | 11 | class BatchSkybox extends Batch { 12 | 13 | constructor(size = 20) { 14 | const mesh = Geom.skybox(size); 15 | const shader = new GLShader(vs, fs); 16 | 17 | super(mesh, shader); 18 | } 19 | 20 | draw(texture) { 21 | this.shader.bind(); 22 | texture.bind(0); 23 | super.draw(); 24 | } 25 | 26 | 27 | } 28 | 29 | 30 | export default BatchSkybox; -------------------------------------------------------------------------------- /src/alfrid/loaders/HDRLoader.js: -------------------------------------------------------------------------------- 1 | // HDRLoader.js 2 | 3 | 'use strict'; 4 | 5 | import BinaryLoader from './BinaryLoader'; 6 | import hdrParser from '../utils/HDRParser'; 7 | 8 | class HDRLoader extends BinaryLoader { 9 | constructor() { 10 | super(true); 11 | } 12 | 13 | parse(mArrayBuffer) { 14 | return hdrParser(mArrayBuffer); 15 | } 16 | 17 | _onLoaded() { 18 | const o = this.parse(this._req.response); 19 | if(this._callback) { 20 | this._callback(o); 21 | } 22 | } 23 | 24 | } 25 | 26 | 27 | HDRLoader.parse = function (mArrayBuffer) { 28 | return hdrParser(mArrayBuffer); 29 | }; 30 | 31 | export default HDRLoader; -------------------------------------------------------------------------------- /src/alfrid/shaders/sky.vert: -------------------------------------------------------------------------------- 1 | // sky.vert 2 | 3 | precision highp float; 4 | attribute vec3 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | attribute vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | 12 | varying vec2 vTextureCoord; 13 | varying vec3 vNormal; 14 | 15 | void main(void) { 16 | mat4 matView = uViewMatrix; 17 | matView[3][0] = 0.0; 18 | matView[3][1] = 0.0; 19 | matView[3][2] = 0.0; 20 | 21 | gl_Position = uProjectionMatrix * matView * uModelMatrix * vec4(aVertexPosition, 1.0); 22 | vTextureCoord = aTextureCoord; 23 | vNormal = aNormal; 24 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .settings 3 | .project 4 | _notes 5 | node_modules 6 | cinder/xcode/build 7 | *.scssc 8 | npm-debug.log 9 | 10 | *.sublime-project 11 | *.sublime-workspace 12 | 13 | # Global Ignores 14 | *.tmproj 15 | *.svn 16 | .svn 17 | .pyc 18 | *.pyc 19 | deploy/gaesessions/__init__.pyc 20 | .svn 21 | *.swp 22 | 23 | # fileMerge 24 | *.orig 25 | 26 | # OS specific files 27 | 28 | # OS X 29 | 30 | *.DS_Store 31 | 32 | # Files that might appear on external disk 33 | .Spotlight-V100 34 | .Trashes 35 | 36 | # Windows image file caches 37 | Thumbs.db 38 | 39 | # Folder config file 40 | Desktop.ini 41 | .sass-cache 42 | compass.txt 43 | note.txt -------------------------------------------------------------------------------- /src/alfrid/utils/getAndApplyExtension.js: -------------------------------------------------------------------------------- 1 | // VertexArrayObject.js 2 | 3 | export default function getAndApplyExtension(gl, name) { 4 | const ext = gl.getExtension(name); 5 | if (!ext) { 6 | return false; 7 | } 8 | const suffix = name.split('_')[0]; 9 | const suffixRE = new RegExp(`${suffix}$`); 10 | 11 | for (const key in ext) { 12 | const val = ext[key]; 13 | if (typeof(val) === 'function') { 14 | const unsuffixedKey = key.replace(suffixRE, ''); 15 | if (key.substring) { 16 | gl[unsuffixedKey] = ext[key].bind(ext); 17 | // console.log('Replacing :', key, '=>', unsuffixedKey); 18 | } 19 | } 20 | } 21 | 22 | return true; 23 | } -------------------------------------------------------------------------------- /src/alfrid/utils/ShaderLibs.js: -------------------------------------------------------------------------------- 1 | // ShaderLbs.js 2 | 3 | 'use strict' 4 | 5 | import simpleColorFrag from '../shaders/simpleColor.frag' 6 | import bigTriangleVert from '../shaders/bigTriangle.vert' 7 | import generalVert from '../shaders/general.vert' 8 | import copyFrag from '../shaders/copy.frag' 9 | import basicVert from '../shaders/basic.vert' 10 | import skyboxVert from '../shaders/skybox.vert' 11 | import skyboxFrag from '../shaders/skybox.frag' 12 | 13 | const ShaderLibs = { 14 | simpleColorFrag, 15 | bigTriangleVert, 16 | generalVert, 17 | copyFrag, 18 | basicVert, 19 | skyboxVert, 20 | skyboxFrag 21 | } 22 | 23 | export default ShaderLibs 24 | -------------------------------------------------------------------------------- /src/alfrid/utils/getFloat.js: -------------------------------------------------------------------------------- 1 | // getFloat.js 2 | 3 | import GL from '../GLTool' 4 | 5 | let hasChecked = false 6 | let _float 7 | 8 | function checkFloat () { 9 | if (GL.webgl2) { 10 | return GL.gl.FLOAT 11 | } else { 12 | const extFloat = GL.getExtension('OES_texture_float') 13 | if (extFloat) { 14 | return GL.gl.FLOAT 15 | } else { 16 | console.warn('USING FLOAT BUT OES_texture_float NOT SUPPORTED') 17 | return GL.gl.UNSIGNED_BYTE 18 | } 19 | } 20 | 21 | hasChecked = true 22 | }; 23 | 24 | export default function () { 25 | if (!hasChecked) { 26 | _float = checkFloat() 27 | } 28 | 29 | return _float 30 | } 31 | -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchCopy.js: -------------------------------------------------------------------------------- 1 | // BatchCopy.js 2 | 3 | import Geom from '../Geom'; 4 | import GLShader from '../GLShader'; 5 | import Batch from '../Batch'; 6 | 7 | import vs from '../shaders/bigTriangle.vert'; 8 | import fs from '../shaders/copy.frag'; 9 | 10 | class BatchCopy extends Batch { 11 | 12 | constructor() { 13 | const mesh = Geom.bigTriangle(); 14 | const shader = new GLShader(vs, fs); 15 | super(mesh, shader); 16 | 17 | shader.bind(); 18 | shader.uniform('texture', 'uniform1i', 0); 19 | } 20 | 21 | 22 | draw(texture) { 23 | this.shader.bind(); 24 | texture.bind(0); 25 | super.draw(); 26 | } 27 | 28 | } 29 | 30 | export default BatchCopy; -------------------------------------------------------------------------------- /tasks/dev.js: -------------------------------------------------------------------------------- 1 | // dev.js 2 | 3 | const budo = require('budo'); 4 | const babelify = require('babelify'); 5 | const browserifyShader = require('browserify-shader'); 6 | 7 | console.log('Arguments :', process.argv); 8 | 9 | const targetFile = process.argv[2]; 10 | console.log('Target File :', targetFile); 11 | 12 | if(!targetFile) { 13 | console.log('Target file missing'); 14 | process.exit(); 15 | } 16 | 17 | 18 | budo(targetFile, { 19 | live: true, 20 | stream: process.stdout, // log to stdout 21 | browserify: { 22 | transform: [babelify, browserifyShader] // use ES6 23 | } 24 | }).on('connect', function(ev) { 25 | //... 26 | console.log(`Server UP at ${ev}`); 27 | }); -------------------------------------------------------------------------------- /dev/shaders/shadowMap300es.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec3 aVertexPosition; 5 | in vec2 aTextureCoord; 6 | in vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | uniform mat4 uShadowMatrix; 12 | 13 | out vec2 vTextureCoord; 14 | out vec3 vNormal; 15 | out vec3 vPosition; 16 | out vec4 vShadowCoord; 17 | 18 | void main(void) { 19 | vec4 vWsPos = uModelMatrix * vec4(aVertexPosition, 1.0); 20 | gl_Position = uProjectionMatrix * uViewMatrix * vWsPos; 21 | 22 | vTextureCoord = aTextureCoord; 23 | vNormal = aNormal; 24 | vPosition = aVertexPosition; 25 | vShadowCoord = uShadowMatrix * vWsPos; 26 | } -------------------------------------------------------------------------------- /tasks/find-folder-promise.js: -------------------------------------------------------------------------------- 1 | // find-folder-promise.js 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const isDir = require('./isDirectory'); 6 | 7 | const contains = (mDir, mTarget) => new Promise((resolve, reject) => { 8 | if (mDir.indexOf(mTarget) > -1) { 9 | resolve(mDir); 10 | } else { 11 | if (!isDir(mDir)) { 12 | reject(); 13 | return; 14 | } 15 | 16 | const paths = fs.readdirSync(mDir); 17 | paths.forEach((subPath)=> { 18 | const _subPath = path.resolve(mDir, subPath); 19 | if(isDir(_subPath)) { 20 | return contains(_subPath, mTarget).then(resolve, reject); 21 | } 22 | }); 23 | } 24 | 25 | }); 26 | 27 | 28 | module.exports = contains; -------------------------------------------------------------------------------- /src/alfrid/utils/getHalfFloat.js: -------------------------------------------------------------------------------- 1 | // getHalfFloat.js 2 | 3 | import GL from '../GLTool'; 4 | 5 | let hasChecked = false; 6 | let halfFloat; 7 | 8 | function checkHalfFloat() { 9 | if(GL.webgl2) { 10 | return GL.gl.HALF_FLOAT; 11 | } else { 12 | const extHalfFloat = GL.getExtension('OES_texture_half_float'); 13 | if(extHalfFloat) { 14 | return extHalfFloat.HALF_FLOAT_OES; 15 | } else { 16 | console.warn('USING HALF FLOAT BUT OES_texture_half_float NOT SUPPORTED'); 17 | return GL.gl.UNSIGNED_BYTE; 18 | } 19 | } 20 | 21 | hasChecked = true; 22 | }; 23 | 24 | export default function () { 25 | if(!hasChecked) { 26 | halfFloat = checkHalfFloat(); 27 | } 28 | 29 | return halfFloat; 30 | } -------------------------------------------------------------------------------- /src/alfrid/FboArray.js: -------------------------------------------------------------------------------- 1 | // FboArray.js 2 | 3 | import FrameBuffer from './FrameBuffer' 4 | 5 | class FboArray { 6 | constructor (mNum, width, height, params = {}, mNumTargets = 1) { 7 | this._fbos = [] 8 | 9 | for (let i = 0; i < mNum; i++) { 10 | const fbo = new FrameBuffer(width, height, params, mNumTargets) 11 | this._fbos.push(fbo) 12 | } 13 | } 14 | 15 | swap () { 16 | const a = this._fbos.shift() 17 | this._fbos.push(a) 18 | } 19 | 20 | get read () { 21 | return this._fbos[this._fbos.length - 1] 22 | } 23 | 24 | get write () { 25 | return this._fbos[0] 26 | } 27 | 28 | get all () { 29 | return this._fbos 30 | } 31 | } 32 | 33 | export default FboArray 34 | -------------------------------------------------------------------------------- /src/alfrid/shaders/dotsPlane.vert: -------------------------------------------------------------------------------- 1 | // basic.vert 2 | 3 | #define SHADER_NAME DOTS_PLANE_VERTEX 4 | 5 | precision highp float; 6 | attribute vec3 aVertexPosition; 7 | attribute vec3 aNormal; 8 | 9 | uniform mat4 uModelMatrix; 10 | uniform mat4 uViewMatrix; 11 | uniform mat4 uProjectionMatrix; 12 | uniform vec2 viewport; 13 | 14 | varying vec3 vNormal; 15 | 16 | const float radius = 0.008; 17 | 18 | void main(void) { 19 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition + aNormal * 0.000001, 1.0); 20 | // gl_PointSize = 1.0; 21 | vNormal = aNormal; 22 | 23 | float distOffset = viewport.y * uProjectionMatrix[1][1] * radius / gl_Position.w; 24 | gl_PointSize = distOffset; 25 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Alfrid 2 | [![npm version](https://badge.fury.io/js/alfrid.svg)](https://badge.fury.io/js/alfrid) 3 | ====== 4 | 5 | A WebGL tool set 6 | 7 | ### Default Vertex Attributes 8 | - aVertexPosition 9 | - aTextureCoord 10 | - aNormal 11 | 12 | ### Default Matrix Uniforms 13 | - uModelMatrix 14 | - uViewMatrix 15 | - uProjectionMatrix 16 | - uNormalMatrix 17 | - uModelViewMatrixInverse 18 | 19 | 20 | ### Installation 21 | 22 | ``` 23 | npm install; 24 | ``` 25 | 26 | ### Development 27 | 28 | ``` 29 | npm run dev; 30 | ``` 31 | 32 | 33 | ### Build 34 | 35 | ``` 36 | npm run build; 37 | ``` 38 | 39 | [npm-image]: https://img.shields.io/npm/v/alfrid.svg?style=flat-square 40 | [npm-url]: https://www.npmjs.com/package/alfrid -------------------------------------------------------------------------------- /dev/shaders/shadowMap.vert: -------------------------------------------------------------------------------- 1 | // cube.vert 2 | 3 | precision highp float; 4 | attribute vec3 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | attribute vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | uniform mat4 uShadowMatrix; 12 | 13 | varying vec2 vTextureCoord; 14 | varying vec3 vNormal; 15 | varying vec3 vPosition; 16 | varying vec4 vShadowCoord; 17 | 18 | void main(void) { 19 | vec4 vWsPos = uModelMatrix * vec4(aVertexPosition, 1.0); 20 | gl_Position = uProjectionMatrix * uViewMatrix * vWsPos; 21 | 22 | vTextureCoord = aTextureCoord; 23 | vNormal = aNormal; 24 | vPosition = aVertexPosition; 25 | vShadowCoord = uShadowMatrix * vWsPos; 26 | } -------------------------------------------------------------------------------- /tasks/html.js: -------------------------------------------------------------------------------- 1 | // html.js 2 | 3 | const fs = require('fs-extra') 4 | 5 | const env = process.env.NODE_ENV 6 | const isProd = env === 'production' 7 | 8 | const processTemplate = (str) => new Promise((resolve, reject) => { 9 | if (isProd) { 10 | str = str.replace(/{{dev.*}}/g, '') 11 | } else { 12 | str = str.replace(/{{dev/g, '') 13 | str = str.replace(/}}/g, '') 14 | } 15 | 16 | resolve(str) 17 | }) 18 | 19 | const writeTemplate = (str) => new Promise((resolve, reject) => { 20 | fs.writeFile('./dist/index.html', str, 'utf8') 21 | }) 22 | 23 | fs.readFile('./src/html/index-template.html', 'utf8') 24 | .then(processTemplate) 25 | .then(writeTemplate) 26 | .catch(err => { 27 | console.log('Error :', err) 28 | }) 29 | -------------------------------------------------------------------------------- /src/alfrid/shaders/skybox.vert: -------------------------------------------------------------------------------- 1 | // basic.vert 2 | 3 | #define SHADER_NAME SKYBOX_VERTEX 4 | 5 | precision highp float; 6 | attribute vec3 aVertexPosition; 7 | attribute vec2 aTextureCoord; 8 | attribute vec3 aNormal; 9 | 10 | uniform mat4 uModelMatrix; 11 | uniform mat4 uViewMatrix; 12 | uniform mat4 uProjectionMatrix; 13 | 14 | varying vec2 vTextureCoord; 15 | varying vec3 vVertex; 16 | varying vec3 vNormal; 17 | 18 | void main(void) { 19 | mat4 matView = uViewMatrix; 20 | matView[3][0] = 0.0; 21 | matView[3][1] = 0.0; 22 | matView[3][2] = 0.0; 23 | 24 | gl_Position = uProjectionMatrix * matView * uModelMatrix * vec4(aVertexPosition, 1.0); 25 | vTextureCoord = aTextureCoord; 26 | 27 | vVertex = aVertexPosition; 28 | vNormal = aNormal; 29 | } -------------------------------------------------------------------------------- /src/alfrid/utils/exposeAttributes.js: -------------------------------------------------------------------------------- 1 | // exposeAttributes.js 2 | 3 | import GL from '../GLTool' 4 | import WebglConst from './WebglConst' 5 | import WebglNumber from './WebglNumber' 6 | 7 | const exposeAttributes = function () { 8 | for (const s in WebglConst) { 9 | if (!GL[s]) { 10 | GL[s] = WebglConst[s] 11 | } else { 12 | if (s !== 'FLOAT') console.log('already exist : ', s) 13 | } 14 | } 15 | 16 | if (GL.webgl2) { 17 | const check = /^[^a-z]*$/ 18 | for (const s in GL.gl) { 19 | if (check.test(s) && s.indexOf('FLOAT') === -1) { 20 | GL[s] = GL.gl[s] 21 | WebglConst[s] = GL.gl[s] 22 | WebglNumber[GL[s]] = s 23 | } 24 | } 25 | } 26 | } 27 | 28 | export default exposeAttributes 29 | -------------------------------------------------------------------------------- /src/alfrid/shaders/general.vert: -------------------------------------------------------------------------------- 1 | // generalWithNormal.vert 2 | 3 | #define SHADER_NAME GENERAL_VERTEX 4 | 5 | precision highp float; 6 | attribute vec3 aVertexPosition; 7 | attribute vec2 aTextureCoord; 8 | attribute vec3 aNormal; 9 | 10 | uniform mat4 uModelMatrix; 11 | uniform mat4 uViewMatrix; 12 | uniform mat4 uProjectionMatrix; 13 | uniform mat3 uNormalMatrix; 14 | 15 | uniform vec3 position; 16 | uniform vec3 scale; 17 | 18 | varying vec2 vTextureCoord; 19 | varying vec3 vNormal; 20 | 21 | void main(void) { 22 | vec3 pos = aVertexPosition * scale; 23 | pos += position; 24 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(pos, 1.0); 25 | 26 | vTextureCoord = aTextureCoord; 27 | vNormal = normalize(uNormalMatrix * aNormal); 28 | } -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchFXAA.js: -------------------------------------------------------------------------------- 1 | // BatchFXAA.js 2 | import GL from '../GLTool'; 3 | import Geom from '../Geom'; 4 | import GLShader from '../GLShader'; 5 | import Batch from '../Batch'; 6 | 7 | const vs = require('../shaders/bigTriangle.vert'); 8 | const fs = require('../shaders/fxaa.frag'); 9 | 10 | class BatchFXAA extends Batch { 11 | 12 | constructor() { 13 | const mesh = Geom.bigTriangle(); 14 | const shader = new GLShader(vs, fs); 15 | super(mesh, shader); 16 | 17 | shader.bind(); 18 | shader.uniform('texture', 'uniform1i', 0); 19 | } 20 | 21 | 22 | draw(texture) { 23 | this.shader.bind(); 24 | texture.bind(0); 25 | this.shader.uniform('uResolution', 'vec2', [1/GL.width, 1/GL.height]); 26 | super.draw(); 27 | } 28 | 29 | } 30 | 31 | export default BatchFXAA; -------------------------------------------------------------------------------- /src/alfrid/loaders/BinaryLoader.js: -------------------------------------------------------------------------------- 1 | // BinaryLoader.js 2 | 3 | class BinaryLoader { 4 | 5 | constructor(isArrayBuffer = false) { 6 | this._req = new XMLHttpRequest(); 7 | this._req.addEventListener('load', (e)=>this._onLoaded(e)); 8 | this._req.addEventListener('progress', (e)=>this._onProgress(e)); 9 | if(isArrayBuffer) { 10 | this._req.responseType = 'arraybuffer'; 11 | } 12 | } 13 | 14 | 15 | load(url, callback) { 16 | console.log('Loading : ', url); 17 | this._callback = callback; 18 | 19 | this._req.open('GET', url); 20 | this._req.send(); 21 | } 22 | 23 | 24 | _onLoaded() { 25 | this._callback(this._req.response); 26 | } 27 | 28 | _onProgress(/* e*/) { 29 | // console.log('on Progress:', (e.loaded/e.total*100).toFixed(2)); 30 | } 31 | } 32 | 33 | export default BinaryLoader; -------------------------------------------------------------------------------- /src/alfrid/shaders/blur5.frag: -------------------------------------------------------------------------------- 1 | // blur5.frag 2 | // source : https://github.com/Jam3/glsl-fast-gaussian-blur 3 | 4 | #define SHADER_NAME BLUR_5 5 | 6 | precision highp float; 7 | varying vec2 vTextureCoord; 8 | uniform sampler2D texture; 9 | uniform vec2 uDirection; 10 | uniform vec2 uResolution; 11 | 12 | vec4 blur5(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 13 | vec4 color = vec4(0.0); 14 | vec2 off1 = vec2(1.3333333333333333) * direction; 15 | color += texture2D(image, uv) * 0.29411764705882354; 16 | color += texture2D(image, uv + (off1 / resolution)) * 0.35294117647058826; 17 | color += texture2D(image, uv - (off1 / resolution)) * 0.35294117647058826; 18 | return color; 19 | } 20 | 21 | 22 | void main(void) { 23 | gl_FragColor = blur5(texture, vTextureCoord, uResolution, uDirection); 24 | } -------------------------------------------------------------------------------- /src/alfrid/post/PassBlurBase.js: -------------------------------------------------------------------------------- 1 | // PassBlurBase.js 2 | 3 | import GL from '../GLTool'; 4 | import Pass from './Pass'; 5 | 6 | const fsBlur5 = require('../shaders/blur5.frag'); 7 | const fsBlur9 = require('../shaders/blur9.frag'); 8 | const fsBlur13 = require('../shaders/blur13.frag'); 9 | 10 | class PassBlurBase extends Pass { 11 | constructor(mQuality = 9, mDirection, mWidth, mHeight, mParams = {}) { 12 | let fs; 13 | switch(mQuality) { 14 | case 5: 15 | default: 16 | fs = fsBlur5; 17 | break; 18 | case 9 : 19 | fs = fsBlur9; 20 | break; 21 | case 13 : 22 | fs = fsBlur13; 23 | break; 24 | 25 | } 26 | super(fs, mWidth, mHeight, mParams); 27 | this.uniform('uDirection', mDirection); 28 | this.uniform('uResolution', [GL.width, GL.height]); 29 | } 30 | } 31 | 32 | export default PassBlurBase; 33 | -------------------------------------------------------------------------------- /src/alfrid/cameras/CameraOrtho.js: -------------------------------------------------------------------------------- 1 | // CameraOrtho.js 2 | 3 | import Camera from './Camera'; 4 | import { vec3 } from 'gl-matrix'; 5 | 6 | class CameraOrtho extends Camera { 7 | constructor() { 8 | super(); 9 | 10 | const eye = vec3.clone([0, 0, 5]); 11 | const center = vec3.create(); 12 | const up = vec3.clone([0, 1, 0]); 13 | this.lookAt(eye, center, up); 14 | this.ortho(-1, 1, 1, -1); 15 | } 16 | 17 | 18 | setBoundary(left, right, top, bottom, near=0.1, far=100) { 19 | this.ortho(left, right, top, bottom, near, far); 20 | } 21 | 22 | 23 | ortho(left, right, top, bottom, near=0.1, far=100) { 24 | this.left = left; 25 | this.right = right; 26 | this.top = top; 27 | this.bottom = bottom; 28 | mat4.ortho(this._projection, left, right, bottom, top, near, far); 29 | } 30 | 31 | } 32 | 33 | 34 | export default CameraOrtho; -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchBall.js: -------------------------------------------------------------------------------- 1 | // BatchBall.js 2 | 3 | import Geom from '../Geom'; 4 | import GLShader from '../GLShader'; 5 | import Batch from '../Batch'; 6 | 7 | import vs from '../shaders/general.vert'; 8 | import fs from '../shaders/simpleColor.frag'; 9 | 10 | class BatchBall extends Batch { 11 | 12 | constructor() { 13 | const mesh = Geom.sphere(1, 24); 14 | const shader = new GLShader(vs, fs); 15 | super(mesh, shader); 16 | } 17 | 18 | 19 | draw(position = [0, 0, 0], scale = [1, 1, 1], color = [1, 1, 1], opacity = 1) { 20 | this.shader.bind(); 21 | this.shader.uniform('position', 'uniform3fv', position); 22 | this.shader.uniform('scale', 'uniform3fv', scale); 23 | this.shader.uniform('color', 'uniform3fv', color); 24 | this.shader.uniform('opacity', 'uniform1f', opacity); 25 | super.draw(); 26 | } 27 | 28 | } 29 | 30 | export default BatchBall; -------------------------------------------------------------------------------- /src/alfrid/shaders/blur9.frag: -------------------------------------------------------------------------------- 1 | // blur9.frag 2 | // source : https://github.com/Jam3/glsl-fast-gaussian-blur 3 | 4 | #define SHADER_NAME BLUR_9 5 | 6 | precision highp float; 7 | varying vec2 vTextureCoord; 8 | uniform sampler2D texture; 9 | uniform vec2 uDirection; 10 | uniform vec2 uResolution; 11 | 12 | vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 13 | vec4 color = vec4(0.0); 14 | vec2 off1 = vec2(1.3846153846) * direction; 15 | vec2 off2 = vec2(3.2307692308) * direction; 16 | color += texture2D(image, uv) * 0.2270270270; 17 | color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; 18 | color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; 19 | color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; 20 | color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; 21 | return color; 22 | } 23 | 24 | 25 | void main(void) { 26 | gl_FragColor = blur9(texture, vTextureCoord, uResolution, uDirection); 27 | } -------------------------------------------------------------------------------- /dev/main.js: -------------------------------------------------------------------------------- 1 | // main.js 2 | import './global.scss' 3 | import quickSetup from './utils/quickSetup' 4 | import AssetsLoader from 'assets-loader' 5 | 6 | function render () { 7 | } 8 | 9 | quickSetup(render) 10 | .then((o) => { 11 | const assets = [ 12 | { id: 'image', url: 'assets/img/test.jpg' }, 13 | { id: 'image1', url: 'assets/img/test1.jpg' }, 14 | { id: 'image2', url: 'assets/img/test2.jpg' }, 15 | { id: 'hdr', url: 'assets/img/singleLight.hdr', type: 'binary' } 16 | ] 17 | 18 | new AssetsLoader({ 19 | assets 20 | }).on('error', (err) => { 21 | console.log('Error loading :', err) 22 | }).on('complete', (o) => { 23 | _onAssetsLoaded(o) 24 | }).start() 25 | }) 26 | 27 | function _onAssetsLoaded (o) { 28 | console.table(o) 29 | window.assets = o 30 | } 31 | 32 | function getAsset (id) { 33 | const o = assets.find(a => { 34 | return a.id === id 35 | }) 36 | 37 | if (!o) { 38 | return null 39 | } 40 | return o.file 41 | } 42 | -------------------------------------------------------------------------------- /tasks/find-folder.js: -------------------------------------------------------------------------------- 1 | // find-folder.js 2 | 3 | 'use strict'; 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const isDir = require('./isDirectory'); 8 | 9 | function contains(mDir, mTarget, mCallback) { 10 | fs.readdir(mDir, (err, files) => { 11 | if(files.indexOf(mTarget) > -1) { 12 | // mCallback(`${mDir}/${mTarget}`); 13 | mCallback(path.resolve(mDir, mTarget)); 14 | } else { 15 | 16 | const onDir = (mSubDir) => { 17 | if(mSubDir !== null) { 18 | mCallback(mSubDir); 19 | } 20 | } 21 | 22 | const folders = files.filter((a)=> { 23 | const subPath = path.resolve(mDir, a); 24 | return isDir(subPath); 25 | }); 26 | 27 | if(folders.length == 0) { 28 | mCallback(null, mTarget); 29 | } else { 30 | folders.forEach((f)=> { 31 | const subPath = path.resolve(mDir, f); 32 | if(isDir(subPath)) { 33 | contains(subPath, mTarget, onDir); 34 | } 35 | }); 36 | } 37 | } 38 | }); 39 | } 40 | 41 | module.exports = contains; -------------------------------------------------------------------------------- /tasks/html-watch.js: -------------------------------------------------------------------------------- 1 | // html-watch.js 2 | const watcher = require('./watch'); 3 | const path = require('path'); 4 | const fs = require('fs-extra'); 5 | 6 | const env = process.env.NODE_ENV; 7 | const isProd = env === 'production'; 8 | const PATH_SRC = path.resolve('./src/html'); 9 | 10 | const watcherViews = watcher([PATH_SRC]); 11 | 12 | 13 | const processTemplate = (str) => new Promise((resolve, reject) => { 14 | if(isProd) { 15 | str = str.replace(/{{dev.*}}/g, ''); 16 | } else { 17 | str = str.replace(/{{dev/g, ''); 18 | str = str.replace(/}}/g, ''); 19 | } 20 | 21 | resolve(str); 22 | }); 23 | 24 | const writeTemplate = (str) => new Promise((resolve, reject) => { 25 | fs.writeFile('./dist/index.html', str, 'utf8'); 26 | }); 27 | 28 | watcherViews.on('all', (event, file) => { 29 | if(file.indexOf('.html') === -1) return; 30 | 31 | fs.readFile(file, 'utf8') 32 | .then( processTemplate ) 33 | .then( writeTemplate ) 34 | .catch(err=> { 35 | console.log('Error :', err); 36 | }); 37 | }); -------------------------------------------------------------------------------- /dev/features/camera-ortho.js: -------------------------------------------------------------------------------- 1 | // camera-ortho.js 2 | import '../global.scss' 3 | import quickSetup from '../utils/quickSetup' 4 | import alfrid, { GL } from 'src/alfrid' 5 | 6 | let shader, mesh, cameraOrtho, ball 7 | 8 | quickSetup(render).then((o) => init(o)).catch(err => { 9 | console.log('Error :', err) 10 | }) 11 | 12 | function init (o) { 13 | console.log('Init', o) 14 | 15 | const w = GL.width / 2 16 | const h = GL.height / 2 17 | cameraOrtho = new alfrid.CameraOrtho() 18 | cameraOrtho.ortho(-w, w, h, -h, 0.1, 200) 19 | // cameraOrtho.lookAt([0, 0, 3], [0, 0, 0], [0, 1, 0]); 20 | 21 | shader = new alfrid.GLShader() 22 | mesh = alfrid.Geom.plane(200, 200, 1) 23 | ball = new alfrid.BatchBall() 24 | } 25 | 26 | function render () { 27 | GL.clear(0, 0, 0, 1) 28 | 29 | GL.setMatrices(cameraOrtho) 30 | shader.bind() 31 | GL.draw(mesh) 32 | 33 | const s = 10 34 | ball.draw([100, 100, 0], [s, s, s], [1, 1, 1]) 35 | ball.draw([-100, 100, 0], [s, s, s], [1, 1, 1]) 36 | ball.draw([0, -100, 0], [s, s, s], [1, 1, 1]) 37 | } 38 | -------------------------------------------------------------------------------- /src/alfrid/cameras/CameraCube.js: -------------------------------------------------------------------------------- 1 | // CameraCube.js 2 | 3 | 'use strict'; 4 | 5 | import CameraPerspective from './CameraPerspective'; 6 | import { vec3 } from 'gl-matrix'; 7 | 8 | const CAMERA_SETTINGS = [ 9 | [vec3.fromValues(0, 0, 0), vec3.fromValues(1, 0, 0), vec3.fromValues(0, -1, 0)], 10 | [vec3.fromValues(0, 0, 0), vec3.fromValues(-1, 0, 0), vec3.fromValues(0, -1, 0)], 11 | [vec3.fromValues(0, 0, 0), vec3.fromValues(0, 1, 0), vec3.fromValues(0, 0, 1)], 12 | [vec3.fromValues(0, 0, 0), vec3.fromValues(0, -1, 0), vec3.fromValues(0, 0, -1)], 13 | [vec3.fromValues(0, 0, 0), vec3.fromValues(0, 0, 1), vec3.fromValues(0, -1, 0)], 14 | [vec3.fromValues(0, 0, 0), vec3.fromValues(0, 0, -1), vec3.fromValues(0, -1, 0)] 15 | ]; 16 | 17 | class CameraCube extends CameraPerspective { 18 | 19 | constructor() { 20 | super(); 21 | 22 | this.setPerspective(Math.PI / 2, 1, 0.1, 1000); 23 | } 24 | 25 | 26 | face(mIndex) { 27 | const o = CAMERA_SETTINGS[mIndex]; 28 | this.lookAt(o[0], o[1], o[2]); 29 | } 30 | } 31 | 32 | 33 | export default CameraCube; -------------------------------------------------------------------------------- /src/alfrid/helpers/ShaderUtils.js: -------------------------------------------------------------------------------- 1 | // ShaderUtils.js 2 | 3 | const ShaderUtils = { 4 | 5 | }; 6 | 7 | const getUniformType = function (mValue) { 8 | if(mValue.length) { 9 | return `vec${mValue.length}`; 10 | } else { 11 | return 'float'; 12 | } 13 | }; 14 | 15 | ShaderUtils.addUniforms = function (mShader, mObjUniforms) { 16 | 17 | let strUniforms = ''; 18 | for(const uniformName in mObjUniforms) { 19 | const uniformValue = mObjUniforms[uniformName]; 20 | const uniformType = getUniformType(uniformValue); 21 | 22 | strUniforms += `uniform ${uniformType} ${uniformName};\n`; 23 | } 24 | 25 | mShader = mShader.replace('{{UNIFORMS}}', strUniforms); 26 | 27 | return mShader; 28 | }; 29 | 30 | 31 | ShaderUtils.bindUniforms = function (mShader, mObjUniforms) { 32 | 33 | for(const uniformName in mObjUniforms) { 34 | const uniformValue = mObjUniforms[uniformName]; 35 | const uniformType = getUniformType(uniformValue); 36 | mShader.uniform(uniformName, uniformType, uniformValue); 37 | } 38 | 39 | }; 40 | 41 | 42 | export default ShaderUtils; -------------------------------------------------------------------------------- /src/alfrid/loaders/ColladaParser.js: -------------------------------------------------------------------------------- 1 | // ColladaParser.js 2 | 3 | import parser from 'collada-parser'; 4 | import Mesh from '../Mesh'; 5 | 6 | 7 | const generateMesh = function (meshes) { 8 | const caches = {}; 9 | 10 | meshes.forEach((mesh)=> { 11 | const { vertices, normals, coords, triangles, name } = mesh.mesh; 12 | if(!caches[name]) { 13 | const glMesh = new Mesh() 14 | .bufferFlattenData(vertices, 'aVertexPosition', 3) 15 | .bufferFlattenData(coords, 'aTextureCoord', 2) 16 | .bufferFlattenData(normals, 'aNormal', 3) 17 | .bufferIndex(triangles); 18 | 19 | caches[name] = glMesh; 20 | } 21 | 22 | mesh.glMesh = caches[name]; 23 | }); 24 | }; 25 | 26 | const parse = function (mData) { 27 | const meshes = parser.parse(mData); 28 | generateMesh(meshes); 29 | 30 | return meshes; 31 | }; 32 | 33 | const load = function (mPath, mCallback) { 34 | parser.load(mPath, (meshes)=> { 35 | generateMesh(meshes); 36 | mCallback(meshes); 37 | }); 38 | }; 39 | 40 | const ColladaParser = { 41 | parse, 42 | load 43 | }; 44 | 45 | 46 | export default ColladaParser; -------------------------------------------------------------------------------- /dev/features/scene-graph.js: -------------------------------------------------------------------------------- 1 | // scene-graph.js 2 | import '../global.scss'; 3 | import quickSetup from '../utils/quickSetup'; 4 | import alfrid, { GL, WebglNumber, Object3D, View3D, Scheduler } from 'src/alfrid'; 5 | import ViewCube from './classes/ViewCube'; 6 | 7 | let shader, fbo, mesh, container, child, count = 0; 8 | let vCube; 9 | 10 | quickSetup(render).then((o)=>init(o)).catch(err=>{ 11 | console.log('Error :', err); 12 | }); 13 | 14 | 15 | function init(o) { 16 | container = new Object3D(); 17 | child = new Object3D(); 18 | vCube = new ViewCube(); 19 | container.addChild(child); 20 | container.addChild(vCube); 21 | 22 | shader = new alfrid.GLShader(); 23 | const s = .2; 24 | mesh = alfrid.Geom.cube(s, s, s); 25 | } 26 | 27 | 28 | function render() { 29 | const r = 1; 30 | 31 | container.x = Math.cos(Scheduler.deltaTime) * r; 32 | container.y = Math.sin(-Scheduler.deltaTime) * r; 33 | // child.z = Math.sin(-Scheduler.deltaTime) * Math.cos(-Scheduler.deltaTime) * r; 34 | // GL.rotate(child.matrix); 35 | 36 | // shader.bind(); 37 | // GL.draw(mesh); 38 | 39 | vCube.render(); 40 | } 41 | -------------------------------------------------------------------------------- /src/alfrid/post/Pass.js: -------------------------------------------------------------------------------- 1 | // Pass.js 2 | 3 | import GLShader from '../GLShader'; 4 | import FrameBuffer from '../FrameBuffer'; 5 | import ShaderLibs from '../utils/ShaderLibs'; 6 | 7 | class Pass { 8 | constructor(mSource, mWidth = 0, mHeight = 0, mParams = {}) { 9 | this.shader = new GLShader(ShaderLibs.bigTriangleVert, mSource); 10 | 11 | this._width = mWidth; 12 | this._height = mHeight; 13 | this._uniforms = {}; 14 | this._hasOwnFbo = this._width > 0 && this._width > 0; 15 | this._uniforms = {}; 16 | 17 | if (this._hasOwnFbo) { 18 | this._fbo = new FrameBuffer(this._width, this.height, mParmas); 19 | } 20 | } 21 | 22 | 23 | uniform(mName, mValue) { 24 | this._uniforms[mName] = mValue; 25 | } 26 | 27 | 28 | render(texture) { 29 | this.shader.bind(); 30 | this.shader.uniform('texture', 'uniform1i', 0); 31 | texture.bind(0); 32 | 33 | this.shader.uniform(this._uniforms); 34 | } 35 | 36 | get width() { return this._width; } 37 | get height() { return this._height; } 38 | get fbo() { return this._fbo; } 39 | get hasFbo() { return this._hasOwnFbo; } 40 | } 41 | 42 | export default Pass; -------------------------------------------------------------------------------- /dev/shaders/render.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec3 aVertexPosition; 5 | in vec2 aTextureCoord; 6 | in vec3 aNormal; 7 | 8 | uniform mat4 uModelMatrix; 9 | uniform mat4 uViewMatrix; 10 | uniform mat4 uProjectionMatrix; 11 | uniform mat4 uShadowMatrix; 12 | 13 | uniform vec2 uViewport; 14 | uniform sampler2D texturePos; 15 | uniform sampler2D textureExtra; 16 | 17 | out vec2 vTextureCoord; 18 | out vec3 vColor; 19 | out vec3 vNormal; 20 | out vec4 vShadowCoord; 21 | 22 | const float radius = 0.009; 23 | 24 | void main(void) { 25 | vec2 uv = aVertexPosition.xy; 26 | vec3 pos = texture(texturePos, uv).xyz; 27 | vec3 extra = texture(textureExtra, uv).xyz; 28 | vColor = vec3(1.0); 29 | 30 | vec4 wsPos = uModelMatrix * vec4(pos, 1.0); 31 | 32 | gl_Position = uProjectionMatrix * uViewMatrix * wsPos; 33 | vTextureCoord = aTextureCoord; 34 | vNormal = aNormal; 35 | 36 | float distOffset = uViewport.y * uProjectionMatrix[1][1] * radius / gl_Position.w; 37 | gl_PointSize = distOffset * mix(1.0, 1.5, extra.g); 38 | 39 | vShadowCoord = uShadowMatrix * wsPos; 40 | } -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchLine.js: -------------------------------------------------------------------------------- 1 | // BatchLine.js 2 | 3 | 4 | import GL from '../GLTool'; 5 | import Mesh from '../Mesh'; 6 | import GLShader from '../GLShader'; 7 | import Batch from '../Batch'; 8 | 9 | const vs = require('../shaders/basic.vert'); 10 | const fs = require('../shaders/simpleColor.frag'); 11 | 12 | 13 | 14 | class BatchAxis extends Batch { 15 | 16 | constructor() { 17 | const positions = []; 18 | const indices = [0, 1]; 19 | const coords = [[0, 0], [1, 1]]; 20 | positions.push([0, 0, 0]); 21 | positions.push([0, 0, 0]); 22 | 23 | const mesh = new Mesh(GL.LINES); 24 | mesh.bufferVertex(positions); 25 | mesh.bufferTexCoord(coords); 26 | mesh.bufferIndex(indices); 27 | 28 | const shader = new GLShader(vs, fs); 29 | 30 | super(mesh, shader); 31 | } 32 | 33 | 34 | draw(mPositionA, mPositionB, color = [1, 1, 1], opacity = 1.0) { 35 | this._mesh.bufferVertex([mPositionA, mPositionB]); 36 | 37 | this._shader.bind(); 38 | this._shader.uniform('color', 'vec3', color); 39 | this._shader.uniform('opacity', 'float', opacity); 40 | super.draw(); 41 | } 42 | 43 | 44 | } 45 | 46 | 47 | export default BatchAxis; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2020 Yi-Wen LIN. http://wensday.co 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchAxis.js: -------------------------------------------------------------------------------- 1 | // BatchAxis.js 2 | 3 | import GL from '../GLTool'; 4 | import Mesh from '../Mesh'; 5 | import GLShader from '../GLShader'; 6 | import Batch from '../Batch'; 7 | 8 | import vs from '../shaders/axis.vert'; 9 | import fs from '../shaders/axis.frag'; 10 | 11 | 12 | class BatchAxis extends Batch { 13 | 14 | constructor() { 15 | const positions = []; 16 | const colors = []; 17 | const indices = [0, 1, 2, 3, 4, 5]; 18 | const r = 9999; 19 | 20 | positions.push([-r, 0, 0]); 21 | positions.push([r, 0, 0]); 22 | positions.push([0, -r, 0]); 23 | positions.push([0, r, 0]); 24 | positions.push([0, 0, -r]); 25 | positions.push([0, 0, r]); 26 | 27 | 28 | colors.push([1, 0, 0]); 29 | colors.push([1, 0, 0]); 30 | colors.push([0, 1, 0]); 31 | colors.push([0, 1, 0]); 32 | colors.push([0, 0, 1]); 33 | colors.push([0, 0, 1]); 34 | 35 | const mesh = new Mesh(GL.LINES); 36 | mesh.bufferVertex(positions); 37 | mesh.bufferIndex(indices); 38 | mesh.bufferData(colors, 'aColor', 3); 39 | 40 | const shader = new GLShader(vs, fs); 41 | 42 | super(mesh, shader); 43 | 44 | } 45 | 46 | 47 | } 48 | 49 | 50 | export default BatchAxis; -------------------------------------------------------------------------------- /dev/shaders/shadowMap.frag: -------------------------------------------------------------------------------- 1 | // cube.frag 2 | 3 | precision highp float; 4 | varying vec2 vTextureCoord; 5 | varying vec3 vNormal; 6 | varying vec4 vShadowCoord; 7 | 8 | uniform sampler2D textureDepth; 9 | 10 | 11 | #define uMapSize vec2(1024.0) 12 | #define FOG_DENSITY 0.2 13 | #define LIGHT_POS vec3(0.0, 10.0, 0.0) 14 | 15 | 16 | float rand(vec4 seed4) { 17 | float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); 18 | return fract(sin(dot_product) * 43758.5453); 19 | } 20 | 21 | 22 | float PCFShadow(sampler2D depths, vec2 size, vec4 shadowCoord) { 23 | float result = 0.0; 24 | float bias = 0.005; 25 | vec2 uv = shadowCoord.xy; 26 | 27 | for(int x=-1; x<=1; x++){ 28 | for(int y=-1; y<=1; y++){ 29 | vec2 off = vec2(x,y) + rand(vec4(gl_FragCoord.xy, float(x), float(y))); 30 | off /= size; 31 | 32 | float d = texture2D(depths, uv + off).r; 33 | if(d < shadowCoord.z - bias) { 34 | result += 1.0; 35 | } 36 | 37 | } 38 | } 39 | return 1.0 -result/9.0; 40 | 41 | } 42 | 43 | void main(void) { 44 | vec4 shadowCoord = vShadowCoord / vShadowCoord.w; 45 | float s = PCFShadow(textureDepth, uMapSize, shadowCoord); 46 | gl_FragColor = vec4(vec3(s), 1.0); 47 | } -------------------------------------------------------------------------------- /dev/features/multi-render-target.js: -------------------------------------------------------------------------------- 1 | // multi-render-target.js 2 | console.log('dev : multi-render-target'); 3 | 4 | import '../global.scss'; 5 | import quickSetup from '../utils/quickSetup'; 6 | import alfrid, { GL, WebglNumber } from 'src/alfrid'; 7 | import vs from 'shaders/cube.vert'; 8 | import fs from 'shaders/cube.frag'; 9 | 10 | 11 | let shader, fbo, mesh, bCopy; 12 | 13 | 14 | quickSetup(render).then((o)=>init(o)).catch(err=>{ 15 | console.log('Error :', err); 16 | }); 17 | 18 | 19 | function init(o) { 20 | console.log('Init', o); 21 | 22 | const s = 1; 23 | mesh = alfrid.Geom.cube(s, s, s); 24 | shader = new alfrid.GLShader(vs, fs); 25 | 26 | const fboSize = 1024; 27 | fbo = new alfrid.FrameBuffer(fboSize, fboSize, {}, 8); 28 | fbo.wrapS = GL.REPEAT; 29 | console.log(WebglNumber[fbo.wrapS]); 30 | 31 | bCopy = new alfrid.BatchCopy(); 32 | } 33 | 34 | 35 | function render() { 36 | 37 | fbo.bind(); 38 | GL.clear(0, 0, 0, 0); 39 | shader.bind(); 40 | GL.draw(mesh); 41 | fbo.unbind(); 42 | 43 | const s = GL.width / fbo.numTargets; 44 | for(let i=0; i { 6 | if (!bLine) { 7 | bLine = new alfrid.BatchLine() 8 | bBall = new alfrid.BatchBall() 9 | } 10 | 11 | const mtx = mat4.create() 12 | 13 | mat4.mul(mtx, camera.projection, camera.matrix) 14 | mat4.invert(mtx, mtx) 15 | 16 | let points = [ 17 | [1, 1, -1, 1], 18 | [-1, 1, -1, 1], 19 | [1, -1, -1, 1], 20 | [-1, -1, -1, 1], 21 | 22 | [1, 1, 1, 1], 23 | [-1, 1, 1, 1], 24 | [1, -1, 1, 1], 25 | [-1, -1, 1, 1] 26 | ] 27 | 28 | const lines = [ 29 | [0, 1], 30 | [1, 3], 31 | [3, 2], 32 | [2, 0], 33 | 34 | [4, 5], 35 | [5, 7], 36 | [7, 6], 37 | [6, 4], 38 | 39 | [0, 4], 40 | [1, 5], 41 | [2, 6], 42 | [3, 7] 43 | ] 44 | 45 | points = points.map(p => { 46 | vec4.transformMat4(p, p, mtx) 47 | p[0] /= p[3] 48 | p[1] /= p[3] 49 | p[2] /= p[3] 50 | return [p[0], p[1], p[2]] 51 | }) 52 | 53 | const s = 0.02 54 | points.forEach(p => { 55 | bBall.draw(p, [s, s, s], mColor) 56 | }) 57 | 58 | lines.forEach(l => { 59 | bLine.draw(points[l[0]], points[l[1]], mColor) 60 | }) 61 | } 62 | 63 | export default draw 64 | -------------------------------------------------------------------------------- /src/alfrid/utils/getTextureParameters.js: -------------------------------------------------------------------------------- 1 | // getTextureParameters.js 2 | 3 | import GL from '../GLTool' 4 | import WebglNumber from './WebglNumber' 5 | 6 | function isPowerOfTwo (x) { 7 | return (x !== 0) && (!(x & (x - 1))) 8 | }; 9 | 10 | const getTextureParameters = function (mParams, mSource, mWidth, mHeight) { 11 | if (!mParams.minFilter) { 12 | let minFilter = GL.LINEAR 13 | if (mWidth && mWidth) { 14 | if (isPowerOfTwo(mWidth) && isPowerOfTwo(mHeight)) { 15 | minFilter = GL.NEAREST_MIPMAP_LINEAR 16 | } 17 | } 18 | 19 | mParams.minFilter = minFilter 20 | } 21 | 22 | mParams.mipmap = mParams.mipmap === undefined ? true : mParams.mipmap 23 | mParams.magFilter = mParams.magFilter || GL.LINEAR 24 | mParams.wrapS = mParams.wrapS || GL.CLAMP_TO_EDGE 25 | mParams.wrapT = mParams.wrapT || GL.CLAMP_TO_EDGE 26 | mParams.internalFormat = mParams.internalFormat || GL.RGBA 27 | mParams.format = mParams.format || GL.RGBA 28 | mParams.premultiplyAlpha = mParams.premultiplyAlpha === undefined ? false : mParams.premultiplyAlpha 29 | mParams.level = mParams.level || 0 30 | 31 | if (GL.webgl2 && mParams.type === GL.FLOAT) { 32 | mParams.internalFormat = GL.gl.RGBA32F 33 | mParams.mipmap = false 34 | } 35 | return mParams 36 | } 37 | 38 | export default getTextureParameters 39 | -------------------------------------------------------------------------------- /src/alfrid/shaders/pbr.vert: -------------------------------------------------------------------------------- 1 | // reflection.vert 2 | 3 | #define SHADER_NAME PBR_VERTEX 4 | 5 | precision highp float; 6 | attribute vec3 aVertexPosition; 7 | attribute vec2 aTextureCoord; 8 | attribute vec3 aNormal; 9 | 10 | uniform mat4 uModelMatrix; 11 | uniform mat4 uViewMatrix; 12 | uniform mat4 uProjectionMatrix; 13 | uniform mat3 uNormalMatrix; 14 | uniform mat3 uModelViewMatrixInverse; 15 | 16 | uniform vec3 uPosition; 17 | uniform vec3 uScale; 18 | 19 | varying vec2 vTextureCoord; 20 | 21 | varying vec3 vNormal; 22 | varying vec3 vPosition; 23 | varying vec3 vWsPosition; 24 | varying vec3 vEyePosition; 25 | varying vec3 vWsNormal; 26 | 27 | 28 | void main(void) { 29 | vec3 position = aVertexPosition * uScale + uPosition; 30 | vec4 worldSpacePosition = uModelMatrix * vec4(position, 1.0); 31 | vec4 viewSpacePosition = uViewMatrix * worldSpacePosition; 32 | 33 | vNormal = uNormalMatrix * aNormal; 34 | vPosition = viewSpacePosition.xyz; 35 | vWsPosition = worldSpacePosition.xyz; 36 | 37 | vec4 eyeDirViewSpace = viewSpacePosition - vec4( 0, 0, 0, 1 ); 38 | vEyePosition = -vec3( uModelViewMatrixInverse * eyeDirViewSpace.xyz ); 39 | vWsNormal = normalize( uModelViewMatrixInverse * vNormal ); 40 | 41 | gl_Position = uProjectionMatrix * viewSpacePosition; 42 | 43 | vTextureCoord = aTextureCoord; 44 | } 45 | -------------------------------------------------------------------------------- /src/alfrid/helpers/BatchDotsPlane.js: -------------------------------------------------------------------------------- 1 | // BatchDotsPlane.js 2 | 3 | import GL from '../GLTool'; 4 | import Mesh from '../Mesh'; 5 | import GLShader from '../GLShader'; 6 | import Batch from '../Batch'; 7 | 8 | import vs from '../shaders/dotsPlane.vert'; 9 | import fs from '../shaders/dotsPlane.frag'; 10 | 11 | class BatchDotsPlane extends Batch { 12 | 13 | constructor() { 14 | const positions = []; 15 | const indices = []; 16 | let index = 0; 17 | const size = 100; 18 | let i, j; 19 | 20 | for(i = -size; i < size; i += 1) { 21 | for(j = -size; j < size; j += 1) { 22 | positions.push([i, j, 0]); 23 | indices.push(index); 24 | index++; 25 | 26 | positions.push([i, 0, j]); 27 | indices.push(index); 28 | index++; 29 | } 30 | } 31 | 32 | const mesh = new Mesh(GL.POINTS); 33 | mesh.bufferVertex(positions); 34 | mesh.bufferIndex(indices); 35 | 36 | const shader = new GLShader(vs, fs); 37 | 38 | super(mesh, shader); 39 | 40 | this.color = [1, 1, 1]; 41 | this.opacity = 0.65; 42 | } 43 | 44 | 45 | draw() { 46 | this.shader.bind(); 47 | this.shader.uniform('color', 'uniform3fv', this.color); 48 | this.shader.uniform('opacity', 'uniform1f', this.opacity); 49 | this.shader.uniform('viewport', 'vec2', [GL.width, GL.height]); 50 | // GL.draw(this.mesh); 51 | super.draw(); 52 | } 53 | } 54 | 55 | export default BatchDotsPlane; -------------------------------------------------------------------------------- /dev/shaders/sim.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec2 vTextureCoord; 5 | uniform sampler2D texturePos; 6 | uniform sampler2D textureVel; 7 | uniform sampler2D textureExtra; 8 | 9 | uniform float uTime; 10 | 11 | #pragma glslify: snoise = require(glsl-utils/snoise.glsl) 12 | #pragma glslify: curlNoise = require(glsl-utils/curlNoise.glsl) 13 | 14 | layout (location = 0) out vec4 oFragColor0; 15 | layout (location = 1) out vec4 oFragColor1; 16 | layout (location = 2) out vec4 oFragColor2; 17 | 18 | void main(void) { 19 | vec3 pos = texture(texturePos, vTextureCoord).xyz; 20 | vec3 vel = texture(textureVel, vTextureCoord).xyz; 21 | vec3 extra = texture(textureExtra, vTextureCoord).xyz; 22 | 23 | float noiseBase = snoise(pos * 0.1 + extra * 0.2 - uTime * 0.02) * .5 + .5; 24 | float posOffset = mix(0.1, 0.2, noiseBase); 25 | vec3 noise = curlNoise(pos * posOffset + uTime * 0.1); 26 | 27 | vec3 acc = noise; 28 | 29 | const float maxRadius = 1.0; 30 | vec3 dir = -normalize(pos); 31 | float dist = length(pos); 32 | 33 | if(dist > maxRadius) { 34 | float f = mix(1.0, 2.0, extra.b); 35 | acc += dir * (dist - maxRadius) * f * 0.5; 36 | } 37 | 38 | float speedOffset = mix(0.5, 1.0, extra.r); 39 | vel += acc * 0.002 * speedOffset; 40 | 41 | pos += vel; 42 | 43 | vel *= 0.96; 44 | 45 | oFragColor0 = vec4(pos, 1.0); 46 | oFragColor1 = vec4(vel, 1.0); 47 | oFragColor2 = vec4(extra, 1.0); 48 | } -------------------------------------------------------------------------------- /dev/utils/setupAlfrid.js: -------------------------------------------------------------------------------- 1 | // setupAlfrid.js 2 | 3 | import alfrid from '../../src/alfrid'; 4 | const { GL, OrbitalControl } = alfrid; 5 | 6 | const setStyle = (mTarget, mStyleName, mValue) => { 7 | mTarget.style[mStyleName] = mValue; 8 | } 9 | 10 | 11 | const resizeCanvas = (mCanvas) => { 12 | setStyle(mCanvas, 'width', '100%'); 13 | setStyle(mCanvas, 'height', '100%'); 14 | setStyle(mCanvas, 'top', '0'); 15 | setStyle(mCanvas, 'left', '0'); 16 | } 17 | 18 | 19 | const setupAlfrid = (initOptions, functions) => new Promise((resolve, reject) => { 20 | const canvas = document.createElement("canvas"); 21 | canvas.width = window.innerWidth; 22 | canvas.height = window.innerHeight; 23 | document.body.appendChild(canvas); 24 | resizeCanvas(canvas); 25 | 26 | 27 | GL.init(canvas, initOptions); 28 | GL.enableAlphaBlending(); 29 | 30 | const { init, render, resize } = functions; 31 | const camera = new alfrid.CameraPerspective(Math.PI/4, canvas.width/canvas.height, .1, 100); 32 | const orbitalControl = new OrbitalControl(camera, window, 10); 33 | 34 | // INIT 35 | if(init) { 36 | init(camera, orbitalControl); 37 | } 38 | 39 | 40 | // LOOPING 41 | if(render) { 42 | alfrid.Scheduler.addEF(render); 43 | } 44 | 45 | 46 | window.addEventListener('resize', () => { 47 | resizeCanvas(canvas); 48 | 49 | GL.setSize(window.innerWidth, window.innerHeight); 50 | camera.setAspectRatio(GL.aspectRatio); 51 | 52 | if(resize) { 53 | resize(); 54 | } 55 | }) 56 | 57 | resolve({ 58 | canvas, 59 | camera 60 | }); 61 | }); 62 | 63 | 64 | module.exports = setupAlfrid; -------------------------------------------------------------------------------- /src/alfrid/utils/EaseNumber.js: -------------------------------------------------------------------------------- 1 | // EaseNumber.js 2 | 3 | import Scheduler from 'scheduling'; 4 | 5 | class EaseNumber { 6 | constructor(mValue, mEasing = 0.1) { 7 | this.easing = mEasing; 8 | this._value = mValue; 9 | this._targetValue = mValue; 10 | this._efIndex = Scheduler.addEF(()=> this._update()); 11 | } 12 | 13 | 14 | _update() { 15 | const MIN_DIFF = 0.0001; 16 | this._checkLimit(); 17 | this._value += (this._targetValue - this._value) * this.easing; 18 | if(Math.abs(this._targetValue - this._value) < MIN_DIFF) { 19 | this._value = this._targetValue; 20 | } 21 | } 22 | 23 | setTo(mValue) { 24 | this._targetValue = this._value = mValue; 25 | } 26 | 27 | 28 | add(mAdd) { 29 | this._targetValue += mAdd; 30 | } 31 | 32 | limit(mMin, mMax) { 33 | if(mMin > mMax) { 34 | this.limit(mMax, mMin); 35 | return; 36 | } 37 | 38 | this._min = mMin; 39 | this._max = mMax; 40 | 41 | this._checkLimit(); 42 | } 43 | 44 | 45 | _checkLimit() { 46 | if(this._min !== undefined && this._targetValue < this._min) { 47 | this._targetValue = this._min; 48 | } 49 | 50 | if(this._max !== undefined && this._targetValue > this._max) { 51 | this._targetValue = this._max; 52 | } 53 | } 54 | 55 | 56 | destroy() { 57 | Scheduler.removeEF(this._efIndex); 58 | } 59 | 60 | 61 | // GETTERS / SETTERS 62 | 63 | set value(mValue) { 64 | this._targetValue = mValue; 65 | } 66 | 67 | get value() { 68 | return this._value; 69 | } 70 | 71 | get targetValue() { 72 | return this._targetValue; 73 | } 74 | 75 | } 76 | 77 | 78 | export default EaseNumber; -------------------------------------------------------------------------------- /src/alfrid/utils/SpringNumber.js: -------------------------------------------------------------------------------- 1 | // SpringNumber.js 2 | 3 | import Scheduler from 'scheduling'; 4 | 5 | class SpringNumber { 6 | 7 | constructor(mValue, mSpeed=0.1, mDecreaseRate=0.9) { 8 | this._value = mValue; 9 | this._targetValue = mValue; 10 | this.speed = mSpeed; 11 | this.decreaseRate = mDecreaseRate; 12 | 13 | this._velocity = 0; 14 | 15 | this._efIndex = Scheduler.addEF(()=> this._update()); 16 | } 17 | 18 | 19 | _update() { 20 | const MIN_DIFF = 0.0001; 21 | this._checkLimit(); 22 | if(Math.abs(this._targetValue - this._value) < MIN_DIFF) { 23 | this._value = this._targetValue; 24 | return; 25 | } 26 | 27 | this._velocity += (this._targetValue - this._value) * this.speed; 28 | 29 | this._value += this._velocity; 30 | this._velocity *= this.decreaseRate; 31 | 32 | if(Math.abs(this._targetValue - this._value) < MIN_DIFF) { 33 | this._value = this._targetValue; 34 | } 35 | } 36 | 37 | 38 | limit(mMin, mMax) { 39 | if(mMin > mMax) { 40 | this.limit(mMax, mMin); 41 | return; 42 | } 43 | 44 | this._min = mMin; 45 | this._max = mMax; 46 | 47 | this._checkLimit(); 48 | } 49 | 50 | 51 | _checkLimit() { 52 | if(this._min !== undefined && this._targetValue < this._min) { 53 | this._targetValue = this._min; 54 | } 55 | 56 | if(this._max !== undefined && this._targetValue > this._max) { 57 | this._targetValue = this._max; 58 | } 59 | } 60 | 61 | 62 | destroy() { 63 | Scheduler.removeEF(this._efIndex); 64 | } 65 | 66 | 67 | set value(mValue) { 68 | this._targetValue = mValue; 69 | } 70 | 71 | 72 | get value() { 73 | return this._value; 74 | } 75 | 76 | } 77 | 78 | 79 | export default SpringNumber; -------------------------------------------------------------------------------- /src/alfrid/cameras/Camera.js: -------------------------------------------------------------------------------- 1 | // Camera.js 2 | 3 | import { mat4, mat3, quat } from 'gl-matrix'; 4 | 5 | class Camera { 6 | constructor() { 7 | // VIEW MATRIX 8 | this._matrix = mat4.create(); 9 | 10 | // FOR TRANSFORM FROM ORIENTATION 11 | this._quat = quat.create(); 12 | this._orientation = mat4.create(); 13 | 14 | // PROJECTION MATRIX 15 | this._projection = mat4.create(); 16 | 17 | // POSITION OF CAMERA 18 | this.position = vec3.create(); 19 | } 20 | 21 | 22 | lookAt(aEye, aCenter, aUp = [0, 1, 0]) { 23 | this._eye = vec3.clone(aEye); 24 | this._center = vec3.clone(aCenter); 25 | 26 | vec3.copy(this.position, aEye); 27 | mat4.identity(this._matrix); 28 | mat4.lookAt(this._matrix, aEye, aCenter, aUp); 29 | } 30 | 31 | 32 | setFromOrientation(x, y, z, w) { 33 | quat.set(this._quat, x, y, z, w); 34 | mat4.fromQuat(this._orientation, this._quat); 35 | mat4.translate(this._matrix, this._orientation, this.positionOffset); 36 | } 37 | 38 | 39 | setProjection(mProj) { 40 | this._projection = mat4.clone(mProj); 41 | } 42 | 43 | 44 | setView(mView) { 45 | this._matrix = mat4.clone(mView); 46 | } 47 | 48 | 49 | setFromViewProj(mView, mProj) { 50 | this.setView(mView); 51 | this.setProjection(mProj); 52 | } 53 | 54 | 55 | // GETTERS 56 | 57 | get matrix() { 58 | return this._matrix; 59 | } 60 | 61 | get viewMatrix() { 62 | return this._matrix; 63 | } 64 | 65 | 66 | get projection() { 67 | return this._projection; 68 | } 69 | 70 | get projectionMatrix() { 71 | return this._projection; 72 | } 73 | 74 | 75 | get eye() { return this._eye; } 76 | 77 | get center() { return this._center; } 78 | } 79 | 80 | 81 | export default Camera; -------------------------------------------------------------------------------- /dev/utils/quickSetup.js: -------------------------------------------------------------------------------- 1 | // quickSetup.js 2 | 3 | import { GL, BatchDotsPlane, BatchAxis, CameraPerspective, OrbitalControl } from '../../src/alfrid' 4 | import Scheduler from 'scheduling' 5 | import dat from 'dat-gui' 6 | /* 7 | export default (mRender, mCallback, mResize) => { 8 | 9 | } 10 | 11 | */ 12 | 13 | const quickSetup = (mRender, mResize) => new Promise((resolve, reject) => { 14 | // create canvas 15 | const canvas = document.createElement('canvas') 16 | document.body.appendChild(canvas) 17 | 18 | // INIT GL 19 | GL.init(canvas, { useWebgl2: true }) 20 | 21 | // Camera 22 | const camera = new CameraPerspective() 23 | camera.setPerspective(Math.PI / 4, window.innerWidth / window.innerHeight, 0.1, 100) 24 | 25 | // Camera control 26 | const orbControl = new OrbitalControl(camera, window, 15) 27 | orbControl.rx.value = orbControl.ry.value = 0.3 28 | orbControl.radius.value = 5 29 | 30 | // Views 31 | const batchDots = new BatchDotsPlane() 32 | const batchAxis = new BatchAxis() 33 | 34 | // render loop 35 | function loop () { 36 | GL.clear(0, 0, 0, 0) 37 | GL.setMatrices(camera) 38 | batchDots.draw() 39 | batchAxis.draw() 40 | 41 | if (mRender) { 42 | mRender() 43 | } 44 | } 45 | Scheduler.addEF(loop) 46 | 47 | // resizing 48 | window.addEventListener('resize', resize) 49 | function resize () { 50 | GL.setSize(window.innerWidth, window.innerHeight) 51 | camera.setPerspective(Math.PI / 4, GL.aspectRatio, 0.1, 100) 52 | 53 | if (mResize) { 54 | mResize() 55 | } 56 | } 57 | resize() 58 | 59 | const o = { 60 | camera, 61 | orbControl 62 | } 63 | 64 | window.gui = new dat.GUI({ width: 300 }) 65 | 66 | resolve(o) 67 | }) 68 | 69 | export default quickSetup 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alfrid", 3 | "version": "0.4.0", 4 | "description": "WebGL 3D toolkit", 5 | "main": "build/alfrid.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "lint": "npm run lint:fix && eslint -c .eslintrc src/**", 11 | "lint:fix": "eslint -c .eslintrc --fix src/**", 12 | "start": "npm run dev", 13 | "shader": "node ./tasks/shader-watcher.js", 14 | "dev": "NODE_ENV=development webpack-dev-server --open", 15 | "build": "NODE_ENV=production webpack --progress --colors", 16 | "build:dev": "NODE_ENV=development webpack --progress --colors" 17 | }, 18 | "author": "Yi-Wen Lin", 19 | "license": "MIT", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/yiwenl/Alfrid" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/yiwenl/Alfrid/issues" 26 | }, 27 | "dependencies": { 28 | "collada-parser": "^1.0.0", 29 | "gl-matrix": "^2.4.0", 30 | "parse-dds": "^1.2.1", 31 | "scheduling": "^1.2.0" 32 | }, 33 | "devDependencies": { 34 | "assets-loader": "^0.5.2", 35 | "babel-core": "^6.26.0", 36 | "babel-loader": "^7.1.2", 37 | "babel-plugin-add-module-exports": "^0.2.1", 38 | "babel-preset-env": "^1.6.0", 39 | "css-loader": "^3.6.0", 40 | "dat-gui": "^0.5.0", 41 | "eslint": "^4.7.1", 42 | "eslint-loader": "^1.9.0", 43 | "fs-extra": "^4.0.2", 44 | "glsl-utils": "github:yiwenl/glsl-utils", 45 | "glslify": "^6.1.0", 46 | "glslify-loader": "^2.0.0", 47 | "node-sass": "^4.14.1", 48 | "randomutils": "github:yiwenl/randomUtils", 49 | "raw-loader": "^0.5.1", 50 | "sass-loader": "^6.0.6", 51 | "stats.js": "^0.17.0", 52 | "style-loader": "^0.18.2", 53 | "webpack": "^3.6.0", 54 | "webpack-dev-server": "^2.11.5" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/alfrid/cameras/CameraPerspective.js: -------------------------------------------------------------------------------- 1 | // CameraPerspective.js 2 | 3 | import Camera from './Camera'; 4 | import Ray from '../math/Ray'; 5 | import { mat4, vec3 } from 'gl-matrix'; 6 | 7 | const mInverseViewProj = mat4.create(); 8 | const cameraDir = vec3.create(); 9 | 10 | 11 | class CameraPerspective extends Camera { 12 | 13 | constructor(mFov, mAspectRatio, mNear, mFar) { 14 | super(); 15 | 16 | if(mFov) { 17 | this.setPerspective(mFov, mAspectRatio, mNear, mFar); 18 | } 19 | } 20 | 21 | setPerspective(mFov, mAspectRatio, mNear, mFar) { 22 | 23 | this._fov = mFov; 24 | this._near = mNear; 25 | this._far = mFar; 26 | this._aspectRatio = mAspectRatio; 27 | mat4.perspective(this._projection, mFov, mAspectRatio, mNear, mFar); 28 | 29 | // this._frustumTop = this._near * Math.tan(this._fov * 0.5); 30 | // this._frustumButtom = -this._frustumTop; 31 | // this._frustumRight = this._frustumTop * this._aspectRatio; 32 | // this._frustumLeft = -this._frustumRight; 33 | } 34 | 35 | 36 | setAspectRatio(mAspectRatio) { 37 | this._aspectRatio = mAspectRatio; 38 | mat4.perspective(this.projection, this._fov, mAspectRatio, this._near, this._far); 39 | } 40 | 41 | 42 | generateRay(mScreenPosition, mRay) { 43 | const proj = this.projectionMatrix; 44 | const view = this.viewMatrix; 45 | 46 | mat4.multiply(mInverseViewProj, proj, view); 47 | mat4.invert(mInverseViewProj, mInverseViewProj); 48 | 49 | vec3.transformMat4(cameraDir, mScreenPosition, mInverseViewProj); 50 | vec3.sub(cameraDir, cameraDir, this.position); 51 | vec3.normalize(cameraDir, cameraDir); 52 | 53 | if (!mRay) { 54 | mRay = new Ray(this.position, cameraDir); 55 | } else { 56 | mRay.origin = this.position; 57 | mRay.direction = cameraDir; 58 | } 59 | 60 | 61 | return mRay; 62 | } 63 | } 64 | 65 | 66 | export default CameraPerspective; -------------------------------------------------------------------------------- /src/alfrid/post/EffectComposer.js: -------------------------------------------------------------------------------- 1 | // EffectComposer.js 2 | 3 | import Pass from './Pass'; 4 | import GL from '../GLTool'; 5 | import Geom from '../Geom'; 6 | import FrameBuffer from '../FrameBuffer'; 7 | 8 | class EffectComposer { 9 | constructor(mWidth, mHeight, mParmas = {}) { 10 | this._width = mWidth || GL.width; 11 | this._height = mHeight || GL.height; 12 | 13 | this._params = {}; 14 | this.setSize(mWidth, mHeight); 15 | this._mesh = Geom.bigTriangle(); 16 | this._passes = []; 17 | this._returnTexture; 18 | } 19 | 20 | 21 | addPass(pass) { 22 | if(pass.passes) { 23 | this.addPass(pass.passes); 24 | return; 25 | } 26 | 27 | if (pass.length) { 28 | for(let i = 0; i < pass.length; i++) { 29 | this._passes.push(pass[i]); 30 | } 31 | } else { 32 | this._passes.push(pass); 33 | } 34 | } 35 | 36 | render(mSource) { 37 | let source = mSource; 38 | let fboTarget; 39 | 40 | this._passes.forEach((pass) => { 41 | 42 | // get target 43 | if(pass.hasFbo) { 44 | fboTarget = pass.fbo; 45 | } else { 46 | fboTarget = this._fboTarget; 47 | } 48 | 49 | // render 50 | fboTarget.bind(); 51 | GL.clear(0, 0, 0, 0); 52 | pass.render(source); 53 | GL.draw(this._mesh); 54 | fboTarget.unbind(); 55 | 56 | // reset source 57 | if(pass.hasFbo) { 58 | source = pass.fbo.getTexture(); 59 | } else { 60 | this._swap(); 61 | source = this._fboCurrent.getTexture(); 62 | } 63 | }); 64 | 65 | this._returnTexture = source; 66 | 67 | return source; 68 | } 69 | 70 | 71 | _swap() { 72 | const tmp = this._fboCurrent; 73 | this._fboCurrent = this._fboTarget; 74 | this._fboTarget = tmp; 75 | 76 | this._current = this._fboCurrent; 77 | this._target = this._fboTarget; 78 | } 79 | 80 | setSize(mWidth, mHeight) { 81 | this._width = mWidth; 82 | this._height = mHeight; 83 | this._fboCurrent = new FrameBuffer(this._width, this._height, this._params); 84 | this._fboTarget = new FrameBuffer(this._width, this._height, this._params); 85 | } 86 | 87 | get passes() { 88 | return this._passes; 89 | } 90 | 91 | getTexture() { 92 | return this._returnTexture; 93 | } 94 | } 95 | 96 | export default EffectComposer; -------------------------------------------------------------------------------- /src/alfrid/TransformFeedbackObject.js: -------------------------------------------------------------------------------- 1 | // TransformFeedbackObject.js 2 | 3 | import GL from './GLTool'; 4 | import GLShader from './GLShader'; 5 | import Mesh from './Mesh'; 6 | 7 | let gl; 8 | 9 | class TransformFeedbackObject { 10 | 11 | 12 | constructor(strVertexShader, strFragmentShader) { 13 | gl = GL.gl; 14 | this._vs = strVertexShader; 15 | this._fs = strFragmentShader; 16 | 17 | this._init(); 18 | } 19 | 20 | 21 | _init() { 22 | this._meshCurrent = new Mesh(); 23 | this._meshTarget = new Mesh(); 24 | this._numPoints = -1; 25 | 26 | this._varyings = []; 27 | this.transformFeedback = gl.createTransformFeedback(); 28 | } 29 | 30 | 31 | bufferData(mData, mName, mVaryingName) { 32 | const isTransformFeedback = !!mVaryingName; 33 | console.log('is Transform feedback ?', mName, isTransformFeedback); 34 | this._meshCurrent.bufferData(mData, mName, null, gl.STREAM_COPY, false); 35 | this._meshTarget.bufferData(mData, mName, null, gl.STREAM_COPY, false); 36 | 37 | if(isTransformFeedback) { 38 | this._varyings.push(mVaryingName); 39 | 40 | if(this._numPoints < 0) { 41 | this._numPoints = mData.length; 42 | } 43 | } 44 | } 45 | 46 | bufferIndex(mArrayIndices) { 47 | this._meshCurrent.bufferIndex(mArrayIndices); 48 | this._meshTarget.bufferIndex(mArrayIndices); 49 | } 50 | 51 | 52 | uniform(mName, mType, mValue) { 53 | if(this.shader) { 54 | this.shader.uniform(mName, mType, mValue); 55 | } 56 | 57 | } 58 | 59 | generate() { 60 | this.shader = new GLShader(this._vs, this._fs, this._varyings); 61 | } 62 | 63 | render() { 64 | if(!this.shader) { this.generate(); } 65 | 66 | this.shader.bind(); 67 | GL.drawTransformFeedback(this); 68 | 69 | this._swap(); 70 | } 71 | 72 | _swap() { 73 | const tmp = this._meshCurrent; 74 | this._meshCurrent = this._meshTarget; 75 | this._meshTarget = tmp; 76 | } 77 | 78 | get numPoints() { return this._numPoints; } 79 | get meshCurrent() { return this._meshCurrent; } 80 | get meshTarget() { return this._meshTarget; } 81 | get meshSource() { return this._meshCurrent; } 82 | get meshDestination() { return this._meshTarget; } 83 | } 84 | 85 | 86 | export default TransformFeedbackObject; -------------------------------------------------------------------------------- /dev/shaders/render.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec3 vColor; 4 | in vec4 vShadowCoord; 5 | 6 | uniform sampler2D textureDepth; 7 | 8 | out vec4 oColor; 9 | 10 | 11 | float samplePCF4x4( vec4 sc ) 12 | { 13 | const int r = 2; 14 | const int s = 2 * r; 15 | 16 | float shadow = 0.0; 17 | float bias = 0.001; 18 | float threshold = sc.z - bias; 19 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s,-s) ).r); 20 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r,-s) ).r); 21 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r,-s) ).r); 22 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s,-s) ).r); 23 | 24 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s,-r) ).r); 25 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r,-r) ).r); 26 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r,-r) ).r); 27 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s,-r) ).r); 28 | 29 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s, r) ).r); 30 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r, r) ).r); 31 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r, r) ).r); 32 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s, r) ).r); 33 | 34 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s, s) ).r); 35 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r, s) ).r); 36 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r, s) ).r); 37 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s, s) ).r); 38 | 39 | return shadow/16.0; 40 | } 41 | 42 | 43 | 44 | void main(void) { 45 | if(distance(gl_PointCoord, vec2(.5)) > .5) { 46 | discard; 47 | } 48 | 49 | vec4 shadowCoord = vShadowCoord / vShadowCoord.w; 50 | float s = samplePCF4x4(shadowCoord); 51 | s = mix(s, 1.0, .25); 52 | oColor = vec4(vColor * s, 1.0); 53 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | const path = require('path') 3 | const webpack = require('webpack') 4 | 5 | const pathOutput = path.resolve(__dirname, 'dev') 6 | const pathBuild = path.resolve(__dirname, 'build') 7 | const pathNodeModules = path.resolve(__dirname, 'node_modules') 8 | 9 | const env = process.env.NODE_ENV 10 | const isProd = env === 'production' 11 | const libraryName = 'alfrid' 12 | console.log('Environment isProd :', isProd) 13 | 14 | const entryPoint = process.argv.filter(arg => arg.indexOf('.js') > -1)[0] 15 | const entryJs = entryPoint || './dev/main.js' 16 | console.log('Entry Point :', entryJs) 17 | 18 | const plugins = isProd 19 | ? [ 20 | new webpack.optimize.UglifyJsPlugin({ 21 | sourceMap: false, 22 | compress: { 23 | drop_debugger: true, 24 | drop_console: false, 25 | screw_ie8: true 26 | }, 27 | comments: false, 28 | mangle: false 29 | }) 30 | ] : [ 31 | new webpack.HotModuleReplacementPlugin() 32 | ] 33 | 34 | const entry = isProd ? { app: `./src/${libraryName}.js` } 35 | : { app: entryJs } 36 | const output = isProd ? { 37 | path: pathBuild, 38 | filename: `./${libraryName}.js`, 39 | library: libraryName, 40 | libraryTarget: 'umd', 41 | umdNamedDefine: true 42 | } : { 43 | filename: 'bundle.js', 44 | path: pathOutput 45 | } 46 | 47 | const devtool = 'source-map' 48 | 49 | const config = { 50 | entry, 51 | devtool, 52 | devServer: { 53 | host: '0.0.0.0', 54 | contentBase: './dev', 55 | hot: true, 56 | disableHostCheck: true 57 | }, 58 | plugins, 59 | output, 60 | module: { 61 | rules: [ 62 | { 63 | test: /\.js$/, 64 | loader: 'babel-loader', 65 | query: { 66 | presets: ['env'] 67 | } 68 | // exclude: pathNodeModules 69 | }, 70 | { 71 | test: /\.css$/, 72 | use: ['style-loader', 'css-loader'], 73 | exclude: pathNodeModules 74 | }, 75 | { 76 | test: /\.scss$/, 77 | use: ['style-loader', 'css-loader', 'sass-loader'], 78 | exclude: pathNodeModules 79 | }, 80 | { 81 | test: /\.(glsl|vert|frag)$/, 82 | use: ['raw-loader', 'glslify-loader'], 83 | exclude: pathNodeModules 84 | } 85 | ] 86 | }, 87 | resolve: { 88 | alias: { 89 | shaders: path.resolve(__dirname, 'dev/shaders'), 90 | src: path.resolve(__dirname, 'src') 91 | } 92 | } 93 | } 94 | 95 | module.exports = config 96 | -------------------------------------------------------------------------------- /src/alfrid/shaders/fxaa.frag: -------------------------------------------------------------------------------- 1 | // fxaa.frag 2 | 3 | #define SHADER_NAME FXAA 4 | 5 | precision highp float; 6 | varying vec2 vTextureCoord; 7 | uniform sampler2D texture; 8 | uniform vec2 uResolution; 9 | 10 | 11 | float FXAA_SUBPIX_SHIFT = 1.0/4.0; 12 | #define FXAA_REDUCE_MIN (1.0/ 128.0) 13 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 14 | #define FXAA_SPAN_MAX 8.0 15 | 16 | 17 | vec4 applyFXAA(sampler2D tex) { 18 | vec4 color; 19 | vec2 fragCoord = gl_FragCoord.xy; 20 | vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * uResolution).xyz; 21 | vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * uResolution).xyz; 22 | vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * uResolution).xyz; 23 | vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * uResolution).xyz; 24 | vec3 rgbM = texture2D(tex, fragCoord * uResolution).xyz; 25 | vec3 luma = vec3(0.299, 0.587, 0.114); 26 | float lumaNW = dot(rgbNW, luma); 27 | float lumaNE = dot(rgbNE, luma); 28 | float lumaSW = dot(rgbSW, luma); 29 | float lumaSE = dot(rgbSE, luma); 30 | float lumaM = dot(rgbM, luma); 31 | float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); 32 | float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); 33 | 34 | vec2 dir; 35 | dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); 36 | dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); 37 | 38 | float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 39 | (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); 40 | 41 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); 42 | dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), 43 | max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), 44 | dir * rcpDirMin)) * uResolution; 45 | 46 | vec3 rgbA = 0.5 * ( 47 | texture2D(tex, fragCoord * uResolution + dir * (1.0 / 3.0 - 0.5)).xyz + 48 | texture2D(tex, fragCoord * uResolution + dir * (2.0 / 3.0 - 0.5)).xyz); 49 | vec3 rgbB = rgbA * 0.5 + 0.25 * ( 50 | texture2D(tex, fragCoord * uResolution + dir * -0.5).xyz + 51 | texture2D(tex, fragCoord * uResolution + dir * 0.5).xyz); 52 | 53 | float lumaB = dot(rgbB, luma); 54 | if ((lumaB < lumaMin) || (lumaB > lumaMax)) 55 | color = vec4(rgbA, 1.0); 56 | else 57 | color = vec4(rgbB, 1.0); 58 | return color; 59 | } 60 | 61 | void main(void) { 62 | vec4 color = applyFXAA(texture); 63 | gl_FragColor = color; 64 | } -------------------------------------------------------------------------------- /tasks/test-shader.js: -------------------------------------------------------------------------------- 1 | // test-shader.js 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const findFolder = require('./find-folder-promise'); 6 | const copyFile = require('./copy-file'); 7 | 8 | const FILE_PATH = 'src/js/ViewObjModel.js'; 9 | const PATH_SRC = './src'; 10 | const TEMPLATE_VERTEX = './tasks/basic.vert'; 11 | const TEMPLATE_FRAGMENT = './tasks/copy.frag'; 12 | const regShader = /shaders\/.+\.(vert|frag)/g; 13 | 14 | let shaderPath; 15 | 16 | findFolder(PATH_SRC, 'shaders').then((path)=> { 17 | shaderPath = path; 18 | 19 | getShaderImports(FILE_PATH).then(shaderImports => { 20 | return shaderImports.reduce((sequence, shaderName)=> { 21 | return sequence.then(()=>{ 22 | return isShaderExist(shaderName); 23 | }).then((mName)=> { 24 | generateShader(mName); 25 | }).catch((err)=> { 26 | console.log(err); 27 | }); 28 | }, Promise.resolve()); 29 | 30 | }).catch((err)=> { 31 | console.log('Error:', err); 32 | }); 33 | }); 34 | 35 | 36 | const getShaderImports = (mPath) => new Promise((resolve, reject) => { 37 | let results = []; 38 | 39 | fs.readFile(mPath, 'utf8', (err, str) => { 40 | if(err) { 41 | reject('Error Loading file !'); 42 | } else { 43 | let match; 44 | while( match = regShader.exec(str)) { 45 | results.push(match[0]); 46 | } 47 | 48 | results = results.map((mPath)=> { 49 | return mPath.replace('shaders/', ''); 50 | }); 51 | 52 | resolve(results); 53 | } 54 | }); 55 | }); 56 | 57 | const isShaderExist = (name) => new Promise((resolve, reject) => { 58 | fs.readdir(shaderPath, (err, files) => { 59 | if(err) { 60 | reject(err); 61 | return; 62 | } 63 | if(files.indexOf(name) === -1) { 64 | resolve(name); 65 | } else { 66 | reject(`Shader existed : ${name}`); 67 | } 68 | }); 69 | }) 70 | 71 | function generateShader(mName) { 72 | if(isVertexShader(mName)) { 73 | generateVertexShader(mName); 74 | } else { 75 | generateFragmentShader(mName); 76 | } 77 | } 78 | 79 | function generateVertexShader(mName) { 80 | console.log('Generate vertex shader :', mName); 81 | copyFile(TEMPLATE_VERTEX, path.resolve(shaderPath, mName), (err)=> { 82 | if(err) console.log('Err', err); 83 | }); 84 | } 85 | 86 | function generateFragmentShader(mName) { 87 | console.log('Generate fragment shader : ', mName); 88 | copyFile(TEMPLATE_FRAGMENT, path.resolve(shaderPath, mName), (err)=> { 89 | if(err) console.log('Err', err); 90 | }); 91 | } 92 | 93 | 94 | function isVertexShader(mName) { 95 | return mName.indexOf('.vert') > -1; 96 | } -------------------------------------------------------------------------------- /src/alfrid/helpers/Scene.js: -------------------------------------------------------------------------------- 1 | // Scene.js 2 | 3 | import Scheduler from 'scheduling'; 4 | import GL from '../GLTool'; 5 | import CameraPerspective from '../cameras/CameraPerspective'; 6 | import CameraOrtho from '../cameras/CameraOrtho'; 7 | import OrbitalControl from '../utils/OrbitalControl'; 8 | 9 | 10 | class Scene { 11 | 12 | 13 | constructor() { 14 | this._children = []; 15 | this._matrixIdentity = mat4.create(); 16 | GL.enableAlphaBlending(); 17 | 18 | this._init(); 19 | this._initTextures(); 20 | this._initViews(); 21 | 22 | this._efIndex = Scheduler.addEF(()=>this._loop()); 23 | window.addEventListener('resize', ()=>this.resize()); 24 | } 25 | 26 | 27 | 28 | // PUBLIC METHODS 29 | 30 | update() { 31 | 32 | } 33 | 34 | render() { 35 | 36 | } 37 | 38 | 39 | stop() { 40 | if(this._efIndex === -1) { return; } 41 | this._efIndex = Scheduler.removeEF(this._efIndex); 42 | } 43 | 44 | 45 | start() { 46 | if(this._efIndex !== -1) { 47 | return; 48 | } 49 | 50 | this._efIndex = Scheduler.addEF(()=>this._loop()); 51 | } 52 | 53 | 54 | resize() { 55 | GL.setSize(window.innerWidth, window.innerHeight); 56 | this.camera.setAspectRatio(GL.aspectRatio); 57 | } 58 | 59 | 60 | addChild(mChild) { 61 | this._children.push(mChild); 62 | } 63 | 64 | removeChild(mChild) { 65 | const index = this._children.indexOf(mChild); 66 | if(index == -1) { console.warn('Child no exist'); return; } 67 | 68 | this._children.splice(index, 1); 69 | } 70 | 71 | 72 | // PROTECTED METHODS TO BE OVERRIDEN BY CHILDREN 73 | 74 | _initTextures() { 75 | 76 | } 77 | 78 | 79 | _initViews() { 80 | 81 | } 82 | 83 | 84 | _renderChildren() { 85 | let child; 86 | for(let i=0; i .99) console.log('bind :', mTargetIndex, this._frameBuffers[mTargetIndex]); 71 | GL.viewport(0, 0, this.width, this.height); 72 | gl.bindFramebuffer(gl.FRAMEBUFFER, this._frameBuffers[mTargetIndex]); 73 | } 74 | 75 | unbind() { 76 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 77 | GL.viewport(0, 0, GL.width, GL.height); 78 | } 79 | 80 | // TEXTURES 81 | 82 | getTexture() { 83 | return this.glTexture; 84 | } 85 | 86 | // GETTERS AND SETTERS 87 | 88 | get width() { 89 | return this._size; 90 | } 91 | 92 | get height() { 93 | return this._size; 94 | } 95 | } 96 | 97 | 98 | export default CubeFrameBuffer; -------------------------------------------------------------------------------- /dev/shaders/shadowMap300es.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | 4 | precision highp float; 5 | // precision mediump sampler2DShadow; 6 | in vec2 vTextureCoord; 7 | in vec3 vNormal; 8 | in vec4 vShadowCoord; 9 | 10 | out vec4 oColor; 11 | 12 | 13 | 14 | // uniform sampler2DShadow textureDepth; 15 | uniform sampler2D textureDepth; 16 | 17 | 18 | #define uMapSize vec2(1024.0) 19 | #define FOG_DENSITY 0.2 20 | #define LIGHT_POS vec3(0.0, 10.0, 0.0) 21 | 22 | 23 | float rand(vec4 seed4) { 24 | float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); 25 | return fract(sin(dot_product) * 43758.5453); 26 | } 27 | 28 | 29 | float PCFShadow(sampler2D depths, vec2 size, vec4 shadowCoord) { 30 | float result = 0.0; 31 | float bias = 0.005; 32 | vec2 uv = shadowCoord.xy; 33 | 34 | for(int x=-1; x<=1; x++){ 35 | for(int y=-1; y<=1; y++){ 36 | vec2 off = vec2(x,y) + rand(vec4(gl_FragCoord.xy, float(x), float(y))); 37 | off /= size; 38 | 39 | float d = texture(depths, uv + off).r; 40 | if(d < shadowCoord.z - bias) { 41 | result += 1.0; 42 | } 43 | 44 | } 45 | } 46 | return 1.0 -result/9.0; 47 | 48 | } 49 | 50 | 51 | float samplePCF4x4( vec4 sc ) 52 | { 53 | const int r = 2; 54 | const int s = 2 * r; 55 | 56 | float shadow = 0.0; 57 | float threshold = 1.0; 58 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s,-s) ).r); 59 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r,-s) ).r); 60 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r,-s) ).r); 61 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s,-s) ).r); 62 | 63 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s,-r) ).r); 64 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r,-r) ).r); 65 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r,-r) ).r); 66 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s,-r) ).r); 67 | 68 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s, r) ).r); 69 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r, r) ).r); 70 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r, r) ).r); 71 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s, r) ).r); 72 | 73 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s, s) ).r); 74 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r, s) ).r); 75 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r, s) ).r); 76 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s, s) ).r); 77 | 78 | return shadow/16.0; 79 | } 80 | 81 | void main(void) { 82 | vec4 shadowCoord = vShadowCoord / vShadowCoord.w; 83 | // float s = PCFShadow(textureDepth, uMapSize, shadowCoord); 84 | float s = samplePCF4x4(shadowCoord); 85 | oColor = vec4(vec3(s), 1.0); 86 | } -------------------------------------------------------------------------------- /dev/features/gltf-parser.js: -------------------------------------------------------------------------------- 1 | // gltf-parser.js 2 | import '../global.scss'; 3 | import quickSetup from '../utils/quickSetup'; 4 | import alfrid, { GL, WebglNumber, GLTFParser, Object3D } from 'src/alfrid'; 5 | import AssetsLoader from 'assets-loader'; 6 | 7 | 8 | let isLoaded = false; 9 | let container, mesh, shader; 10 | 11 | quickSetup(render).then((o)=>init(o)).catch(err=>{ 12 | console.log('Error :', err); 13 | }); 14 | 15 | 16 | function init(o) { 17 | console.log('Init', o); 18 | 19 | shader = new alfrid.GLShader(); 20 | 21 | const assetsToLoad = [ 22 | {"id":"deer","url":"assets/gltf/deer/deer1.gltf","type":"text"}, 23 | {"id":"deer_bin","url":"assets/gltf/deer/deer1.bin","type":"binary"}, 24 | {"id":"head","url":"assets/gltf/head/model.gltf","type":"text"}, 25 | {"id":"head_bin","url":"assets/gltf/head/model.bin","type":"binary"}, 26 | {"id":"microphone","url":"assets/gltf/microphone/microphone.gltf","type":"text"}, 27 | {"id":"microphone_bin","url":"assets/gltf/microphone/microphone.bin","type":"binary"}, 28 | ]; 29 | 30 | 31 | const loader = new AssetsLoader({ 32 | assets:assetsToLoad 33 | }) 34 | .on('error', (error)=>{ 35 | console.log('Error :', error); 36 | }) 37 | .on('progress', (p) => { 38 | console.log('Progress : ', p); 39 | }) 40 | .on('complete', onAssetsLoaded) 41 | .start(); 42 | 43 | } 44 | 45 | function onAssetsLoaded(o) { 46 | console.log('Assets Loaded :', o); 47 | 48 | const getAsset = (mID) => { 49 | const a = o.filter( a => a.id === mID); 50 | if(a.length === 0) { 51 | return null; 52 | } 53 | return a[0].file; 54 | } 55 | 56 | // const modelID = 'microphone'; 57 | const modelID = 'head'; 58 | const gltf = JSON.parse(getAsset(modelID)); 59 | const bin = getAsset(`${modelID}_bin`); 60 | 61 | GLTFParser.parse(gltf, bin) 62 | .then( 63 | (o)=> { 64 | console.log('Parsed :', o); 65 | console.log('Nodes :', o.nodes); 66 | container = new Object3D(); 67 | // const rotation = quat.clone(gltf.nodes[0].rotation) 68 | // container.setRotationFromQuaternion(rotation); 69 | // mesh = o.output.meshes[0]; 70 | 71 | }, 72 | (err) => { 73 | console.log('Error:', err); 74 | } 75 | ); 76 | 77 | const modelID1 = 'microphone'; 78 | const gltf1 = JSON.parse(getAsset(modelID1)); 79 | const bin1 = getAsset(`${modelID1}_bin`); 80 | 81 | GLTFParser.parse(gltf1, bin1) 82 | .then( 83 | (o)=> { 84 | console.log('Parsed :', o); 85 | console.log('Nodes :', o.nodes); 86 | // container = new Object3D(); 87 | // const rotation = quat.clone(gltf.nodes[0].rotation) 88 | // container.setRotationFromQuaternion(rotation); 89 | // mesh = o.output.meshes[0]; 90 | 91 | }, 92 | (err) => { 93 | console.log('Error:', err); 94 | } 95 | ); 96 | } 97 | 98 | 99 | function render() { 100 | if(!container) { 101 | return 102 | } 103 | 104 | // GL.rotate(container.matrix); 105 | // shader.bind(); 106 | // shader.uniform('color', 'vec3', [1, 1, 1]); 107 | // shader.uniform('opacity', 'float', 1); 108 | // GL.draw(mesh); 109 | } 110 | -------------------------------------------------------------------------------- /dev/assets/gltf/deer/deer1.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "componentType" : 5123, 6 | "count" : 10071, 7 | "max" : [ 8 | 2486 9 | ], 10 | "min" : [ 11 | 0 12 | ], 13 | "type" : "SCALAR" 14 | }, 15 | { 16 | "bufferView" : 1, 17 | "componentType" : 5126, 18 | "count" : 2487, 19 | "max" : [ 20 | 0.6151880025863647, 21 | 0.2339009940624237, 22 | 0.00012892199447378516 23 | ], 24 | "min" : [ 25 | -1.2360399961471558, 26 | -0.26256000995635986, 27 | -2.497450113296509 28 | ], 29 | "type" : "VEC3" 30 | }, 31 | { 32 | "bufferView" : 2, 33 | "componentType" : 5126, 34 | "count" : 2487, 35 | "max" : [ 36 | 0.999969482421875, 37 | 0.9998779296875, 38 | 0.999969482421875 39 | ], 40 | "min" : [ 41 | -0.999969482421875, 42 | -0.9998779296875, 43 | -0.99951171875 44 | ], 45 | "type" : "VEC3" 46 | } 47 | ], 48 | "asset" : { 49 | "generator" : "Khronos Blender glTF 2.0 exporter", 50 | "version" : "2.0" 51 | }, 52 | "bufferViews" : [ 53 | { 54 | "buffer" : 0, 55 | "byteLength" : 20142, 56 | "byteOffset" : 0, 57 | "target" : 34963 58 | }, 59 | { 60 | "buffer" : 0, 61 | "byteLength" : 29844, 62 | "byteOffset" : 20144, 63 | "target" : 34962 64 | }, 65 | { 66 | "buffer" : 0, 67 | "byteLength" : 29844, 68 | "byteOffset" : 49988, 69 | "target" : 34962 70 | } 71 | ], 72 | "buffers" : [ 73 | { 74 | "byteLength" : 79832, 75 | "uri" : "deer1.bin" 76 | } 77 | ], 78 | "meshes" : [ 79 | { 80 | "name" : "deer", 81 | "primitives" : [ 82 | { 83 | "attributes" : { 84 | "NORMAL" : 2, 85 | "POSITION" : 1 86 | }, 87 | "indices" : 0 88 | } 89 | ] 90 | } 91 | ], 92 | "nodes" : [ 93 | { 94 | "mesh" : 0, 95 | "name" : "deer", 96 | "rotation" : [ 97 | 0.7071068286895752, 98 | 0.0, 99 | -0.0, 100 | 0.7071067094802856 101 | ] 102 | } 103 | ], 104 | "scene" : 0, 105 | "scenes" : [ 106 | { 107 | "name" : "Scene", 108 | "nodes" : [ 109 | 0 110 | ] 111 | } 112 | ] 113 | } 114 | -------------------------------------------------------------------------------- /tasks/shader-watcher.js: -------------------------------------------------------------------------------- 1 | // shader-watcher.js 2 | 3 | 'use strict' 4 | 5 | const fs = require('fs') 6 | const path = require('path') 7 | const findFolder = require('./find-folder') 8 | const watcher = require('./watch') 9 | const copyFile = require('./copy-file') 10 | const checkExtension = require('./checkExtension') 11 | 12 | const PATH_SRC = './dev' 13 | const TEMPLATE_VERTEX = './tasks/basic.vert' 14 | const TEMPLATE_FRAGMENT = './tasks/copy.frag' 15 | const regShader = /shaders\/.+\.(vert|frag)/g 16 | 17 | let shaderPath 18 | 19 | findFolder(PATH_SRC, 'shaders', (mPath) => { 20 | shaderPath = mPath 21 | startWatch() 22 | }) 23 | 24 | const watcherViews = watcher([PATH_SRC]) 25 | 26 | function startWatch () { 27 | watcherViews.on('all', (event, file) => { 28 | if (file.indexOf('.DS_Store') > -1) return 29 | if (!checkExtension(file, ['js'])) return 30 | console.log('Event:', event, 'file :', file) 31 | if (event !== 'add' && event !== 'change') return 32 | 33 | checkFile(file) 34 | }) 35 | } 36 | 37 | const checkFile = (file) => { 38 | getShaderImports(file).then(shaderImports => { 39 | return shaderImports.reduce((sequence, shaderName) => { 40 | return sequence.then(() => { 41 | return isShaderExist(shaderName) 42 | }).then((mName) => { 43 | generateShader(mName) 44 | }).catch((err) => { 45 | console.log(err) 46 | }) 47 | }, Promise.resolve()) 48 | }).catch((err) => { 49 | console.log('Error:', err) 50 | }) 51 | } 52 | 53 | const getShaderImports = (mPath) => new Promise((resolve, reject) => { 54 | let results = [] 55 | 56 | fs.readFile(mPath, 'utf8', (err, str) => { 57 | if (err) { 58 | reject('Error Loading file !') 59 | } else { 60 | let match 61 | while (match = regShader.exec(str)) { 62 | results.push(match[0]) 63 | } 64 | 65 | results = results.map((mPath) => { 66 | return mPath.replace('shaders/', '') 67 | }) 68 | 69 | resolve(results) 70 | } 71 | }) 72 | }) 73 | 74 | const isShaderExist = (name) => new Promise((resolve, reject) => { 75 | fs.readdir(shaderPath, (err, files) => { 76 | if (err) { 77 | reject(err) 78 | return 79 | } 80 | if (files.indexOf(name) === -1) { 81 | resolve(name) 82 | } else { 83 | reject(`Shader existed : ${name}`) 84 | } 85 | }) 86 | }) 87 | 88 | function generateShader (mName) { 89 | if (isVertexShader(mName)) { 90 | generateVertexShader(mName) 91 | } else { 92 | generateFragmentShader(mName) 93 | } 94 | } 95 | 96 | function generateVertexShader (mName) { 97 | console.log('Generate vertex shader :', mName) 98 | copyFile(TEMPLATE_VERTEX, path.resolve(shaderPath, mName), (err) => { 99 | if (err) console.log('Err', err) 100 | }) 101 | } 102 | 103 | function generateFragmentShader (mName) { 104 | console.log('Generate fragment shader : ', mName) 105 | copyFile(TEMPLATE_FRAGMENT, path.resolve(shaderPath, mName), (err) => { 106 | if (err) console.log('Err', err) 107 | }) 108 | } 109 | 110 | function isVertexShader (mName) { 111 | return mName.indexOf('.vert') > -1 112 | } 113 | -------------------------------------------------------------------------------- /dev/features/webgl2.js: -------------------------------------------------------------------------------- 1 | 2 | import '../global.scss' 3 | import alfrid, { GL } from '../../src/alfrid' 4 | import debugCamera from './utils/debugCamera' 5 | 6 | import vs from '../shaders/shadowMap.vert' 7 | import fs from '../shaders/shadowMap.frag' 8 | 9 | import vsES from '../shaders/shadowMap300es.vert' 10 | import fsES from '../shaders/shadowMap300es.frag' 11 | 12 | function init () { 13 | const canvas = document.createElement('canvas') 14 | document.body.appendChild(canvas) 15 | 16 | // INIT GL 17 | GL.init(canvas, { useWebgl2: true }) 18 | // GL.init(canvas, { useWebgl2: false }) 19 | GL.setSize(window.innerWidth, window.innerHeight) 20 | 21 | console.log(GL.webgl2) 22 | 23 | // cameras 24 | const camera = new alfrid.CameraPerspective() 25 | camera.setPerspective(Math.PI / 4, GL.aspectRatio, 1, 100) 26 | const orbControl = new alfrid.OrbitalControl(camera, window, 5) 27 | orbControl.rx.value = orbControl.ry.value = 0.3 28 | 29 | const cameraSide = new alfrid.CameraPerspective() 30 | cameraSide.setPerspective(Math.PI / 4, GL.aspectRatio, 2, 4.5) 31 | cameraSide.lookAt([0, 4, 1], [0, 0, 0]) 32 | 33 | const biasMatrix = mat4.fromValues( 34 | 0.5, 0.0, 0.0, 0.0, 35 | 0.0, 0.5, 0.0, 0.0, 36 | 0.0, 0.0, 0.5, 0.0, 37 | 0.5, 0.5, 0.5, 1.0 38 | ) 39 | const mtxShadow = mat4.create() 40 | mat4.mul(mtxShadow, cameraSide.projection, cameraSide.matrix) 41 | mat4.mul(mtxShadow, biasMatrix, mtxShadow) 42 | 43 | const mtxCube = mat4.create() 44 | const mtxFloor = mat4.create() 45 | mat4.translate(mtxCube, mtxCube, [0, 1, 0]) 46 | 47 | // fbo 48 | const fboSize = 1024 * 2 49 | const fbo = new alfrid.FrameBuffer(fboSize, fboSize) 50 | 51 | // helpers 52 | const bAxis = new alfrid.BatchAxis() 53 | const bDots = new alfrid.BatchDotsPlane() 54 | const bCopy = new alfrid.BatchCopy() 55 | 56 | let s = 0.25 57 | const drawCube = new alfrid.Draw() 58 | .setMesh(alfrid.Geom.cube(s, s, s)) 59 | .useProgram(null, alfrid.ShaderLibs.simpleColorFrag) 60 | .uniform('color', 'vec3', [1, 1, 0]) 61 | .uniform('opacity', 'float', 1) 62 | 63 | s = 3 64 | const drawFloor = new alfrid.Draw() 65 | .setMesh(alfrid.Geom.plane(s, s, 1, 'xz')) 66 | 67 | if (GL.webgl2) { 68 | drawFloor.useProgram(vsES, fsES) 69 | } else { 70 | drawFloor.useProgram(vs, fs) 71 | } 72 | 73 | const render = () => { 74 | GL.clear(0, 0, 0, 1) 75 | 76 | let s = 0 77 | 78 | // render fo fbo 79 | fbo.bind() 80 | GL.clear(s, s, s, 1) 81 | GL.setMatrices(cameraSide) 82 | GL.rotate(mtxCube) 83 | drawCube.draw() 84 | fbo.unbind() 85 | 86 | GL.setMatrices(camera) 87 | bAxis.draw() 88 | bDots.draw() 89 | 90 | GL.rotate(mtxCube) 91 | drawCube.draw() 92 | GL.rotate(mtxFloor) 93 | drawFloor 94 | .uniform('uShadowMatrix', 'mat4', mtxShadow) 95 | .uniformTexture('textureDepth', fbo.depthTexture, 0) 96 | .draw() 97 | debugCamera(cameraSide) 98 | 99 | s = 300 100 | GL.viewport(0, 0, s, s / GL.aspectRatio) 101 | bCopy.draw(fbo.texture) 102 | 103 | GL.viewport(s, 0, s, s / GL.aspectRatio) 104 | bCopy.draw(fbo.depthTexture) 105 | } 106 | 107 | window.addEventListener('resize', () => { 108 | GL.setSize(window.innerWidth, window.innerHeight) 109 | camera.setAspectRatio(GL.aspectRatio) 110 | cameraSide.setAspectRatio(GL.aspectRatio) 111 | }) 112 | 113 | alfrid.Scheduler.addEF(render) 114 | } 115 | 116 | init() 117 | -------------------------------------------------------------------------------- /dev/shaders/floor.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | in vec4 vShadowCoord; 5 | uniform sampler2D textureDepth; 6 | out vec4 oColor; 7 | 8 | 9 | float samplePCF4x4( vec4 sc ) 10 | { 11 | const float r = 2.0; 12 | const float s = 2.0 * r; 13 | 14 | float shadow = 0.0; 15 | float threshold = 1.0; 16 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s,-s) ).r); 17 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r,-s) ).r); 18 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r,-s) ).r); 19 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s,-s) ).r); 20 | 21 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s,-r) ).r); 22 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r,-r) ).r); 23 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r,-r) ).r); 24 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s,-r) ).r); 25 | 26 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s, r) ).r); 27 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r, r) ).r); 28 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r, r) ).r); 29 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s, r) ).r); 30 | 31 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-s, s) ).r); 32 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2(-r, s) ).r); 33 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( r, s) ).r); 34 | shadow += step(threshold, textureProjOffset( textureDepth, sc, ivec2( s, s) ).r); 35 | 36 | return shadow/16.0; 37 | } 38 | 39 | 40 | float samplePCF4x4NoThreshold( vec4 sc ) 41 | { 42 | const int r = 2; 43 | const int s = 2 * r; 44 | 45 | float shadow = 0.0; 46 | shadow += textureProjOffset( textureDepth, sc, ivec2(-s,-s) ).r; 47 | shadow += textureProjOffset( textureDepth, sc, ivec2(-r,-s) ).r; 48 | shadow += textureProjOffset( textureDepth, sc, ivec2( r,-s) ).r; 49 | shadow += textureProjOffset( textureDepth, sc, ivec2( s,-s) ).r; 50 | 51 | shadow += textureProjOffset( textureDepth, sc, ivec2(-s,-r) ).r; 52 | shadow += textureProjOffset( textureDepth, sc, ivec2(-r,-r) ).r; 53 | shadow += textureProjOffset( textureDepth, sc, ivec2( r,-r) ).r; 54 | shadow += textureProjOffset( textureDepth, sc, ivec2( s,-r) ).r; 55 | 56 | shadow += textureProjOffset( textureDepth, sc, ivec2(-s, r) ).r; 57 | shadow += textureProjOffset( textureDepth, sc, ivec2(-r, r) ).r; 58 | shadow += textureProjOffset( textureDepth, sc, ivec2( r, r) ).r; 59 | shadow += textureProjOffset( textureDepth, sc, ivec2( s, r) ).r; 60 | 61 | shadow += textureProjOffset( textureDepth, sc, ivec2(-s, s) ).r; 62 | shadow += textureProjOffset( textureDepth, sc, ivec2(-r, s) ).r; 63 | shadow += textureProjOffset( textureDepth, sc, ivec2( r, s) ).r; 64 | shadow += textureProjOffset( textureDepth, sc, ivec2( s, s) ).r; 65 | 66 | return shadow/16.0; 67 | } 68 | 69 | 70 | void main(void) { 71 | vec4 shadowCoord = vShadowCoord / vShadowCoord.w; 72 | // float s = PCFShadow(textureDepth, uMapSize, shadowCoord); 73 | float s = samplePCF4x4(shadowCoord); 74 | // float s = samplePCF4x4NoThreshold(shadowCoord); 75 | 76 | oColor = vec4(vec3(s), 1.0); 77 | } -------------------------------------------------------------------------------- /src/alfrid/utils/TouchDetector.js: -------------------------------------------------------------------------------- 1 | // TouchDetector.js 2 | import GL from '../GLTool'; 3 | import EventDispatcher from './EventDispatcher'; 4 | import Ray from '../math/Ray'; 5 | import getMouse from './getMouse'; 6 | 7 | function distance(a, b) { 8 | const dx = a.x - b.x; 9 | const dy = a.y - b.y; 10 | return Math.sqrt(dx * dx + dy * dy); 11 | } 12 | 13 | class TouchDetector extends EventDispatcher { 14 | constructor(mMesh, mCamera, mSkipMoveCheck=false, mListenerTarget=window) { 15 | super(); 16 | 17 | this._mesh = mMesh; 18 | this._mesh.generateFaces(); 19 | this._camera = mCamera; 20 | this.faceVertices = mMesh.faces.map((face)=>(face.vertices)); 21 | this.clickTolerance = 8; 22 | 23 | this._ray = new Ray([0, 0, 0], [0, 0, -1]); 24 | this._hit = vec3.fromValues(-999, -999, -999); 25 | this._lastPos; 26 | this._firstPos; 27 | this.mtxModel = mat4.create(); 28 | 29 | this._listenerTarget = mListenerTarget; 30 | this._skippingMove = mSkipMoveCheck; 31 | 32 | this._onMoveBind = (e) => this._onMove(e); 33 | this._onDownBind = (e) => this._onDown(e); 34 | this._onUpBind = () => this._onUp(); 35 | 36 | this.connect(); 37 | } 38 | 39 | connect() { 40 | this._listenerTarget.addEventListener('mousedown', this._onDownBind); 41 | this._listenerTarget.addEventListener('mousemove', this._onMoveBind); 42 | this._listenerTarget.addEventListener('mouseup', this._onUpBind); 43 | } 44 | 45 | disconnect() { 46 | this._listenerTarget.removeEventListener('mousedown', this._onDownBind); 47 | this._listenerTarget.removeEventListener('mousemove', this._onMoveBind); 48 | this._listenerTarget.removeEventListener('mouseup', this._onUpBind); 49 | } 50 | 51 | 52 | _checkHit(mType='onHit') { 53 | const camera = this._camera; 54 | if(!camera) { 55 | return; 56 | } 57 | 58 | 59 | const mx = (this._lastPos.x / GL.width) * 2.0 - 1.0; 60 | const my = - (this._lastPos.y / GL.height) * 2.0 + 1.0; 61 | 62 | camera.generateRay([mx, my, 0], this._ray); 63 | 64 | let hit; 65 | const v0 = vec3.create(); 66 | const v1 = vec3.create(); 67 | const v2 = vec3.create(); 68 | let dist = 0; 69 | 70 | const getVector = (v, target) => { 71 | vec3.transformMat4(target, v, this.mtxModel); 72 | }; 73 | 74 | for(let i = 0; i < this.faceVertices.length; i++) { 75 | const vertices = this.faceVertices[i]; 76 | getVector(vertices[0], v0); 77 | getVector(vertices[1], v1); 78 | getVector(vertices[2], v2); 79 | const t = this._ray.intersectTriangle(v0, v1, v2); 80 | 81 | if(t) { 82 | if(hit) { 83 | const distToCam = vec3.dist(t, camera.position); 84 | if(distToCam < dist) { 85 | hit = vec3.clone(t); 86 | dist = distToCam; 87 | } 88 | } else { 89 | hit = vec3.clone(t); 90 | dist = vec3.dist(hit, camera.position); 91 | } 92 | } 93 | } 94 | 95 | 96 | if(hit) { 97 | this._hit = vec3.clone(hit); 98 | this.dispatchCustomEvent(mType, { hit }); 99 | } else { 100 | this.dispatchCustomEvent('onUp'); 101 | } 102 | } 103 | 104 | 105 | _onDown(e) { 106 | this._firstPos = getMouse(e); 107 | this._lastPos = getMouse(e); 108 | this._checkHit('onDown'); 109 | } 110 | 111 | _onMove(e) { 112 | this._lastPos = getMouse(e); 113 | if(!this._skippingMove) { 114 | this._checkHit(); 115 | } 116 | } 117 | 118 | _onUp() { 119 | const dist = distance(this._firstPos, this._lastPos); 120 | if(dist < this.clickTolerance) { 121 | this._checkHit(); 122 | } 123 | 124 | } 125 | 126 | } 127 | 128 | export default TouchDetector; -------------------------------------------------------------------------------- /src/alfrid/helpers/Draw.js: -------------------------------------------------------------------------------- 1 | // Draw.js 2 | 3 | import GL from '../GLTool'; 4 | import Mesh from '../Mesh'; 5 | import GLShader from '../GLShader'; 6 | 7 | class Draw { 8 | 9 | constructor() { 10 | this._uniforms = {}; 11 | this._uniformTextures = []; 12 | this._fbo; 13 | 14 | this._clearColor = { r:0, g:0, b:0, a:0 }; 15 | 16 | return this; 17 | } 18 | 19 | 20 | setClearColor(r=0, g=0, b=0, a=0) { 21 | this._clearColor.r = r; 22 | this._clearColor.g = g; 23 | this._clearColor.b = b; 24 | this._clearColor.a = a; 25 | return this; 26 | } 27 | 28 | 29 | useProgram(vs, fs) { 30 | if(vs instanceof GLShader) { 31 | this._shader = vs; 32 | } else { 33 | this._shader = new GLShader(vs, fs); 34 | } 35 | 36 | return this; 37 | } 38 | 39 | 40 | setMesh(mMesh) { 41 | this._mesh = mMesh; 42 | return this; 43 | } 44 | 45 | 46 | createMesh(mType) { 47 | this._mesh = new Mesh(mType); 48 | return this; 49 | } 50 | 51 | 52 | bufferVertex(mArrayVertices) { 53 | if(!this._mesh) { 54 | this._mesh = new Mesh(); 55 | } 56 | this._mesh.bufferVertex(mArrayVertices); 57 | return this; 58 | } 59 | 60 | 61 | bufferTexCoord(mArrayTexCoords) { 62 | if(!this._mesh) { 63 | this._mesh = new Mesh(); 64 | } 65 | this._mesh.bufferTexCoord(mArrayTexCoords); 66 | return this; 67 | } 68 | 69 | 70 | bufferNormal(mArrayNormals) { 71 | if(!this._mesh) { 72 | this._mesh = new Mesh(); 73 | } 74 | this._mesh.bufferNormal(mArrayNormals); 75 | return this; 76 | } 77 | 78 | 79 | bufferIndex(mIndices) { 80 | if(!this._mesh) { 81 | this._mesh = new Mesh(); 82 | } 83 | this._mesh.bufferIndex(mIndices); 84 | return this; 85 | } 86 | 87 | bufferInstance(mData, mName) { 88 | if(!this._mesh) { 89 | console.warn('Need to create mesh first'); 90 | return this; 91 | } 92 | 93 | this._mesh.bufferInstance(mData, mName); 94 | 95 | return this; 96 | } 97 | 98 | 99 | bufferData(mArrayData, mName) { 100 | if(!this._mesh) { 101 | this._mesh = new Mesh(); 102 | } 103 | this._mesh.bufferData(mArrayData, mName); 104 | return this; 105 | } 106 | 107 | 108 | uniform(name, type, value) { 109 | this._uniforms[name] = { 110 | type, 111 | value 112 | }; 113 | 114 | return this; 115 | } 116 | 117 | 118 | uniformTexture(name, texture, index) { 119 | if(index !== undefined) { 120 | this._uniformTextures[index] = { 121 | name, 122 | texture 123 | }; 124 | } else { 125 | this._uniformTextures.push({ 126 | name, 127 | texture 128 | }); 129 | } 130 | 131 | 132 | return this; 133 | } 134 | 135 | 136 | bindFrameBuffer(fbo) { 137 | this._fbo = fbo; 138 | return this; 139 | } 140 | 141 | 142 | draw() { 143 | if(!this._shader) { return; } 144 | if(!this._mesh) { return; } 145 | 146 | 147 | if(this._fbo) { 148 | const { r, g, b, a } = this._clearColor; 149 | this._fbo.bind(); 150 | GL.clear(r, g, b, a); 151 | } 152 | 153 | this._shader.bind(); 154 | for(const s in this._uniforms) { 155 | const o = this._uniforms[s]; 156 | this._shader.uniform(s, o.type, o.value); 157 | } 158 | 159 | this._uniformTextures.forEach((o, i) => { 160 | if(o !== undefined) { 161 | this._shader.uniform(o.name, 'uniform1i', i); 162 | o.texture.bind(i); 163 | } 164 | }); 165 | 166 | GL.draw(this._mesh); 167 | 168 | if(this._fbo) { 169 | this._fbo.unbind(); 170 | } 171 | 172 | return this; 173 | } 174 | 175 | 176 | get shader() { 177 | return this._shader; 178 | } 179 | 180 | get framebuffer() { 181 | return this._fbo; 182 | } 183 | } 184 | 185 | export default Draw; -------------------------------------------------------------------------------- /dev/noise.frag: -------------------------------------------------------------------------------- 1 | // noise.frag 2 | 3 | #define SHADER_NAME SIMPLE_TEXTURE 4 | 5 | precision highp float; 6 | varying vec2 vTextureCoord; 7 | uniform sampler2D texture; 8 | uniform float uTime; 9 | 10 | 11 | vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 12 | 13 | vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 14 | 15 | vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); } 16 | 17 | vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r;} 18 | 19 | float snoise(vec3 v) { 20 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 21 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 22 | 23 | vec3 i = floor(v + dot(v, C.yyy) ); 24 | vec3 x0 = v - i + dot(i, C.xxx) ; 25 | 26 | vec3 g = step(x0.yzx, x0.xyz); 27 | vec3 l = 1.0 - g; 28 | vec3 i1 = min( g.xyz, l.zxy ); 29 | vec3 i2 = max( g.xyz, l.zxy ); 30 | 31 | vec3 x1 = x0 - i1 + C.xxx; 32 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 33 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 34 | 35 | i = mod289(i); 36 | vec4 p = permute( permute( permute( 37 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 38 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 39 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 40 | 41 | float n_ = 0.142857142857; // 1.0/7.0 42 | vec3 ns = n_ * D.wyz - D.xzx; 43 | 44 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 45 | 46 | vec4 x_ = floor(j * ns.z); 47 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 48 | 49 | vec4 x = x_ *ns.x + ns.yyyy; 50 | vec4 y = y_ *ns.x + ns.yyyy; 51 | vec4 h = 1.0 - abs(x) - abs(y); 52 | 53 | vec4 b0 = vec4( x.xy, y.xy ); 54 | vec4 b1 = vec4( x.zw, y.zw ); 55 | 56 | vec4 s0 = floor(b0)*2.0 + 1.0; 57 | vec4 s1 = floor(b1)*2.0 + 1.0; 58 | vec4 sh = -step(h, vec4(0.0)); 59 | 60 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 61 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 62 | 63 | vec3 p0 = vec3(a0.xy,h.x); 64 | vec3 p1 = vec3(a0.zw,h.y); 65 | vec3 p2 = vec3(a1.xy,h.z); 66 | vec3 p3 = vec3(a1.zw,h.w); 67 | 68 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 69 | p0 *= norm.x; 70 | p1 *= norm.y; 71 | p2 *= norm.z; 72 | p3 *= norm.w; 73 | 74 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 75 | m = m * m; 76 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 77 | dot(p2,x2), dot(p3,x3) ) ); 78 | } 79 | 80 | vec3 snoiseVec3( vec3 x ){ 81 | 82 | float s = snoise(vec3( x )); 83 | float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 )); 84 | float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 )); 85 | vec3 c = vec3( s , s1 , s2 ); 86 | return c; 87 | 88 | } 89 | 90 | 91 | vec3 curlNoise( vec3 p ){ 92 | 93 | const float e = .1; 94 | vec3 dx = vec3( e , 0.0 , 0.0 ); 95 | vec3 dy = vec3( 0.0 , e , 0.0 ); 96 | vec3 dz = vec3( 0.0 , 0.0 , e ); 97 | 98 | vec3 p_x0 = snoiseVec3( p - dx ); 99 | vec3 p_x1 = snoiseVec3( p + dx ); 100 | vec3 p_y0 = snoiseVec3( p - dy ); 101 | vec3 p_y1 = snoiseVec3( p + dy ); 102 | vec3 p_z0 = snoiseVec3( p - dz ); 103 | vec3 p_z1 = snoiseVec3( p + dz ); 104 | 105 | float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y; 106 | float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z; 107 | float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x; 108 | 109 | const float divisor = 1.0 / ( 2.0 * e ); 110 | return normalize( vec3( x , y , z ) * divisor ); 111 | 112 | } 113 | 114 | void main(void) { 115 | float scale = sin(uTime * 0.5) * 49.0 + 50.0; 116 | vec3 noise = curlNoise(vec3(vTextureCoord * scale, 0.0)); 117 | gl_FragColor = vec4(noise, 1.0); 118 | } -------------------------------------------------------------------------------- /dev/assets/gltf/head/model.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "componentType" : 5123, 6 | "count" : 181614, 7 | "max" : [ 8 | 39938 9 | ], 10 | "min" : [ 11 | 0 12 | ], 13 | "type" : "SCALAR" 14 | }, 15 | { 16 | "bufferView" : 1, 17 | "componentType" : 5126, 18 | "count" : 39939, 19 | "max" : [ 20 | 0.786670982837677, 21 | 1.1945680379867554, 22 | 1.6297520399093628 23 | ], 24 | "min" : [ 25 | -0.7864000201225281, 26 | -1.194195032119751, 27 | -1.6292200088500977 28 | ], 29 | "type" : "VEC3" 30 | }, 31 | { 32 | "bufferView" : 2, 33 | "componentType" : 5126, 34 | "count" : 39939, 35 | "max" : [ 36 | 0.999969482421875, 37 | 0.99993896484375, 38 | 0.999969482421875 39 | ], 40 | "min" : [ 41 | -0.999969482421875, 42 | -0.999969482421875, 43 | -0.9998779296875 44 | ], 45 | "type" : "VEC3" 46 | }, 47 | { 48 | "bufferView" : 3, 49 | "componentType" : 5126, 50 | "count" : 39939, 51 | "max" : [ 52 | 1.0, 53 | 1.0 54 | ], 55 | "min" : [ 56 | 0.0, 57 | 0.0 58 | ], 59 | "type" : "VEC2" 60 | } 61 | ], 62 | "asset" : { 63 | "generator" : "Khronos Blender glTF 2.0 exporter", 64 | "version" : "2.0" 65 | }, 66 | "bufferViews" : [ 67 | { 68 | "buffer" : 0, 69 | "byteLength" : 363228, 70 | "byteOffset" : 0, 71 | "target" : 34963 72 | }, 73 | { 74 | "buffer" : 0, 75 | "byteLength" : 479268, 76 | "byteOffset" : 363228, 77 | "target" : 34962 78 | }, 79 | { 80 | "buffer" : 0, 81 | "byteLength" : 479268, 82 | "byteOffset" : 842496, 83 | "target" : 34962 84 | }, 85 | { 86 | "buffer" : 0, 87 | "byteLength" : 319512, 88 | "byteOffset" : 1321764, 89 | "target" : 34962 90 | } 91 | ], 92 | "buffers" : [ 93 | { 94 | "byteLength" : 1641276, 95 | "uri" : "model.bin" 96 | } 97 | ], 98 | "meshes" : [ 99 | { 100 | "name" : "Object", 101 | "primitives" : [ 102 | { 103 | "attributes" : { 104 | "NORMAL" : 2, 105 | "POSITION" : 1, 106 | "TEXCOORD_0" : 3 107 | }, 108 | "indices" : 0 109 | } 110 | ] 111 | } 112 | ], 113 | "nodes" : [ 114 | { 115 | "mesh" : 0, 116 | "name" : "Object", 117 | "rotation" : [ 118 | 0.7071068286895752, 119 | 0.0, 120 | -0.0, 121 | 0.7071067094802856 122 | ] 123 | } 124 | ], 125 | "scene" : 0, 126 | "scenes" : [ 127 | { 128 | "name" : "Scene", 129 | "nodes" : [ 130 | 0 131 | ] 132 | } 133 | ] 134 | } 135 | -------------------------------------------------------------------------------- /src/alfrid/math/Ray.js: -------------------------------------------------------------------------------- 1 | // Ray.js 2 | 3 | import { mat4, vec3 } from 'gl-matrix'; 4 | 5 | 6 | const a = vec3.create(); 7 | const b = vec3.create(); 8 | const c = vec3.create(); 9 | const target = vec3.create(); 10 | const edge1 = vec3.create(); 11 | const edge2 = vec3.create(); 12 | const normal = vec3.create(); 13 | const diff = vec3.create(); 14 | 15 | class Ray { 16 | constructor(mOrigin, mDirection) { 17 | this.origin = vec3.clone(mOrigin); 18 | this.direction = vec3.clone(mDirection); 19 | } 20 | 21 | at(t) { 22 | vec3.copy(target, this.direction); 23 | vec3.scale(target, target, t); 24 | vec3.add(target, target, this.origin); 25 | 26 | return target; 27 | } 28 | 29 | 30 | lookAt(mTarget) { 31 | vec3.sub(this.direction, mTarget, this.origin); 32 | vec3.normalize(this.origin, this.origin); 33 | } 34 | 35 | closestPointToPoint(mPoint) { 36 | const result = vec3.create(); 37 | vec3.sub(mPoint, this.origin); 38 | const directionDistance = vec3.dot(result, this.direction); 39 | 40 | if (directionDistance < 0) { 41 | return vec3.clone(this.origin); 42 | } 43 | 44 | vec3.copy(result, this.direction); 45 | vec3.scale(result, result, directionDistance); 46 | vec3.add(result, result, this.origin); 47 | 48 | return result; 49 | } 50 | 51 | 52 | distanceToPoint(mPoint) { 53 | return Math.sqrt(this.distanceSqToPoint(mPoint)); 54 | } 55 | 56 | 57 | distanceSqToPoint(mPoint) { 58 | const v1 = vec3.create(); 59 | 60 | vec3.sub(v1, mPoint, this.origin); 61 | const directionDistance = vec3.dot(v1, this.direction); 62 | 63 | if (directionDistance < 0) { 64 | return vec3.squaredDistance(this.origin, mPoint); 65 | } 66 | 67 | vec3.copy(v1, this.direction); 68 | vec3.scale(v1, v1, directionDistance); 69 | vec3.add(v1, v1, this.origin); 70 | return vec3.squaredDistance(v1, mPoint); 71 | } 72 | 73 | 74 | intersectsSphere(mCenter, mRadius) { 75 | return this.distanceToPoint(mCenter) <= mRadius; 76 | } 77 | 78 | 79 | intersectSphere(mCenter, mRadius) { 80 | const v1 = vec3.create(); 81 | vec3.sub(v1, mCenter, this.origin); 82 | const tca = vec3.dot(v1, this.direction); 83 | const d2 = vec3.dot(v1, v1) - tca * tca; 84 | const radius2 = mRadius * mRadius; 85 | 86 | if(d2 > radius2) return null; 87 | 88 | const thc = Math.sqrt(radius2 - d2); 89 | 90 | const t0 = tca - thc; 91 | 92 | const t1 = tca + thc; 93 | 94 | if(t0 < 0 && t1 < 0) return null; 95 | 96 | if(t0 < 0) return this.at(t1); 97 | 98 | return this.at(t0); 99 | } 100 | 101 | 102 | distanceToPlane(mPlaneCenter, mNormal) { 103 | const denominator = vec3.dot(mNormal, this.direction); 104 | 105 | if(denominator === 0) { 106 | } 107 | } 108 | 109 | 110 | intersectTriangle(mPA, mPB, mPC, backfaceCulling = true) { 111 | vec3.copy(a, mPA); 112 | vec3.copy(b, mPB); 113 | vec3.copy(c, mPC); 114 | 115 | 116 | vec3.sub(edge1, b, a); 117 | vec3.sub(edge2, c, a); 118 | vec3.cross(normal, edge1, edge2); 119 | 120 | let DdN = vec3.dot(this.direction, normal); 121 | let sign; 122 | 123 | if (DdN > 0) { 124 | if (backfaceCulling) { return null; } 125 | sign = 1; 126 | } else if (DdN < 0) { 127 | sign = -1; 128 | DdN = - DdN; 129 | } else { 130 | return null; 131 | } 132 | 133 | vec3.sub(diff, this.origin, a); 134 | 135 | vec3.cross(edge2, diff, edge2); 136 | const DdQxE2 = sign * vec3.dot(this.direction, edge2); 137 | if (DdQxE2 < 0) { return null; } 138 | 139 | vec3.cross(edge1, edge1, diff); 140 | const DdE1xQ = sign * vec3.dot(this.direction, edge1); 141 | if (DdE1xQ < 0) { return null; } 142 | 143 | if(DdQxE2 + DdE1xQ > DdN) { return null; } 144 | 145 | const Qdn = - sign * vec3.dot(diff, normal); 146 | if(Qdn < 0) { return null; } 147 | 148 | return this.at(Qdn / DdN); 149 | } 150 | 151 | } 152 | 153 | export default Ray; 154 | -------------------------------------------------------------------------------- /src/alfrid/shaders/pbrColor.frag: -------------------------------------------------------------------------------- 1 | // pbrColor.frag 2 | 3 | #define SHADER_NAME PBR_COLOR_FRAG 4 | 5 | #extension GL_EXT_shader_texture_lod : enable 6 | 7 | precision highp float; 8 | 9 | uniform sampler2D uAoMap; 10 | uniform samplerCube uRadianceMap; 11 | uniform samplerCube uIrradianceMap; 12 | 13 | uniform vec3 uBaseColor; 14 | uniform float uRoughness; 15 | uniform float uMetallic; 16 | uniform float uSpecular; 17 | 18 | uniform float uExposure; 19 | uniform float uGamma; 20 | 21 | varying vec3 vNormal; 22 | varying vec3 vPosition; 23 | varying vec3 vEyePosition; 24 | varying vec3 vWsNormal; 25 | varying vec3 vWsPosition; 26 | varying vec2 vTextureCoord; 27 | 28 | #define saturate(x) clamp(x, 0.0, 1.0) 29 | #define PI 3.1415926535897932384626433832795 30 | 31 | 32 | // Filmic tonemapping from 33 | // http://filmicgames.com/archives/75 34 | 35 | const float A = 0.15; 36 | const float B = 0.50; 37 | const float C = 0.10; 38 | const float D = 0.20; 39 | const float E = 0.02; 40 | const float F = 0.30; 41 | 42 | vec3 Uncharted2Tonemap( vec3 x ) 43 | { 44 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 45 | } 46 | 47 | // https://www.unrealengine.com/blog/physically-based-shading-on-mobile 48 | vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV ) 49 | { 50 | const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022 ); 51 | const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04 ); 52 | vec4 r = Roughness * c0 + c1; 53 | float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; 54 | vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; 55 | return SpecularColor * AB.x + AB.y; 56 | } 57 | 58 | 59 | // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ 60 | vec3 fix_cube_lookup( vec3 v, float cube_size, float lod ) { 61 | float M = max(max(abs(v.x), abs(v.y)), abs(v.z)); 62 | float scale = 1.0 - exp2(lod) / cube_size; 63 | if (abs(v.x) != M) v.x *= scale; 64 | if (abs(v.y) != M) v.y *= scale; 65 | if (abs(v.z) != M) v.z *= scale; 66 | return v; 67 | } 68 | 69 | vec3 correctGamma(vec3 color, float g) { 70 | return pow(color, vec3(1.0/g)); 71 | } 72 | 73 | vec3 getPbr(vec3 N, vec3 V, vec3 baseColor, float roughness, float metallic, float specular) { 74 | vec3 diffuseColor = baseColor - baseColor * metallic; 75 | vec3 specularColor = mix( vec3( 0.08 * specular ), baseColor, specular ); 76 | 77 | vec3 color; 78 | float roughness4 = pow(roughness, 4.0); 79 | 80 | // sample the pre-filtered cubemap at the corresponding mipmap level 81 | float numMips = 6.0; 82 | float mip = numMips - 1.0 + log2(roughness); 83 | vec3 lookup = -reflect( V, N ); 84 | lookup = fix_cube_lookup( lookup, 512.0, mip ); 85 | vec3 radiance = pow( textureCubeLodEXT( uRadianceMap, lookup, mip ).rgb, vec3( 2.2 ) ); 86 | vec3 irradiance = pow( textureCube( uIrradianceMap, N ).rgb, vec3( 1 ) ); 87 | 88 | // get the approximate reflectance 89 | float NoV = saturate( dot( N, V ) ); 90 | vec3 reflectance = EnvBRDFApprox( specularColor, roughness4, NoV ); 91 | 92 | // combine the specular IBL and the BRDF 93 | vec3 diffuse = diffuseColor * irradiance; 94 | vec3 _specular = radiance * reflectance; 95 | color = diffuse + _specular; 96 | 97 | return color; 98 | } 99 | 100 | void main() { 101 | vec3 N = normalize( vWsNormal ); 102 | vec3 V = normalize( vEyePosition ); 103 | 104 | vec3 color = getPbr(N, V, uBaseColor, uRoughness, uMetallic, uSpecular); 105 | 106 | vec3 ao = texture2D(uAoMap, vTextureCoord).rgb; 107 | color *= ao; 108 | 109 | // apply the tone-mapping 110 | color = Uncharted2Tonemap( color * uExposure ); 111 | // white balance 112 | color = color * ( 1.0 / Uncharted2Tonemap( vec3( 20.0 ) ) ); 113 | 114 | // gamma correction 115 | color = pow( color, vec3( 1.0 / uGamma ) ); 116 | 117 | // output the fragment color 118 | gl_FragColor = vec4( color, 1.0 ); 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /src/alfrid/utils/EventDispatcher.js: -------------------------------------------------------------------------------- 1 | // EventDispatcher.js 2 | 3 | let supportsCustomEvents = true; 4 | try { 5 | let newTestCustomEvent = document.createEvent('CustomEvent'); 6 | newTestCustomEvent = null; 7 | } catch(e) { 8 | supportsCustomEvents = false; 9 | } 10 | 11 | class EventDispatcher { 12 | 13 | constructor() { 14 | this._eventListeners = {}; 15 | } 16 | 17 | 18 | addEventListener(aEventType, aFunction) { 19 | 20 | if(this._eventListeners === null || this._eventListeners === undefined) { 21 | this._eventListeners = {}; 22 | } 23 | 24 | if(!this._eventListeners[aEventType]) { 25 | this._eventListeners[aEventType] = []; 26 | } 27 | this._eventListeners[aEventType].push(aFunction); 28 | 29 | return this; 30 | 31 | } 32 | 33 | on(aEventType, aFunction) { return this.addEventListener(aEventType, aFunction); } 34 | 35 | removeEventListener(aEventType, aFunction) { 36 | if(this._eventListeners === null || this._eventListeners === undefined) { 37 | this._eventListeners = {}; 38 | } 39 | const currentArray = this._eventListeners[aEventType]; 40 | 41 | if (typeof(currentArray) === 'undefined') { 42 | return this; 43 | } 44 | 45 | let currentArrayLength = currentArray.length; 46 | for(let i = 0; i < currentArrayLength; i++) { 47 | if(currentArray[i] === aFunction) { 48 | currentArray.splice(i, 1); 49 | i--; 50 | currentArrayLength--; 51 | } 52 | } 53 | return this; 54 | } 55 | 56 | off(aEventType, aFunction) { return this.removeEventListener(aEventType, aFunction); } 57 | 58 | dispatchEvent(aEvent) { 59 | if(this._eventListeners === null || this._eventListeners === undefined) { 60 | this._eventListeners = {}; 61 | } 62 | const eventType = aEvent.type; 63 | 64 | try { 65 | if(aEvent.target === null) { 66 | aEvent.target = this; 67 | } 68 | aEvent.currentTarget = this; 69 | } catch(theError) { 70 | const newEvent = { type: eventType, detail: aEvent.detail, dispatcher: this }; 71 | return this.dispatchEvent(newEvent); 72 | } 73 | 74 | const currentEventListeners = this._eventListeners[eventType]; 75 | if(currentEventListeners !== null && currentEventListeners !== undefined) { 76 | const currentArray = this._copyArray(currentEventListeners); 77 | const currentArrayLength = currentArray.length; 78 | for(let i = 0; i < currentArrayLength; i++) { 79 | const currentFunction = currentArray[i]; 80 | currentFunction.call(this, aEvent); 81 | } 82 | } 83 | return this; 84 | } 85 | 86 | dispatchCustomEvent(aEventType, aDetail) { 87 | let newEvent; 88 | if (supportsCustomEvents) { 89 | newEvent = document.createEvent('CustomEvent'); 90 | newEvent.dispatcher = this; 91 | newEvent.initCustomEvent(aEventType, false, false, aDetail); 92 | } else { 93 | newEvent = { type: aEventType, detail: aDetail, dispatcher: this }; 94 | } 95 | return this.dispatchEvent(newEvent); 96 | } 97 | 98 | trigger(aEventType, aDetail) { return this.dispatchCustomEvent(aEventType, aDetail); } 99 | 100 | _destroy() { 101 | if(this._eventListeners !== null) { 102 | for(const objectName in this._eventListeners) { 103 | if(this._eventListeners.hasOwnProperty(objectName)) { 104 | const currentArray = this._eventListeners[objectName]; 105 | const currentArrayLength = currentArray.length; 106 | for(let i = 0; i < currentArrayLength; i++) { 107 | currentArray[i] = null; 108 | } 109 | delete this._eventListeners[objectName]; 110 | } 111 | } 112 | this._eventListeners = null; 113 | } 114 | } 115 | 116 | _copyArray(aArray) { 117 | const currentArray = new Array(aArray.length); 118 | const currentArrayLength = currentArray.length; 119 | for(let i = 0; i < currentArrayLength; i++) { 120 | currentArray[i] = aArray[i]; 121 | } 122 | return currentArray; 123 | } 124 | } 125 | 126 | 127 | export default EventDispatcher; -------------------------------------------------------------------------------- /src/alfrid/shaders/pbrTexture.frag: -------------------------------------------------------------------------------- 1 | // pbrTexture.frag 2 | 3 | #define SHADER_NAME PBR_TEXTURE_FRAG 4 | 5 | #extension GL_EXT_shader_texture_lod : enable 6 | 7 | precision highp float; 8 | 9 | uniform sampler2D uAoMap; 10 | uniform sampler2D uColorMap; 11 | uniform samplerCube uRadianceMap; 12 | uniform samplerCube uIrradianceMap; 13 | 14 | uniform float uRoughness; 15 | uniform float uMetallic; 16 | uniform float uSpecular; 17 | 18 | uniform float uExposure; 19 | uniform float uGamma; 20 | 21 | varying vec3 vNormal; 22 | varying vec3 vPosition; 23 | varying vec3 vEyePosition; 24 | varying vec3 vWsNormal; 25 | varying vec3 vWsPosition; 26 | varying vec2 vTextureCoord; 27 | 28 | #define saturate(x) clamp(x, 0.0, 1.0) 29 | #define PI 3.1415926535897932384626433832795 30 | 31 | 32 | // Filmic tonemapping from 33 | // http://filmicgames.com/archives/75 34 | 35 | const float A = 0.15; 36 | const float B = 0.50; 37 | const float C = 0.10; 38 | const float D = 0.20; 39 | const float E = 0.02; 40 | const float F = 0.30; 41 | 42 | vec3 Uncharted2Tonemap( vec3 x ) 43 | { 44 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 45 | } 46 | 47 | // https://www.unrealengine.com/blog/physically-based-shading-on-mobile 48 | vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV ) 49 | { 50 | const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022 ); 51 | const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04 ); 52 | vec4 r = Roughness * c0 + c1; 53 | float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; 54 | vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; 55 | return SpecularColor * AB.x + AB.y; 56 | } 57 | 58 | 59 | // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ 60 | vec3 fix_cube_lookup( vec3 v, float cube_size, float lod ) { 61 | float M = max(max(abs(v.x), abs(v.y)), abs(v.z)); 62 | float scale = 1.0 - exp2(lod) / cube_size; 63 | if (abs(v.x) != M) v.x *= scale; 64 | if (abs(v.y) != M) v.y *= scale; 65 | if (abs(v.z) != M) v.z *= scale; 66 | return v; 67 | } 68 | 69 | vec3 correctGamma(vec3 color, float g) { 70 | return pow(color, vec3(1.0/g)); 71 | } 72 | 73 | vec3 getPbr(vec3 N, vec3 V, vec3 baseColor, float roughness, float metallic, float specular) { 74 | vec3 diffuseColor = baseColor - baseColor * metallic; 75 | vec3 specularColor = mix( vec3( 0.08 * specular ), baseColor, specular ); 76 | 77 | vec3 color; 78 | float roughness4 = pow(roughness, 4.0); 79 | 80 | // sample the pre-filtered cubemap at the corresponding mipmap level 81 | float numMips = 6.0; 82 | float mip = numMips - 1.0 + log2(roughness); 83 | vec3 lookup = -reflect( V, N ); 84 | lookup = fix_cube_lookup( lookup, 512.0, mip ); 85 | vec3 radiance = pow( textureCubeLodEXT( uRadianceMap, lookup, mip ).rgb, vec3( 2.2 ) ); 86 | vec3 irradiance = pow( textureCube( uIrradianceMap, N ).rgb, vec3( 1 ) ); 87 | 88 | // get the approximate reflectance 89 | float NoV = saturate( dot( N, V ) ); 90 | vec3 reflectance = EnvBRDFApprox( specularColor, roughness4, NoV ); 91 | 92 | // combine the specular IBL and the BRDF 93 | vec3 diffuse = diffuseColor * irradiance; 94 | vec3 _specular = radiance * reflectance; 95 | color = diffuse + _specular; 96 | 97 | return color; 98 | } 99 | 100 | void main() { 101 | vec3 N = normalize( vWsNormal ); 102 | vec3 V = normalize( vEyePosition ); 103 | vec3 baseColor = texture2D( uColorMap, vTextureCoord).rgb; 104 | 105 | vec3 color = getPbr(N, V, baseColor, uRoughness, uMetallic, uSpecular); 106 | 107 | vec3 ao = texture2D(uAoMap, vTextureCoord).rgb; 108 | color *= ao; 109 | 110 | // apply the tone-mapping 111 | color = Uncharted2Tonemap( color * uExposure ); 112 | // white balance 113 | color = color * ( 1.0 / Uncharted2Tonemap( vec3( 20.0 ) ) ); 114 | 115 | // gamma correction 116 | color = pow( color, vec3( 1.0 / uGamma ) ); 117 | 118 | // output the fragment color 119 | gl_FragColor = vec4( color, 1.0 ); 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/alfrid/GLCubeTexture.js: -------------------------------------------------------------------------------- 1 | // GLCubeTexture.js 2 | 3 | 'use strict'; 4 | 5 | import GL from './GLTool'; 6 | import parse from 'parse-dds'; 7 | let gl; 8 | const DDSD_MIPMAPCOUNT = 0x20000; 9 | const OFF_MIPMAPCOUNT = 7; 10 | const headerLengthInt = 31; 11 | 12 | class GLCubeTexture { 13 | constructor(mSource, mParameters = {}, isCubeTexture = false) { 14 | gl = GL.gl; 15 | 16 | if(isCubeTexture) { 17 | this.texture = mSource; 18 | return; 19 | } 20 | 21 | let hasMipmaps = mSource.length > 6; 22 | if(mSource[0].mipmapCount) { 23 | hasMipmaps = mSource[0].mipmapCount > 1; 24 | } 25 | 26 | this.texture = gl.createTexture(); 27 | this.magFilter = mParameters.magFilter || gl.LINEAR; 28 | this.minFilter = mParameters.minFilter || gl.LINEAR_MIPMAP_LINEAR; 29 | this.wrapS = mParameters.wrapS || gl.CLAMP_TO_EDGE; 30 | this.wrapT = mParameters.wrapT || gl.CLAMP_TO_EDGE; 31 | 32 | if(!hasMipmaps && this.minFilter == gl.LINEAR_MIPMAP_LINEAR) { 33 | this.minFilter = gl.LINEAR; 34 | } 35 | 36 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture); 37 | const targets = [ 38 | gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 39 | gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 40 | gl.TEXTURE_CUBE_MAP_POSITIVE_Z, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z 41 | ]; 42 | 43 | 44 | let numLevels = 1; 45 | let index = 0; 46 | numLevels = mSource.length / 6; 47 | this.numLevels = numLevels; 48 | 49 | if (hasMipmaps) { 50 | for (let j = 0; j < 6; j++) { 51 | for (let i = 0; i < numLevels; i++) { 52 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 53 | 54 | index = j * numLevels + i; 55 | if(mSource[index].shape) { 56 | gl.texImage2D(targets[j], i, gl.RGBA, mSource[index].shape[0], mSource[index].shape[1], 0, gl.RGBA, gl.FLOAT, mSource[index].data); 57 | } else { 58 | gl.texImage2D(targets[j], i, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mSource[index]); 59 | } 60 | 61 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, this.wrapS); 62 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, this.wrapT); 63 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, this.magFilter); 64 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, this.minFilter); 65 | } 66 | } 67 | } else { 68 | let index = 0; 69 | for (let j = 0; j < 6; j++) { 70 | index = j * numLevels; 71 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 72 | if(mSource[index].shape) { 73 | gl.texImage2D(targets[j], 0, gl.RGBA, mSource[index].shape[0], mSource[index].shape[1], 0, gl.RGBA, gl.FLOAT, mSource[index].data); 74 | } else { 75 | gl.texImage2D(targets[j], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mSource[index]); 76 | } 77 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, this.wrapS); 78 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, this.wrapT); 79 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, this.magFilter); 80 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, this.minFilter); 81 | } 82 | 83 | gl.generateMipmap(gl.TEXTURE_CUBE_MAP); 84 | } 85 | 86 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); 87 | } 88 | 89 | 90 | 91 | // PUBLIC METHOD 92 | 93 | bind(index = 0) { 94 | if(!GL.shader) { return; } 95 | 96 | gl.activeTexture(gl.TEXTURE0 + index); 97 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture); 98 | gl.uniform1i(GL.shader.uniformTextures[index], index); 99 | this._bindIndex = index; 100 | } 101 | 102 | unbind() { 103 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); 104 | } 105 | } 106 | 107 | 108 | GLCubeTexture.parseDDS = function parseDDS(mArrayBuffer) { 109 | 110 | function clamp(value, min, max) { 111 | if (min > max) { 112 | return clamp(value, max, min); 113 | } 114 | 115 | if (value < min) return min; 116 | else if (value > max) return max; 117 | else return value; 118 | } 119 | 120 | // CHECKING MIP MAP LEVELS 121 | const ddsInfos = parse(mArrayBuffer); 122 | const { flags } = ddsInfos; 123 | const header = new Int32Array(mArrayBuffer, 0, headerLengthInt); 124 | let mipmapCount = 1; 125 | if (flags & DDSD_MIPMAPCOUNT) { 126 | mipmapCount = Math.max(1, header[OFF_MIPMAPCOUNT]); 127 | } 128 | const sources = ddsInfos.images.map((img) => { 129 | const faceData = new Float32Array(mArrayBuffer.slice(img.offset, img.offset + img.length)); 130 | return { 131 | data: faceData, 132 | shape: img.shape, 133 | mipmapCount, 134 | }; 135 | }); 136 | 137 | return new GLCubeTexture(sources); 138 | }; 139 | 140 | 141 | export default GLCubeTexture; -------------------------------------------------------------------------------- /tasks/watch-asset.js: -------------------------------------------------------------------------------- 1 | // watch-asset.js 2 | 3 | 'use strict'; 4 | 5 | const fs = require('fs-extra'); 6 | const watcher = require('./watch'); 7 | const getExtension = require('./getExtension'); 8 | const getFileName = require('./getFileName'); 9 | 10 | const ASSETS_PATH = [ 11 | './dist/assets/img', 12 | './dist/assets/obj' 13 | ]; 14 | 15 | 16 | const OUTPUT_PATH = './src/js/asset-list.js'; 17 | const TEMPLATE_PATH = './tasks/asset-template.js'; 18 | let assets = []; 19 | let needUpdate = true; 20 | 21 | 22 | function replace(str, pattern, strToReplace) { 23 | return str.replace(new RegExp(pattern, 'g'), strToReplace); 24 | } 25 | 26 | function saveFile(str) { 27 | fs.writeFile(OUTPUT_PATH, str, (err, data) => { 28 | if(err) { 29 | console.log('Error Writing File'); 30 | } else { 31 | console.log('asset-list.js updated'); 32 | } 33 | }); 34 | } 35 | 36 | function isDir(mPath) { 37 | return fs.lstatSync(mPath).isDirectory() 38 | } 39 | 40 | function getAssetsInDir(mSourceDir, mCallback) { 41 | fs.readdir(mSourceDir, (err, files) => { 42 | 43 | const assetPath = mSourceDir.replace('./dist/', ''); 44 | console.log('Dir path :', mSourceDir, assetPath); 45 | 46 | // ERROR GETTING FOLDER 47 | if(err) { 48 | console.log('Error :', err); 49 | return; 50 | } 51 | 52 | let assets = files.filter((f)=> { 53 | return f.indexOf('DS_Store') === -1 && f.indexOf('.mtl') === -1; 54 | }); 55 | 56 | // console.log('Assets in ', mSourceDir, assets); 57 | 58 | for(let i=0; i { 65 | return isDir(`${mSourceDir}/${a}`); 66 | }); 67 | 68 | assets = assets.filter((a)=> { 69 | return !isDir(`${mSourceDir}/${a}`); 70 | }); 71 | 72 | assets = assets.map((f) => { 73 | return `${assetPath}/${f}`; 74 | }); 75 | 76 | console.log('Folders:', assets); 77 | 78 | if(folders.length == 0) { 79 | mCallback(assets); 80 | } else { 81 | let count = 0; 82 | const onAssets = (a) => { 83 | assets = assets.concat(a); 84 | count ++; 85 | if(count === folders.length) { 86 | mCallback(assets); 87 | } 88 | } 89 | 90 | for(let i=0; i { 104 | assets = assets.concat(files); 105 | count ++; 106 | 107 | if (count == ASSETS_PATH.length) { 108 | generateAssetList(); 109 | } 110 | } 111 | 112 | for(let i=0; i { 135 | const id = getFileName(file); 136 | const url = file; 137 | const ext = getExtension(file); 138 | const type = getAssetType(ext); 139 | 140 | return { 141 | id, 142 | url, 143 | type 144 | } 145 | }); 146 | 147 | let strList = JSON.stringify(list); 148 | strList = strList.replace('[', '[\n\t'); 149 | strList = strList.replace(']', '\n]'); 150 | strList = strList.split('},{').join('},\n\t{'); 151 | console.log(strList); 152 | 153 | fs.readFile(TEMPLATE_PATH, 'utf8', (err, str) => { 154 | if(err) { 155 | console.log('Error Loading file !'); 156 | } else { 157 | str = replace(str, '{{ASSETS}}', strList); 158 | saveFile(str); 159 | } 160 | }); 161 | } 162 | 163 | 164 | // getAssets(); 165 | 166 | function loop() { 167 | if(needUpdate) { 168 | console.log('Update Assets'); 169 | getAssets(); 170 | needUpdate = false; 171 | } 172 | } 173 | 174 | 175 | 176 | const dirPaths = ASSETS_PATH.concat(); 177 | dirPaths.reduce((sequence, dirPath)=> { 178 | return sequence.then(()=>{ 179 | console.log('dirPath', dirPath); 180 | return fs.ensureDir(dirPath); 181 | }).then(()=> { 182 | startWatch(); 183 | }).catch((err)=> { 184 | console.log('Error :', err); 185 | }) 186 | }, Promise.resolve()); 187 | 188 | 189 | const startWatch = () => { 190 | setInterval(loop, 500); 191 | const watcherAssets = watcher([ ASSETS_PATH ]); 192 | 193 | watcherAssets.on('all',(event, file) => { 194 | console.log('Event:',event); 195 | if(file.indexOf('.DS_Store') > -1) return; 196 | needUpdate = true; 197 | }); 198 | } 199 | -------------------------------------------------------------------------------- /src/alfrid/objects/Object3D.js: -------------------------------------------------------------------------------- 1 | // Object3D.js 2 | 3 | import { vec3, mat4, quat } from 'gl-matrix'; 4 | import Scheduler from 'scheduling'; 5 | 6 | class Object3D { 7 | 8 | constructor() { 9 | this._needUpdate = true; 10 | 11 | this._x = 0; 12 | this._y = 0; 13 | this._z = 0; 14 | 15 | this._sx = 1; 16 | this._sy = 1; 17 | this._sz = 1; 18 | 19 | this._rx = 0; 20 | this._ry = 0; 21 | this._rz = 0; 22 | 23 | this._position = vec3.create(); 24 | this._scale = vec3.fromValues(1, 1, 1); 25 | this._rotation = vec3.create(); 26 | 27 | this._matrix = mat4.create(); 28 | this._matrixParent = mat4.create(); 29 | this._matrixRotation = mat4.create(); 30 | this._matrixScale = mat4.create(); 31 | this._matrixTranslation = mat4.create(); 32 | this._matrixQuaternion = mat4.create(); 33 | this._quat = quat.create(); 34 | 35 | this._children = []; 36 | } 37 | 38 | 39 | addChild(mChild) { 40 | this._children.push(mChild); 41 | } 42 | 43 | 44 | removeChild(mChild) { 45 | const index = this._children.indexOf(mChild); 46 | if(index == -1) { console.warn('Child no exist'); return; } 47 | 48 | this._children.splice(index, 1); 49 | } 50 | 51 | 52 | _update() { 53 | if(!this._needUpdate) { return; } 54 | 55 | vec3.set(this._scale, this._sx, this._sy, this._sz); 56 | vec3.set(this._rotation, this._rx, this._ry, this._rz); 57 | vec3.set(this._position, this._x, this._y, this._z); 58 | 59 | mat4.identity(this._matrixTranslation, this._matrixTranslation); 60 | mat4.identity(this._matrixScale, this._matrixScale); 61 | mat4.identity(this._matrixRotation, this._matrixRotation); 62 | 63 | mat4.rotateX(this._matrixRotation, this._matrixRotation, this._rx); 64 | mat4.rotateY(this._matrixRotation, this._matrixRotation, this._ry); 65 | mat4.rotateZ(this._matrixRotation, this._matrixRotation, this._rz); 66 | 67 | 68 | mat4.fromQuat(this._matrixQuaternion, this._quat); 69 | mat4.mul(this._matrixRotation, this._matrixQuaternion, this._matrixRotation); 70 | 71 | mat4.scale(this._matrixScale, this._matrixScale, this._scale); 72 | mat4.translate(this._matrixTranslation, this._matrixTranslation, this._position); 73 | 74 | mat4.mul(this._matrix, this._matrixTranslation, this._matrixRotation); 75 | mat4.mul(this._matrix, this._matrix, this._matrixScale); 76 | // mat4.mul(this._matrix, this._matrix, this._matrixParent); 77 | mat4.mul(this._matrix, this._matrixParent, this._matrix); 78 | 79 | this.updateMatrix(); 80 | 81 | this._needUpdate = false; 82 | } 83 | 84 | 85 | updateMatrix(mParentMatrix) { 86 | if(mParentMatrix) { 87 | this._needUpdate = true; 88 | mat4.copy(this._matrixParent, mParentMatrix); 89 | } 90 | 91 | if(!this._needUpdate) { return; } 92 | 93 | this._children.forEach(child => { 94 | child.updateMatrix(this._matrix); 95 | }); 96 | } 97 | 98 | setRotationFromQuaternion(mQuat) { 99 | quat.copy(this._quat, mQuat); 100 | this._needUpdate = true; 101 | Scheduler.next(()=>this._update()); 102 | } 103 | 104 | 105 | get matrix() { 106 | if(this._needUpdate) { 107 | this._update(); 108 | } 109 | return this._matrix; 110 | } 111 | 112 | get x() { return this._x; } 113 | set x(mValue) { 114 | this._needUpdate = true; 115 | this._x = mValue; 116 | Scheduler.next(()=>this._update()); 117 | } 118 | 119 | get y() { return this._y; } 120 | set y(mValue) { 121 | this._needUpdate = true; 122 | this._y = mValue; 123 | Scheduler.next(()=>this._update()); 124 | } 125 | 126 | get z() { return this._z; } 127 | set z(mValue) { 128 | this._needUpdate = true; 129 | this._z = mValue; 130 | Scheduler.next(()=>this._update()); 131 | } 132 | 133 | 134 | get scaleX() { return this._sx; } 135 | set scaleX(mValue) { 136 | this._needUpdate = true; 137 | this._sx = mValue; 138 | Scheduler.next(()=>this._update()); 139 | } 140 | 141 | get scaleY() { return this._sy; } 142 | set scaleY(mValue) { 143 | this._needUpdate = true; 144 | this._sy = mValue; 145 | Scheduler.next(()=>this._update()); 146 | } 147 | 148 | get scaleZ() { return this._sz; } 149 | set scaleZ(mValue) { 150 | this._needUpdate = true; 151 | this._sz = mValue; 152 | Scheduler.next(()=>this._update()); 153 | } 154 | 155 | 156 | get rotationX() { return this._rx; } 157 | set rotationX(mValue) { 158 | this._needUpdate = true; 159 | this._rx = mValue; 160 | Scheduler.next(()=>this._update()); 161 | } 162 | 163 | get rotationY() { return this._ry; } 164 | set rotationY(mValue) { 165 | this._needUpdate = true; 166 | this._ry = mValue; 167 | Scheduler.next(()=>this._update()); 168 | } 169 | 170 | get rotationZ() { return this._rz; } 171 | set rotationZ(mValue) { 172 | this._needUpdate = true; 173 | this._rz = mValue; 174 | Scheduler.next(()=>this._update()); 175 | } 176 | 177 | get children() { return this._children; } 178 | } 179 | 180 | 181 | export default Object3D; -------------------------------------------------------------------------------- /src/alfrid/MultisampleFrameBuffer.js: -------------------------------------------------------------------------------- 1 | // MultisampleFrameBuffer.js 2 | 3 | import GL from './GLTool'; 4 | import GLTexture from './GLTexture'; 5 | 6 | let gl; 7 | 8 | function isPowerOfTwo(x) { 9 | return (x !== 0) && (!(x & (x - 1))); 10 | }; 11 | 12 | class MultisampleFrameBuffer { 13 | constructor(mWidth, mHeight, mParameters = {}) { 14 | gl = GL.gl; 15 | 16 | this.width = mWidth; 17 | this.height = mHeight; 18 | 19 | this.magFilter = mParameters.magFilter || gl.LINEAR; 20 | this.minFilter = mParameters.minFilter || gl.LINEAR; 21 | this.wrapS = mParameters.wrapS || gl.CLAMP_TO_EDGE; 22 | this.wrapT = mParameters.wrapT || gl.CLAMP_TO_EDGE; 23 | this.useDepth = mParameters.useDepth || true; 24 | this.useStencil = mParameters.useStencil || false; 25 | this.texelType = mParameters.type; 26 | this._numSample = mParameters.numSample || 8; 27 | 28 | if(!isPowerOfTwo(this.width) || !isPowerOfTwo(this.height)) { 29 | this.wrapS = this.wrapT = gl.CLAMP_TO_EDGE; 30 | 31 | if(this.minFilter === gl.LINEAR_MIPMAP_NEAREST) { 32 | this.minFilter = gl.LINEAR; 33 | } 34 | } 35 | 36 | this._init(); 37 | } 38 | 39 | _init() { 40 | let texelType = gl.UNSIGNED_BYTE; 41 | if (this.texelType) { 42 | texelType = this.texelType; 43 | } 44 | 45 | this.texelType = texelType; 46 | 47 | this.frameBuffer = gl.createFramebuffer(); 48 | this.frameBufferColor = gl.createFramebuffer(); 49 | this.renderBufferColor = gl.createRenderbuffer(); 50 | this.renderBufferDepth = gl.createRenderbuffer(); 51 | this.glTexture = this._createTexture(); 52 | this.glDepthTexture = this._createTexture(gl.DEPTH_COMPONENT16, gl.UNSIGNED_SHORT, gl.DEPTH_COMPONENT, true); 53 | 54 | gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBufferColor); 55 | gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this._numSample, gl.RGBA8, this.width, this.height); 56 | 57 | gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBufferDepth); 58 | gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this._numSample, gl.DEPTH_COMPONENT16, this.width, this.height); 59 | 60 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); 61 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.renderBufferColor); 62 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.renderBufferDepth); 63 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 64 | 65 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBufferColor); 66 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.glTexture.texture, 0); 67 | // gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.glDepthTexture.texture, 0); 68 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 69 | 70 | // gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBufferDepth); 71 | // gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.glDepthTexture.texture, 0); 72 | // gl.bindFramebuffer(gl.FRAMEBUFFER, null); 73 | } 74 | 75 | _createTexture(mInternalformat, mTexelType, mFormat, forceNearest = false) { 76 | if(mInternalformat === undefined) { mInternalformat = gl.RGBA; } 77 | if(mTexelType === undefined) { mTexelType = this.texelType; } 78 | if(!mFormat) { mFormat = mInternalformat; } 79 | 80 | const t = gl.createTexture(); 81 | const glt = new GLTexture(t, true); 82 | const magFilter = forceNearest ? GL.NEAREST : this.magFilter; 83 | const minFilter = forceNearest ? GL.NEAREST : this.minFilter; 84 | 85 | gl.bindTexture(gl.TEXTURE_2D, t); 86 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); 87 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); 88 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.wrapS); 89 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.wrapT); 90 | gl.texImage2D(gl.TEXTURE_2D, 0, mInternalformat, this.width, this.height, 0, mFormat, mTexelType, null); 91 | gl.bindTexture(gl.TEXTURE_2D, null); 92 | 93 | return glt; 94 | } 95 | 96 | 97 | bind(mAutoSetViewport=true) { 98 | if(mAutoSetViewport) { 99 | GL.viewport(0, 0, this.width, this.height); 100 | } 101 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); 102 | } 103 | 104 | 105 | unbind(mAutoSetViewport=true) { 106 | if(mAutoSetViewport) { 107 | GL.viewport(0, 0, GL.width, GL.height); 108 | } 109 | 110 | const { width, height } = this; 111 | 112 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 113 | 114 | gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.frameBuffer); 115 | gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.frameBufferColor); 116 | gl.clearBufferfv(gl.COLOR, 0, [0.0, 0.0, 0.0, 0.0]); 117 | gl.blitFramebuffer( 118 | 0, 0, width, height, 119 | 0, 0, width, height, 120 | gl.COLOR_BUFFER_BIT, GL.NEAREST 121 | ); 122 | // gl.blitFramebuffer( 123 | // 0, 0, width, height, 124 | // 0, 0, width, height, 125 | // gl.COLOR_BUFFER_BIT|gl.DEPTH_STENCIL, GL.NEAREST 126 | // ); 127 | 128 | // gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.frameBuffer); 129 | // gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.frameBufferDepth); 130 | // gl.clearBufferfi(gl.DEPTH_STENCIL, 0, 1.0, 0); 131 | // gl.blitFramebuffer( 132 | // 0, 0, width, height, 133 | // 0, 0, width, height, 134 | // gl.DEPTH_BUFFER_BIT, gl.NEAREST 135 | // ); 136 | 137 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 138 | } 139 | 140 | 141 | getTexture(mIndex = 0) { 142 | return this.glTexture; 143 | } 144 | 145 | 146 | getDepthTexture() { 147 | return this.glDepthTexture; 148 | } 149 | 150 | } 151 | 152 | 153 | export default MultisampleFrameBuffer; -------------------------------------------------------------------------------- /src/alfrid/utils/OrbitalControl.js: -------------------------------------------------------------------------------- 1 | // OrbitalControl.js 2 | 'use strict'; 3 | 4 | import EaseNumber from './EaseNumber'; 5 | import Scheduler from 'scheduling'; 6 | import { vec3 } from 'gl-matrix'; 7 | 8 | const getMouse = function (mEvent, mTarget) { 9 | 10 | const o = mTarget || {}; 11 | if(mEvent.touches) { 12 | o.x = mEvent.touches[0].pageX; 13 | o.y = mEvent.touches[0].pageY; 14 | } else { 15 | o.x = mEvent.clientX; 16 | o.y = mEvent.clientY; 17 | } 18 | 19 | return o; 20 | }; 21 | 22 | class OrbitalControl { 23 | 24 | constructor(mTarget, mListenerTarget = window, mRadius = 500) { 25 | this._target = mTarget; 26 | this._listenerTarget = mListenerTarget; 27 | this._mouse = {}; 28 | this._preMouse = {}; 29 | this.center = vec3.create(); 30 | this._up = vec3.fromValues(0, 1, 0); 31 | this.radius = new EaseNumber(mRadius); 32 | this.position = vec3.fromValues(0, 0, this.radius.value); 33 | this.positionOffset = vec3.create(); 34 | this._rx = new EaseNumber(0); 35 | this._rx.limit(-Math.PI / 2, Math.PI / 2); 36 | this._ry = new EaseNumber(0); 37 | this._preRX = 0; 38 | this._preRY = 0; 39 | 40 | this._isLockZoom = false; 41 | this._isLockRotation = false; 42 | this._isInvert = false; 43 | this.sensitivity = 1.0; 44 | 45 | 46 | this._wheelBind = (e) => this._onWheel(e); 47 | this._downBind = (e) => this._onDown(e); 48 | this._moveBind = (e) => this._onMove(e); 49 | this._upBind = () => this._onUp(); 50 | 51 | this.connect(); 52 | Scheduler.addEF(() => this._loop()); 53 | } 54 | 55 | connect() { 56 | this.disconnect(); 57 | 58 | this._listenerTarget.addEventListener('mousewheel', this._wheelBind); 59 | this._listenerTarget.addEventListener('DOMMouseScroll', this._wheelBind); 60 | this._listenerTarget.addEventListener('mousedown', this._downBind); 61 | this._listenerTarget.addEventListener('touchstart', this._downBind); 62 | this._listenerTarget.addEventListener('mousemove', this._moveBind); 63 | this._listenerTarget.addEventListener('touchmove', this._moveBind); 64 | window.addEventListener('touchend', this._upBind); 65 | window.addEventListener('mouseup', this._upBind); 66 | } 67 | 68 | disconnect() { 69 | this._listenerTarget.removeEventListener('mousewheel', this._wheelBind); 70 | this._listenerTarget.removeEventListener('DOMMouseScroll', this._wheelBind); 71 | 72 | this._listenerTarget.removeEventListener('mousedown', this._downBind); 73 | this._listenerTarget.removeEventListener('touchstart', this._downBind); 74 | this._listenerTarget.removeEventListener('mousemove', this._moveBind); 75 | this._listenerTarget.removeEventListener('touchmove', this._moveBind); 76 | window.removeEventListener('touchend', this._upBind); 77 | window.removeEventListener('mouseup', this._upBind); 78 | } 79 | 80 | 81 | // PUBLIC METHODS 82 | 83 | lock(mValue = true) { 84 | this._isLockZoom = mValue; 85 | this._isLockRotation = mValue; 86 | this._isMouseDown = false; 87 | } 88 | 89 | lockZoom(mValue = true) { 90 | this._isLockZoom = mValue; 91 | } 92 | 93 | 94 | lockRotation(mValue = true) { 95 | this._isLockRotation = mValue; 96 | } 97 | 98 | 99 | inverseControl(isInvert = true) { 100 | this._isInvert = isInvert; 101 | } 102 | 103 | 104 | // EVENT HANDLERES 105 | _onDown(mEvent) { 106 | if(this._isLockRotation) { return; } 107 | this._isMouseDown = true; 108 | getMouse(mEvent, this._mouse); 109 | getMouse(mEvent, this._preMouse); 110 | this._preRX = this._rx.targetValue; 111 | this._preRY = this._ry.targetValue; 112 | } 113 | 114 | 115 | _onMove(mEvent) { 116 | if(this._isLockRotation) { return; } 117 | getMouse(mEvent, this._mouse); 118 | if(mEvent.touches) { mEvent.preventDefault(); } 119 | 120 | if(this._isMouseDown) { 121 | let diffX = -(this._mouse.x - this._preMouse.x); 122 | if(this._isInvert) { diffX *= -1; } 123 | this._ry.value = this._preRY - diffX * 0.01 * this.sensitivity; 124 | 125 | let diffY = -(this._mouse.y - this._preMouse.y); 126 | if(this._isInvert) { diffY *= -1; } 127 | this._rx.value = this._preRX - diffY * 0.01 * this.sensitivity; 128 | } 129 | } 130 | 131 | 132 | _onUp() { 133 | if(this._isLockRotation) { return; } 134 | this._isMouseDown = false; 135 | } 136 | 137 | 138 | _onWheel(mEvent) { 139 | if(this._isLockZoom) { return; } 140 | const w = mEvent.wheelDelta; 141 | const d = mEvent.detail; 142 | let value = 0; 143 | if (d) { 144 | if (w) { 145 | value = w / d / 40 * d > 0 ? 1 : -1; // Opera 146 | } else { 147 | value = -d / 3; // Firefox; TODO: do not /3 for OS X 148 | } 149 | } else { 150 | value = w / 120; 151 | } 152 | 153 | this.radius.add(-value * 2); 154 | } 155 | 156 | 157 | // PRIVATE METHODS 158 | 159 | _loop() { 160 | 161 | this._updatePosition(); 162 | 163 | if(this._target) { 164 | this._updateCamera(); 165 | } 166 | } 167 | 168 | 169 | _updatePosition() { 170 | this.position[1] = Math.sin(this._rx.value) * this.radius.value; 171 | const tr = Math.cos(this._rx.value) * this.radius.value; 172 | this.position[0] = Math.cos(this._ry.value + Math.PI * 0.5) * tr; 173 | this.position[2] = Math.sin(this._ry.value + Math.PI * 0.5) * tr; 174 | vec3.add(this.position, this.position, this.positionOffset); 175 | } 176 | 177 | 178 | _updateCamera() { 179 | this._target.lookAt(this.position, this.center, this._up); 180 | } 181 | 182 | 183 | // GETTER / SETTER 184 | 185 | 186 | get rx() { 187 | return this._rx; 188 | } 189 | 190 | 191 | get ry() { 192 | return this._ry; 193 | } 194 | } 195 | 196 | 197 | export default OrbitalControl; -------------------------------------------------------------------------------- /src/alfrid.js: -------------------------------------------------------------------------------- 1 | // alfrid.js 2 | 3 | import * as GLM from 'gl-matrix' 4 | import GL from './alfrid/GLTool' 5 | import GLShader from './alfrid/GLShader' 6 | import GLTexture from './alfrid/GLTexture2' 7 | import GLCubeTexture from './alfrid/GLCubeTexture' 8 | import Mesh from './alfrid/Mesh' 9 | import Geom from './alfrid/Geom' 10 | import Batch from './alfrid/Batch' 11 | import FrameBuffer from './alfrid/FrameBuffer' 12 | import CubeFrameBuffer from './alfrid/CubeFrameBuffer' 13 | 14 | // WEBGL 2 15 | import MultisampleFrameBuffer from './alfrid/MultisampleFrameBuffer' 16 | import TransformFeedbackObject from './alfrid/TransformFeedbackObject' 17 | 18 | // TOOLS 19 | import Scheduler from 'scheduling' 20 | import EventDispatcher from './alfrid/utils/EventDispatcher' 21 | import EaseNumber from './alfrid/utils/EaseNumber' 22 | import SpringNumber from './alfrid/utils/SpringNumber' 23 | import TweenNumber from './alfrid/utils/TweenNumber' 24 | import OrbitalControl from './alfrid/utils/OrbitalControl' 25 | import QuatRotation from './alfrid/utils/QuatRotation' 26 | import TouchDetector from './alfrid/utils/TouchDetector' 27 | import WebglNumber from './alfrid/utils/WebglNumber' 28 | // import WebglConst from './alfrid/utils/WebglConst' 29 | 30 | // CAMERAS 31 | import Camera from './alfrid/cameras/Camera' 32 | import CameraOrtho from './alfrid/cameras/CameraOrtho' 33 | import CameraPerspective from './alfrid/cameras/CameraPerspective' 34 | import CameraCube from './alfrid/cameras/CameraCube' 35 | 36 | // MATH 37 | import Ray from './alfrid/math/Ray' 38 | 39 | // OBJECT 40 | import Object3D from './alfrid/objects/Object3D' 41 | 42 | // LOADERS 43 | import BinaryLoader from './alfrid/loaders/BinaryLoader' 44 | import ObjLoader from './alfrid/loaders/ObjLoader' 45 | import HDRLoader from './alfrid/loaders/HDRLoader' 46 | import GLTFParser from './alfrid/loaders/GLTFParser' 47 | // import ColladaParser from './alfrid/loaders/ColladaParser'; 48 | 49 | // HELPERS 50 | import BatchCopy from './alfrid/helpers/BatchCopy' 51 | import BatchAxis from './alfrid/helpers/BatchAxis' 52 | import BatchBall from './alfrid/helpers/BatchBall' 53 | import BatchDotsPlane from './alfrid/helpers/BatchDotsPlane' 54 | import BatchLine from './alfrid/helpers/BatchLine' 55 | import BatchSkybox from './alfrid/helpers/BatchSkybox' 56 | import BatchSky from './alfrid/helpers/BatchSky' 57 | import Scene from './alfrid/helpers/Scene' 58 | import View from './alfrid/helpers/View' 59 | import View3D from './alfrid/helpers/View3D' 60 | import Draw from './alfrid/helpers/Draw' 61 | import ShaderLibs from './alfrid/utils/ShaderLibs' 62 | 63 | import FboArray from './alfrid/FboArray' 64 | import FboPingPong from './alfrid/FboPingPong' 65 | 66 | const VERSION = '0.3.9' 67 | 68 | class Alfrid { 69 | constructor () { 70 | this.glm = GLM 71 | this.GL = GL 72 | this.GLTool = GL 73 | this.GLShader = GLShader 74 | this.GLTexture = GLTexture 75 | this.GLCubeTexture = GLCubeTexture 76 | this.Mesh = Mesh 77 | this.Geom = Geom 78 | this.Batch = Batch 79 | this.FrameBuffer = FrameBuffer 80 | this.CubeFrameBuffer = CubeFrameBuffer 81 | this.Scheduler = Scheduler 82 | this.EventDispatcher = EventDispatcher 83 | this.EaseNumber = EaseNumber 84 | this.SpringNumber = SpringNumber 85 | this.TweenNumber = TweenNumber 86 | this.Camera = Camera 87 | this.CameraOrtho = CameraOrtho 88 | this.CameraPerspective = CameraPerspective 89 | this.Ray = Ray 90 | this.CameraCube = CameraCube 91 | this.OrbitalControl = OrbitalControl 92 | this.QuatRotation = QuatRotation 93 | this.BinaryLoader = BinaryLoader 94 | this.ObjLoader = ObjLoader 95 | this.GLTFParser = GLTFParser 96 | // this.ColladaParser = ColladaParser; 97 | this.HDRLoader = HDRLoader 98 | this.BatchCopy = BatchCopy 99 | this.BatchAxis = BatchAxis 100 | this.BatchBall = BatchBall 101 | this.BatchBall = BatchBall 102 | this.BatchLine = BatchLine 103 | this.BatchSkybox = BatchSkybox 104 | this.BatchSky = BatchSky 105 | this.BatchDotsPlane = BatchDotsPlane 106 | this.Scene = Scene 107 | this.View = View 108 | this.View3D = View3D 109 | this.Draw = Draw 110 | this.Object3D = Object3D 111 | this.ShaderLibs = ShaderLibs 112 | this.WebglNumber = WebglNumber 113 | 114 | this.FboArray = FboArray 115 | this.FboPingPong = FboPingPong 116 | 117 | this.MultisampleFrameBuffer = MultisampleFrameBuffer 118 | this.TransformFeedbackObject = TransformFeedbackObject 119 | 120 | for (const s in GLM) { 121 | if (GLM[s]) { 122 | window[s] = GLM[s] 123 | } 124 | } 125 | } 126 | 127 | log () { 128 | if (navigator.userAgent.indexOf('Chrome') > -1) { 129 | console.log(`%clib alfrid : VERSION ${VERSION}`, 'background: #193441; color: #FCFFF5') 130 | } else { 131 | console.log('lib alfrid : VERSION ', VERSION) 132 | } 133 | console.log('%cClasses : ', 'color: #193441') 134 | 135 | for (const s in this) { 136 | if (this[s]) { 137 | console.log(`%c - ${s}`, 'color: #3E606F') 138 | } 139 | } 140 | } 141 | } 142 | 143 | const al = new Alfrid() 144 | 145 | export default al 146 | export { 147 | GL, 148 | GLShader, 149 | GLTexture, 150 | GLCubeTexture, 151 | Mesh, 152 | Geom, 153 | Batch, 154 | FrameBuffer, 155 | CubeFrameBuffer, 156 | MultisampleFrameBuffer, 157 | TransformFeedbackObject, 158 | Scheduler, 159 | EventDispatcher, 160 | EaseNumber, 161 | SpringNumber, 162 | TweenNumber, 163 | OrbitalControl, 164 | WebglNumber, 165 | QuatRotation, 166 | TouchDetector, 167 | Camera, 168 | CameraOrtho, 169 | CameraPerspective, 170 | CameraCube, 171 | Ray, 172 | Object3D, 173 | BinaryLoader, 174 | ObjLoader, 175 | HDRLoader, 176 | GLTFParser, 177 | BatchCopy, 178 | BatchAxis, 179 | BatchBall, 180 | BatchDotsPlane, 181 | BatchLine, 182 | BatchSkybox, 183 | BatchSky, 184 | Scene, 185 | View, 186 | View3D, 187 | Draw, 188 | ShaderLibs, 189 | FboArray, 190 | FboPingPong 191 | } 192 | -------------------------------------------------------------------------------- /tasks/uniforms-checker.js: -------------------------------------------------------------------------------- 1 | // uniforms-checker.js 2 | 3 | 'use strict'; 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const findFolder = require('./find-folder'); 8 | const watcher = require('./watch'); 9 | const getAllMatches = require('./getAllMatches'); 10 | const insertString = require('./insertString'); 11 | const checkExtension = require('./checkExtension'); 12 | 13 | const PATH_SRC = './src'; 14 | const regShader = /shaders\/.+\.(vert|frag)/g; 15 | const regUniform = /shader\.uniform\(.*/g; 16 | const regUniformGLSL = /uniform\s.+/g; 17 | const regUniformLast = /uniform\s.+;/g; 18 | 19 | const uniformMapping = { 20 | float: 'uniform1f', 21 | vec2: 'uniform2fv', 22 | vec3: 'uniform3fv', 23 | vec4: 'uniform4fv', 24 | mat3: 'uniformMatrix3fv', 25 | mat4: 'uniformMatrix4fv' 26 | }; 27 | 28 | let shaderPath; 29 | 30 | findFolder(PATH_SRC, 'shaders', (mPath)=> { 31 | shaderPath = mPath; 32 | startWatch(); 33 | }); 34 | 35 | let watcherViews = watcher([PATH_SRC]); 36 | 37 | function startWatch() { 38 | // onFileChange('./src/js/ViewGiant.js'); 39 | 40 | watcherViews.on('all',(event, file) => { 41 | if(file.indexOf('.DS_Store') > -1) return; 42 | if(!checkExtension(file, ['js'])) return; 43 | if(event !== 'add' && event !== 'change') return; 44 | onFileChange(file); 45 | }); 46 | } 47 | 48 | 49 | function replace(str, pattern, strToReplace) { 50 | return str.replace(new RegExp(pattern, 'g'), strToReplace); 51 | } 52 | 53 | function onFileChange(mPath) { 54 | console.log('File Changed :', mPath); 55 | let results; 56 | 57 | parseJS(mPath, (results) => { 58 | // console.log('Results:', results); 59 | 60 | if(results.shaders.length == 0) return; 61 | 62 | let count = 0; 63 | 64 | let uniforms = []; 65 | 66 | const combineUniforms = ()=> { 67 | let final = []; 68 | 69 | uniforms.forEach((u)=> { 70 | if(final.length == 0) { 71 | final.push(u); 72 | } else { 73 | let result = final.filter((a)=> { 74 | return a.uniformName === u.uniformName; 75 | }); 76 | 77 | if(result.length == 0) { 78 | final.push(u); 79 | } 80 | } 81 | }); 82 | 83 | const uniformsToAdd = []; 84 | results.uniforms.forEach((uniform)=> { 85 | let match = final.find((u)=> { 86 | return u.uniformName === uniform.uniformName; 87 | }); 88 | 89 | if(!match) { uniformsToAdd.push(uniform); } 90 | }); 91 | 92 | 93 | if(uniformsToAdd.length == 0) { return; } 94 | results.shaders.forEach((shader)=> { 95 | addUniforms(shader, uniformsToAdd); 96 | }); 97 | } 98 | 99 | const onParsed = (oUniforms) => { 100 | uniforms = uniforms.concat(oUniforms); 101 | count ++; 102 | if(count == results.shaders.length) { 103 | combineUniforms(); 104 | } 105 | } 106 | 107 | results.shaders.forEach((mShaderPath)=> { 108 | // checkUniforms(mShaderPath, results.uniforms); 109 | getShaderUniforms(mShaderPath, onParsed); 110 | }) 111 | }); 112 | } 113 | 114 | 115 | function parseJS(mPath, mCallback) { 116 | let shaders = []; 117 | fs.readFile(mPath, 'utf8', (err, str) => { 118 | if(err) { 119 | console.log('Error Loading file !'); 120 | } else { 121 | shaders = getAllMatches(str, regShader); 122 | 123 | shaders = shaders.map((path)=> { 124 | return path.replace('shaders/', ''); 125 | }); 126 | 127 | 128 | getUniforms(str, (uniforms)=> { 129 | const o = { 130 | shaders, 131 | uniforms, 132 | } 133 | mCallback(o); 134 | }); 135 | } 136 | }); 137 | } 138 | 139 | function getUniforms(mFile, mCb) { 140 | let uniforms = getAllMatches(mFile, regUniform); 141 | 142 | const getUniformType = (mType) => { 143 | for(let s in uniformMapping) { 144 | if(s === mType) { 145 | return s; 146 | } else if(uniformMapping[s] === mType) { 147 | return s; 148 | } 149 | } 150 | 151 | return mType; 152 | } 153 | 154 | // console.log('Uniforms:', uniforms); 155 | 156 | uniforms = uniforms.map((u) => { 157 | let s = replace(u, '"', ""); 158 | s = s.split("'").join(""); 159 | 160 | console.log('Uniform String :', s); 161 | s = replace(s, "'"); 162 | s = s.split('(')[1]; 163 | s = s.split(')')[0]; 164 | const ary = s.split(', '); 165 | const uniformName = ary[0]; 166 | const uniformType = getUniformType(ary[1]); 167 | 168 | console.log('Uniform Type :', uniformType, ary[1]); 169 | 170 | return { 171 | uniformName, 172 | uniformType 173 | }; 174 | }); 175 | 176 | 177 | uniforms = uniforms.filter((u)=> { 178 | return u.uniformType !== 'uniform1i'; 179 | }); 180 | 181 | mCb(uniforms); 182 | } 183 | 184 | function getShaderUniforms(mShaderPath, mCb) { 185 | fs.readFile(path.resolve(shaderPath, mShaderPath), 'utf8', (err, str) => { 186 | if(err) { 187 | console.log('Error Loading file !'); 188 | } else { 189 | let uniformsGlsl = getAllMatches(str, regUniformGLSL); 190 | 191 | uniformsGlsl = uniformsGlsl.map((u)=> { 192 | let s = u.replace(';', ''); 193 | s = s.replace(/(\t)*/g, ''); 194 | const tmp = s.split(' '); 195 | const uniformType = tmp[1]; 196 | const uniformName = tmp[2]; 197 | return { 198 | uniformName, 199 | uniformType 200 | }; 201 | }); 202 | 203 | mCb(uniformsGlsl); 204 | } 205 | }); 206 | 207 | } 208 | 209 | function addUniforms(mPath, mUniformsToAdd) { 210 | console.log('Add uniform :', mPath, mUniformsToAdd); 211 | const targetShaderPath = path.resolve(shaderPath, mPath); 212 | 213 | fs.readFile(targetShaderPath, 'utf8', (err, str) => { 214 | if(err) { 215 | console.log('Error Loading file !'); 216 | } else { 217 | const uniformsGlsl = getAllMatches(str, regUniformLast, true); 218 | const temp = uniformsGlsl.pop(); 219 | const index = temp.index + temp[0].length; 220 | 221 | let strUniform = '\n'; 222 | mUniformsToAdd.forEach((uniform)=> { 223 | strUniform += `uniform ${uniform.uniformType} \t\t${uniform.uniformName};\n`; 224 | }); 225 | 226 | strUniform = strUniform.substring(0, strUniform.length-1); 227 | 228 | str = insertString(str, strUniform, index); 229 | 230 | fs.writeFile(targetShaderPath, str, (err, data) => { 231 | if(err) { 232 | console.log('Error Writing File'); 233 | } else { 234 | console.log(`shader ${mPath} updated`); 235 | } 236 | }); 237 | } 238 | }); 239 | } 240 | -------------------------------------------------------------------------------- /src/alfrid/utils/QuatRotation.js: -------------------------------------------------------------------------------- 1 | // QuatRotation.js 2 | 3 | 'use strict'; 4 | 5 | import { mat4, vec3, quat } from 'gl-matrix'; 6 | import EaseNumber from './EaseNumber'; 7 | import Scheduler from 'scheduling'; 8 | 9 | const getMouse = function (mEvent, mTarget) { 10 | 11 | const o = mTarget || {}; 12 | if(mEvent.touches) { 13 | o.x = mEvent.touches[0].pageX; 14 | o.y = mEvent.touches[0].pageY; 15 | } else { 16 | o.x = mEvent.clientX; 17 | o.y = mEvent.clientY; 18 | } 19 | 20 | return o; 21 | }; 22 | 23 | class QuatRotation { 24 | constructor(mTarget, mListenerTarget = window, mEasing = 0.1) { 25 | 26 | this._target = mTarget; 27 | this._listenerTarget = mListenerTarget; 28 | 29 | this.matrix = mat4.create(); 30 | this.m = mat4.create(); 31 | this._vZaxis = vec3.clone([0, 0, 0]); 32 | this._zAxis = vec3.clone([0, 0, 1]); 33 | this.preMouse = { x:0, y:0 }; 34 | this.mouse = { x:0, y:0 }; 35 | this._isMouseDown = false; 36 | this._rotation = quat.create(); 37 | this.tempRotation = quat.create(); 38 | this._rotateZMargin = 0; 39 | this._offset = 0.004; 40 | this._slerp = -1; 41 | this._isLocked = false; 42 | 43 | this._diffX = new EaseNumber(0, mEasing); 44 | this._diffY = new EaseNumber(0, mEasing); 45 | 46 | this._listenerTarget.addEventListener('mousedown', (e) => this._onDown(e)); 47 | this._listenerTarget.addEventListener('touchstart', (e) => this._onDown(e)); 48 | this._listenerTarget.addEventListener('mousemove', (e) => this._onMove(e)); 49 | this._listenerTarget.addEventListener('touchmove', (e) => this._onMove(e)); 50 | window.addEventListener('touchend', () => this._onUp()); 51 | window.addEventListener('mouseup', () => this._onUp()); 52 | 53 | Scheduler.addEF(() => this._loop()); 54 | } 55 | 56 | // PUBLIC METHODS 57 | 58 | inverseControl(isInvert = true) { 59 | this._isInvert = isInvert; 60 | } 61 | 62 | lock(mValue = true) { 63 | this._isLocked = mValue; 64 | } 65 | 66 | setCameraPos(mQuat, speed = 0.1) { 67 | this.easing = speed; 68 | if(this._slerp > 0) { return; } 69 | 70 | const tempRotation = quat.clone(this._rotation); 71 | this._updateRotation(tempRotation); 72 | this._rotation = quat.clone(tempRotation); 73 | this._currDiffX = this.diffX = 0; 74 | this._currDiffY = this.diffY = 0; 75 | 76 | this._isMouseDown = false; 77 | this._isRotateZ = 0; 78 | 79 | this._targetQuat = quat.clone(mQuat); 80 | this._slerp = 1; 81 | } 82 | 83 | resetQuat() { 84 | this._rotation = quat.clone([0, 0, 1, 0]); 85 | this.tempRotation = quat.clone([0, 0, 0, 0]); 86 | this._targetQuat = undefined; 87 | this._slerp = -1; 88 | } 89 | 90 | // EVENT HANDLER 91 | 92 | _onDown(mEvent) { 93 | if(this._isLocked) { return; } 94 | 95 | const mouse = getMouse(mEvent); 96 | const tempRotation = quat.clone(this._rotation); 97 | this._updateRotation(tempRotation); 98 | this._rotation = tempRotation; 99 | 100 | this._isMouseDown = true; 101 | this._isRotateZ = 0; 102 | this.preMouse = { x:mouse.x, y:mouse.y }; 103 | 104 | if(mouse.y < this._rotateZMargin || mouse.y > (window.innerHeight - this._rotateZMargin)) { 105 | this._isRotateZ = 1; 106 | } else if(mouse.x < this._rotateZMargin || mouse.x > (window.innerWidth - this._rotateZMargin)) { 107 | this._isRotateZ = 2; 108 | } 109 | 110 | this._diffX.setTo(0); 111 | this._diffY.setTo(0); 112 | } 113 | 114 | 115 | _onMove(mEvent) { 116 | if(this._isLocked) { return; } 117 | getMouse(mEvent, this.mouse); 118 | } 119 | 120 | 121 | _onUp() { 122 | if(this._isLocked) { return; } 123 | this._isMouseDown = false; 124 | } 125 | 126 | 127 | // PRIVATE METHODS 128 | 129 | _updateRotation(mTempRotation) { 130 | if(this._isMouseDown && !this._isLocked) { 131 | this._diffX.value = -(this.mouse.x - this.preMouse.x); 132 | this._diffY.value = (this.mouse.y - this.preMouse.y); 133 | 134 | if(this._isInvert) { 135 | this._diffX.value = -this._diffX.targetValue; 136 | this._diffY.value = -this._diffY.targetValue; 137 | } 138 | } 139 | 140 | let angle, _quat; 141 | 142 | if(this._isRotateZ > 0) { 143 | if(this._isRotateZ === 1) { 144 | angle = -this._diffX.value * this._offset; 145 | angle *= (this.preMouse.y < this._rotateZMargin) ? -1 : 1; 146 | _quat = quat.clone([0, 0, Math.sin(angle), Math.cos(angle)]); 147 | quat.multiply(_quat, mTempRotation, _quat); 148 | } else { 149 | angle = -this._diffY.value * this._offset; 150 | angle *= (this.preMouse.x < this._rotateZMargin) ? 1 : -1; 151 | _quat = quat.clone([0, 0, Math.sin(angle), Math.cos(angle)]); 152 | quat.multiply(_quat, mTempRotation, _quat); 153 | } 154 | } else { 155 | const v = vec3.clone([this._diffX.value, this._diffY.value, 0]); 156 | const axis = vec3.create(); 157 | vec3.cross(axis, v, this._zAxis); 158 | vec3.normalize(axis, axis); 159 | angle = vec3.length(v) * this._offset; 160 | _quat = quat.clone([Math.sin(angle) * axis[0], Math.sin(angle) * axis[1], Math.sin(angle) * axis[2], Math.cos(angle)]); 161 | quat.multiply(mTempRotation, _quat, mTempRotation); 162 | } 163 | } 164 | 165 | _loop() { 166 | mat4.identity(this.m); 167 | 168 | if(this._targetQuat === undefined) { 169 | quat.set(this.tempRotation, this._rotation[0], this._rotation[1], this._rotation[2], this._rotation[3]); 170 | this._updateRotation(this.tempRotation); 171 | } else { 172 | this._slerp += (0 - this._slerp) * 0.1; 173 | 174 | if(this._slerp < 0.0005) { 175 | quat.copy(this._rotation, this._targetQuat); 176 | quat.copy(this.tempRotation, this._targetQuat); 177 | this._targetQuat = undefined; 178 | this._diffX.setTo(0); 179 | this._diffY.setTo(0); 180 | this._slerp = -1; 181 | } else { 182 | quat.set(this.tempRotation, 0, 0, 0, 0); 183 | quat.slerp(this.tempRotation, this._targetQuat, this._rotation, this._slerp); 184 | } 185 | } 186 | 187 | vec3.transformQuat(this._vZaxis, this._vZaxis, this.tempRotation); 188 | 189 | mat4.fromQuat(this.matrix, this.tempRotation); 190 | } 191 | 192 | 193 | // GETTER AND SETTER 194 | 195 | set easing(mValue) { 196 | this._diffX.easing = mValue; 197 | this._diffY.easing = mValue; 198 | } 199 | 200 | get easing() { 201 | return this._diffX.easing; 202 | } 203 | } 204 | 205 | export default QuatRotation; -------------------------------------------------------------------------------- /src/alfrid/utils/HDRParser.js: -------------------------------------------------------------------------------- 1 | // HDRParser.js 2 | 3 | 'use strict'; 4 | 5 | // Code ported by Marcin Ignac (2014) 6 | // Based on Java implementation from 7 | // https://code.google.com/r/cys12345-research/source/browse/hdr/image_processor/RGBE.java?r=7d84e9fd866b24079dbe61fa0a966ce8365f5726 8 | const radiancePattern = '#\\?RADIANCE'; 9 | const commentPattern = '#.*'; 10 | // let gammaPattern = 'GAMMA='; 11 | const exposurePattern = 'EXPOSURE=\\s*([0-9]*[.][0-9]*)'; 12 | const formatPattern = 'FORMAT=32-bit_rle_rgbe'; 13 | const widthHeightPattern = '-Y ([0-9]+) \\+X ([0-9]+)'; 14 | 15 | // http://croquetweak.blogspot.co.uk/2014/08/deconstructing-floats-frexp-and-ldexp.html 16 | // function ldexp(mantissa, exponent) { 17 | // return exponent > 1023 ? mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023) : exponent < -1074 ? mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074) : mantissa * Math.pow(2, exponent); 18 | // } 19 | 20 | function readPixelsRawRLE(buffer, data, offset, fileOffset, scanlineWidth, numScanlines) { 21 | const rgbe = new Array(4); 22 | let scanlineBuffer = null; 23 | let ptr; 24 | let ptrEnd; 25 | let count; 26 | const buf = new Array(2); 27 | const bufferLength = buffer.length; 28 | 29 | function readBuf(buf) { 30 | let bytesRead = 0; 31 | do { 32 | buf[bytesRead++] = buffer[fileOffset]; 33 | } while(++fileOffset < bufferLength && bytesRead < buf.length); 34 | return bytesRead; 35 | } 36 | 37 | function readBufOffset(buf, offset, length) { 38 | let bytesRead = 0; 39 | do { 40 | buf[offset + bytesRead++] = buffer[fileOffset]; 41 | } while(++fileOffset < bufferLength && bytesRead < length); 42 | return bytesRead; 43 | } 44 | 45 | function readPixelsRaw(buffer, data, offset, numpixels) { 46 | const numExpected = 4 * numpixels; 47 | const numRead = readBufOffset(data, offset, numExpected); 48 | if (numRead < numExpected) { 49 | throw new Error(`Error reading raw pixels: got ${numRead} bytes, expected ${numExpected}`); 50 | } 51 | } 52 | 53 | while (numScanlines > 0) { 54 | if (readBuf(rgbe) < rgbe.length) { 55 | throw new Error(`Error reading bytes: expected ${rgbe.length}`); 56 | } 57 | 58 | if ((rgbe[0] !== 2) || (rgbe[1] !== 2) || ((rgbe[2] & 0x80) !== 0)) { 59 | // this file is not run length encoded 60 | data[offset++] = rgbe[0]; 61 | data[offset++] = rgbe[1]; 62 | data[offset++] = rgbe[2]; 63 | data[offset++] = rgbe[3]; 64 | readPixelsRaw(buffer, data, offset, scanlineWidth * numScanlines - 1); 65 | return; 66 | } 67 | 68 | if ((((rgbe[2] & 0xFF) << 8) | (rgbe[3] & 0xFF)) !== scanlineWidth) { 69 | throw new Error(`Wrong scanline width ${(((rgbe[2] & 0xFF) << 8) | (rgbe[3] & 0xFF))}, expected ${scanlineWidth}`); 70 | } 71 | 72 | if (scanlineBuffer === null) { 73 | scanlineBuffer = new Array(4 * scanlineWidth); 74 | } 75 | 76 | ptr = 0; 77 | /* read each of the four channels for the scanline into the buffer */ 78 | for (let i = 0; i < 4; i++) { 79 | ptrEnd = (i + 1) * scanlineWidth; 80 | while(ptr < ptrEnd) { 81 | if (readBuf(buf) < buf.length) { 82 | throw new Error('Error reading 2-byte buffer'); 83 | } 84 | if ((buf[0] & 0xFF) > 128) { 85 | /* a run of the same value */ 86 | count = (buf[0] & 0xFF) - 128; 87 | if ((count === 0) || (count > ptrEnd - ptr)) { 88 | throw new Error('Bad scanline data'); 89 | } 90 | while(count-- > 0) { 91 | scanlineBuffer[ptr++] = buf[1]; 92 | } 93 | } else { 94 | /* a non-run */ 95 | count = buf[0] & 0xFF; 96 | if ((count === 0) || (count > ptrEnd - ptr)) { 97 | throw new Error('Bad scanline data'); 98 | } 99 | scanlineBuffer[ptr++] = buf[1]; 100 | if (--count > 0) { 101 | if (readBufOffset(scanlineBuffer, ptr, count) < count) { 102 | throw new Error('Error reading non-run data'); 103 | } 104 | ptr += count; 105 | } 106 | } 107 | } 108 | } 109 | 110 | /* copy byte data to output */ 111 | for(let i = 0; i < scanlineWidth; i++) { 112 | data[offset + 0] = scanlineBuffer[i]; 113 | data[offset + 1] = scanlineBuffer[i + scanlineWidth]; 114 | data[offset + 2] = scanlineBuffer[i + 2 * scanlineWidth]; 115 | data[offset + 3] = scanlineBuffer[i + 3 * scanlineWidth]; 116 | offset += 4; 117 | } 118 | 119 | numScanlines--; 120 | } 121 | 122 | } 123 | 124 | // Returns data as floats and flipped along Y by default 125 | function parseHdr(buffer) { 126 | if (buffer instanceof ArrayBuffer) { 127 | buffer = new Uint8Array(buffer); 128 | } 129 | 130 | let fileOffset = 0; 131 | const bufferLength = buffer.length; 132 | 133 | const NEW_LINE = 10; 134 | 135 | function readLine() { 136 | let buf = ''; 137 | do { 138 | const b = buffer[fileOffset]; 139 | if (b === NEW_LINE) { 140 | ++fileOffset; 141 | break; 142 | } 143 | buf += String.fromCharCode(b); 144 | } while(++fileOffset < bufferLength); 145 | return buf; 146 | } 147 | 148 | let width = 0; 149 | let height = 0; 150 | let exposure = 1; 151 | const gamma = 1; 152 | let rle = false; 153 | 154 | for(let i = 0; i < 20; i++) { 155 | const line = readLine(); 156 | let match; 157 | if (match = line.match(radiancePattern)) { 158 | } else if (match = line.match(formatPattern)) { 159 | rle = true; 160 | } else if (match = line.match(exposurePattern)) { 161 | exposure = Number(match[1]); 162 | } else if (match = line.match(commentPattern)) { 163 | } else if (match = line.match(widthHeightPattern)) { 164 | height = Number(match[1]); 165 | width = Number(match[2]); 166 | break; 167 | } 168 | } 169 | 170 | if (!rle) { 171 | throw new Error('File is not run length encoded!'); 172 | } 173 | 174 | const data = new Uint8Array(width * height * 4); 175 | const scanlineWidth = width; 176 | const numScanlines = height; 177 | 178 | readPixelsRawRLE(buffer, data, 0, fileOffset, scanlineWidth, numScanlines); 179 | 180 | // TODO: Should be Float16 181 | const floatData = new Float32Array(width * height * 4); 182 | for(let offset = 0; offset < data.length; offset += 4) { 183 | let r = data[offset + 0] / 255; 184 | let g = data[offset + 1] / 255; 185 | let b = data[offset + 2] / 255; 186 | const e = data[offset + 3]; 187 | const f = Math.pow(2.0, e - 128.0); 188 | 189 | r *= f; 190 | g *= f; 191 | b *= f; 192 | 193 | const floatOffset = offset; 194 | 195 | floatData[floatOffset + 0] = r; 196 | floatData[floatOffset + 1] = g; 197 | floatData[floatOffset + 2] = b; 198 | floatData[floatOffset + 3] = 1.0; 199 | } 200 | 201 | return { 202 | shape: [width, height], 203 | exposure, 204 | gamma, 205 | data: floatData 206 | }; 207 | } 208 | 209 | 210 | export default parseHdr; -------------------------------------------------------------------------------- /src/alfrid/GLShader.js: -------------------------------------------------------------------------------- 1 | // GLShader.js 2 | 3 | 'use strict'; 4 | 5 | import GL from './GLTool'; 6 | const glslify = require('glslify'); 7 | const isSame = (array1, array2) => { 8 | if(array1.length !== array2.length) { 9 | return false; 10 | } 11 | 12 | for(let i = 0; i < array1.length; i++) { 13 | if(array1[i] !== array2[i]) { 14 | return false; 15 | } 16 | } 17 | 18 | return true; 19 | }; 20 | 21 | const addLineNumbers = (string) => { 22 | const lines = string.split('\n'); 23 | for (let i = 0; i < lines.length; i ++) { 24 | lines[i] = `${(i + 1)}: ${lines[i]}`; 25 | } 26 | return lines.join('\n'); 27 | }; 28 | 29 | 30 | const cloneArray = (mArray) => { 31 | if(mArray.slice) { 32 | return mArray.slice(0); 33 | } else { 34 | return new Float32Array(mArray); 35 | } 36 | }; 37 | 38 | let gl; 39 | const defaultVertexShader = require('./shaders/basic.vert'); 40 | const defaultFragmentShader = require('./shaders/basic.frag'); 41 | 42 | const uniformMapping = { 43 | float: 'uniform1f', 44 | vec2: 'uniform2fv', 45 | vec3: 'uniform3fv', 46 | vec4: 'uniform4fv', 47 | int: 'uniform1i', 48 | mat3: 'uniformMatrix3fv', 49 | mat4: 'uniformMatrix4fv' 50 | }; 51 | 52 | class GLShader { 53 | constructor(strVertexShader = defaultVertexShader, strFragmentShader = defaultFragmentShader, mVaryings) { 54 | 55 | gl = GL.gl; 56 | this.parameters = []; 57 | this.uniformTextures = []; 58 | this._varyings = mVaryings; 59 | 60 | if(!strVertexShader) { strVertexShader = defaultVertexShader; } 61 | if(!strFragmentShader) { strFragmentShader = defaultVertexShader; } 62 | 63 | const vsShader = this._createShaderProgram(strVertexShader, true); 64 | const fsShader = this._createShaderProgram(strFragmentShader, false); 65 | this._attachShaderProgram(vsShader, fsShader); 66 | 67 | } 68 | 69 | 70 | bind() { 71 | 72 | if(GL.shader === this) { 73 | return; 74 | } 75 | gl.useProgram(this.shaderProgram); 76 | GL.useShader(this); 77 | this.uniformTextures = []; 78 | 79 | } 80 | 81 | 82 | uniform(mName, mType, mValue) { 83 | if(typeof mName === 'object') { 84 | this.uniformObject(mName); 85 | return; 86 | } 87 | /* 88 | if(!!mValue === undefined || mValue === null) { 89 | console.warn('mValue Error:', mName); 90 | return; 91 | } 92 | */ 93 | const uniformType = uniformMapping[mType] || mType; 94 | 95 | let hasUniform = false; 96 | let oUniform; 97 | let parameterIndex = -1; 98 | 99 | 100 | for(let i = 0; i < this.parameters.length; i++) { 101 | oUniform = this.parameters[i]; 102 | if(oUniform.name === mName) { 103 | hasUniform = true; 104 | parameterIndex = i; 105 | break; 106 | } 107 | } 108 | 109 | let isNumber = false; 110 | 111 | if(!hasUniform) { 112 | isNumber = uniformType === 'uniform1i' || uniformType === 'uniform1f'; 113 | this.shaderProgram[mName] = gl.getUniformLocation(this.shaderProgram, mName); 114 | if(isNumber) { 115 | this.parameters.push({ name : mName, type: uniformType, value: mValue, uniformLoc: this.shaderProgram[mName], isNumber }); 116 | } else { 117 | this.parameters.push({ name : mName, type: uniformType, value: cloneArray(mValue), uniformLoc: this.shaderProgram[mName], isNumber }); 118 | } 119 | 120 | parameterIndex = this.parameters.length - 1; 121 | } else { 122 | this.shaderProgram[mName] = oUniform.uniformLoc; 123 | isNumber = oUniform.isNumber; 124 | } 125 | 126 | 127 | if(!this.parameters[parameterIndex].uniformLoc) { 128 | return; 129 | } 130 | 131 | 132 | if(uniformType.indexOf('Matrix') === -1) { 133 | if(!isNumber) { 134 | if(!isSame(this.parameters[parameterIndex].value, mValue) || !hasUniform) { 135 | gl[uniformType](this.shaderProgram[mName], mValue); 136 | this.parameters[parameterIndex].value = cloneArray(mValue); 137 | } 138 | } else { 139 | const needUpdate = (this.parameters[parameterIndex].value !== mValue || !hasUniform); 140 | if(needUpdate) { 141 | gl[uniformType](this.shaderProgram[mName], mValue); 142 | this.parameters[parameterIndex].value = mValue; 143 | } 144 | } 145 | 146 | } else { 147 | if(!isSame(this.parameters[parameterIndex].value, mValue) || !hasUniform) { 148 | gl[uniformType](this.shaderProgram[mName], false, mValue); 149 | this.parameters[parameterIndex].value = cloneArray(mValue); 150 | 151 | } 152 | } 153 | 154 | } 155 | 156 | uniformObject(mUniformObj) { 157 | for(const uniformName in mUniformObj) { 158 | let uniformValue = mUniformObj[uniformName]; 159 | const uniformType = GLShader.getUniformType(uniformValue); 160 | 161 | if(uniformValue.concat && uniformValue[0].concat) { 162 | let tmp = []; 163 | for(let i=0; i { 22 | return a + Math.random() * (b - a) 23 | } 24 | 25 | function init () { 26 | const canvas = document.createElement('canvas') 27 | document.body.appendChild(canvas) 28 | 29 | // INIT GL 30 | GL.init(canvas, { useWebgl2: true }) 31 | // GL.init(canvas, { useWebgl2: false }) 32 | GL.setSize(window.innerWidth, window.innerHeight) 33 | 34 | // cameras 35 | const camera = new alfrid.CameraPerspective() 36 | camera.setPerspective(Math.PI / 4, GL.aspectRatio, 1, 100) 37 | const orbControl = new alfrid.OrbitalControl(camera, window, 10) 38 | orbControl.rx.value = orbControl.ry.value = 0.3 39 | 40 | const cameraLight = new alfrid.CameraOrtho() 41 | const s = 4 42 | cameraLight.ortho(-s, s, s, -s, 1, 8) 43 | cameraLight.lookAt([0, 4, 1], [0, 0, 0]) 44 | 45 | const biasMatrix = mat4.fromValues( 46 | 0.5, 0.0, 0.0, 0.0, 47 | 0.0, 0.5, 0.0, 0.0, 48 | 0.0, 0.0, 0.5, 0.0, 49 | 0.5, 0.5, 0.5, 1.0 50 | ) 51 | const mtxShadow = mat4.create() 52 | mat4.mul(mtxShadow, cameraLight.projection, cameraLight.matrix) 53 | mat4.mul(mtxShadow, biasMatrix, mtxShadow) 54 | 55 | // fbo 56 | const fbo = new alfrid.FboPingPong(size, size, { 57 | type: GL.FLOAT, 58 | minFilter: GL.NEAREST, 59 | magFilter: GL.NEAREST 60 | }, 3) 61 | 62 | const fboShadow = new alfrid.FrameBuffer(1024 * shadowMapScale, 1024 * shadowMapScale) 63 | // const fbo = new alfrid.FrameBuffer(size, size) 64 | 65 | // draw calls 66 | const bAxis = new alfrid.BatchAxis() 67 | const bDots = new alfrid.BatchDotsPlane() 68 | const bCopy = new alfrid.BatchCopy() 69 | 70 | const meshSave = () => { 71 | const mesh = new alfrid.Mesh(GL.POINTS) 72 | const positions = [] 73 | const normals = [] 74 | const uvs = [] 75 | const indices = [] 76 | let index = 0 77 | for (let i = 0; i < size; i++) { 78 | for (let j = 0; j < size; j++) { 79 | positions.push([random(-1, 1), random(-1, 1), random(-1, 1)]) 80 | uvs.push([i / size * 2 - 1, j / size * 2 - 1]) 81 | normals.push([Math.random(), Math.random(), Math.random()]) 82 | indices.push(index) 83 | index++ 84 | } 85 | } 86 | mesh.bufferVertex(positions) 87 | mesh.bufferNormal(normals) 88 | mesh.bufferTexCoord(uvs) 89 | mesh.bufferIndex(indices) 90 | return mesh 91 | } 92 | 93 | new alfrid.Draw() 94 | .setMesh(meshSave()) 95 | .useProgram(vsSave, fsSave) 96 | .bindFrameBuffer(fbo.read) 97 | .draw() 98 | .bindFrameBuffer(fbo.write) 99 | .draw() 100 | 101 | const meshRender = () => { 102 | const positions = [] 103 | const indices = [] 104 | let index = 0 105 | 106 | for (let i = 0; i < size; i++) { 107 | for (let j = 0; j < size; j++) { 108 | positions.push([i / size, j / size, 0]) 109 | indices.push(index) 110 | index++ 111 | } 112 | } 113 | const mesh = new alfrid.Mesh(GL.POINTS) 114 | mesh.bufferVertex(positions) 115 | mesh.bufferIndex(indices) 116 | return mesh 117 | } 118 | 119 | const drawSim = new alfrid.Draw() 120 | .setMesh(alfrid.Geom.bigTriangle()) 121 | .useProgram(vsPass, fsSim) 122 | .setClearColor(0, 0, 0, 1) 123 | 124 | const mesh = meshRender() 125 | 126 | const drawRender = new alfrid.Draw() 127 | .setMesh(mesh) 128 | .useProgram(vsRender, fsRender) 129 | 130 | const drawDepth = new alfrid.Draw() 131 | .setMesh(mesh) 132 | .useProgram(vsRender, fsDepth) 133 | 134 | const drawFloor = new alfrid.Draw() 135 | .setMesh(alfrid.Geom.plane(10, 10, 1, 'xz')) 136 | .useProgram(vsFloor, fsFloor) 137 | 138 | const mtxFloor = mat4.create() 139 | mat4.translate(mtxFloor, mtxFloor, vec3.fromValues(0, -3, 0)) 140 | 141 | const seed = Math.random() * 0xFF 142 | 143 | const render = () => { 144 | GL.viewport(0, 0, GL.width, GL.height) 145 | 146 | drawSim 147 | .bindFrameBuffer(fbo.write) 148 | .uniformTexture('texturePos', fbo.read.getTexture(0), 0) 149 | .uniformTexture('textureVel', fbo.read.getTexture(1), 1) 150 | .uniformTexture('textureExtra', fbo.read.getTexture(2), 2) 151 | .uniform('uTime', 'float', alfrid.Scheduler.deltaTime + seed) 152 | .draw() 153 | fbo.swap() 154 | 155 | // update shadow map 156 | fboShadow.bind() 157 | GL.clear(1, 0, 0, 1) 158 | GL.setMatrices(cameraLight) 159 | drawDepth 160 | .uniformTexture('texturePos', fbo.read.getTexture(0), 0) 161 | .uniformTexture('textureExtra', fbo.read.getTexture(2), 1) 162 | .uniform('uViewport', 'vec2', [GL.width * shadowMapScale, GL.height * shadowMapScale]) 163 | .uniform('uShadowMatrix', 'mat4', mtxShadow) 164 | .draw() 165 | 166 | fboShadow.unbind() 167 | 168 | GL.clear(0, 0, 0, 1) 169 | GL.setMatrices(camera) 170 | bAxis.draw() 171 | bDots.draw() 172 | 173 | // drawCube.draw() 174 | drawRender 175 | .uniformTexture('texturePos', fbo.read.getTexture(0), 0) 176 | .uniformTexture('textureExtra', fbo.read.getTexture(2), 1) 177 | .uniform('uViewport', 'vec2', [GL.width, GL.height]) 178 | .uniform('uShadowMatrix', 'mat4', mtxShadow) 179 | .uniformTexture('textureDepth', fboShadow.depthTexture, 2) 180 | .draw() 181 | 182 | GL.rotate(mtxFloor) 183 | drawFloor 184 | .uniform('uShadowMatrix', 'mat4', mtxShadow) 185 | .uniformTexture('textureDepth', fboShadow.depthTexture, 2) 186 | .draw() 187 | 188 | GL.rotate(mat4.create()) 189 | 190 | debugCamera(cameraLight) 191 | 192 | const s = Math.min(256, GL.width / 4) 193 | GL.viewport(0, 0, s, s) 194 | bCopy.draw(fbo.read.getTexture(0)) 195 | GL.viewport(s, 0, s, s) 196 | bCopy.draw(fbo.read.getTexture(2)) 197 | GL.viewport(s * 2, 0, s, s) 198 | bCopy.draw(fboShadow.depthTexture) 199 | GL.viewport(s * 3, 0, s, s) 200 | bCopy.draw(fboShadow.texture) 201 | } 202 | 203 | window.addEventListener('resize', () => { 204 | GL.setSize(window.innerWidth, window.innerHeight) 205 | camera.setAspectRatio(GL.aspectRatio) 206 | }) 207 | 208 | alfrid.Scheduler.addEF(render) 209 | } 210 | 211 | init() 212 | --------------------------------------------------------------------------------