├── data
├── models
│ ├── table-indices.bin
│ ├── smoke100-indices.bin
│ ├── sky-indices.bin
│ ├── sky-strides.bin
│ ├── table-strides.bin
│ ├── buddha-indices.bin
│ ├── buddha-strides.bin
│ ├── shafts-indices.bin
│ ├── shafts-strides.bin
│ ├── smoke100-strides.bin
│ ├── particles_20-strides.bin
│ └── particles_20-indices.bin
└── textures
│ ├── dust.png
│ ├── shafts.png
│ ├── smoke.png
│ ├── buddha_lm.png
│ ├── sky
│ └── sky1.png
│ ├── buddha-normals.png
│ ├── sphere_gold3.png
│ └── table
│ ├── marble.pkm
│ ├── marble.png
│ └── table_lm.png
├── .gitignore
├── favicon.ico
├── icon-192.png
├── sass-watch.bat
├── styles
├── scss
│ ├── _common.scss
│ └── site.scss
└── css
│ ├── site.css.map
│ └── site.css
├── js
├── app.js
├── app
│ ├── framework
│ │ ├── BinaryDataLoader.js
│ │ ├── UncompressedTextureLoader.js
│ │ ├── TextureUtils.js
│ │ ├── CompressedTextureLoader.js
│ │ ├── FullModel.js
│ │ ├── FrameBuffer.js
│ │ ├── BaseShader.js
│ │ ├── utils
│ │ │ └── FullscreenUtils.js
│ │ └── BaseRenderer.js
│ ├── VignetteData.js
│ ├── DiffuseShader.js
│ ├── PointSpriteScaledColoredShader.js
│ ├── main.js
│ ├── LMTableShader.js
│ ├── LightShaftShader.js
│ ├── SoftDiffuseColoredShader.js
│ ├── SphericalMapLMShader.js
│ └── BuddhaRenderer.js
└── lib
│ └── require.js
├── .editorconfig
├── manifest.json
├── LICENSE.txt
├── README.md
└── index.html
/data/models/table-indices.bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.sass-cache
2 | /node_modules
3 | /temp
4 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/favicon.ico
--------------------------------------------------------------------------------
/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/icon-192.png
--------------------------------------------------------------------------------
/sass-watch.bat:
--------------------------------------------------------------------------------
1 | sass --watch styles\scss\site.scss:styles\css\site.css --style compressed
2 |
--------------------------------------------------------------------------------
/data/textures/dust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/dust.png
--------------------------------------------------------------------------------
/data/models/smoke100-indices.bin:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/textures/shafts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/shafts.png
--------------------------------------------------------------------------------
/data/textures/smoke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/smoke.png
--------------------------------------------------------------------------------
/styles/scss/_common.scss:
--------------------------------------------------------------------------------
1 | $screen-sm-min: 768px;
2 | $screen-md-min: 992px;
3 | $screen-lg-min: 1200px;
4 |
--------------------------------------------------------------------------------
/data/models/sky-indices.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/sky-indices.bin
--------------------------------------------------------------------------------
/data/models/sky-strides.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/sky-strides.bin
--------------------------------------------------------------------------------
/data/textures/buddha_lm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/buddha_lm.png
--------------------------------------------------------------------------------
/data/textures/sky/sky1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/sky/sky1.png
--------------------------------------------------------------------------------
/data/models/table-strides.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/table-strides.bin
--------------------------------------------------------------------------------
/data/models/buddha-indices.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/buddha-indices.bin
--------------------------------------------------------------------------------
/data/models/buddha-strides.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/buddha-strides.bin
--------------------------------------------------------------------------------
/data/models/shafts-indices.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/shafts-indices.bin
--------------------------------------------------------------------------------
/data/models/shafts-strides.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/shafts-strides.bin
--------------------------------------------------------------------------------
/data/models/smoke100-strides.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/smoke100-strides.bin
--------------------------------------------------------------------------------
/data/textures/buddha-normals.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/buddha-normals.png
--------------------------------------------------------------------------------
/data/textures/sphere_gold3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/sphere_gold3.png
--------------------------------------------------------------------------------
/data/textures/table/marble.pkm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/table/marble.pkm
--------------------------------------------------------------------------------
/data/textures/table/marble.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/table/marble.png
--------------------------------------------------------------------------------
/data/textures/table/table_lm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/textures/table/table_lm.png
--------------------------------------------------------------------------------
/data/models/particles_20-strides.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keaukraine/webgl-buddha/HEAD/data/models/particles_20-strides.bin
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | requirejs.config({
2 | 'baseUrl': 'js/app',
3 | 'paths': {
4 | 'app': '../app'
5 | }
6 | });
7 |
8 | requirejs(['app/main']);
9 |
--------------------------------------------------------------------------------
/data/models/particles_20-indices.bin:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.html]
2 | indent_style = space
3 | indent_size = 4
4 |
5 | [*.css]
6 | indent_style = space
7 | indent_size = 4
8 |
9 | [*.scss]
10 | indent_style = space
11 | indent_size = 4
12 |
13 | [*.js]
14 | indent_style = space
15 | indent_size = 4
16 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "3D Buddha WebGL Demo",
3 | "name": "Buddha Live Wallpaper Web Demo",
4 | "icons": [
5 | {
6 | "src": "icon-192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "/index.html",
12 | "display": "fullscreen",
13 | "orientation": "portrait",
14 | "background_color": "#111111",
15 | "theme_color": "#111111"
16 | }
17 |
--------------------------------------------------------------------------------
/js/app/framework/BinaryDataLoader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(function() {
4 |
5 | function BinaryDataLoader() {}
6 |
7 | /**
8 | * Static function to load binary data
9 | * @param {string} url - URL for content
10 | * @param {Function} - callback to receive binary data
11 | */
12 | BinaryDataLoader.load = function(url, callback) {
13 | var root = this,
14 | xhr = new XMLHttpRequest();
15 |
16 | xhr.open('GET', url, true);
17 | xhr.responseType = 'arraybuffer';
18 | xhr.onload = function() {
19 | callback && callback(this.response);
20 | };
21 | xhr.send(null);
22 | }
23 |
24 | return BinaryDataLoader;
25 | });
26 |
--------------------------------------------------------------------------------
/js/app/VignetteData.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(function() {
4 |
5 | /**
6 | * Helper class to render billboard taking whole screen.
7 | * @class
8 | */
9 | function VignetteData() {
10 | this.quadTriangles = [
11 | // X, Y, Z, U, V
12 | -1.0, -1.0, -5.0, 0.0, 0.0, // 0. left-bottom
13 | 1.0, -1.0, -5.0, 1.0, 0.0, // 1. right-bottom
14 | -1.0, 1.0, -5.0, 0.0, 1.0, // 2. left-top
15 | 1.0, 1.0, -5.0, 1.0, 1.0 // 3. right-top
16 | ];
17 | }
18 |
19 | VignetteData.prototype = {
20 | buffer: null,
21 |
22 | initGL: function(gl) {
23 | this.buffer = gl.createBuffer();
24 | gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
25 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.quadTriangles), gl.STATIC_DRAW);
26 | }
27 | }
28 |
29 | return VignetteData;
30 | });
31 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Oleksandr Popov, Dmytro Popov
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
4 | files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
5 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
6 | is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
11 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
12 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
13 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
--------------------------------------------------------------------------------
/js/app/DiffuseShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['framework/BaseShader'], function(BaseShader) {
4 |
5 | /**
6 | * Simple diffuse shader.
7 | * @class
8 | */
9 | class DiffuseShader extends BaseShader {
10 | fillCode() {
11 | this.vertexShaderCode = 'uniform mat4 view_proj_matrix;\n' +
12 | 'attribute vec4 rm_Vertex;\n' +
13 | 'attribute vec2 rm_TexCoord0;\n' +
14 | 'varying vec2 vTextureCoord;\n' +
15 | '\n' +
16 | 'void main() {\n' +
17 | ' gl_Position = view_proj_matrix * rm_Vertex;\n' +
18 | ' vTextureCoord = rm_TexCoord0;\n' +
19 | '}';
20 |
21 | this.fragmentShaderCode = 'precision mediump float;\n' +
22 | 'varying vec2 vTextureCoord;\n' +
23 | 'uniform sampler2D sTexture;\n' +
24 | '\n' +
25 | 'void main() {\n' +
26 | ' gl_FragColor = texture2D(sTexture, vTextureCoord);\n' +
27 | '}';
28 | }
29 |
30 | fillUniformsAttributes() {
31 | this.view_proj_matrix = this.getUniform('view_proj_matrix');
32 | this.rm_Vertex = this.getAttrib('rm_Vertex');
33 | this.rm_TexCoord0 = this.getAttrib('rm_TexCoord0');
34 | this.sTexture = this.getUniform('sTexture');
35 | }
36 | }
37 |
38 | return DiffuseShader;
39 | });
40 |
--------------------------------------------------------------------------------
/js/app/framework/UncompressedTextureLoader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(function() {
4 |
5 | /**
6 | * Constructor. No need to create an instance of this class because it has only static methods
7 | */
8 | function UncompressedTextureLoader() {}
9 |
10 | /**
11 | * Loads texture from any image format supported by browser
12 | * @param {string} url - URL to texture
13 | * @param {Function} callbak - callback called after texture is loaded to GPU
14 | * @return {number} - WebGL texture
15 | */
16 | UncompressedTextureLoader.load = function(url, callback) {
17 | var texture = gl.createTexture();
18 |
19 | texture.image = new Image();
20 | texture.image.src = url;
21 | texture.image.onload = function() {
22 | gl.bindTexture(gl.TEXTURE_2D, texture);
23 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
24 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
25 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
26 | gl.bindTexture(gl.TEXTURE_2D, null);
27 |
28 | if (texture.image && texture.image.src) {
29 | console.log('Loaded texture ' + url + ' [' + texture.image.width + 'x' + texture.image.height + ']');
30 | }
31 |
32 | callback && callback();
33 | };
34 |
35 | return texture;
36 | }
37 |
38 | return UncompressedTextureLoader;
39 | });
40 |
--------------------------------------------------------------------------------
/styles/css/site.css.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "mappings": "AAEA,IAAK,CACD,gBAAgB,CAAE,IAAI,CAG1B,aAAc,CACV,KAAK,CAAE,IAAI,CAGf,SAAU,CACN,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,KAAK,CAEZ,MAAM,CAAE,KAAK,CACb,MAAM,CAAE,IAAI,CACZ,OAAO,CAAE,CAAC,CACV,MAAM,CAAE,MAAM,CAGlB,YAAa,CACT,OAAO,CAAE,YAAY,CAGzB,MAAO,CACH,OAAO,CAAE,CAAC,CACV,QAAQ,CAAE,QAAQ,CAClB,MAAM,CAAE,GAAG,CACX,KAAK,CAAE,IAAI,CACX,UAAU,CAAE,MAAM,CAClB,UAAU,CAAE,mDAAmD,CAE/D,UAAI,CACA,KAAK,CAAE,KAAK,CAGhB,YAAM,CACF,KAAK,CAAE,IAAI,CACX,WAAW,CAAE,mBAAmB,CAIxC,SAAU,CACN,gBAAgB,CAAE,IAAI,CACtB,QAAQ,CAAE,QAAQ,CAClB,GAAG,CAAE,CAAC,CACN,MAAM,CAAE,CAAC,CACT,IAAI,CAAE,CAAC,CACP,KAAK,CAAE,CAAC,CACR,OAAO,CAAE,IAAI,CACb,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,GAAG,CAEZ,UAAU,CAAE,kDAAkD,CAE9D,yBAAkC,CAbtC,SAAU,CAcF,WAAW,CAAE,IAAI,CACjB,YAAY,CAAE,IAAI,EAGtB,aAAM,CACF,OAAO,CAAE,CAAC,CACV,WAAW,CAAE,IAAI,CAEjB,yBAAkC,CAJtC,aAAM,CAKE,WAAW,CAAE,CAAC,CACd,YAAY,CAAE,IAAI,EAI1B,eAAM,CACF,KAAK,CAAE,IAAI,CACX,WAAW,CAAE,mBAAwB,CAI7C,aAAc,CACV,OAAO,CAAE,IAAI,CACb,QAAQ,CAAE,QAAQ,CAElB,UAAU,CAAE,kDAAkD,CAE9D,6BAAgB,CACZ,KAAK,CAAE,IAAI,CACX,WAAW,CAAE,mBAAwB,CAGzC,wBAAa,CACT,KAAK,CAAE,IAAI,CACX,GAAG,CAAE,IAAI,CAET,iCAAkC,CAJtC,wBAAa,CAKL,OAAO,CAAE,eAAe,EAG5B,uCAAe,CACX,OAAO,CAAE,YAAY,CAGzB,sCAAc,CACV,OAAO,CAAE,IAAI,CAIb,2CAAe,CACX,OAAO,CAAE,IAAI,CAGjB,0CAAc,CACV,OAAO,CAAE,YAAY,CAKjC,8BAAmB,CACf,IAAI,CAAE,IAAI,CACV,GAAG,CAAE,IAAI,CAET,kDAAoB,CAChB,OAAO,CAAE,YAAY,CAGzB,kDAAoB,CAChB,OAAO,CAAE,IAAI,CAGjB,kCAAM,CACF,IAAI,CAAE,IAAI,CACV,GAAG,CAAE,IAAI,CAIT,uDAAoB,CAChB,OAAO,CAAE,IAAI,CAGjB,uDAAoB,CAChB,OAAO,CAAE,YAAY,CAOjC,qBAAQ,CACJ,SAAS,CAAE,IAAI,CAEnB,qBAAQ,CACJ,SAAS,CAAE,IAAI,CAEnB,qBAAQ,CACJ,SAAS,CAAE,IAAI,CAEnB,qBAAQ,CACJ,SAAS,CAAE,IAAI",
4 | "sources": ["../scss/site.scss"],
5 | "names": [],
6 | "file": "site.css"
7 | }
--------------------------------------------------------------------------------
/js/app/PointSpriteScaledColoredShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['framework/BaseShader'], function (BaseShader) {
4 | /**
5 | * Shader to render colored point sprites.
6 | */
7 | class PointSpriteScaledColoredShader extends BaseShader {
8 | fillCode() {
9 | this.vertexShaderCode = "uniform mat4 uMvp;\n" +
10 | "uniform float uThickness;\n" +
11 | "\n" +
12 | "attribute vec3 aPosition;\n" +
13 | "\n" +
14 | "void main() {\n" +
15 | " vec4 position = uMvp * vec4(aPosition.xyz, 1.0);\n" +
16 | " vec3 ndc = position.xyz / position.w; // perspective divide.\n" +
17 | " float zDist = 1.0 - ndc.z; // 1 is close (right up in your face,)\n" +
18 | " gl_PointSize = uThickness * zDist;\n" +
19 | " gl_Position = position; \n" +
20 | "}";
21 | this.fragmentShaderCode = "precision mediump float;\n" +
22 | "uniform sampler2D tex0;\n" +
23 | "uniform vec4 color;\n" +
24 | "\n" +
25 | "void main() \n" +
26 | "{\n" +
27 | " gl_FragColor = texture2D(tex0, gl_PointCoord) * color;\n" +
28 | "}";
29 | }
30 |
31 | fillUniformsAttributes() {
32 | this.uMvp = this.getUniform("uMvp");
33 | this.uThickness = this.getUniform("uThickness");
34 | this.aPosition = this.getAttrib("aPosition");
35 | this.tex0 = this.getUniform("tex0");
36 | this.color = this.getUniform("color");
37 | }
38 | }
39 |
40 | return PointSpriteScaledColoredShader;
41 | });
42 |
--------------------------------------------------------------------------------
/styles/css/site.css:
--------------------------------------------------------------------------------
1 | body{background-color:#000}#row-progress{color:#fff}.canvasGL{display:block;width:100vw;height:100vh;border:none;padding:0;margin:0 auto}.transparent{opacity:0 !important}.promo{opacity:1;position:absolute;bottom:5vh;width:100%;text-align:center;transition:opacity 1000ms cubic-bezier(0.645, 0.045, 0.355, 1)}.promo img{width:220px}.promo .text{color:#fff;text-shadow:#030303 0px 0px 5px}.controls{background-color:#000;position:absolute;top:0;bottom:0;left:0;right:0;padding:20px;padding-top:60px;opacity:1.0;transition:opacity 800ms cubic-bezier(0.645, 0.045, 0.355, 1)}@media (min-width: 768px){.controls{padding-top:20px;padding-left:50px}}.fs .controls{padding:0;padding-top:60px}@media (min-width: 768px){.fs .controls{padding-top:0;padding-left:30px}}.controls label{color:#fff;text-shadow:#030303 0px 0px 8px}.control-icon{opacity:0.85;position:absolute;transition:opacity 800ms cubic-bezier(0.645, 0.045, 0.355, 1)}.control-icon .material-icons{color:#fff;text-shadow:#030303 0px 0px 8px}.control-icon.control-fs{right:30px;top:30px}@media (display-mode: fullscreen){.control-icon.control-fs{display:none !important}}.control-icon.control-fs .icon-enter-fs{display:inline-block}.control-icon.control-fs .icon-exit-fs{display:none}.fs .control-icon.control-fs .icon-enter-fs{display:none}.fs .control-icon.control-fs .icon-exit-fs{display:inline-block}.control-icon.control-settings{left:40px;top:40px}.control-icon.control-settings .icon-show-settings{display:inline-block}.control-icon.control-settings .icon-hide-settings{display:none}.fs .control-icon.control-settings{left:20px;top:20px}.control-icon.control-settings.open .icon-show-settings{display:none}.control-icon.control-settings.open .icon-hide-settings{display:inline-block}.material-icons.md-18{font-size:18px}.material-icons.md-24{font-size:24px}.material-icons.md-36{font-size:36px}.material-icons.md-48{font-size:48px}
2 | /*# sourceMappingURL=site.css.map */
3 |
--------------------------------------------------------------------------------
/js/app/framework/TextureUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(() => {
4 |
5 | function TextureUtils() { }
6 |
7 | TextureUtils.createNPOTTexture = (texWidth, texHeight, bUseAlpha) => {
8 | const textureID = gl.createTexture();
9 | gl.bindTexture(gl.TEXTURE_2D, textureID);
10 | gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
11 | gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
12 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
13 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
14 |
15 | let glFormat = null, glInternalFormat = null;
16 |
17 | if (bUseAlpha) {
18 | glFormat = gl.RGBA;
19 | glInternalFormat = gl.RGBA;
20 | } else {
21 | glFormat = gl.RGB;
22 | glInternalFormat = gl.RGB;
23 | }
24 |
25 | gl.texImage2D(gl.TEXTURE_2D, 0, glInternalFormat, texWidth, texHeight, 0, glFormat, gl.UNSIGNED_BYTE, null);
26 |
27 | return textureID;
28 | };
29 |
30 | TextureUtils.createDepthTexture = (texWidth, texHeight) => {
31 | const textureID = gl.createTexture();
32 | gl.bindTexture(gl.TEXTURE_2D, textureID);
33 |
34 | gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
35 | gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
36 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
37 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
38 |
39 | const glFormat = gl.DEPTH_COMPONENT;
40 | const glInternalFormat = gl.DEPTH_COMPONENT16;
41 | const type = gl.UNSIGNED_SHORT;
42 |
43 | // In WebGL, we cannot pass array to depth texture.
44 | gl.texImage2D(gl.TEXTURE_2D, 0, glInternalFormat, texWidth, texHeight, 0, glFormat, type, null);
45 |
46 | return textureID;
47 | };
48 |
49 | return TextureUtils;
50 | });
51 |
--------------------------------------------------------------------------------
/js/app/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define([
4 | 'BuddhaRenderer',
5 | 'framework/utils/FullscreenUtils'
6 | ],
7 | function (
8 | BuddhaRenderer,
9 | FullScreenUtils) {
10 |
11 | var renderer;
12 |
13 | /**
14 | * Initialize renderer with current scene configuration
15 | */
16 | function initRenderer() {
17 | window.gl = null;
18 |
19 | if (renderer) {
20 | renderer.resetLoaded();
21 | oldYaw = renderer.angleYaw;
22 | }
23 |
24 | renderer = new BuddhaRenderer();
25 |
26 | renderer.init('canvasGL');
27 | renderer.angleYaw = 30;
28 | }
29 |
30 | function loadedHandler() {
31 | initRenderer();
32 |
33 | if (FullScreenUtils.isFullScreenSupported()) {
34 | const toggleFullscreenElement = document.getElementById('toggleFullscreen');
35 |
36 | toggleFullscreenElement.addEventListener('click', () => {
37 | if (document.body.classList.contains('fs')) {
38 | FullScreenUtils.exitFullScreen();
39 | } else {
40 | FullScreenUtils.enterFullScreen();
41 | }
42 | FullScreenUtils.addFullScreenListener(function () {
43 | if (FullScreenUtils.isFullScreen()) {
44 | document.body.classList.add('fs');
45 | } else {
46 | document.body.classList.remove('fs');
47 | }
48 | });
49 | });
50 | } else {
51 | toggleFullscreenElement.classList.add('hidden');
52 | }
53 | }
54 |
55 | if (document.readyState === 'loading') {
56 | document.addEventListener('DOMContentLoaded', loadedHandler);
57 | } else {
58 | loadedHandler();
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/js/app/LMTableShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['framework/BaseShader'], function(BaseShader) {
4 |
5 | /**
6 | * Shader for table. Uses diffuse and lightmap textures.
7 | * @class
8 | */
9 | class LMTableShader extends BaseShader {
10 | fillCode() {
11 | this.vertexShaderCode = 'uniform mat4 view_proj_matrix;\n' +
12 | 'attribute vec4 rm_Vertex;\n' +
13 | 'attribute vec2 rm_TexCoord0;\n' +
14 | 'attribute vec2 rm_TexCoord1;\n' +
15 | 'varying vec2 vTextureCoord;\n' +
16 | 'varying vec2 vTextureCoordLM;\n' +
17 | 'uniform float diffuseScale;\n' +
18 | '\n' +
19 | 'void main() {\n' +
20 | ' gl_Position = view_proj_matrix * rm_Vertex;\n' +
21 | ' vTextureCoord = rm_TexCoord0 * diffuseScale;\n' +
22 | ' vTextureCoordLM = rm_TexCoord1;\n' +
23 | '}';
24 |
25 | this.fragmentShaderCode = 'precision mediump float;\n' +
26 | 'varying vec2 vTextureCoord;\n' +
27 | 'varying vec2 vTextureCoordLM;\n' +
28 | 'uniform sampler2D sTexture;\n' +
29 | 'uniform sampler2D sLM;\n' +
30 | '\n' +
31 | 'void main() {\n' +
32 | ' vec4 lmColor = texture2D(sLM, vTextureCoordLM);\n' +
33 | ' gl_FragColor = texture2D(sTexture, vTextureCoord) * lmColor.r * lmColor.g;\n' +
34 | ' gl_FragColor.a = lmColor.g;\n' +
35 | '}';
36 | }
37 |
38 | fillUniformsAttributes() {
39 | this.view_proj_matrix = this.getUniform('view_proj_matrix');
40 | this.rm_Vertex = this.getAttrib('rm_Vertex');
41 | this.rm_TexCoord0 = this.getAttrib('rm_TexCoord0');
42 | this.rm_TexCoord1 = this.getAttrib('rm_TexCoord1');
43 | this.sTexture = this.getUniform('sTexture');
44 | this.sLM = this.getUniform('sLM');
45 | this.diffuseScale = this.getUniform('diffuseScale');
46 | }
47 | }
48 |
49 | return LMTableShader;
50 | });
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 3D Buddha WebGL Demo
2 |
3 | This demo is a WebGL port of Android live wallpaper *[3D Buddha Live Wallpaper](https://play.google.com/store/apps/details?id=org.androidworks.livewallpaperbuddha)*.
4 |
5 | Live demo page is available here: https://keaukraine.github.io/webgl-buddha/index.html
6 |
7 | Article explaining implementation of soft particles used in this demo - https://keaukraine.medium.com/implementing-soft-particles-in-webgl-and-opengl-es-b968d61133b0
8 |
9 | Works in latest Chrome, Firefox, Safari and Edge.
10 |
11 | # Used Libraries
12 |
13 | * Twitter Bootstrap used under the MIT License https://github.com/twbs/bootstrap/blob/master/LICENSE
14 | * Material Icons by Google used under Apache License https://design.google.com/icons/
15 | * RequireJS, Released under the MIT license https://github.com/requirejs/requirejs/blob/master/LICENSE
16 | * gl-matrix, a high performance matrix and vector operations by Brandon Jones and Colin MacKenzie IV
17 | * WebGL initialization code is based on http://learningwebgl.com/ tutorials
18 | * Google Play and the Google Play logo are trademarks of Google LLC.
19 |
20 | # License
21 |
22 | **The MIT License**
23 |
24 | Copyright (c) 2018 Oleksandr Popov, Dmytro Popov
25 |
26 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
29 |
30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 |
--------------------------------------------------------------------------------
/js/app/framework/CompressedTextureLoader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['./BinaryDataLoader'], function(BinaryDataLoader) {
4 |
5 | /**
6 | * Constructor. No need to create an instance of this class because it has only static methods
7 | */
8 | function CompressedTextureLoader() {}
9 |
10 | /**
11 | * Loads ETC1 texture from PKM format
12 | * @param {string} url - URL to texture in PKM format
13 | * @param {Function} callbak - callback called after texture is loaded to GPU
14 | * @return {number} - WebGL texture
15 | */
16 | CompressedTextureLoader.loadETC1 = function(url, callback) {
17 | var root = this,
18 | texture = gl.createTexture();
19 |
20 | var PKM_HEADER_SIZE = 16; // size of PKM header
21 | var PKM_HEADER_WIDTH_OFFSET = 8; // offset to texture width
22 | var PKM_HEADER_HEIGHT_OFFSET = 10; // offset to texture height
23 |
24 | BinaryDataLoader.load(url, function(data) {
25 | var bufWidth, bufHeight, bufData,
26 | width, height;
27 |
28 | var ETC1_RGB8_OES = 36196;
29 |
30 | if (data.byteLength > 0) {
31 | // Endianness depends on machine architecture, can't read Int16
32 | // In PKM, width and height are big-endian, and x86 is little-endian and ARM is bi-endian
33 | bufWidth = new Uint8Array(data, PKM_HEADER_WIDTH_OFFSET, 2);
34 | width = bufWidth[0]*256 + bufWidth[1];
35 | bufHeight = new Uint8Array(data, PKM_HEADER_HEIGHT_OFFSET, 2);
36 | height = bufHeight[0]*256 + bufHeight[1];
37 | bufData = new Uint8Array(data, PKM_HEADER_SIZE, data.byteLength - PKM_HEADER_SIZE);
38 |
39 | gl.bindTexture(gl.TEXTURE_2D, texture);
40 | gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ETC1_RGB8_OES, width, height, 0, bufData);
41 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
42 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
43 | gl.bindTexture(gl.TEXTURE_2D, null);
44 |
45 | console.log('Loaded texture ' + url + ' [' + width + 'x' + height + ']');
46 |
47 | callback && callback();
48 | }
49 | });
50 |
51 | return texture;
52 | }
53 |
54 | return CompressedTextureLoader;
55 | });
56 |
--------------------------------------------------------------------------------
/js/app/framework/FullModel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['./BinaryDataLoader'], function(BinaryDataLoader) {
4 |
5 | /**
6 | * Class to represent mesh data
7 | * @class
8 | */
9 | function FullModel() {
10 | this.bufferIndices = null;
11 | this.bufferStrides = null;
12 | this.numIndices = 0;
13 | }
14 |
15 | FullModel.prototype = {
16 |
17 | /**
18 | * Loads binary data for mesh
19 | * @param {string} url - URL for mesh files without trailing "-*.bin"
20 | * @param {Function} callback when data is loaded
21 | */
22 | load: function(url, callback) {
23 | var root = this;
24 |
25 | function loadBuffer(buffer, target, arrayBuffer) {
26 | var byteArray = new Uint8Array(arrayBuffer, 0, arrayBuffer.byteLength);
27 | gl.bindBuffer(target, buffer);
28 | gl.bufferData(target, byteArray, gl.STATIC_DRAW);
29 | }
30 |
31 | BinaryDataLoader.load(url + '-indices.bin',
32 | function(data) {
33 | root.bufferIndices = gl.createBuffer();
34 | console.log('Loaded ' + url + '-indices.bin: ' + data.byteLength + ' bytes');
35 | loadBuffer(root.bufferIndices, gl.ELEMENT_ARRAY_BUFFER, data);
36 | root.numIndices = data.byteLength / 2 / 3;
37 | root.bufferIndices && root.bufferStrides && callback();
38 | }
39 | );
40 | BinaryDataLoader.load(url + '-strides.bin',
41 | function(data) {
42 | root.bufferStrides = gl.createBuffer();
43 | console.log('Loaded ' + url + '-strides.bin: ' + data.byteLength + ' bytes');
44 | loadBuffer(root.bufferStrides, gl.ARRAY_BUFFER, data);
45 | root.bufferIndices && root.bufferStrides && callback();
46 | }
47 | );
48 | },
49 |
50 | /**
51 | * Binds buffers for glDrawElements() call
52 | */
53 | bindBuffers: function() {
54 | gl.bindBuffer(gl.ARRAY_BUFFER, this.bufferStrides);
55 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.bufferIndices);
56 | },
57 |
58 | /**
59 | * Returns number of inices in model
60 | * @return {number} - number of indices
61 | */
62 | getNumIndices: function() {
63 | return this.numIndices;
64 | }
65 | }
66 |
67 | return FullModel;
68 | });
69 |
--------------------------------------------------------------------------------
/js/app/LightShaftShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['framework/BaseShader'], function(BaseShader) {
4 |
5 | /**
6 | * Sahder to render coins. Uses spherical map for reflection and diffuse, normal and light maps.
7 | */
8 | class LightShaftShader extends BaseShader {
9 | fillCode() {
10 | this.vertexShaderCode = 'precision highp float;\r\n' +
11 | 'uniform mat4 view_proj_matrix;\r\n' +
12 | 'uniform mat4 view_matrix;\r\n' +
13 | '\r\n' +
14 | 'attribute vec4 rm_Vertex;\r\n' +
15 | 'attribute vec2 rm_TexCoord0;\r\n' +
16 | 'attribute vec3 rm_Normal;\r\n' +
17 | '\r\n' +
18 | 'varying vec3 vNormal;\r\n' +
19 | 'varying vec2 vTexCoord0;\r\n' +
20 | '\r\n' +
21 | 'void main( void )\r\n' +
22 | '{\r\n' +
23 | ' gl_Position = view_proj_matrix * rm_Vertex;\r\n' +
24 | '\r\n' +
25 | ' vTexCoord0 = vec2(rm_TexCoord0);\r\n' +
26 | '\r\n' +
27 | ' vNormal = (view_matrix * vec4(rm_Normal, 0.0)).xyz;\r\n' +
28 | '}';
29 |
30 | this.fragmentShaderCode = 'precision highp float;\r\n' +
31 | 'uniform sampler2D diffuseMap;\r\n' +
32 | 'varying vec2 vTexCoord0;\r\n' +
33 | 'varying vec3 vNormal;\r\n' +
34 | '\r\n' +
35 | 'void main()\r\n' +
36 | '{\r\n' +
37 | ' vec3 normColor = texture2D(diffuseMap,vTexCoord0).rgb;\r\n' +
38 | ' \r\n' +
39 | ' float rimAmount = clamp(pow(vNormal.b - 0.65, 1.0), 0.0, 1.0);' +
40 | ' ' +
41 | ' ' +
42 | ' gl_FragColor = vec4(normColor.r,normColor.g,normColor.b, normColor.b*rimAmount);\r\n' +
43 | '}';
44 | }
45 |
46 | fillUniformsAttributes() {
47 | this.view_proj_matrix = this.getUniform('view_proj_matrix');
48 | this.view_matrix = this.getUniform('view_matrix');
49 | this.rm_Vertex = this.getAttrib('rm_Vertex');
50 | this.rm_TexCoord0 = this.getAttrib('rm_TexCoord0');
51 | this.rm_Normal = this.getAttrib('rm_Normal');
52 | this.diffuseMap = this.getUniform('diffuseMap');
53 | this.sphereMap = this.getUniform('sphereMap');
54 | this.aoMap = this.getUniform('aoMap');
55 | }
56 | }
57 |
58 | return LightShaftShader;
59 | });
60 |
--------------------------------------------------------------------------------
/js/app/framework/FrameBuffer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(() => {
4 |
5 | class FrameBuffer {
6 |
7 | /**
8 | * Constructor
9 | */
10 | constructor() {
11 | this.textureHandle = null;
12 | this.depthTextureHandle = null;
13 | this.framebufferHandle = null;
14 | this.depthbufferHandle = null;
15 | this.width = 0;
16 | this.height = 0;
17 | }
18 |
19 | /**
20 | * Creates OpenGL objects
21 | */
22 | createGLData() {
23 | if (this.textureHandle !== null && this.width > 0 && this.height > 0) {
24 | this.framebufferHandle = gl.createFramebuffer(); // alternative to GLES20.glGenFramebuffers()
25 |
26 | if (this.textureHandle !== null) {
27 | gl.bindTexture(gl.TEXTURE_2D, this.textureHandle);
28 |
29 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebufferHandle);
30 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.textureHandle, 0);
31 | this.checkGlError("FB");
32 | }
33 |
34 | if (this.depthTextureHandle === null) {
35 | this.depthbufferHandle = gl.createRenderBuffer();
36 |
37 |
38 | gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthbufferHandle);
39 | this.checkGlError("FB - glBindRenderbuffer");
40 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
41 | this.checkGlError("FB - glRenderbufferStorage");
42 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthbufferHandle);
43 | this.checkGlError("FB - glFramebufferRenderbuffer");
44 | } else {
45 | gl.bindTexture(gl.TEXTURE_2D, this.depthTextureHandle);
46 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebufferHandle);
47 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.depthTextureHandle, 0);
48 | this.checkGlError("FB depth");
49 | }
50 |
51 | const result = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
52 | if (result != gl.FRAMEBUFFER_COMPLETE) {
53 | console.error(`Error creating framebufer: ${result}`);
54 | }
55 |
56 | gl.bindRenderbuffer(gl.RENDERBUFFER, null);
57 | // gl.bindTexture(gl.TEXTURE_2D, 0);
58 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
59 | }
60 | }
61 |
62 | checkGlError(op) {
63 | let error;
64 |
65 | while ((error = gl.getError()) !== gl.NO_ERROR) {
66 | console.error(`${op}: glError ${error}`);
67 | }
68 | }
69 | };
70 |
71 | return FrameBuffer;
72 | });
73 |
--------------------------------------------------------------------------------
/js/app/SoftDiffuseColoredShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['framework/BaseShader'], function (BaseShader) {
4 |
5 | /**
6 | * Shader for table. Uses diffuse and lightmap textures.
7 | * @class
8 | */
9 | class SoftDiffuseColoredShader extends BaseShader {
10 | fillCode() {
11 | this.vertexShaderCode = "uniform mat4 view_proj_matrix;\n" +
12 | "attribute vec4 rm_Vertex;\n" +
13 | "attribute vec2 rm_TexCoord0;\n" +
14 | "varying vec2 vTextureCoord;\n" +
15 | "\n" +
16 | "void main() {\n" +
17 | " gl_Position = view_proj_matrix * rm_Vertex;\n" +
18 | " vTextureCoord = rm_TexCoord0;\n" +
19 | "}";
20 |
21 |
22 | this.fragmentShaderCode = "precision highp float;\n" +
23 | "uniform vec2 uCameraRange;\n" +
24 | "uniform vec2 uInvViewportSize;\n" +
25 | "uniform float uTransitionSize;\n" +
26 | "float calc_depth(in float z)\n" +
27 | "{\n" +
28 | " return (2.0 * uCameraRange.x) / (uCameraRange.y + uCameraRange.x - z*(uCameraRange.y - uCameraRange.x));\n" +
29 | "}\n" +
30 | "uniform sampler2D sDepth;\n" +
31 | "varying vec2 vTextureCoord;\n" +
32 | "uniform sampler2D sTexture;\n" +
33 | "uniform vec4 color;\n" +
34 | "\n" +
35 | "void main() {\n" +
36 | " vec4 diffuse = texture2D(sTexture, vTextureCoord) * color;\n" + // particle base diffuse color
37 | // " diffuse += vec4(0.0, 0.0, 1.0, 1.0);\n"+ // uncomment to visualize particle shape
38 | " vec2 coords = gl_FragCoord.xy * uInvViewportSize;\n" + // calculate depth texture coordinates
39 | " float geometryZ = calc_depth(texture2D(sDepth, coords).r);\n" + // lineriarize particle depth
40 | " float sceneZ = calc_depth(gl_FragCoord.z);\n" + // lineriarize scene depth
41 | " float a = clamp(geometryZ - sceneZ, 0.0, 1.0);\n" + // linear clamped diff between scene and particle depth
42 | " float b = smoothstep(0.0, uTransitionSize, a);\n" + // apply smoothstep to make soft transition
43 | " gl_FragColor = diffuse * b;\n" + // final color with soft edge
44 | // " gl_FragColor = vec4(a, a, a, 1.0);\n" + // uncomment to visualize raw Z difference
45 | // " gl_FragColor = vec4(b, b, b, 1.0);\n" + // uncomment to visualize blending coefficient
46 | "}";
47 | }
48 |
49 | fillUniformsAttributes() {
50 | this.view_proj_matrix = this.getUniform("view_proj_matrix");
51 | this.rm_Vertex = this.getAttrib("rm_Vertex");
52 | this.rm_TexCoord0 = this.getAttrib("rm_TexCoord0");
53 | this.sTexture = this.getUniform("sTexture");
54 | this.cameraRange = this.getUniform("uCameraRange");
55 | this.sDepth = this.getUniform("sDepth");
56 | this.invViewportSize = this.getUniform("uInvViewportSize");
57 | this.transitionSize = this.getUniform("uTransitionSize");
58 | this.color = this.getUniform("color");
59 | }
60 | }
61 |
62 | return SoftDiffuseColoredShader;
63 | });
64 |
--------------------------------------------------------------------------------
/js/app/framework/BaseShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(function() {
4 |
5 | class BaseShader {
6 |
7 | /**
8 | * Constructor. Compiles shader.
9 | */
10 | constructor() {
11 | this.vertexShaderCode = '';
12 | this.fragmentShaderCode = '';
13 | this.program = null;
14 |
15 | this.fillCode();
16 | this.initShader();
17 | }
18 |
19 | /**
20 | * Used to fill shader code. Put actual shader code to this.vertexShaderCode and this.fragmentShaderCode
21 | */
22 | fillCode() {}
23 |
24 | getShader(gl, type, code) {
25 | var shader = gl.createShader(type);
26 |
27 | gl.shaderSource(shader, code);
28 | gl.compileShader(shader);
29 |
30 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
31 | console.warn(gl.getShaderInfoLog(shader));
32 | return null;
33 | }
34 |
35 | return shader;
36 | }
37 |
38 | /**
39 | * Retrieve and save uniforms and attributes for actual shader here
40 | */
41 | fillUniformsAttributes() {}
42 |
43 | /**
44 | * Get shader unform location
45 | * @param {string} uniform - uniform name
46 | * @return {number} - uniform location
47 | */
48 | getUniform(uniform) {
49 | return gl.getUniformLocation(this.program, uniform);
50 | }
51 |
52 | /**
53 | * Get shader attribute location
54 | * @param {string} attrib - uniform name
55 | * @return {number} - attribute location
56 | */
57 | getAttrib(attrib) {
58 | return gl.getAttribLocation(this.program, attrib);
59 | }
60 |
61 | /**
62 | * Initializes shader. No need to call this manually, this is called in constructor.
63 | */
64 | initShader() {
65 | var shaderProgram,
66 | fragmentShader = this.getShader(gl, gl.FRAGMENT_SHADER, this.fragmentShaderCode),
67 | vertexShader = this.getShader(gl, gl.VERTEX_SHADER, this.vertexShaderCode);
68 |
69 | shaderProgram = gl.createProgram();
70 | gl.attachShader(shaderProgram, vertexShader);
71 | gl.attachShader(shaderProgram, fragmentShader);
72 | gl.linkProgram(shaderProgram);
73 |
74 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
75 | console.warn(this.constructor.name + ': Could not initialise shader');
76 | } else {
77 | console.log(this.constructor.name + ': Initialised shader');
78 | }
79 |
80 | gl.useProgram(shaderProgram);
81 | this.program = shaderProgram;
82 |
83 | this.fillUniformsAttributes();
84 | }
85 |
86 | /**
87 | * Activates shader.
88 | */
89 | use() {
90 | if (this.program) {
91 | gl.useProgram(this.program);
92 | }
93 | }
94 |
95 | /**
96 | * Deletes shader.
97 | */
98 | deleteProgram() {
99 | gl.deleteProgram(this.program);
100 | }
101 | };
102 |
103 | return BaseShader;
104 | });
105 |
--------------------------------------------------------------------------------
/styles/scss/site.scss:
--------------------------------------------------------------------------------
1 | @import "common";
2 |
3 | body {
4 | background-color: #000;
5 | }
6 |
7 | #row-progress {
8 | color: #fff;
9 | }
10 |
11 | .canvasGL {
12 | display: block;
13 | width: 100vw;
14 | // max-width: 240vh;
15 | height: 100vh;
16 | border: none;
17 | padding: 0;
18 | margin: 0 auto;
19 | }
20 |
21 | .transparent {
22 | opacity: 0 !important;
23 | }
24 |
25 | .promo {
26 | opacity: 1;
27 | position: absolute;
28 | bottom: 5vh;
29 | width: 100%;
30 | text-align: center;
31 | transition: opacity 1000ms cubic-bezier(0.645, 0.045, 0.355, 1);
32 |
33 | img {
34 | width: 220px;
35 | }
36 |
37 | .text {
38 | color: #fff;
39 | text-shadow: #030303 0px 0px 5px;
40 | }
41 | }
42 |
43 | .controls {
44 | background-color: #000;
45 | position: absolute;
46 | top: 0;
47 | bottom: 0;
48 | left: 0;
49 | right: 0;
50 | padding: 20px;
51 | padding-top: 60px;
52 | opacity: 1.0;
53 |
54 | transition: opacity 800ms cubic-bezier(0.645, 0.045, 0.355, 1);
55 |
56 | @media(min-width: $screen-sm-min) {
57 | padding-top: 20px;
58 | padding-left: 50px;
59 | }
60 |
61 | .fs & {
62 | padding: 0;
63 | padding-top: 60px;
64 |
65 | @media(min-width: $screen-sm-min) {
66 | padding-top: 0;
67 | padding-left: 30px;
68 | }
69 | }
70 |
71 | label {
72 | color: #fff;
73 | text-shadow: rgb(3, 3, 3) 0px 0px 8px;
74 | }
75 | }
76 |
77 | .control-icon {
78 | opacity: 0.85;
79 | position: absolute;
80 |
81 | transition: opacity 800ms cubic-bezier(0.645, 0.045, 0.355, 1);
82 |
83 | .material-icons {
84 | color: #fff;
85 | text-shadow: rgb(3, 3, 3) 0px 0px 8px;
86 | }
87 |
88 | &.control-fs {
89 | right: 30px;
90 | top: 30px;
91 |
92 | @media (display-mode: fullscreen) {
93 | display: none !important;
94 | }
95 |
96 | .icon-enter-fs {
97 | display: inline-block;
98 | }
99 |
100 | .icon-exit-fs {
101 | display: none;
102 | }
103 |
104 | .fs & {
105 | .icon-enter-fs {
106 | display: none;
107 | }
108 |
109 | .icon-exit-fs {
110 | display: inline-block;
111 | }
112 | }
113 | }
114 |
115 | &.control-settings {
116 | left: 40px;
117 | top: 40px;
118 |
119 | .icon-show-settings {
120 | display: inline-block;
121 | }
122 |
123 | .icon-hide-settings {
124 | display: none;
125 | }
126 |
127 | .fs & {
128 | left: 20px;
129 | top: 20px;
130 | }
131 |
132 | &.open {
133 | .icon-show-settings {
134 | display: none;
135 | }
136 |
137 | .icon-hide-settings {
138 | display: inline-block;
139 | }
140 | }
141 | }
142 | }
143 |
144 | .material-icons {
145 | &.md-18 {
146 | font-size: 18px;
147 | }
148 | &.md-24 {
149 | font-size: 24px;
150 | }
151 | &.md-36 {
152 | font-size: 36px;
153 | }
154 | &.md-48 {
155 | font-size: 48px;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 3D Buddha WebGL demo
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
34 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
Loading...
46 |
52 |
53 |
54 |
55 |
56 |
57 | Cannot initialize WebGL context.
58 |
59 |
60 |
61 |
62 |
63 |
64 | fullscreen
65 | fullscreen_exit
66 |
67 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/js/app/framework/utils/FullscreenUtils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview Cross-browser full-screen utilities
3 | * @author Oleksandr Popov
4 | */
5 |
6 | 'use strict';
7 |
8 | define(function () {
9 |
10 | function FullScreenUtils() { }
11 |
12 | /**
13 | * Enters fullscreen
14 | */
15 | FullScreenUtils.enterFullScreen = function () {
16 | if (document.documentElement.requestFullscreen) {
17 | if (!document.fullscreenElement) {
18 | document.documentElement.requestFullscreen({ navigationUI: "hide" });
19 | }
20 | }
21 | if (document.documentElement.webkitRequestFullScreen) {
22 | if (!document.webkitFullscreenElement) {
23 | document.documentElement.webkitRequestFullscreen();
24 | }
25 | }
26 | if (document.documentElement.mozRequestFullScreen) {
27 | if (!document.mozFullscreenElement) {
28 | document.documentElement.mozRequestFullScreen();
29 | }
30 | }
31 | }
32 |
33 | /**
34 | * Exits fullscreen
35 | */
36 | FullScreenUtils.exitFullScreen = function () {
37 | if (document.documentElement.requestFullscreen) {
38 | if (document.exitFullscreen) {
39 | document.exitFullscreen();
40 | }
41 | }
42 | if (document.documentElement.webkitRequestFullscreen) {
43 | if (document.webkitExitFullscreen) {
44 | document.webkitExitFullscreen();
45 | }
46 | }
47 | if (document.documentElement.mozRequestFullScreen) {
48 | if (document.mozCancelFullScreen) {
49 | document.mozCancelFullScreen();
50 | }
51 | }
52 | }
53 |
54 | /**
55 | * Adds cross-browser fullscreenchange event
56 | * @param {function} exitHandler - function to be called on fullscreenchange event
57 | */
58 | FullScreenUtils.addFullScreenListener = function (exitHandler) {
59 | if (document.documentElement.requestFullscreen) {
60 | document.addEventListener('fullscreenchange', exitHandler, false);
61 | }
62 | if (document.documentElement.webkitRequestFullScreen) {
63 | document.addEventListener('webkitfullscreenchange', exitHandler, false);
64 | }
65 | if (document.documentElement.mozRequestFullScreen) {
66 | document.addEventListener('mozfullscreenchange', exitHandler, false);
67 | }
68 | }
69 |
70 | /**
71 | * Checks fullscreen state
72 | * @return {Boolean} - true if fullscreen is active, false if not
73 | */
74 | FullScreenUtils.isFullScreen = function () {
75 | if (document.documentElement.requestFullscreen) {
76 | return !!document.fullscreenElement;
77 | }
78 | if (document.documentElement.webkitRequestFullscreen) {
79 | return !!document.webkitFullscreenElement;
80 | }
81 | if (document.documentElement.mozRequestFullScreen) {
82 | return !!document.mozFullScreenElement;
83 | }
84 | }
85 |
86 | /**
87 | * Checks for full-screen API availability
88 | * @return {Boolean} - true if fullscrees is supported, false if not
89 | */
90 | FullScreenUtils.isFullScreenSupported = function () {
91 | return !!document.documentElement.requestFullscreen || !!document.documentElement.webkitRequestFullScreen || !!document.documentElement.mozRequestFullScreen;
92 | }
93 |
94 | return FullScreenUtils;
95 | });
96 |
--------------------------------------------------------------------------------
/js/app/SphericalMapLMShader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define(['framework/BaseShader'], function(BaseShader) {
4 |
5 | /**
6 | * Sahder to render coins. Uses spherical map for reflection and diffuse, normal and light maps.
7 | */
8 | class SphericalMapLMShader extends BaseShader {
9 | fillCode() {
10 | this.vertexShaderCode = 'precision highp float;\r\n' +
11 | 'uniform mat4 view_proj_matrix;\r\n' +
12 | 'uniform mat4 view_matrix;\r\n' +
13 | '\r\n' +
14 | 'attribute vec4 rm_Vertex;\r\n' +
15 | 'attribute vec2 rm_TexCoord0;\r\n' +
16 | 'attribute vec2 rm_TexCoord1;\r\n' +
17 | 'attribute vec3 rm_Normal;\r\n' +
18 | '\r\n' +
19 | 'varying vec3 vNormal;\r\n' +
20 | 'varying vec2 vTexCoord0;\r\n' +
21 | 'varying vec2 vTexCoord1;\r\n' +
22 | '\r\n' +
23 | 'void main( void )\r\n' +
24 | '{\r\n' +
25 | ' gl_Position = view_proj_matrix * rm_Vertex;\r\n' +
26 | '\r\n' +
27 | ' vTexCoord0 = vec2(rm_TexCoord0);\r\n' +
28 | ' vTexCoord1 = vec2(rm_TexCoord1);\r\n' +
29 | '\r\n' +
30 | ' vNormal = (view_matrix * vec4(rm_Normal, 0.0)).xyz;\r\n' +
31 | '}';
32 |
33 | this.fragmentShaderCode = 'precision highp float;\r\n' +
34 | 'uniform sampler2D normalMap;\r\n' +
35 | 'uniform sampler2D sphereMap;\r\n' +
36 | 'uniform sampler2D aoMap;\r\n' +
37 | 'varying vec2 vTexCoord0;\r\n' +
38 | 'varying vec2 vTexCoord1;\r\n' +
39 | 'varying vec3 vNormal;\r\n' +
40 | 'const vec4 SMALL_VALUE = vec4(0.01, 0.01, 0.01, 0.01);\r\n' +
41 | 'const vec4 VEC4_ONE = vec4(1.0, 1.0, 1.0, 1.0);\r\n' +
42 | '\r\n' +
43 | 'void main()\r\n' +
44 | '{\r\n' +
45 | ' vec3 normColor = texture2D(normalMap,vTexCoord0).rgb;\r\n' +
46 | ' vec4 aoColor = texture2D(aoMap,vTexCoord1);\r\n' +
47 | ' vec3 norm = normColor * 2.0 - 1.0;\r\n' +
48 | ' \r\n' +
49 | ' vec3 vNormal2 = normalize(\r\n' +
50 | ' vNormal\r\n' +
51 | ' + norm\r\n' +
52 | ' ) * 0.5\r\n' +
53 | ' ;\r\n' +
54 | '\r\n' +
55 | ' vec4 sphereColor = texture2D(sphereMap,\r\n' +
56 | ' vec2(vNormal2.x, vNormal2.y));\r\n' +
57 | //' sphereColor = pow(sphereColor, VEC4_ONE / (aoColor + SMALL_VALUE));\r\n' +
58 | ' sphereColor = pow(sphereColor, VEC4_ONE / (vec4(aoColor.g, aoColor.g, aoColor.g, 1.0) + SMALL_VALUE));\r\n' +
59 | ' sphereColor *= vec4(aoColor.b, aoColor.b, aoColor.b, 1.0);\r\n' +
60 | ' \r\n' +
61 | ' gl_FragColor = sphereColor;// * normColor.b;\r\n' +
62 | '}';
63 | }
64 |
65 | fillUniformsAttributes() {
66 | this.view_proj_matrix = this.getUniform('view_proj_matrix');
67 | this.view_matrix = this.getUniform('view_matrix');
68 | this.rm_Vertex = this.getAttrib('rm_Vertex');
69 | this.rm_TexCoord0 = this.getAttrib('rm_TexCoord0');
70 | this.rm_TexCoord1 = this.getAttrib('rm_TexCoord1');
71 | this.rm_Normal = this.getAttrib('rm_Normal');
72 | this.normalMap = this.getUniform('normalMap');
73 | this.sphereMap = this.getUniform('sphereMap');
74 | this.aoMap = this.getUniform('aoMap');
75 | }
76 | }
77 |
78 | return SphericalMapLMShader;
79 | });
80 |
--------------------------------------------------------------------------------
/js/app/framework/BaseRenderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define([
4 | './utils/MatrixUtils'
5 | ],
6 | function(MatrixUtils) {
7 |
8 | /**
9 | * Constructor
10 | */
11 | function BaseRenderer() {
12 | this.mMMatrix = MatrixUtils.mat4.create();
13 | this.mVMatrix = MatrixUtils.mat4.create();
14 | this.mMVPMatrix = MatrixUtils.mat4.create();
15 | this.mProjMatrix = MatrixUtils.mat4.create();
16 |
17 | this.matOrtho = MatrixUtils.mat4.create();
18 | MatrixUtils.mat4.ortho(this.matOrtho, -1, 1, -1, 1, 2.0, 250);
19 |
20 | this.boundTick = this.tick.bind(this);
21 | }
22 |
23 | /**
24 | * Logs last GL error to console
25 | */
26 | BaseRenderer.prototype.logGLError = function() {
27 | var err = gl.getError();
28 | if (err !== gl.NO_ERROR) {
29 | console.warn('WebGL error #' + err);
30 | }
31 | }
32 |
33 | /**
34 | * Binds 2D texture.
35 | * @param {number} textureUnit - texture unit to a
36 | * @param {number} textureID - texture to be used
37 | * @param {number} uniformID - shader's uniform ID
38 | */
39 | BaseRenderer.prototype.setTexture2D = function(textureUnit, textureID, uniformID) {
40 | gl.activeTexture(gl.TEXTURE0 + textureUnit);
41 | gl.bindTexture(gl.TEXTURE_2D, textureID);
42 | gl.uniform1i(uniformID, textureUnit);
43 | }
44 |
45 | /**
46 | * Binds cubemap texture.
47 | * @param {number} textureUnit - texture unit to a
48 | * @param {number} textureID - texture to be used
49 | * @param {number} uniformID - shader's uniform ID
50 | */
51 | BaseRenderer.prototype.setTextureCubemap = function(textureUnit, textureID, uniformID) {
52 | gl.ActiveTexture(gl.TEXTURE0 + textureUnit);
53 | gl.BindTexture(gl.TEXTURE_CUBE_MAP, textureID);
54 | gl.Uniform1i(uniformID, textureUnit);
55 | }
56 |
57 | /**
58 | * Calculates FOV for matrix.
59 | * @param {Mat4} matrix - output matrix
60 | * @param {number} fovY - vertical FOV in degrees
61 | * @param {number} aspect - aspect ratio of viewport
62 | * @param {number} zNear - near clipping plane distance
63 | * @param {number} zFar - far clipping plane distance
64 | */
65 | BaseRenderer.prototype.setFOV = function(matrix, fovY, aspect, zNear, zFar) {
66 | var fW, fH;
67 |
68 | fH = Math.tan(fovY / 360.0 * 3.1415926) * zNear;
69 | fW = fH * aspect;
70 | MatrixUtils.mat4.frustum(matrix, -fW, fW, -fH, fH, zNear, zFar);
71 | }
72 |
73 | /**
74 | * Calculates MVP matrix. Saved in this.mMVPMatrix
75 | */
76 | BaseRenderer.prototype.calculateMVPMatrix = function(tx, ty, tz, rx, ry, rz, sx, sy, sz) {
77 | MatrixUtils.mat4.identity(this.mMMatrix);
78 | MatrixUtils.mat4.rotate(this.mMMatrix, this.mMMatrix, 0, [1, 0, 0]);
79 | MatrixUtils.mat4.translate(this.mMMatrix, this.mMMatrix, [tx, ty, tz]);
80 | MatrixUtils.mat4.scale(this.mMMatrix, this.mMMatrix, [sx, sy, sz]);
81 | MatrixUtils.mat4.rotateX(this.mMMatrix, this.mMMatrix, rx);
82 | MatrixUtils.mat4.rotateY(this.mMMatrix, this.mMMatrix, ry);
83 | MatrixUtils.mat4.rotateZ(this.mMMatrix, this.mMMatrix, rz);
84 | MatrixUtils.mat4.multiply(this.mMVPMatrix, this.mVMatrix, this.mMMatrix);
85 | MatrixUtils.mat4.multiply(this.mMVPMatrix, this.mProjMatrix, this.mMVPMatrix);
86 | }
87 |
88 | /**
89 | * Called before WebGL initialization
90 | */
91 | BaseRenderer.prototype.onBeforeInit = function() {}
92 |
93 | /**
94 | * Called right after WebGL initialization
95 | */
96 | BaseRenderer.prototype.onAfterInit = function() {}
97 |
98 | /**
99 | * Called on WebGL initialization error
100 | */
101 | BaseRenderer.prototype.onInitError = function() {}
102 |
103 | /**
104 | * Shaders initialization code goes here
105 | */
106 | BaseRenderer.prototype.initShaders = function() {}
107 |
108 | /**
109 | * Load WebGL data here
110 | */
111 | BaseRenderer.prototype.loadData = function() {}
112 |
113 | /**
114 | * Perform each frame's draw calls here
115 | */
116 | BaseRenderer.prototype.drawScene = function() {
117 | gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
118 | gl.clearColor(0.0, 0.0, 0.0, 1.0);
119 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
120 | }
121 |
122 | /**
123 | * Update timers and aminate stuff here
124 | */
125 | BaseRenderer.prototype.animate = function() {}
126 |
127 | /**
128 | * Called on each frame
129 | */
130 | BaseRenderer.prototype.tick = function() {
131 | requestAnimationFrame(this.boundTick);
132 | this.resizeCanvas();
133 | this.drawScene();
134 | this.animate();
135 | }
136 |
137 | /**
138 | * Initializes WebGL context
139 | * @param {HTMLElement} canvas - canvas to initialize WebGL
140 | */
141 | BaseRenderer.prototype.initGL = function(canvas) {
142 | var gl = null;
143 |
144 | try {
145 | gl = canvas.getContext('webgl2', { alpha: false }) || canvas.getContext('experimental-webgl', { alpha: false });
146 | gl.viewportWidth = canvas.width;
147 | gl.viewportHeight = canvas.height;
148 | this.isETC1Supported = !!gl.getExtension('WEBGL_compressed_texture_etc1');
149 | } catch (e) { }
150 | if (!gl) {
151 | console.warn('Could not initialise WebGL');
152 | }
153 |
154 | return gl;
155 | };
156 |
157 | /**
158 | * Initializes WebGL 2 context
159 | * @param {HTMLElement} canvas - canvas to initialize WebGL
160 | */
161 | BaseRenderer.prototype.initGL2 = function (canvas) {
162 | var gl = null;
163 |
164 | try {
165 | gl = canvas.getContext('webgl2', { alpha: false });
166 | } catch (e) { }
167 | this.isWebGL2 = !!gl;
168 |
169 | if (!this.isWebGL2) {
170 | console.warn('Could not initialise WebGL 2, falling back to WebGL 1');
171 | return this.initGL(canvas);
172 | } else {
173 | return gl;
174 | }
175 | };
176 |
177 | /**
178 | * Initializes WebGL and calls all callbacks. Assigns current WebGL context to global window.gl
179 | * @param {String} canvasID - ID of canvas element to initialize WebGL
180 | */
181 | BaseRenderer.prototype.init = function(canvasID, requestWebGL2) {
182 | this.onBeforeInit();
183 |
184 | this.canvas = document.getElementById(canvasID);
185 | window.gl = !!requestWebGL2 ? this.initGL2(this.canvas) : this.initGL(this.canvas);
186 |
187 | if (window.gl) {
188 | this.resizeCanvas();
189 | this.onAfterInit();
190 | this.initShaders();
191 | this.loadData();
192 | this.boundTick();
193 | } else {
194 | this.onInitError();
195 | }
196 | }
197 |
198 | /**
199 | * Adjusts viewport according to resizing of canvas
200 | */
201 | BaseRenderer.prototype.resizeCanvas = function() {
202 | var cssToRealPixels = window.devicePixelRatio || 1,
203 | displayWidth = Math.floor(this.canvas.clientWidth * cssToRealPixels),
204 | displayHeight = Math.floor(this.canvas.clientHeight * cssToRealPixels);
205 |
206 | if (this.canvas.width != displayWidth || this.canvas.height != displayHeight) {
207 | this.canvas.width = displayWidth;
208 | this.canvas.height = displayHeight;
209 | }
210 | }
211 |
212 | BaseRenderer.prototype.checkGlError = function(op) {
213 | let error;
214 |
215 | while ((error = gl.getError()) !== gl.NO_ERROR) {
216 | console.error(`${op}: glError ${error}`);
217 | }
218 | }
219 |
220 | return BaseRenderer;
221 | });
222 |
--------------------------------------------------------------------------------
/js/lib/require.js:
--------------------------------------------------------------------------------
1 | /** vim: et:ts=4:sw=4:sts=4
2 | * @license RequireJS 2.3.1 Copyright jQuery Foundation and other contributors.
3 | * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE
4 | */
5 | var requirejs,require,define;!function(global,setTimeout){function commentReplace(e,t){return t||""}function isFunction(e){return"[object Function]"===ostring.call(e)}function isArray(e){return"[object Array]"===ostring.call(e)}function each(e,t){if(e){var i;for(i=0;i-1&&(!e[i]||!t(e[i],i,e));i-=1);}}function hasProp(e,t){return hasOwn.call(e,t)}function getOwn(e,t){return hasProp(e,t)&&e[t]}function eachProp(e,t){var i;for(i in e)if(hasProp(e,i)&&t(e[i],i))break}function mixin(e,t,i,r){return t&&eachProp(t,function(t,n){!i&&hasProp(e,n)||(!r||"object"!=typeof t||!t||isArray(t)||isFunction(t)||t instanceof RegExp?e[n]=t:(e[n]||(e[n]={}),mixin(e[n],t,i,r)))}),e}function bind(e,t){return function(){return t.apply(e,arguments)}}function scripts(){return document.getElementsByTagName("script")}function defaultOnError(e){throw e}function getGlobal(e){if(!e)return e;var t=global;return each(e.split("."),function(e){t=t[e]}),t}function makeError(e,t,i,r){var n=new Error(t+"\nhttp://requirejs.org/docs/errors.html#"+e);return n.requireType=e,n.requireModules=r,i&&(n.originalError=i),n}function newContext(e){function t(e){var t,i;for(t=0;t0&&(e.splice(t-1,2),t-=2)}}function i(e,i,r){var n,o,a,s,u,c,d,p,f,l,h,m,g=i&&i.split("/"),v=y.map,x=v&&v["*"];if(e&&(e=e.split("/"),d=e.length-1,y.nodeIdCompat&&jsSuffixRegExp.test(e[d])&&(e[d]=e[d].replace(jsSuffixRegExp,"")),"."===e[0].charAt(0)&&g&&(m=g.slice(0,g.length-1),e=m.concat(e)),t(e),e=e.join("/")),r&&v&&(g||x)){a=e.split("/");e:for(s=a.length;s>0;s-=1){if(c=a.slice(0,s).join("/"),g)for(u=g.length;u>0;u-=1)if(o=getOwn(v,g.slice(0,u).join("/")),o&&(o=getOwn(o,c))){p=o,f=s;break e}!l&&x&&getOwn(x,c)&&(l=getOwn(x,c),h=s)}!p&&l&&(p=l,f=h),p&&(a.splice(0,f,p),e=a.join("/"))}return n=getOwn(y.pkgs,e),n?n:e}function r(e){isBrowser&&each(scripts(),function(t){if(t.getAttribute("data-requiremodule")===e&&t.getAttribute("data-requirecontext")===q.contextName)return t.parentNode.removeChild(t),!0})}function n(e){var t=getOwn(y.paths,e);if(t&&isArray(t)&&t.length>1)return t.shift(),q.require.undef(e),q.makeRequire(null,{skipMap:!0})([e]),!0}function o(e){var t,i=e?e.indexOf("!"):-1;return i>-1&&(t=e.substring(0,i),e=e.substring(i+1,e.length)),[t,e]}function a(e,t,r,n){var a,s,u,c,d=null,p=t?t.name:null,f=e,l=!0,h="";return e||(l=!1,e="_@r"+(A+=1)),c=o(e),d=c[0],e=c[1],d&&(d=i(d,p,n),s=getOwn(j,d)),e&&(d?h=s&&s.normalize?s.normalize(e,function(e){return i(e,p,n)}):e.indexOf("!")===-1?i(e,p,n):e:(h=i(e,p,n),c=o(h),d=c[0],h=c[1],r=!0,a=q.nameToUrl(h))),u=!d||s||r?"":"_unnormalized"+(T+=1),{prefix:d,name:h,parentMap:t,unnormalized:!!u,url:a,originalName:f,isDefine:l,id:(d?d+"!"+h:h)+u}}function s(e){var t=e.id,i=getOwn(S,t);return i||(i=S[t]=new q.Module(e)),i}function u(e,t,i){var r=e.id,n=getOwn(S,r);!hasProp(j,r)||n&&!n.defineEmitComplete?(n=s(e),n.error&&"error"===t?i(n.error):n.on(t,i)):"defined"===t&&i(j[r])}function c(e,t){var i=e.requireModules,r=!1;t?t(e):(each(i,function(t){var i=getOwn(S,t);i&&(i.error=e,i.events.error&&(r=!0,i.emit("error",e)))}),r||req.onError(e))}function d(){globalDefQueue.length&&(each(globalDefQueue,function(e){var t=e[0];"string"==typeof t&&(q.defQueueMap[t]=!0),O.push(e)}),globalDefQueue=[])}function p(e){delete S[e],delete k[e]}function f(e,t,i){var r=e.map.id;e.error?e.emit("error",e.error):(t[r]=!0,each(e.depMaps,function(r,n){var o=r.id,a=getOwn(S,o);!a||e.depMatched[n]||i[o]||(getOwn(t,o)?(e.defineDep(n,j[o]),e.check()):f(a,t,i))}),i[r]=!0)}function l(){var e,t,i=1e3*y.waitSeconds,o=i&&q.startTime+i<(new Date).getTime(),a=[],s=[],u=!1,d=!0;if(!x){if(x=!0,eachProp(k,function(e){var i=e.map,c=i.id;if(e.enabled&&(i.isDefine||s.push(e),!e.error))if(!e.inited&&o)n(c)?(t=!0,u=!0):(a.push(c),r(c));else if(!e.inited&&e.fetched&&i.isDefine&&(u=!0,!i.prefix))return d=!1}),o&&a.length)return e=makeError("timeout","Load timeout for modules: "+a,null,a),e.contextName=q.contextName,c(e);d&&each(s,function(e){f(e,{},{})}),o&&!t||!u||!isBrowser&&!isWebWorker||w||(w=setTimeout(function(){w=0,l()},50)),x=!1}}function h(e){hasProp(j,e[0])||s(a(e[0],null,!0)).init(e[1],e[2])}function m(e,t,i,r){e.detachEvent&&!isOpera?r&&e.detachEvent(r,t):e.removeEventListener(i,t,!1)}function g(e){var t=e.currentTarget||e.srcElement;return m(t,q.onScriptLoad,"load","onreadystatechange"),m(t,q.onScriptError,"error"),{node:t,id:t&&t.getAttribute("data-requiremodule")}}function v(){var e;for(d();O.length;){if(e=O.shift(),null===e[0])return c(makeError("mismatch","Mismatched anonymous define() module: "+e[e.length-1]));h(e)}q.defQueueMap={}}var x,b,q,E,w,y={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},S={},k={},M={},O=[],j={},P={},R={},A=1,T=1;return E={require:function(e){return e.require?e.require:e.require=q.makeRequire(e.map)},exports:function(e){if(e.usingExports=!0,e.map.isDefine)return e.exports?j[e.map.id]=e.exports:e.exports=j[e.map.id]={}},module:function(e){return e.module?e.module:e.module={id:e.map.id,uri:e.map.url,config:function(){return getOwn(y.config,e.map.id)||{}},exports:e.exports||(e.exports={})}}},b=function(e){this.events=getOwn(M,e.id)||{},this.map=e,this.shim=getOwn(y.shim,e.id),this.depExports=[],this.depMaps=[],this.depMatched=[],this.pluginMaps={},this.depCount=0},b.prototype={init:function(e,t,i,r){r=r||{},this.inited||(this.factory=t,i?this.on("error",i):this.events.error&&(i=bind(this,function(e){this.emit("error",e)})),this.depMaps=e&&e.slice(0),this.errback=i,this.inited=!0,this.ignore=r.ignore,r.enabled||this.enabled?this.enable():this.check())},defineDep:function(e,t){this.depMatched[e]||(this.depMatched[e]=!0,this.depCount-=1,this.depExports[e]=t)},fetch:function(){if(!this.fetched){this.fetched=!0,q.startTime=(new Date).getTime();var e=this.map;return this.shim?void q.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],bind(this,function(){return e.prefix?this.callPlugin():this.load()})):e.prefix?this.callPlugin():this.load()}},load:function(){var e=this.map.url;P[e]||(P[e]=!0,q.load(this.map.id,e))},check:function(){if(this.enabled&&!this.enabling){var e,t,i=this.map.id,r=this.depExports,n=this.exports,o=this.factory;if(this.inited){if(this.error)this.emit("error",this.error);else if(!this.defining){if(this.defining=!0,this.depCount<1&&!this.defined){if(isFunction(o)){if(this.events.error&&this.map.isDefine||req.onError!==defaultOnError)try{n=q.execCb(i,o,r,n)}catch(t){e=t}else n=q.execCb(i,o,r,n);if(this.map.isDefine&&void 0===n&&(t=this.module,t?n=t.exports:this.usingExports&&(n=this.exports)),e)return e.requireMap=this.map,e.requireModules=this.map.isDefine?[this.map.id]:null,e.requireType=this.map.isDefine?"define":"require",c(this.error=e)}else n=o;if(this.exports=n,this.map.isDefine&&!this.ignore&&(j[i]=n,req.onResourceLoad)){var a=[];each(this.depMaps,function(e){a.push(e.normalizedMap||e)}),req.onResourceLoad(q,this.map,a)}p(i),this.defined=!0}this.defining=!1,this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else hasProp(q.defQueueMap,i)||this.fetch()}},callPlugin:function(){var e=this.map,t=e.id,r=a(e.prefix);this.depMaps.push(r),u(r,"defined",bind(this,function(r){var n,o,d,f=getOwn(R,this.map.id),l=this.map.name,h=this.map.parentMap?this.map.parentMap.name:null,m=q.makeRequire(e.parentMap,{enableBuildCallback:!0});return this.map.unnormalized?(r.normalize&&(l=r.normalize(l,function(e){return i(e,h,!0)})||""),o=a(e.prefix+"!"+l,this.map.parentMap),u(o,"defined",bind(this,function(e){this.map.normalizedMap=o,this.init([],function(){return e},null,{enabled:!0,ignore:!0})})),d=getOwn(S,o.id),void(d&&(this.depMaps.push(o),this.events.error&&d.on("error",bind(this,function(e){this.emit("error",e)})),d.enable()))):f?(this.map.url=q.nameToUrl(f),void this.load()):(n=bind(this,function(e){this.init([],function(){return e},null,{enabled:!0})}),n.error=bind(this,function(e){this.inited=!0,this.error=e,e.requireModules=[t],eachProp(S,function(e){0===e.map.id.indexOf(t+"_unnormalized")&&p(e.map.id)}),c(e)}),n.fromText=bind(this,function(i,r){var o=e.name,u=a(o),d=useInteractive;r&&(i=r),d&&(useInteractive=!1),s(u),hasProp(y.config,t)&&(y.config[o]=y.config[t]);try{req.exec(i)}catch(e){return c(makeError("fromtexteval","fromText eval for "+t+" failed: "+e,e,[t]))}d&&(useInteractive=!0),this.depMaps.push(u),q.completeLoad(o),m([o],n)}),void r.load(e.name,m,n,y))})),q.enable(r,this),this.pluginMaps[r.id]=r},enable:function(){k[this.map.id]=this,this.enabled=!0,this.enabling=!0,each(this.depMaps,bind(this,function(e,t){var i,r,n;if("string"==typeof e){if(e=a(e,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap),this.depMaps[t]=e,n=getOwn(E,e.id))return void(this.depExports[t]=n(this));this.depCount+=1,u(e,"defined",bind(this,function(e){this.undefed||(this.defineDep(t,e),this.check())})),this.errback?u(e,"error",bind(this,this.errback)):this.events.error&&u(e,"error",bind(this,function(e){this.emit("error",e)}))}i=e.id,r=S[i],hasProp(E,i)||!r||r.enabled||q.enable(e,this)})),eachProp(this.pluginMaps,bind(this,function(e){var t=getOwn(S,e.id);t&&!t.enabled&&q.enable(e,this)})),this.enabling=!1,this.check()},on:function(e,t){var i=this.events[e];i||(i=this.events[e]=[]),i.push(t)},emit:function(e,t){each(this.events[e],function(e){e(t)}),"error"===e&&delete this.events[e]}},q={config:y,contextName:e,registry:S,defined:j,urlFetched:P,defQueue:O,defQueueMap:{},Module:b,makeModuleMap:a,nextTick:req.nextTick,onError:c,configure:function(e){if(e.baseUrl&&"/"!==e.baseUrl.charAt(e.baseUrl.length-1)&&(e.baseUrl+="/"),"string"==typeof e.urlArgs){var t=e.urlArgs;e.urlArgs=function(e,i){return(i.indexOf("?")===-1?"?":"&")+t}}var i=y.shim,r={paths:!0,bundles:!0,config:!0,map:!0};eachProp(e,function(e,t){r[t]?(y[t]||(y[t]={}),mixin(y[t],e,!0,!0)):y[t]=e}),e.bundles&&eachProp(e.bundles,function(e,t){each(e,function(e){e!==t&&(R[e]=t)})}),e.shim&&(eachProp(e.shim,function(e,t){isArray(e)&&(e={deps:e}),!e.exports&&!e.init||e.exportsFn||(e.exportsFn=q.makeShimExports(e)),i[t]=e}),y.shim=i),e.packages&&each(e.packages,function(e){var t,i;e="string"==typeof e?{name:e}:e,i=e.name,t=e.location,t&&(y.paths[i]=e.location),y.pkgs[i]=e.name+"/"+(e.main||"main").replace(currDirRegExp,"").replace(jsSuffixRegExp,"")}),eachProp(S,function(e,t){e.inited||e.map.unnormalized||(e.map=a(t,null,!0))}),(e.deps||e.callback)&&q.require(e.deps||[],e.callback)},makeShimExports:function(e){function t(){var t;return e.init&&(t=e.init.apply(global,arguments)),t||e.exports&&getGlobal(e.exports)}return t},makeRequire:function(t,n){function o(i,r,u){var d,p,f;return n.enableBuildCallback&&r&&isFunction(r)&&(r.__requireJsBuild=!0),"string"==typeof i?isFunction(r)?c(makeError("requireargs","Invalid require call"),u):t&&hasProp(E,i)?E[i](S[t.id]):req.get?req.get(q,i,t,o):(p=a(i,t,!1,!0),d=p.id,hasProp(j,d)?j[d]:c(makeError("notloaded",'Module name "'+d+'" has not been loaded yet for context: '+e+(t?"":". Use require([])")))):(v(),q.nextTick(function(){v(),f=s(a(null,t)),f.skipMap=n.skipMap,f.init(i,r,u,{enabled:!0}),l()}),o)}return n=n||{},mixin(o,{isBrowser:isBrowser,toUrl:function(e){var r,n=e.lastIndexOf("."),o=e.split("/")[0],a="."===o||".."===o;return n!==-1&&(!a||n>1)&&(r=e.substring(n,e.length),e=e.substring(0,n)),q.nameToUrl(i(e,t&&t.id,!0),r,!0)},defined:function(e){return hasProp(j,a(e,t,!1,!0).id)},specified:function(e){return e=a(e,t,!1,!0).id,hasProp(j,e)||hasProp(S,e)}}),t||(o.undef=function(e){d();var i=a(e,t,!0),n=getOwn(S,e);n.undefed=!0,r(e),delete j[e],delete P[i.url],delete M[e],eachReverse(O,function(t,i){t[0]===e&&O.splice(i,1)}),delete q.defQueueMap[e],n&&(n.events.defined&&(M[e]=n.events),p(e))}),o},enable:function(e){var t=getOwn(S,e.id);t&&s(e).enable()},completeLoad:function(e){var t,i,r,o=getOwn(y.shim,e)||{},a=o.exports;for(d();O.length;){if(i=O.shift(),null===i[0]){if(i[0]=e,t)break;t=!0}else i[0]===e&&(t=!0);h(i)}if(q.defQueueMap={},r=getOwn(S,e),!t&&!hasProp(j,e)&&r&&!r.inited){if(!(!y.enforceDefine||a&&getGlobal(a)))return n(e)?void 0:c(makeError("nodefine","No define call for "+e,null,[e]));h([e,o.deps||[],o.exportsFn])}l()},nameToUrl:function(e,t,i){var r,n,o,a,s,u,c,d=getOwn(y.pkgs,e);if(d&&(e=d),c=getOwn(R,e))return q.nameToUrl(c,t,i);if(req.jsExtRegExp.test(e))s=e+(t||"");else{for(r=y.paths,n=e.split("/"),o=n.length;o>0;o-=1)if(a=n.slice(0,o).join("/"),u=getOwn(r,a)){isArray(u)&&(u=u[0]),n.splice(0,o,u);break}s=n.join("/"),s+=t||(/^data\:|^blob\:|\?/.test(s)||i?"":".js"),s=("/"===s.charAt(0)||s.match(/^[\w\+\.\-]+:/)?"":y.baseUrl)+s}return y.urlArgs&&!/^blob\:/.test(s)?s+y.urlArgs(e,s):s},load:function(e,t){req.load(q,e,t)},execCb:function(e,t,i,r){return t.apply(r,i)},onScriptLoad:function(e){if("load"===e.type||readyRegExp.test((e.currentTarget||e.srcElement).readyState)){interactiveScript=null;var t=g(e);q.completeLoad(t.id)}},onScriptError:function(e){var t=g(e);if(!n(t.id)){var i=[];return eachProp(S,function(e,r){0!==r.indexOf("_@r")&&each(e.depMaps,function(e){if(e.id===t.id)return i.push(r),!0})}),c(makeError("scripterror",'Script error for "'+t.id+(i.length?'", needed by: '+i.join(", "):'"'),e,[t.id]))}}},q.require=q.makeRequire(),q}function getInteractiveScript(){return interactiveScript&&"interactive"===interactiveScript.readyState?interactiveScript:(eachReverse(scripts(),function(e){if("interactive"===e.readyState)return interactiveScript=e}),interactiveScript)}var req,s,head,baseElement,dataMain,src,interactiveScript,currentlyAddingScript,mainScript,subPath,version="2.3.1",commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,currDirRegExp=/^\.\//,op=Object.prototype,ostring=op.toString,hasOwn=op.hasOwnProperty,isBrowser=!("undefined"==typeof window||"undefined"==typeof navigator||!window.document),isWebWorker=!isBrowser&&"undefined"!=typeof importScripts,readyRegExp=isBrowser&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,defContextName="_",isOpera="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),contexts={},cfg={},globalDefQueue=[],useInteractive=!1;if("undefined"==typeof define){if("undefined"!=typeof requirejs){if(isFunction(requirejs))return;cfg=requirejs,requirejs=void 0}"undefined"==typeof require||isFunction(require)||(cfg=require,require=void 0),req=requirejs=function(e,t,i,r){var n,o,a=defContextName;return isArray(e)||"string"==typeof e||(o=e,isArray(t)?(e=t,t=i,i=r):e=[]),o&&o.context&&(a=o.context),n=getOwn(contexts,a),n||(n=contexts[a]=req.s.newContext(a)),o&&n.configure(o),n.require(e,t,i)},req.config=function(e){return req(e)},req.nextTick="undefined"!=typeof setTimeout?function(e){setTimeout(e,4)}:function(e){e()},require||(require=req),req.version=version,req.jsExtRegExp=/^\/|:|\?|\.js$/,req.isBrowser=isBrowser,s=req.s={contexts:contexts,newContext:newContext},req({}),each(["toUrl","undef","defined","specified"],function(e){req[e]=function(){var t=contexts[defContextName];return t.require[e].apply(t,arguments)}}),isBrowser&&(head=s.head=document.getElementsByTagName("head")[0],baseElement=document.getElementsByTagName("base")[0],baseElement&&(head=s.head=baseElement.parentNode)),req.onError=defaultOnError,req.createNode=function(e,t,i){var r=e.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");return r.type=e.scriptType||"text/javascript",r.charset="utf-8",r.async=!0,r},req.load=function(e,t,i){var r,n=e&&e.config||{};if(isBrowser)return r=req.createNode(n,t,i),r.setAttribute("data-requirecontext",e.contextName),r.setAttribute("data-requiremodule",t),!r.attachEvent||r.attachEvent.toString&&r.attachEvent.toString().indexOf("[native code")<0||isOpera?(r.addEventListener("load",e.onScriptLoad,!1),r.addEventListener("error",e.onScriptError,!1)):(useInteractive=!0,r.attachEvent("onreadystatechange",e.onScriptLoad)),r.src=i,n.onNodeCreated&&n.onNodeCreated(r,n,t,i),currentlyAddingScript=r,baseElement?head.insertBefore(r,baseElement):head.appendChild(r),currentlyAddingScript=null,r;if(isWebWorker)try{setTimeout(function(){},0),importScripts(i),e.completeLoad(t)}catch(r){e.onError(makeError("importscripts","importScripts failed for "+t+" at "+i,r,[t]))}},isBrowser&&!cfg.skipDataMain&&eachReverse(scripts(),function(e){if(head||(head=e.parentNode),dataMain=e.getAttribute("data-main"))return mainScript=dataMain,cfg.baseUrl||mainScript.indexOf("!")!==-1||(src=mainScript.split("/"),mainScript=src.pop(),subPath=src.length?src.join("/")+"/":"./",cfg.baseUrl=subPath),mainScript=mainScript.replace(jsSuffixRegExp,""),req.jsExtRegExp.test(mainScript)&&(mainScript=dataMain),cfg.deps=cfg.deps?cfg.deps.concat(mainScript):[mainScript],!0}),define=function(e,t,i){var r,n;"string"!=typeof e&&(i=t,t=e,e=null),isArray(t)||(i=t,t=null),!t&&isFunction(i)&&(t=[],i.length&&(i.toString().replace(commentRegExp,commentReplace).replace(cjsRequireRegExp,function(e,i){t.push(i)}),t=(1===i.length?["require"]:["require","exports","module"]).concat(t))),useInteractive&&(r=currentlyAddingScript||getInteractiveScript(),r&&(e||(e=r.getAttribute("data-requiremodule")),n=contexts[r.getAttribute("data-requirecontext")])),n?(n.defQueue.push([e,t,i]),n.defQueueMap[e]=!0):globalDefQueue.push([e,t,i])},define.amd={jQuery:!0},req.exec=function(text){return eval(text)},req(cfg)}}(this,setTimeout);
--------------------------------------------------------------------------------
/js/app/BuddhaRenderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | define([
4 | 'framework/BaseRenderer',
5 | 'DiffuseShader',
6 | 'LightShaftShader',
7 | 'SphericalMapLMShader',
8 | 'LMTableShader',
9 | 'PointSpriteScaledColoredShader',
10 | 'framework/utils/MatrixUtils',
11 | 'framework/FullModel',
12 | 'framework/UncompressedTextureLoader',
13 | 'framework/CompressedTextureLoader',
14 | 'framework/FrameBuffer',
15 | 'framework/TextureUtils',
16 | 'SoftDiffuseColoredShader'
17 | ],
18 | function (
19 | BaseRenderer,
20 | DiffuseShader,
21 | LightShaftShader,
22 | SphericalMapLMShader,
23 | LMTableShader,
24 | PointSpriteScaledColoredShader,
25 | MatrixUtils,
26 | FullModel,
27 | UncompressedTextureLoader,
28 | CompressedTextureLoader,
29 | FrameBuffer,
30 | TextureUtils,
31 | SoftDiffuseColoredShader
32 | ) {
33 | class BuddhaRenderer extends BaseRenderer {
34 | constructor() {
35 | super();
36 |
37 | this.loadedItemsCount = 0; // counter of loaded OpenGL buffers+textures
38 | this.loaded = false; // won't draw until this is true
39 |
40 | this.angleYaw = 0; // camera rotation angle
41 | this.lastTime = 0; // used for animating camera
42 |
43 | this.ITEMS_TO_LOAD = 15; // total number of OpenGL buffers+textures to load
44 | this.FLOAT_SIZE_BYTES = 4; // float size, used to calculate stride sizes
45 | this.TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * this.FLOAT_SIZE_BYTES;
46 | this.TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
47 | this.TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
48 | this.FOV_LANDSCAPE = 25.0; // FOV for landscape
49 | this.FOV_PORTRAIT = 40.0; // FOV for portrait
50 | this.YAW_COEFF_NORMAL = 80.0; // camera rotation speed
51 |
52 | this.SCENE_SIZE = { x: 350, y: 350, z: 200 };
53 | this.DUST_SPEED = 350000 * 60;
54 | this.DUST_FLICKER_SPEED = 10000;
55 | this.DUST_COUNT = 8;
56 | this.DUST_OFFSET_Z = 200;
57 | this.DUST_COLOR = { r: 20 / 256, g: 18 / 256, b: 15 / 256, a: 1 };
58 | this.DUST_SPRITE_SIZE = 0.015;
59 | this.DUST_SCALE = 0.75;
60 |
61 | this.timerDustRotation = 0;
62 | this.timerDustFlicker = 0;
63 | this.timerSmokeRotation = 0;
64 | this.dustSpriteSize = 0;
65 |
66 | this.Z_NEAR = 100;
67 | this.Z_FAR = 2000;
68 | this.SMOKE_SOFTNESS = 0.08;
69 | this.m_smokeCoordinates = [
70 | [-219.188324, -77.765877, -32.000130],
71 | [-164.917282, -155.531754, -3.268164],
72 | [-74.093452, -109.379417, -100.979820],
73 | [-54.972435, -218.313690, -125.006828],
74 | [68.940094, -109.379417, -57.751869],
75 | [54.972427, -218.313690, -46.009144],
76 | [209.912292, -77.765877, -144.106689],
77 | [164.917282, -155.531754, 37.452091],
78 | [-237.278671, -0.000009, 63.925690],
79 | [224.910599, -0.000009, 69.076576],
80 | [-219.188324, 77.765869, -143.545853],
81 | [-74.093452, 107.153793, -18.438652],
82 | [68.940109, 107.153816, 9.984768],
83 | [209.912292, 77.765869, -87.094765],
84 | [-164.917297, 155.531754, -42.647652],
85 | [-54.972443, 214.752686, 36.233562],
86 | [54.972427, 214.752686, 29.778637],
87 | [164.917282, 155.531754, -194.976959],
88 | ];
89 | this.SMOKE_COLOR = {
90 | r: 75 / 255,
91 | g: 76 / 255,
92 | b: 92 / 255
93 | };
94 | this.SMOKE_SPEED = 90333;
95 |
96 | this.mOrthoMatrix = new Array(16);
97 | MatrixUtils.mat4.ortho(this.mOrthoMatrix, -1, 1, -1, 1, 2.0, 250);
98 | }
99 |
100 | initVignette() {
101 | this.mQuadTriangles = new Float32Array([
102 | // X, Y, Z, U, V
103 | -1.0, -1.0, -5.0, 0.0, 0.0, // 0. left-bottom
104 | 1.0, -1.0, -5.0, 1.0, 0.0, // 1. right-bottom
105 | -1.0, 1.0, -5.0, 0.0, 1.0, // 2. left-top
106 | 1.0, 1.0, -5.0, 1.0, 1.0, // 3. right-top
107 | ]);
108 | this.mTriangleVerticesVignette = gl.createBuffer();
109 | gl.bindBuffer(gl.ARRAY_BUFFER, this.mTriangleVerticesVignette);
110 | gl.bufferData(gl.ARRAY_BUFFER, this.mQuadTriangles, gl.STATIC_DRAW);
111 | }
112 |
113 | resizeCanvas() {
114 | super.resizeCanvas();
115 |
116 | this.dustSpriteSize = Math.min(this.canvas.height, this.canvas.width) * this.DUST_SPRITE_SIZE;
117 | }
118 |
119 | /**
120 | * Resets loaded state for renderer
121 | */
122 | resetLoaded() {
123 | this.loaded = false;
124 | this.loadedItemsCount = 0;
125 | }
126 |
127 | onBeforeInit() {
128 | super.onBeforeInit();
129 |
130 | document.getElementById('canvasGL').classList.remove('hidden');
131 | }
132 |
133 | onInitError() {
134 | super.onInitError();
135 |
136 | $(canvas).hide();
137 | document.getElementById('alertError').classList.remove('hidden');
138 | }
139 |
140 | initShaders() {
141 | this.shaderSphericalMapLM = new SphericalMapLMShader();
142 | this.shaderLMTable = new LMTableShader();
143 | this.shaderDiffuse = new DiffuseShader();
144 | this.shaderShaft = new LightShaftShader();
145 | this.shaderPointSpriteScaledColored = new PointSpriteScaledColoredShader();
146 | this.shaderSoftDiffuseColored = new SoftDiffuseColoredShader();
147 | }
148 |
149 | /**
150 | * Callback for all loading function. Updates loading progress and allows rendering after loading all stuff
151 | */
152 | updateLoadedObjectsCount() {
153 | var percent,
154 | progressElement = document.getElementById('progressLoading');
155 |
156 | this.loadedItemsCount++; // increase loaded objects counter
157 |
158 | percent = Math.floor(this.loadedItemsCount * 100 / this.ITEMS_TO_LOAD) + '%';
159 | progressElement.innerHTML = percent;
160 | progressElement.style.width = percent;
161 |
162 | if (this.loadedItemsCount >= this.ITEMS_TO_LOAD) {
163 | this.loaded = true; // allow rendering
164 | console.log('Loaded all assets');
165 | document.getElementById('divControls').classList.add('transparent');
166 | setTimeout(() => document.getElementById('divControls').classList.add('hidden'), 1000);
167 | setTimeout(() => document.querySelector('.control-icon').classList.remove('transparent'), 1200);
168 | setTimeout(() => document.querySelector('.promo').classList.remove('transparent'), 1800);
169 | }
170 | }
171 |
172 | /**
173 | * loads all WebGL buffers and textures. Uses updateLoadedObjectsCount() callback to indicate that data is loaded to GPU
174 | */
175 | loadData() {
176 | this.initVignette();
177 |
178 | var boundUpdateCallback = this.updateLoadedObjectsCount.bind(this);
179 |
180 | this.textureBuddhaNormalMap = UncompressedTextureLoader.load('data/textures/buddha-normals.png', boundUpdateCallback);
181 | this.textureSphericalMap = UncompressedTextureLoader.load('data/textures/sphere_gold3.png', boundUpdateCallback);
182 | this.textureBuddhaLightMap = UncompressedTextureLoader.load('data/textures/buddha_lm.png', boundUpdateCallback);
183 | this.textureTable = this.loadETC1WithFallback('data/textures/table/marble');
184 | this.textureTableLM = UncompressedTextureLoader.load('data/textures/table/table_lm.png', boundUpdateCallback);
185 | this.textureSky = UncompressedTextureLoader.load('data/textures/sky/sky1.png', boundUpdateCallback);
186 | this.textureShaft = UncompressedTextureLoader.load('data/textures/shafts.png', boundUpdateCallback);
187 | this.textureSmoke = UncompressedTextureLoader.load('data/textures/smoke.png', boundUpdateCallback);
188 |
189 | this.modelTable = new FullModel();
190 | this.modelTable.load('data/models/table', boundUpdateCallback);
191 | this.modelBuddha = new FullModel();
192 | this.modelBuddha.load('data/models/buddha', boundUpdateCallback);
193 |
194 | this.fmSky = new FullModel();
195 | this.fmSky.load('data/models/sky', boundUpdateCallback);
196 |
197 | this.fmShaft = new FullModel();
198 | this.fmShaft.load('data/models/shafts', boundUpdateCallback);
199 |
200 | this.fmDustPatch = new FullModel();
201 | this.fmDustPatch.load('data/models/particles_20', boundUpdateCallback);
202 | this.textureDust = UncompressedTextureLoader.load('data/textures/dust.png', boundUpdateCallback);
203 |
204 | this.fmQuad = new FullModel();
205 | this.fmQuad.load('data/models/smoke100', boundUpdateCallback);
206 |
207 | this.fillParticles();
208 |
209 | this.initOffscreen();
210 | }
211 |
212 | fillParticles() {
213 | this.dustCoordinates = [];
214 |
215 | for (let i = 0; i < this.DUST_COUNT; i++) {
216 | this.dustCoordinates[i] = {
217 | x: (0.5 - Math.random()) * this.SCENE_SIZE.x,
218 | y: (0.5 - Math.random()) * this.SCENE_SIZE.y,
219 | z: (0.5 - Math.random()) * this.SCENE_SIZE.z + this.DUST_OFFSET_Z
220 | };
221 | }
222 | }
223 |
224 | initOffscreen() {
225 | this.textureOffscreenColorID = TextureUtils.createNPOTTexture(this.canvas.width, this.canvas.height, false);
226 | this.checkGlError("color");
227 | this.textureOffscreenDepthID = TextureUtils.createDepthTexture(this.canvas.width, this.canvas.height);
228 | this.checkGlError("depth");
229 | this.fboOffscreen = new FrameBuffer();
230 | this.fboOffscreen.textureHandle = this.textureOffscreenColorID;
231 | this.fboOffscreen.depthTextureHandle = this.textureOffscreenDepthID;
232 | this.fboOffscreen.width = this.canvas.width;
233 | this.fboOffscreen.height = this.canvas.height;
234 | this.fboOffscreen.createGLData();
235 | this.checkGlError("offscreen FBO");
236 | }
237 |
238 | /**
239 | * Loads either ETC1 from PKM or falls back to loading PNG
240 | * @param {string} url - URL to texture without extension
241 | */
242 | loadETC1WithFallback(url) {
243 | var boundUpdateCallback = this.updateLoadedObjectsCount.bind(this);
244 |
245 | if (this.isETC1Supported) {
246 | return CompressedTextureLoader.loadETC1(url + '.pkm', boundUpdateCallback);
247 | } else {
248 | return UncompressedTextureLoader.load(url + '.png', boundUpdateCallback);
249 | }
250 | }
251 |
252 | /**
253 | * Calculates camera matrix
254 | * @param {number} a - position in [0...1] range
255 | */
256 | positionCamera(a) {
257 | var x, y, z,
258 | sina, cosa;
259 |
260 | x = 0;
261 | y = 0;
262 | z = (Math.sin(a * 6.2831852) * 200.0) + 250.0;
263 | sina = Math.sin(this.angleYaw / 360.0 * 6.2831852);
264 | cosa = Math.cos(this.angleYaw / 360.0 * 6.2831852);
265 | x = sina * 460.0 * 1.5;
266 | y = cosa * 460.0 * 1.5;
267 | const lookAtZ = 75 + 20 * sina;
268 |
269 | MatrixUtils.mat4.identity(this.mVMatrix);
270 | MatrixUtils.mat4.lookAt(this.mVMatrix, [x, y, z], [0, 0, lookAtZ], [0, 0, 1]);
271 | }
272 |
273 | /**
274 | * Calculates projection matrix
275 | */
276 | setCameraFOV(multiplier) {
277 | var ratio;
278 |
279 | if (gl.canvas.height > 0) {
280 | ratio = gl.canvas.width / gl.canvas.height;
281 | } else {
282 | ratio = 1.0;
283 | }
284 |
285 | if (gl.canvas.width >= gl.canvas.height) {
286 | this.setFOV(this.mProjMatrix, this.FOV_LANDSCAPE * multiplier, ratio, this.Z_NEAR, this.Z_FAR);
287 | } else {
288 | this.setFOV(this.mProjMatrix, this.FOV_PORTRAIT * multiplier, ratio, this.Z_NEAR, this.Z_FAR);
289 | }
290 | }
291 |
292 | /**
293 | * Issues actual draw calls
294 | */
295 | drawScene() {
296 | if (!this.loaded) {
297 | return;
298 | }
299 |
300 | this.positionCamera(0.0);
301 | this.setCameraFOV(1.0);
302 |
303 | gl.clearColor(0.0, 1.0, 0.0, 1.0);
304 |
305 | gl.colorMask(false, false, false, false);
306 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.fboOffscreen.framebufferHandle);
307 | gl.viewport(0, 0, this.fboOffscreen.width, this.fboOffscreen.height);
308 | gl.depthMask(true);
309 | gl.enable(gl.DEPTH_TEST);
310 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
311 | this.drawDepthObjects();
312 |
313 | gl.enable(gl.DEPTH_TEST);
314 | gl.enable(gl.CULL_FACE);
315 | gl.cullFace(gl.BACK);
316 |
317 | gl.colorMask(true, true, true, true);
318 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); // This differs from OpenGL ES
319 | gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
320 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
321 |
322 | this.drawSceneObjects();
323 | // this.drawTestDepth();
324 | }
325 |
326 | drawSceneObjects() {
327 | this.drawBuddha();
328 | this.drawSky();
329 | this.drawTable(1.0);
330 | this.drawShaft();
331 | this.drawDust();
332 | this.drawSmoke();
333 | }
334 |
335 | drawDepthObjects() {
336 | gl.depthMask(true);
337 | gl.disable(gl.BLEND);
338 | this.drawBuddha();
339 | this.drawTable(2.0);
340 | }
341 |
342 | drawTestDepth() {
343 | gl.enable(gl.CULL_FACE);
344 | gl.cullFace(gl.BACK);
345 | gl.disable(gl.BLEND);
346 |
347 | this.shaderDiffuse.use();
348 |
349 | this.setTexture2D(0, this.textureOffscreenDepthID, this.shaderDiffuse.sTexture);
350 | this.drawVignette(this.shaderDiffuse);
351 | }
352 |
353 | drawSmoke() {
354 | gl.enable(gl.BLEND);
355 | gl.blendFunc(gl.ONE, gl.ONE);
356 | gl.depthMask(false);
357 |
358 | const cosa = Math.cos(this.timerSmokeRotation * Math.PI * 2);
359 | const sina = Math.sin(this.timerSmokeRotation * Math.PI * 2);
360 |
361 | this.shaderSoftDiffuseColored.use();
362 | this.initDepthReadShader(this.shaderSoftDiffuseColored);
363 | this.setTexture2D(0, this.textureSmoke, this.shaderSoftDiffuseColored.sTexture);
364 | gl.uniform4f(
365 | this.shaderSoftDiffuseColored.color,
366 | this.SMOKE_COLOR.r, this.SMOKE_COLOR.g, this.SMOKE_COLOR.b, 1
367 | );
368 |
369 | for (let i = 0; i < this.m_smokeCoordinates.length; i++) {
370 | const x = this.m_smokeCoordinates[i][0] * 1.5;
371 | const y = this.m_smokeCoordinates[i][1] * 1.5;
372 | const z = this.m_smokeCoordinates[i][2] * 0.2 + 20;
373 | const rotation = i * 35 + this.timerDustRotation * 3 * (i % 2 === 0 ? 360 : -360);
374 |
375 | this.drawDiffuseVBOFacingCamera(
376 | this.shaderSoftDiffuseColored,
377 | this.fmQuad,
378 | (x * cosa - y * sina),
379 | (x * sina + y * cosa),
380 | z,
381 | 2.3, 2.3, 2.3,
382 | rotation
383 | );
384 | }
385 |
386 | gl.disable(gl.BLEND);
387 | gl.depthMask(true);
388 | }
389 |
390 | initDepthReadShader(shader) {
391 | gl.uniform2f(shader.cameraRange, this.Z_NEAR, this.Z_FAR); // near and far clipping planes
392 | gl.uniform2f(shader.invViewportSize, 1.0 / this.canvas.width, 1.0 / this.canvas.height); // inverted screen size
393 | gl.uniform1f(shader.transitionSize, this.SMOKE_SOFTNESS);
394 | this.setTexture2D(2, this.textureOffscreenDepthID, shader.sDepth);
395 | }
396 |
397 | drawDiffuseVBOFacingCamera(shader, model, tx, ty, tz, sx, sy, sz, rotation) {
398 | model.bindBuffers();
399 |
400 | gl.enableVertexAttribArray(shader.rm_Vertex);
401 | gl.enableVertexAttribArray(shader.rm_TexCoord0);
402 | gl.vertexAttribPointer(shader.rm_Vertex, 3, gl.FLOAT, false, 4 * (3 + 2), 0);
403 | gl.vertexAttribPointer(shader.rm_TexCoord0, 2, gl.FLOAT, false, 4 * (3 + 2), 4 * 3);
404 |
405 | this.calculateMVPMatrixForSprite(tx, ty, tz, sx, sy, sz, rotation);
406 |
407 | gl.uniformMatrix4fv(shader.view_proj_matrix, false, this.mMVPMatrix);
408 | gl.drawElements(gl.TRIANGLES, model.getNumIndices() * 3, gl.UNSIGNED_SHORT, 0);
409 | this.checkGlError("glDrawElements");
410 | }
411 |
412 | calculateMVPMatrixForSprite(tx, ty, tz, sx, sy, sz, rotation) {
413 | MatrixUtils.mat4.identity(this.mMMatrix);
414 | MatrixUtils.mat4.translate(this.mMMatrix, this.mMMatrix, [tx, ty, tz]);
415 | MatrixUtils.mat4.scale(this.mMMatrix, this.mMMatrix, [sx, sy, sz]);
416 | MatrixUtils.mat4.multiply(this.mMVPMatrix, this.mVMatrix, this.mMMatrix);
417 | this.resetMatrixRotations(this.mMVPMatrix);
418 | MatrixUtils.mat4.rotateZ(this.mMVPMatrix, this.mMVPMatrix, rotation);
419 | MatrixUtils.mat4.multiply(this.mMVPMatrix, this.mProjMatrix, this.mMVPMatrix);
420 | }
421 |
422 | resetMatrixRotations(matrix) {
423 | const d = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1] + matrix[2] * matrix[2]);
424 | matrix[0] = d;
425 | matrix[4] = 0;
426 | matrix[8] = 0;
427 |
428 | matrix[1] = 0;
429 | matrix[5] = d;
430 | matrix[9] = 0;
431 |
432 | matrix[2] = 0;
433 | matrix[6] = 0;
434 | matrix[10] = d;
435 |
436 | matrix[3] = 0;
437 | matrix[7] = 0;
438 | matrix[11] = 0;
439 |
440 | matrix[15] = 1;
441 | }
442 |
443 | drawDust() {
444 | gl.enable(gl.BLEND);
445 | gl.blendFunc(gl.ONE, gl.ONE);
446 | gl.depthMask(false);
447 |
448 | const cosa = 0.5 + Math.cos(this.timerDustFlicker * Math.PI * 2) * 0.5;
449 | const sina = 0.5 + Math.sin(this.timerDustFlicker * Math.PI * 2) * 0.5;
450 |
451 | this.shaderPointSpriteScaledColored.use();
452 | this.setTexture2D(0, this.textureDust, this.shaderPointSpriteScaledColored.tex0);
453 | gl.uniform1f(this.shaderPointSpriteScaledColored.uThickness, this.dustSpriteSize);
454 | gl.uniform4f(this.shaderPointSpriteScaledColored.color, this.DUST_COLOR.r * cosa, this.DUST_COLOR.g * cosa, this.DUST_COLOR.b * cosa, this.DUST_COLOR.a);
455 |
456 | const a = this.timerDustRotation * 360;
457 | const b = -this.timerDustRotation * 360;
458 |
459 | for (let i = 0; i < this.dustCoordinates.length; i++) {
460 | if (i < this.dustCoordinates.length / 2) {
461 | this.drawPointSpritesVBOTranslatedRotatedScaled(this.shaderPointSpriteScaledColored, this.fmDustPatch,
462 | this.dustCoordinates[i].x, this.dustCoordinates[i].y, this.dustCoordinates[i].z,
463 | a, b, a,
464 | this.DUST_SCALE, this.DUST_SCALE, this.DUST_SCALE);
465 | } else {
466 | this.drawPointSpritesVBOTranslatedRotatedScaled(this.shaderPointSpriteScaledColored, this.fmDustPatch,
467 | this.dustCoordinates[i].x, this.dustCoordinates[i].y, this.dustCoordinates[i].z,
468 | b, a, b,
469 | this.DUST_SCALE, this.DUST_SCALE, this.DUST_SCALE);
470 | }
471 | if (i == this.dustCoordinates.length / 2) {
472 | gl.uniform4f(this.shaderPointSpriteScaledColored.color, this.DUST_COLOR.r * sina, this.DUST_COLOR.g * sina, this.DUST_COLOR.b * sina, this.DUST_COLOR.a);
473 | }
474 | }
475 |
476 | gl.disable(gl.BLEND);
477 | gl.depthMask(true);
478 | }
479 |
480 | drawSky() {
481 | this.shaderDiffuse.use();
482 | this.setTexture2D(0, this.textureSky, this.shaderDiffuse.sTexture);
483 | this.drawDiffuseVBOTranslatedRotatedScaled(this.shaderDiffuse, this.fmSky, 0, 0, 0, 0, 0, 0, 7, 7, 3.5);
484 | }
485 |
486 | drawTable(scale) {
487 | // gl.depthMask(false);
488 | gl.enable(gl.BLEND);
489 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
490 |
491 | this.shaderLMTable.use();
492 |
493 | this.setTexture2D(0, this.textureTable, this.shaderLMTable.sTexture);
494 | this.setTexture2D(1, this.textureTableLM, this.shaderLMTable.sLM);
495 | gl.uniform1f(this.shaderLMTable.diffuseScale, 5.0);
496 | this.drawLMVBOTranslatedRotatedScaled(this.shaderLMTable, this.modelTable, 0, 0, 0, 0, 0, 0, scale, scale, 1);
497 |
498 | // gl.depthMask(true);
499 | gl.disable(gl.BLEND);
500 | }
501 |
502 | drawBuddha() {
503 | this.shaderSphericalMapLM.use();
504 |
505 | this.setTexture2D(0, this.textureBuddhaNormalMap, this.shaderSphericalMapLM.normalMap);
506 | this.setTexture2D(1, this.textureSphericalMap, this.shaderSphericalMapLM.sphereMap);
507 | this.setTexture2D(2, this.textureBuddhaLightMap, this.shaderSphericalMapLM.aoMap);
508 | this.drawSphericalMapLmVBOTranslatedRotatedScaled(this.shaderSphericalMapLM, this.modelBuddha, 0, 0, 0, 0, 0, 0, 1, 1, 1);
509 | }
510 |
511 | drawShaft() {
512 | gl.depthMask(false);
513 | gl.enable(gl.BLEND);
514 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
515 |
516 | this.shaderShaft.use();
517 |
518 | this.setTexture2D(0, this.textureShaft, this.shaderShaft.diffuseMap);
519 | this.drawShaftVBOTranslatedRotatedScaled(this.shaderShaft, this.fmShaft, 0, 0, 0, 0, 0, 0, 1, 1, 1);
520 |
521 | gl.depthMask(true);
522 | gl.disable(gl.BLEND);
523 | }
524 |
525 | drawVignette(shader) {
526 | gl.bindBuffer(gl.ARRAY_BUFFER, this.mTriangleVerticesVignette);
527 |
528 | gl.enableVertexAttribArray(shader.rm_Vertex);
529 | gl.vertexAttribPointer(shader.rm_Vertex, 3, gl.FLOAT, false, this.TRIANGLE_VERTICES_DATA_STRIDE_BYTES, 0);
530 | gl.enableVertexAttribArray(shader.rm_TexCoord0);
531 | gl.vertexAttribPointer(shader.rm_TexCoord0, 2, gl.FLOAT, false, this.TRIANGLE_VERTICES_DATA_STRIDE_BYTES, 4 * 3);
532 |
533 | gl.uniformMatrix4fv(shader.view_proj_matrix, false, this.mOrthoMatrix);
534 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
535 | }
536 |
537 | drawPointSpritesVBOTranslatedRotatedScaled(shader, model, tx, ty, tz, rx, ry, rz, sx, sy, sz) {
538 | model.bindBuffers();
539 |
540 | gl.enableVertexAttribArray(shader.aPosition);
541 | gl.vertexAttribPointer(shader.aPosition, 3, gl.FLOAT, false, 4 * (3 + 2), 0);
542 |
543 | this.calculateMVPMatrix(tx, ty, tz, rx, ry, rz, sx, sy, sz);
544 |
545 | gl.uniformMatrix4fv(shader.uMvp, false, this.mMVPMatrix);
546 | gl.drawElements(gl.POINTS, model.getNumIndices() * 3, gl.UNSIGNED_SHORT, 0);
547 | }
548 |
549 | drawShaftVBOTranslatedRotatedScaled(shader, model, tx, ty, tz, rx, ry, rz, sx, sy, sz) {
550 | model.bindBuffers();
551 |
552 | gl.enableVertexAttribArray(shader.rm_Vertex);
553 | gl.enableVertexAttribArray(shader.rm_TexCoord0);
554 | gl.enableVertexAttribArray(shader.rm_Normal);
555 |
556 | gl.vertexAttribPointer(shader.rm_Vertex, 3, gl.FLOAT, false, 4 * (3 + 2 + 2 + 3), 0);
557 | gl.vertexAttribPointer(shader.rm_TexCoord0, 2, gl.FLOAT, false, 4 * (3 + 2 + 2 + 3), 4 * (3));
558 | gl.vertexAttribPointer(shader.rm_Normal, 3, gl.FLOAT, false, 4 * (3 + 2 + 2 + 3), 4 * (3 + 2 + 2));
559 |
560 | this.calculateMVPMatrix(tx, ty, tz, rx, ry, rz, sx, sy, sz);
561 |
562 | gl.uniformMatrix4fv(shader.view_matrix, false, this.mVMatrix);
563 | gl.uniformMatrix4fv(shader.view_proj_matrix, false, this.mMVPMatrix);
564 | gl.drawElements(gl.TRIANGLES, model.getNumIndices() * 3, gl.UNSIGNED_SHORT, 0);
565 | }
566 |
567 | drawSphericalMapLmVBOTranslatedRotatedScaled(shader, model, tx, ty, tz, rx, ry, rz, sx, sy, sz) {
568 | model.bindBuffers();
569 |
570 | gl.enableVertexAttribArray(shader.rm_Vertex);
571 | gl.enableVertexAttribArray(shader.rm_TexCoord0);
572 | gl.enableVertexAttribArray(shader.rm_TexCoord1);
573 | gl.enableVertexAttribArray(shader.rm_Normal);
574 |
575 | gl.vertexAttribPointer(shader.rm_Vertex, 3, gl.HALF_FLOAT, false, 16, 0);
576 | gl.vertexAttribPointer(shader.rm_TexCoord0, 2, gl.UNSIGNED_BYTE, true, 16, 2 * 3);
577 | gl.vertexAttribPointer(shader.rm_TexCoord1, 2, gl.UNSIGNED_BYTE, true, 16, 2 * 3 + 1 * 2);
578 | gl.vertexAttribPointer(shader.rm_Normal, 3, gl.BYTE, true, 16, 2 * 3 + 1 * 2 + 1 * 2);
579 | // gl.vertexAttribPointer(shader.rm_Normal, 4, gl.INT_2_10_10_10_REV, true, 16, 12);
580 |
581 | this.calculateMVPMatrix(tx, ty, tz, rx, ry, rz, sx, sy, sz);
582 |
583 | gl.uniformMatrix4fv(shader.view_matrix, false, this.mVMatrix);
584 | gl.uniformMatrix4fv(shader.view_proj_matrix, false, this.mMVPMatrix);
585 | gl.drawElements(gl.TRIANGLES, model.getNumIndices() * 3, gl.UNSIGNED_SHORT, 0);
586 | }
587 |
588 | drawDiffuseVBOTranslatedRotatedScaled(shader, model, tx, ty, tz, rx, ry, rz, sx, sy, sz) {
589 | model.bindBuffers();
590 |
591 | gl.enableVertexAttribArray(shader.rm_Vertex);
592 | gl.enableVertexAttribArray(shader.rm_TexCoord0);
593 |
594 | gl.vertexAttribPointer(shader.rm_Vertex, 3, gl.HALF_FLOAT, false, 8, 0);
595 | gl.vertexAttribPointer(shader.rm_TexCoord0, 2, gl.UNSIGNED_BYTE, true, 8, 2 * 3);
596 |
597 | this.calculateMVPMatrix(tx, ty, tz, rx, ry, rz, sx, sy, sz);
598 |
599 | gl.uniformMatrix4fv(shader.view_proj_matrix, false, this.mMVPMatrix);
600 | gl.drawElements(gl.TRIANGLES, model.getNumIndices() * 3, gl.UNSIGNED_SHORT, 0);
601 | }
602 |
603 | drawLMVBOTranslatedRotatedScaled(shader, model, tx, ty, tz, rx, ry, rz, sx, sy, sz) {
604 | model.bindBuffers();
605 |
606 | gl.enableVertexAttribArray(shader.rm_Vertex);
607 | gl.enableVertexAttribArray(shader.rm_TexCoord0);
608 | gl.enableVertexAttribArray(shader.rm_TexCoord1);
609 |
610 | gl.vertexAttribPointer(shader.rm_Vertex, 3, gl.FLOAT, false, 4 * (3 + 2 + 2), 0);
611 | gl.vertexAttribPointer(shader.rm_TexCoord0, 2, gl.FLOAT, false, 4 * (3 + 2 + 2), 4 * (3));
612 | gl.vertexAttribPointer(shader.rm_TexCoord1, 2, gl.FLOAT, false, 4 * (3 + 2 + 2), 4 * (3 + 2));
613 |
614 | this.calculateMVPMatrix(tx, ty, tz, rx, ry, rz, sx, sy, sz);
615 |
616 | gl.uniformMatrix4fv(shader.view_proj_matrix, false, this.mMVPMatrix);
617 | gl.drawElements(gl.TRIANGLES, model.getNumIndices() * 3, gl.UNSIGNED_SHORT, 0);
618 | }
619 |
620 | /**
621 | * Updates camera rotation
622 | */
623 | animate() {
624 | var timeNow = new Date().getTime(),
625 | elapsed;
626 |
627 | if (this.lastTime != 0) {
628 | elapsed = timeNow - this.lastTime;
629 |
630 | this.angleYaw += elapsed / this.YAW_COEFF_NORMAL;
631 | this.angleYaw %= 360.0;
632 |
633 | this.timerDustRotation = (timeNow % this.DUST_SPEED) / this.DUST_SPEED;
634 | this.timerDustFlicker = (timeNow % this.DUST_FLICKER_SPEED) / this.DUST_FLICKER_SPEED;
635 | this.timerSmokeRotation = (timeNow % this.SMOKE_SPEED) / this.SMOKE_SPEED;
636 | }
637 |
638 | this.lastTime = timeNow;
639 | }
640 | }
641 |
642 | return BuddhaRenderer;
643 | });
644 |
--------------------------------------------------------------------------------