├── .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 | [](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 |
--------------------------------------------------------------------------------