From 167cd2c6cfa9abafe10d1d742722bd319f06307c Mon Sep 17 00:00:00 2001 From: "teo.kaltrach" <teo.kaltrachian@etu.hesge.ch> Date: Fri, 18 Dec 2020 08:15:58 +0100 Subject: [PATCH] last commit --- lab3/src/lab3.js | 3 +- lab3/src/scene_objects.js | 60 +-- lab4/README.md | 6 + lab4/docs/Cours 3D Labo 4.pdf | Bin 0 -> 74144 bytes lab4/lib/cuon-matrix.js | 741 ++++++++++++++++++++++++++++++++++ lab4/lib/cuon-utils.js | 113 ++++++ lab4/lib/webgl-debug.js | 677 +++++++++++++++++++++++++++++++ lab4/lib/webgl-utils.js | 197 +++++++++ lab4/src/WebGl.js | 141 +++++++ lab4/src/lab4.html | 41 ++ lab4/src/lab4.js | 306 ++++++++++++++ lab4/src/scene_objects.js | 126 ++++++ 12 files changed, 2379 insertions(+), 32 deletions(-) create mode 100644 lab4/README.md create mode 100644 lab4/docs/Cours 3D Labo 4.pdf create mode 100644 lab4/lib/cuon-matrix.js create mode 100644 lab4/lib/cuon-utils.js create mode 100644 lab4/lib/webgl-debug.js create mode 100644 lab4/lib/webgl-utils.js create mode 100644 lab4/src/WebGl.js create mode 100644 lab4/src/lab4.html create mode 100644 lab4/src/lab4.js create mode 100644 lab4/src/scene_objects.js diff --git a/lab3/src/lab3.js b/lab3/src/lab3.js index 9e3c3b9..74134da 100644 --- a/lab3/src/lab3.js +++ b/lab3/src/lab3.js @@ -88,7 +88,7 @@ function main() { // Calculate the view projection matrix var viewProjMatrix = new Matrix4(); viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 10.0); //Profondeur - viewProjMatrix.lookAt(5.0, 3.0, 5.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0); //Position, Point de vision, Direction + viewProjMatrix.lookAt(3.0, 3.0, 5.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0); //Position, Point de vision, Direction var mvpMatrix = new Matrix4(); // Model view projection matrix var modelMatrix = new Matrix4(); @@ -135,7 +135,6 @@ function EventHandlers(document, canvas, gl, mouseMovement, u_LightColor_Point, lightFactor = 0.3; isPointLight = true; - //Mouse down canvas.onmousedown = function(ev) { dragging = true; diff --git a/lab3/src/scene_objects.js b/lab3/src/scene_objects.js index 33b71ac..23dabc0 100644 --- a/lab3/src/scene_objects.js +++ b/lab3/src/scene_objects.js @@ -45,13 +45,13 @@ function Scene_model() { this.colors = new Float32Array([ //Down | Top | Back | Left | Right | Front | Plane - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 5.0, 1.0, 0.0, 5.0, 1.0, 0.0, 5.0, 1.0, 0.0, 5.0, ]); this.normals = new Float32Array([ @@ -66,29 +66,29 @@ function Scene_model() { 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, - 0.5, 0.0, 1.0, - 0.5, 0.0, 1.0, - 0.5, 0.0, 1.0, - 0.5, 0.0, 1.0, - 0.5, 0.0, 1.0, - - -1.0, 0.0, 0.5, - -1.0, 0.0, 0.5, - -1.0, 0.0, 0.5, - -1.0, 0.0, 0.5, - -1.0, 0.0, 0.5, - - 1.0, 0.0, 0.5, - 1.0, 0.0, 0.5, - 1.0, 0.0, 0.5, - 1.0, 0.0, 0.5, - 1.0, 0.0, 0.5, - - 0.5, 0.0, -1.0, - 0.5, 0.0, -1.0, - 0.5, 0.0, -1.0, - 0.5, 0.0, -1.0, - 0.5, 0.0, -1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, diff --git a/lab4/README.md b/lab4/README.md new file mode 100644 index 0000000..ec24bbc --- /dev/null +++ b/lab4/README.md @@ -0,0 +1,6 @@ +# Lab4 IHM + +* **docs**: contains the lab's statement +* **libs**: contains WebGL libraries and utilities +* **src**: contains the source code of the lab4 that you should complete (`main()` function in **lab4.js** file) +* **DO NOT CHANGE** the name of the file `lab4.html`, however, you are free to change its content. diff --git a/lab4/docs/Cours 3D Labo 4.pdf b/lab4/docs/Cours 3D Labo 4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e639f4bfba3ee962e43ab8c26c7d1e2e56830580 GIT binary patch literal 74144 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%0AE}EcVvUiLV7WaCcYzzPP`!*zxE^i$DBF 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 MajB}h`nz!f0G18cbN~PV literal 0 HcmV?d00001 diff --git a/lab4/lib/cuon-matrix.js b/lab4/lib/cuon-matrix.js new file mode 100644 index 0000000..b67a5dd --- /dev/null +++ b/lab4/lib/cuon-matrix.js @@ -0,0 +1,741 @@ +// cuon-matrix.js (c) 2012 kanda and matsuda +/** + * This is a class treating 4x4 matrix. + * This class contains the function that is equivalent to OpenGL matrix stack. + * The matrix after conversion is calculated by multiplying a conversion matrix from the right. + * The matrix is replaced by the calculated result. + */ + +/** + * Constructor of Matrix4 + * If opt_src is specified, new matrix is initialized by opt_src. + * Otherwise, new matrix is initialized by identity matrix. + * @param opt_src source matrix(option) + */ +var Matrix4 = function(opt_src) { + var i, s, d; + if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) { + s = opt_src.elements; + d = new Float32Array(16); + for (i = 0; i < 16; ++i) { + d[i] = s[i]; + } + this.elements = d; + } else { + this.elements = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]); + } +}; + +/** + * Set the identity matrix. + * @return this + */ +Matrix4.prototype.setIdentity = function() { + var e = this.elements; + e[0] = 1; e[4] = 0; e[8] = 0; e[12] = 0; + e[1] = 0; e[5] = 1; e[9] = 0; e[13] = 0; + e[2] = 0; e[6] = 0; e[10] = 1; e[14] = 0; + e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; + return this; +}; + +/** + * Copy matrix. + * @param src source matrix + * @return this + */ +Matrix4.prototype.set = function(src) { + var i, s, d; + + s = src.elements; + d = this.elements; + + if (s === d) { + return; + } + + for (i = 0; i < 16; ++i) { + d[i] = s[i]; + } + + return this; +}; + +/** + * Multiply the matrix from the right. + * @param other The multiply matrix + * @return this + */ +Matrix4.prototype.concat = function(other) { + var i, e, a, b, ai0, ai1, ai2, ai3; + + // Calculate e = a * b + e = this.elements; + a = this.elements; + b = other.elements; + + // If e equals b, copy b to temporary matrix. + if (e === b) { + b = new Float32Array(16); + for (i = 0; i < 16; ++i) { + b[i] = e[i]; + } + } + + for (i = 0; i < 4; i++) { + ai0=a[i]; ai1=a[i+4]; ai2=a[i+8]; ai3=a[i+12]; + e[i] = ai0 * b[0] + ai1 * b[1] + ai2 * b[2] + ai3 * b[3]; + e[i+4] = ai0 * b[4] + ai1 * b[5] + ai2 * b[6] + ai3 * b[7]; + e[i+8] = ai0 * b[8] + ai1 * b[9] + ai2 * b[10] + ai3 * b[11]; + e[i+12] = ai0 * b[12] + ai1 * b[13] + ai2 * b[14] + ai3 * b[15]; + } + + return this; +}; +Matrix4.prototype.multiply = Matrix4.prototype.concat; + +/** + * Multiply the three-dimensional vector. + * @param pos The multiply vector + * @return The result of multiplication(Float32Array) + */ +Matrix4.prototype.multiplyVector3 = function(pos) { + var e = this.elements; + var p = pos.elements; + var v = new Vector3(); + var result = v.elements; + + result[0] = p[0] * e[0] + p[1] * e[4] + p[2] * e[ 8] + e[12]; + result[1] = p[0] * e[1] + p[1] * e[5] + p[2] * e[ 9] + e[13]; + result[2] = p[0] * e[2] + p[1] * e[6] + p[2] * e[10] + e[14]; + + return v; +}; + +/** + * Multiply the four-dimensional vector. + * @param pos The multiply vector + * @return The result of multiplication(Float32Array) + */ +Matrix4.prototype.multiplyVector4 = function(pos) { + var e = this.elements; + var p = pos.elements; + var v = new Vector4(); + var result = v.elements; + + result[0] = p[0] * e[0] + p[1] * e[4] + p[2] * e[ 8] + p[3] * e[12]; + result[1] = p[0] * e[1] + p[1] * e[5] + p[2] * e[ 9] + p[3] * e[13]; + result[2] = p[0] * e[2] + p[1] * e[6] + p[2] * e[10] + p[3] * e[14]; + result[3] = p[0] * e[3] + p[1] * e[7] + p[2] * e[11] + p[3] * e[15]; + + return v; +}; + +/** + * Transpose the matrix. + * @return this + */ +Matrix4.prototype.transpose = function() { + var e, t; + + e = this.elements; + + t = e[ 1]; e[ 1] = e[ 4]; e[ 4] = t; + t = e[ 2]; e[ 2] = e[ 8]; e[ 8] = t; + t = e[ 3]; e[ 3] = e[12]; e[12] = t; + t = e[ 6]; e[ 6] = e[ 9]; e[ 9] = t; + t = e[ 7]; e[ 7] = e[13]; e[13] = t; + t = e[11]; e[11] = e[14]; e[14] = t; + + return this; +}; + +/** + * Calculate the inverse matrix of specified matrix, and set to this. + * @param other The source matrix + * @return this + */ +Matrix4.prototype.setInverseOf = function(other) { + var i, s, d, inv, det; + + s = other.elements; + d = this.elements; + inv = new Float32Array(16); + + inv[0] = s[5]*s[10]*s[15] - s[5] *s[11]*s[14] - s[9] *s[6]*s[15] + + s[9]*s[7] *s[14] + s[13]*s[6] *s[11] - s[13]*s[7]*s[10]; + inv[4] = - s[4]*s[10]*s[15] + s[4] *s[11]*s[14] + s[8] *s[6]*s[15] + - s[8]*s[7] *s[14] - s[12]*s[6] *s[11] + s[12]*s[7]*s[10]; + inv[8] = s[4]*s[9] *s[15] - s[4] *s[11]*s[13] - s[8] *s[5]*s[15] + + s[8]*s[7] *s[13] + s[12]*s[5] *s[11] - s[12]*s[7]*s[9]; + inv[12] = - s[4]*s[9] *s[14] + s[4] *s[10]*s[13] + s[8] *s[5]*s[14] + - s[8]*s[6] *s[13] - s[12]*s[5] *s[10] + s[12]*s[6]*s[9]; + + inv[1] = - s[1]*s[10]*s[15] + s[1] *s[11]*s[14] + s[9] *s[2]*s[15] + - s[9]*s[3] *s[14] - s[13]*s[2] *s[11] + s[13]*s[3]*s[10]; + inv[5] = s[0]*s[10]*s[15] - s[0] *s[11]*s[14] - s[8] *s[2]*s[15] + + s[8]*s[3] *s[14] + s[12]*s[2] *s[11] - s[12]*s[3]*s[10]; + inv[9] = - s[0]*s[9] *s[15] + s[0] *s[11]*s[13] + s[8] *s[1]*s[15] + - s[8]*s[3] *s[13] - s[12]*s[1] *s[11] + s[12]*s[3]*s[9]; + inv[13] = s[0]*s[9] *s[14] - s[0] *s[10]*s[13] - s[8] *s[1]*s[14] + + s[8]*s[2] *s[13] + s[12]*s[1] *s[10] - s[12]*s[2]*s[9]; + + inv[2] = s[1]*s[6]*s[15] - s[1] *s[7]*s[14] - s[5] *s[2]*s[15] + + s[5]*s[3]*s[14] + s[13]*s[2]*s[7] - s[13]*s[3]*s[6]; + inv[6] = - s[0]*s[6]*s[15] + s[0] *s[7]*s[14] + s[4] *s[2]*s[15] + - s[4]*s[3]*s[14] - s[12]*s[2]*s[7] + s[12]*s[3]*s[6]; + inv[10] = s[0]*s[5]*s[15] - s[0] *s[7]*s[13] - s[4] *s[1]*s[15] + + s[4]*s[3]*s[13] + s[12]*s[1]*s[7] - s[12]*s[3]*s[5]; + inv[14] = - s[0]*s[5]*s[14] + s[0] *s[6]*s[13] + s[4] *s[1]*s[14] + - s[4]*s[2]*s[13] - s[12]*s[1]*s[6] + s[12]*s[2]*s[5]; + + inv[3] = - s[1]*s[6]*s[11] + s[1]*s[7]*s[10] + s[5]*s[2]*s[11] + - s[5]*s[3]*s[10] - s[9]*s[2]*s[7] + s[9]*s[3]*s[6]; + inv[7] = s[0]*s[6]*s[11] - s[0]*s[7]*s[10] - s[4]*s[2]*s[11] + + s[4]*s[3]*s[10] + s[8]*s[2]*s[7] - s[8]*s[3]*s[6]; + inv[11] = - s[0]*s[5]*s[11] + s[0]*s[7]*s[9] + s[4]*s[1]*s[11] + - s[4]*s[3]*s[9] - s[8]*s[1]*s[7] + s[8]*s[3]*s[5]; + inv[15] = s[0]*s[5]*s[10] - s[0]*s[6]*s[9] - s[4]*s[1]*s[10] + + s[4]*s[2]*s[9] + s[8]*s[1]*s[6] - s[8]*s[2]*s[5]; + + det = s[0]*inv[0] + s[1]*inv[4] + s[2]*inv[8] + s[3]*inv[12]; + if (det === 0) { + return this; + } + + det = 1 / det; + for (i = 0; i < 16; i++) { + d[i] = inv[i] * det; + } + + return this; +}; + +/** + * Calculate the inverse matrix of this, and set to this. + * @return this + */ +Matrix4.prototype.invert = function() { + return this.setInverseOf(this); +}; + +/** + * Set the orthographic projection matrix. + * @param left The coordinate of the left of clipping plane. + * @param right The coordinate of the right of clipping plane. + * @param bottom The coordinate of the bottom of clipping plane. + * @param top The coordinate of the top top clipping plane. + * @param near The distances to the nearer depth clipping plane. This value is minus if the plane is to be behind the viewer. + * @param far The distances to the farther depth clipping plane. This value is minus if the plane is to be behind the viewer. + * @return this + */ +Matrix4.prototype.setOrtho = function(left, right, bottom, top, near, far) { + var e, rw, rh, rd; + + if (left === right || bottom === top || near === far) { + throw 'null frustum'; + } + + rw = 1 / (right - left); + rh = 1 / (top - bottom); + rd = 1 / (far - near); + + e = this.elements; + + e[0] = 2 * rw; + e[1] = 0; + e[2] = 0; + e[3] = 0; + + e[4] = 0; + e[5] = 2 * rh; + e[6] = 0; + e[7] = 0; + + e[8] = 0; + e[9] = 0; + e[10] = -2 * rd; + e[11] = 0; + + e[12] = -(right + left) * rw; + e[13] = -(top + bottom) * rh; + e[14] = -(far + near) * rd; + e[15] = 1; + + return this; +}; + +/** + * Multiply the orthographic projection matrix from the right. + * @param left The coordinate of the left of clipping plane. + * @param right The coordinate of the right of clipping plane. + * @param bottom The coordinate of the bottom of clipping plane. + * @param top The coordinate of the top top clipping plane. + * @param near The distances to the nearer depth clipping plane. This value is minus if the plane is to be behind the viewer. + * @param far The distances to the farther depth clipping plane. This value is minus if the plane is to be behind the viewer. + * @return this + */ +Matrix4.prototype.ortho = function(left, right, bottom, top, near, far) { + return this.concat(new Matrix4().setOrtho(left, right, bottom, top, near, far)); +}; + +/** + * Set the perspective projection matrix. + * @param left The coordinate of the left of clipping plane. + * @param right The coordinate of the right of clipping plane. + * @param bottom The coordinate of the bottom of clipping plane. + * @param top The coordinate of the top top clipping plane. + * @param near The distances to the nearer depth clipping plane. This value must be plus value. + * @param far The distances to the farther depth clipping plane. This value must be plus value. + * @return this + */ +Matrix4.prototype.setFrustum = function(left, right, bottom, top, near, far) { + var e, rw, rh, rd; + + if (left === right || top === bottom || near === far) { + throw 'null frustum'; + } + if (near <= 0) { + throw 'near <= 0'; + } + if (far <= 0) { + throw 'far <= 0'; + } + + rw = 1 / (right - left); + rh = 1 / (top - bottom); + rd = 1 / (far - near); + + e = this.elements; + + e[ 0] = 2 * near * rw; + e[ 1] = 0; + e[ 2] = 0; + e[ 3] = 0; + + e[ 4] = 0; + e[ 5] = 2 * near * rh; + e[ 6] = 0; + e[ 7] = 0; + + e[ 8] = (right + left) * rw; + e[ 9] = (top + bottom) * rh; + e[10] = -(far + near) * rd; + e[11] = -1; + + e[12] = 0; + e[13] = 0; + e[14] = -2 * near * far * rd; + e[15] = 0; + + return this; +}; + +/** + * Multiply the perspective projection matrix from the right. + * @param left The coordinate of the left of clipping plane. + * @param right The coordinate of the right of clipping plane. + * @param bottom The coordinate of the bottom of clipping plane. + * @param top The coordinate of the top top clipping plane. + * @param near The distances to the nearer depth clipping plane. This value must be plus value. + * @param far The distances to the farther depth clipping plane. This value must be plus value. + * @return this + */ +Matrix4.prototype.frustum = function(left, right, bottom, top, near, far) { + return this.concat(new Matrix4().setFrustum(left, right, bottom, top, near, far)); +}; + +/** + * Set the perspective projection matrix by fovy and aspect. + * @param fovy The angle between the upper and lower sides of the frustum. + * @param aspect The aspect ratio of the frustum. (width/height) + * @param near The distances to the nearer depth clipping plane. This value must be plus value. + * @param far The distances to the farther depth clipping plane. This value must be plus value. + * @return this + */ +Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) { + var e, rd, s, ct; + + if (near === far || aspect === 0) { + throw 'null frustum'; + } + if (near <= 0) { + throw 'near <= 0'; + } + if (far <= 0) { + throw 'far <= 0'; + } + + fovy = Math.PI * fovy / 180 / 2; + s = Math.sin(fovy); + if (s === 0) { + throw 'null frustum'; + } + + rd = 1 / (far - near); + ct = Math.cos(fovy) / s; + + e = this.elements; + + e[0] = ct / aspect; + e[1] = 0; + e[2] = 0; + e[3] = 0; + + e[4] = 0; + e[5] = ct; + e[6] = 0; + e[7] = 0; + + e[8] = 0; + e[9] = 0; + e[10] = -(far + near) * rd; + e[11] = -1; + + e[12] = 0; + e[13] = 0; + e[14] = -2 * near * far * rd; + e[15] = 0; + + return this; +}; + +/** + * Multiply the perspective projection matrix from the right. + * @param fovy The angle between the upper and lower sides of the frustum. + * @param aspect The aspect ratio of the frustum. (width/height) + * @param near The distances to the nearer depth clipping plane. This value must be plus value. + * @param far The distances to the farther depth clipping plane. This value must be plus value. + * @return this + */ +Matrix4.prototype.perspective = function(fovy, aspect, near, far) { + return this.concat(new Matrix4().setPerspective(fovy, aspect, near, far)); +}; + +/** + * Set the matrix for scaling. + * @param x The scale factor along the X axis + * @param y The scale factor along the Y axis + * @param z The scale factor along the Z axis + * @return this + */ +Matrix4.prototype.setScale = function(x, y, z) { + var e = this.elements; + e[0] = x; e[4] = 0; e[8] = 0; e[12] = 0; + e[1] = 0; e[5] = y; e[9] = 0; e[13] = 0; + e[2] = 0; e[6] = 0; e[10] = z; e[14] = 0; + e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; + return this; +}; + +/** + * Multiply the matrix for scaling from the right. + * @param x The scale factor along the X axis + * @param y The scale factor along the Y axis + * @param z The scale factor along the Z axis + * @return this + */ +Matrix4.prototype.scale = function(x, y, z) { + var e = this.elements; + e[0] *= x; e[4] *= y; e[8] *= z; + e[1] *= x; e[5] *= y; e[9] *= z; + e[2] *= x; e[6] *= y; e[10] *= z; + e[3] *= x; e[7] *= y; e[11] *= z; + return this; +}; + +/** + * Set the matrix for translation. + * @param x The X value of a translation. + * @param y The Y value of a translation. + * @param z The Z value of a translation. + * @return this + */ +Matrix4.prototype.setTranslate = function(x, y, z) { + var e = this.elements; + e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x; + e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y; + e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z; + e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; + return this; +}; + +/** + * Multiply the matrix for translation from the right. + * @param x The X value of a translation. + * @param y The Y value of a translation. + * @param z The Z value of a translation. + * @return this + */ +Matrix4.prototype.translate = function(x, y, z) { + var e = this.elements; + e[12] += e[0] * x + e[4] * y + e[8] * z; + e[13] += e[1] * x + e[5] * y + e[9] * z; + e[14] += e[2] * x + e[6] * y + e[10] * z; + e[15] += e[3] * x + e[7] * y + e[11] * z; + return this; +}; + +/** + * Set the matrix for rotation. + * The vector of rotation axis may not be normalized. + * @param angle The angle of rotation (degrees) + * @param x The X coordinate of vector of rotation axis. + * @param y The Y coordinate of vector of rotation axis. + * @param z The Z coordinate of vector of rotation axis. + * @return this + */ +Matrix4.prototype.setRotate = function(angle, x, y, z) { + var e, s, c, len, rlen, nc, xy, yz, zx, xs, ys, zs; + + angle = Math.PI * angle / 180; + e = this.elements; + + s = Math.sin(angle); + c = Math.cos(angle); + + if (0 !== x && 0 === y && 0 === z) { + // Rotation around X axis + if (x < 0) { + s = -s; + } + e[0] = 1; e[4] = 0; e[ 8] = 0; e[12] = 0; + e[1] = 0; e[5] = c; e[ 9] =-s; e[13] = 0; + e[2] = 0; e[6] = s; e[10] = c; e[14] = 0; + e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; + } else if (0 === x && 0 !== y && 0 === z) { + // Rotation around Y axis + if (y < 0) { + s = -s; + } + e[0] = c; e[4] = 0; e[ 8] = s; e[12] = 0; + e[1] = 0; e[5] = 1; e[ 9] = 0; e[13] = 0; + e[2] =-s; e[6] = 0; e[10] = c; e[14] = 0; + e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; + } else if (0 === x && 0 === y && 0 !== z) { + // Rotation around Z axis + if (z < 0) { + s = -s; + } + e[0] = c; e[4] =-s; e[ 8] = 0; e[12] = 0; + e[1] = s; e[5] = c; e[ 9] = 0; e[13] = 0; + e[2] = 0; e[6] = 0; e[10] = 1; e[14] = 0; + e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; + } else { + // Rotation around another axis + len = Math.sqrt(x*x + y*y + z*z); + if (len !== 1) { + rlen = 1 / len; + x *= rlen; + y *= rlen; + z *= rlen; + } + nc = 1 - c; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * s; + ys = y * s; + zs = z * s; + + e[ 0] = x*x*nc + c; + e[ 1] = xy *nc + zs; + e[ 2] = zx *nc - ys; + e[ 3] = 0; + + e[ 4] = xy *nc - zs; + e[ 5] = y*y*nc + c; + e[ 6] = yz *nc + xs; + e[ 7] = 0; + + e[ 8] = zx *nc + ys; + e[ 9] = yz *nc - xs; + e[10] = z*z*nc + c; + e[11] = 0; + + e[12] = 0; + e[13] = 0; + e[14] = 0; + e[15] = 1; + } + + return this; +}; + +/** + * Multiply the matrix for rotation from the right. + * The vector of rotation axis may not be normalized. + * @param angle The angle of rotation (degrees) + * @param x The X coordinate of vector of rotation axis. + * @param y The Y coordinate of vector of rotation axis. + * @param z The Z coordinate of vector of rotation axis. + * @return this + */ +Matrix4.prototype.rotate = function(angle, x, y, z) { + return this.concat(new Matrix4().setRotate(angle, x, y, z)); +}; + +/** + * Set the viewing matrix. + * @param eyeX, eyeY, eyeZ The position of the eye point. + * @param centerX, centerY, centerZ The position of the reference point. + * @param upX, upY, upZ The direction of the up vector. + * @return this + */ +Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { + var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz; + + fx = centerX - eyeX; + fy = centerY - eyeY; + fz = centerZ - eyeZ; + + // Normalize f. + rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz); + fx *= rlf; + fy *= rlf; + fz *= rlf; + + // Calculate cross product of f and up. + sx = fy * upZ - fz * upY; + sy = fz * upX - fx * upZ; + sz = fx * upY - fy * upX; + + // Normalize s. + rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz); + sx *= rls; + sy *= rls; + sz *= rls; + + // Calculate cross product of s and f. + ux = sy * fz - sz * fy; + uy = sz * fx - sx * fz; + uz = sx * fy - sy * fx; + + // Set to this. + e = this.elements; + e[0] = sx; + e[1] = ux; + e[2] = -fx; + e[3] = 0; + + e[4] = sy; + e[5] = uy; + e[6] = -fy; + e[7] = 0; + + e[8] = sz; + e[9] = uz; + e[10] = -fz; + e[11] = 0; + + e[12] = 0; + e[13] = 0; + e[14] = 0; + e[15] = 1; + + // Translate. + return this.translate(-eyeX, -eyeY, -eyeZ); +}; + +/** + * Multiply the viewing matrix from the right. + * @param eyeX, eyeY, eyeZ The position of the eye point. + * @param centerX, centerY, centerZ The position of the reference point. + * @param upX, upY, upZ The direction of the up vector. + * @return this + */ +Matrix4.prototype.lookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { + return this.concat(new Matrix4().setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)); +}; + +/** + * Multiply the matrix for project vertex to plane from the right. + * @param plane The array[A, B, C, D] of the equation of plane "Ax + By + Cz + D = 0". + * @param light The array which stored coordinates of the light. if light[3]=0, treated as parallel light. + * @return this + */ +Matrix4.prototype.dropShadow = function(plane, light) { + var mat = new Matrix4(); + var e = mat.elements; + + var dot = plane[0] * light[0] + plane[1] * light[1] + plane[2] * light[2] + plane[3] * light[3]; + + e[ 0] = dot - light[0] * plane[0]; + e[ 1] = - light[1] * plane[0]; + e[ 2] = - light[2] * plane[0]; + e[ 3] = - light[3] * plane[0]; + + e[ 4] = - light[0] * plane[1]; + e[ 5] = dot - light[1] * plane[1]; + e[ 6] = - light[2] * plane[1]; + e[ 7] = - light[3] * plane[1]; + + e[ 8] = - light[0] * plane[2]; + e[ 9] = - light[1] * plane[2]; + e[10] = dot - light[2] * plane[2]; + e[11] = - light[3] * plane[2]; + + e[12] = - light[0] * plane[3]; + e[13] = - light[1] * plane[3]; + e[14] = - light[2] * plane[3]; + e[15] = dot - light[3] * plane[3]; + + return this.concat(mat); +} + +/** + * Multiply the matrix for project vertex to plane from the right.(Projected by parallel light.) + * @param normX, normY, normZ The normal vector of the plane.(Not necessary to be normalized.) + * @param planeX, planeY, planeZ The coordinate of arbitrary points on a plane. + * @param lightX, lightY, lightZ The vector of the direction of light.(Not necessary to be normalized.) + * @return this + */ +Matrix4.prototype.dropShadowDirectionally = function(normX, normY, normZ, planeX, planeY, planeZ, lightX, lightY, lightZ) { + var a = planeX * normX + planeY * normY + planeZ * normZ; + return this.dropShadow([normX, normY, normZ, -a], [lightX, lightY, lightZ, 0]); +}; + +/** + * Constructor of Vector3 + * If opt_src is specified, new vector is initialized by opt_src. + * @param opt_src source vector(option) + */ +var Vector3 = function(opt_src) { + var v = new Float32Array(3); + if (opt_src && typeof opt_src === 'object') { + v[0] = opt_src[0]; v[1] = opt_src[1]; v[2] = opt_src[2]; + } + this.elements = v; +} + +/** + * Normalize. + * @return this + */ +Vector3.prototype.normalize = function() { + var v = this.elements; + var c = v[0], d = v[1], e = v[2], g = Math.sqrt(c*c+d*d+e*e); + if(g){ + if(g == 1) + return this; + } else { + v[0] = 0; v[1] = 0; v[2] = 0; + return this; + } + g = 1/g; + v[0] = c*g; v[1] = d*g; v[2] = e*g; + return this; +}; + +/** + * Constructor of Vector4 + * If opt_src is specified, new vector is initialized by opt_src. + * @param opt_src source vector(option) + */ +var Vector4 = function(opt_src) { + var v = new Float32Array(4); + if (opt_src && typeof opt_src === 'object') { + v[0] = opt_src[0]; v[1] = opt_src[1]; v[2] = opt_src[2]; v[3] = opt_src[3]; + } + this.elements = v; +} diff --git a/lab4/lib/cuon-utils.js b/lab4/lib/cuon-utils.js new file mode 100644 index 0000000..dc08b2e --- /dev/null +++ b/lab4/lib/cuon-utils.js @@ -0,0 +1,113 @@ +// cuon-utils.js (c) 2012 kanda and matsuda +/** + * Create a program object and make current + * @param gl GL context + * @param vshader a vertex shader program (string) + * @param fshader a fragment shader program (string) + * @return true, if the program object was created and successfully made current + */ +function initShaders(gl, vshader, fshader) { + var program = createProgram(gl, vshader, fshader); + if (!program) { + console.log('Failed to create program'); + return false; + } + + gl.useProgram(program); + gl.program = program; + + return true; +} + +/** + * Create the linked program object + * @param gl GL context + * @param vshader a vertex shader program (string) + * @param fshader a fragment shader program (string) + * @return created program object, or null if the creation has failed + */ +function createProgram(gl, vshader, fshader) { + // Create shader object + var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader); + var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader); + if (!vertexShader || !fragmentShader) { + return null; + } + + // Create a program object + var program = gl.createProgram(); + if (!program) { + return null; + } + + // Attach the shader objects + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + // Link the program object + gl.linkProgram(program); + + // Check the result of linking + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + var error = gl.getProgramInfoLog(program); + console.log('Failed to link program: ' + error); + gl.deleteProgram(program); + gl.deleteShader(fragmentShader); + gl.deleteShader(vertexShader); + return null; + } + return program; +} + +/** + * Create a shader object + * @param gl GL context + * @param type the type of the shader object to be created + * @param source shader program (string) + * @return created shader object, or null if the creation has failed. + */ +function loadShader(gl, type, source) { + // Create shader object + var shader = gl.createShader(type); + if (shader == null) { + console.log('unable to create shader'); + return null; + } + + // Set the shader program + gl.shaderSource(shader, source); + + // Compile the shader + gl.compileShader(shader); + + // Check the result of compilation + var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + if (!compiled) { + var error = gl.getShaderInfoLog(shader); + console.log('Failed to compile shader: ' + error); + gl.deleteShader(shader); + return null; + } + + return shader; +} + +/** + * Initialize and get the rendering for WebglInstance + * @param canvas <cavnas> element + * @param opt_debug flag to initialize the context for debugging + * @return the rendering context for WebglInstance + */ +function getWebGLContext(canvas, opt_debug) { + // Get the rendering context for WebglInstance + var gl = WebGLUtils.setupWebGL(canvas); + if (!gl) return null; + + // if opt_debug is explicitly false, create the context for debugging + if (arguments.length < 2 || opt_debug) { + gl = WebGLDebugUtils.makeDebugContext(gl); + } + + return gl; +} diff --git a/lab4/lib/webgl-debug.js b/lab4/lib/webgl-debug.js new file mode 100644 index 0000000..685868d --- /dev/null +++ b/lab4/lib/webgl-debug.js @@ -0,0 +1,677 @@ +//Copyright (c) 2009 The Chromium Authors. All rights reserved. +//Use of this source code is governed by a BSD-style license that can be +//found in the LICENSE file. + +// Various functions for helping debug WebglInstance apps. + +WebGLDebugUtils = function() { + +/** + * Wrapped logging function. + * @param {string} msg Message to log. + */ +var log = function(msg) { + if (window.console && window.console.log) { + window.console.log(msg); + } +}; + +/** + * Which arguements are enums. + * @type {!Object.<number, string>} + */ +var glValidEnumContexts = { + + // Generic setters and getters + + 'enable': { 0:true }, + 'disable': { 0:true }, + 'getParameter': { 0:true }, + + // Rendering + + 'drawArrays': { 0:true }, + 'drawElements': { 0:true, 2:true }, + + // Shaders + + 'createShader': { 0:true }, + 'getShaderParameter': { 1:true }, + 'getProgramParameter': { 1:true }, + + // Vertex attributes + + 'getVertexAttrib': { 1:true }, + 'vertexAttribPointer': { 2:true }, + + // Textures + + 'bindTexture': { 0:true }, + 'activeTexture': { 0:true }, + 'getTexParameter': { 0:true, 1:true }, + 'texParameterf': { 0:true, 1:true }, + 'texParameteri': { 0:true, 1:true, 2:true }, + 'texImage2D': { 0:true, 2:true, 6:true, 7:true }, + 'texSubImage2D': { 0:true, 6:true, 7:true }, + 'copyTexImage2D': { 0:true, 2:true }, + 'copyTexSubImage2D': { 0:true }, + 'generateMipmap': { 0:true }, + + // Buffer objects + + 'bindBuffer': { 0:true }, + 'bufferData': { 0:true, 2:true }, + 'bufferSubData': { 0:true }, + 'getBufferParameter': { 0:true, 1:true }, + + // Renderbuffers and framebuffers + + 'pixelStorei': { 0:true, 1:true }, + 'readPixels': { 4:true, 5:true }, + 'bindRenderbuffer': { 0:true }, + 'bindFramebuffer': { 0:true }, + 'checkFramebufferStatus': { 0:true }, + 'framebufferRenderbuffer': { 0:true, 1:true, 2:true }, + 'framebufferTexture2D': { 0:true, 1:true, 2:true }, + 'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true }, + 'getRenderbufferParameter': { 0:true, 1:true }, + 'renderbufferStorage': { 0:true, 1:true }, + + // Frame buffer operations (clear, blend, depth test, stencil) + + 'clear': { 0:true }, + 'depthFunc': { 0:true }, + 'blendFunc': { 0:true, 1:true }, + 'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true }, + 'blendEquation': { 0:true }, + 'blendEquationSeparate': { 0:true, 1:true }, + 'stencilFunc': { 0:true }, + 'stencilFuncSeparate': { 0:true, 1:true }, + 'stencilMaskSeparate': { 0:true }, + 'stencilOp': { 0:true, 1:true, 2:true }, + 'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true }, + + // Culling + + 'cullFace': { 0:true }, + 'frontFace': { 0:true }, +}; + +/** + * Map of numbers to names. + * @type {Object} + */ +var glEnums = null; + +/** + * Initializes this module. Safe to call more than once. + * @param {!WebGLRenderingContext} ctx A WebglInstance context. If + * you have more than one context it doesn't matter which one + * you pass in, it is only used to pull out constants. + */ +function init(ctx) { + if (glEnums == null) { + glEnums = { }; + for (var propertyName in ctx) { + if (typeof ctx[propertyName] == 'number') { + glEnums[ctx[propertyName]] = propertyName; + } + } + } +} + +/** + * Checks the utils have been initialized. + */ +function checkInit() { + if (glEnums == null) { + throw 'WebGLDebugUtils.init(ctx) not called'; + } +} + +/** + * Returns true or false if value matches any WebglInstance enum + * @param {*} value Value to check if it might be an enum. + * @return {boolean} True if value matches one of the WebglInstance defined enums + */ +function mightBeEnum(value) { + checkInit(); + return (glEnums[value] !== undefined); +} + +/** + * Gets an string version of an WebglInstance enum. + * + * Example: + * var str = WebGLDebugUtil.glEnumToString(ctx.getError()); + * + * @param {number} value Value to return an enum for + * @return {string} The string version of the enum. + */ +function glEnumToString(value) { + checkInit(); + var name = glEnums[value]; + return (name !== undefined) ? name : + ("*UNKNOWN WebglInstance ENUM (0x" + value.toString(16) + ")"); +} + +/** + * Returns the string version of a WebglInstance argument. + * Attempts to convert enum arguments to strings. + * @param {string} functionName the name of the WebglInstance function. + * @param {number} argumentIndx the index of the argument. + * @param {*} value The value of the argument. + * @return {string} The value as a string. + */ +function glFunctionArgToString(functionName, argumentIndex, value) { + var funcInfo = glValidEnumContexts[functionName]; + if (funcInfo !== undefined) { + if (funcInfo[argumentIndex]) { + return glEnumToString(value); + } + } + return value.toString(); +} + +/** + * Given a WebglInstance context returns a wrapped context that calls + * gl.getError after every command and calls a function if the + * result is not gl.NO_ERROR. + * + * @param {!WebGLRenderingContext} ctx The webgl context to + * wrap. + * @param {!function(err, funcName, args): void} opt_onErrorFunc + * The function to call when gl.getError returns an + * error. If not specified the default function calls + * console.log with a message. + */ +function makeDebugContext(ctx, opt_onErrorFunc) { + init(ctx); + opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) { + // apparently we can't do args.join(","); + var argStr = ""; + for (var ii = 0; ii < args.length; ++ii) { + argStr += ((ii == 0) ? '' : ', ') + + glFunctionArgToString(functionName, ii, args[ii]); + } + log("WebglInstance error "+ glEnumToString(err) + " in "+ functionName + + "(" + argStr + ")"); + }; + + // Holds booleans for each GL error so after we get the error ourselves + // we can still return it to the client app. + var glErrorShadow = { }; + + // Makes a function that calls a WebglInstance function and then calls getError. + function makeErrorWrapper(ctx, functionName) { + return function() { + var result = ctx[functionName].apply(ctx, arguments); + var err = ctx.getError(); + if (err != 0) { + glErrorShadow[err] = true; + opt_onErrorFunc(err, functionName, arguments); + } + return result; + }; + } + + // Make a an object that has a copy of every property of the WebglInstance context + // but wraps all functions. + var wrapper = {}; + for (var propertyName in ctx) { + if (typeof ctx[propertyName] == 'function') { + wrapper[propertyName] = makeErrorWrapper(ctx, propertyName); + } else { + wrapper[propertyName] = ctx[propertyName]; + } + } + + // Override the getError function with one that returns our saved results. + wrapper.getError = function() { + for (var err in glErrorShadow) { + if (glErrorShadow[err]) { + glErrorShadow[err] = false; + return err; + } + } + return ctx.NO_ERROR; + }; + + return wrapper; +} + +function resetToInitialState(ctx) { + var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS); + var tmp = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp); + for (var ii = 0; ii < numAttribs; ++ii) { + ctx.disableVertexAttribArray(ii); + ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0); + ctx.vertexAttrib1f(ii, 0); + } + ctx.deleteBuffer(tmp); + + var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS); + for (var ii = 0; ii < numTextureUnits; ++ii) { + ctx.activeTexture(ctx.TEXTURE0 + ii); + ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null); + ctx.bindTexture(ctx.TEXTURE_2D, null); + } + + ctx.activeTexture(ctx.TEXTURE0); + ctx.useProgram(null); + ctx.bindBuffer(ctx.ARRAY_BUFFER, null); + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); + ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); + ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); + ctx.disable(ctx.BLEND); + ctx.disable(ctx.CULL_FACE); + ctx.disable(ctx.DEPTH_TEST); + ctx.disable(ctx.DITHER); + ctx.disable(ctx.SCISSOR_TEST); + ctx.blendColor(0, 0, 0, 0); + ctx.blendEquation(ctx.FUNC_ADD); + ctx.blendFunc(ctx.ONE, ctx.ZERO); + ctx.clearColor(0, 0, 0, 0); + ctx.clearDepth(1); + ctx.clearStencil(-1); + ctx.colorMask(true, true, true, true); + ctx.cullFace(ctx.BACK); + ctx.depthFunc(ctx.LESS); + ctx.depthMask(true); + ctx.depthRange(0, 1); + ctx.frontFace(ctx.CCW); + ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE); + ctx.lineWidth(1); + ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4); + ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4); + ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false); + ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + // TODO: Delete this IF. + if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) { + ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL); + } + ctx.polygonOffset(0, 0); + ctx.sampleCoverage(1, false); + ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF); + ctx.stencilMask(0xFFFFFFFF); + ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP); + ctx.viewport(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight); + ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT); + + // TODO: This should NOT be needed but Firefox fails with 'hint' + while(ctx.getError()); +} + +function makeLostContextSimulatingContext(ctx) { + var wrapper_ = {}; + var contextId_ = 1; + var contextLost_ = false; + var resourceId_ = 0; + var resourceDb_ = []; + var onLost_ = undefined; + var onRestored_ = undefined; + var nextOnRestored_ = undefined; + + // Holds booleans for each GL error so can simulate errors. + var glErrorShadow_ = { }; + + function isWebGLObject(obj) { + //return false; + return (obj instanceof WebGLBuffer || + obj instanceof WebGLFramebuffer || + obj instanceof WebGLProgram || + obj instanceof WebGLRenderbuffer || + obj instanceof WebGLShader || + obj instanceof WebGLTexture); + } + + function checkResources(args) { + for (var ii = 0; ii < args.length; ++ii) { + var arg = args[ii]; + if (isWebGLObject(arg)) { + return arg.__webglDebugContextLostId__ == contextId_; + } + } + return true; + } + + function clearErrors() { + var k = Object.keys(glErrorShadow_); + for (var ii = 0; ii < k.length; ++ii) { + delete glErrorShdow_[k]; + } + } + + // Makes a function that simulates WebglInstance when out of context. + function makeLostContextWrapper(ctx, functionName) { + var f = ctx[functionName]; + return function() { + // Only call the functions if the context is not lost. + if (!contextLost_) { + if (!checkResources(arguments)) { + glErrorShadow_[ctx.INVALID_OPERATION] = true; + return; + } + var result = f.apply(ctx, arguments); + return result; + } + }; + } + + for (var propertyName in ctx) { + if (typeof ctx[propertyName] == 'function') { + wrapper_[propertyName] = makeLostContextWrapper(ctx, propertyName); + } else { + wrapper_[propertyName] = ctx[propertyName]; + } + } + + function makeWebGLContextEvent(statusMessage) { + return {statusMessage: statusMessage}; + } + + function freeResources() { + for (var ii = 0; ii < resourceDb_.length; ++ii) { + var resource = resourceDb_[ii]; + if (resource instanceof WebGLBuffer) { + ctx.deleteBuffer(resource); + } else if (resource instanceof WebctxFramebuffer) { + ctx.deleteFramebuffer(resource); + } else if (resource instanceof WebctxProgram) { + ctx.deleteProgram(resource); + } else if (resource instanceof WebctxRenderbuffer) { + ctx.deleteRenderbuffer(resource); + } else if (resource instanceof WebctxShader) { + ctx.deleteShader(resource); + } else if (resource instanceof WebctxTexture) { + ctx.deleteTexture(resource); + } + } + } + + wrapper_.loseContext = function() { + if (!contextLost_) { + contextLost_ = true; + ++contextId_; + while (ctx.getError()); + clearErrors(); + glErrorShadow_[ctx.CONTEXT_LOST_WEBGL] = true; + setTimeout(function() { + if (onLost_) { + onLost_(makeWebGLContextEvent("context lost")); + } + }, 0); + } + }; + + wrapper_.restoreContext = function() { + if (contextLost_) { + if (onRestored_) { + setTimeout(function() { + freeResources(); + resetToInitialState(ctx); + contextLost_ = false; + if (onRestored_) { + var callback = onRestored_; + onRestored_ = nextOnRestored_; + nextOnRestored_ = undefined; + callback(makeWebGLContextEvent("context restored")); + } + }, 0); + } else { + throw "You can not restore the context without a listener" + } + } + }; + + // Wrap a few functions specially. + wrapper_.getError = function() { + if (!contextLost_) { + var err; + while (err = ctx.getError()) { + glErrorShadow_[err] = true; + } + } + for (var err in glErrorShadow_) { + if (glErrorShadow_[err]) { + delete glErrorShadow_[err]; + return err; + } + } + return ctx.NO_ERROR; + }; + + var creationFunctions = [ + "createBuffer", + "createFramebuffer", + "createProgram", + "createRenderbuffer", + "createShader", + "createTexture" + ]; + for (var ii = 0; ii < creationFunctions.length; ++ii) { + var functionName = creationFunctions[ii]; + wrapper_[functionName] = function(f) { + return function() { + if (contextLost_) { + return null; + } + var obj = f.apply(ctx, arguments); + obj.__webglDebugContextLostId__ = contextId_; + resourceDb_.push(obj); + return obj; + }; + }(ctx[functionName]); + } + + var functionsThatShouldReturnNull = [ + "getActiveAttrib", + "getActiveUniform", + "getBufferParameter", + "getContextAttributes", + "getAttachedShaders", + "getFramebufferAttachmentParameter", + "getParameter", + "getProgramParameter", + "getProgramInfoLog", + "getRenderbufferParameter", + "getShaderParameter", + "getShaderInfoLog", + "getShaderSource", + "getTexParameter", + "getUniform", + "getUniformLocation", + "getVertexAttrib" + ]; + for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) { + var functionName = functionsThatShouldReturnNull[ii]; + wrapper_[functionName] = function(f) { + return function() { + if (contextLost_) { + return null; + } + return f.apply(ctx, arguments); + } + }(wrapper_[functionName]); + } + + var isFunctions = [ + "isBuffer", + "isEnabled", + "isFramebuffer", + "isProgram", + "isRenderbuffer", + "isShader", + "isTexture" + ]; + for (var ii = 0; ii < isFunctions.length; ++ii) { + var functionName = isFunctions[ii]; + wrapper_[functionName] = function(f) { + return function() { + if (contextLost_) { + return false; + } + return f.apply(ctx, arguments); + } + }(wrapper_[functionName]); + } + + wrapper_.checkFramebufferStatus = function(f) { + return function() { + if (contextLost_) { + return ctx.FRAMEBUFFER_UNSUPPORTED; + } + return f.apply(ctx, arguments); + }; + }(wrapper_.checkFramebufferStatus); + + wrapper_.getAttribLocation = function(f) { + return function() { + if (contextLost_) { + return -1; + } + return f.apply(ctx, arguments); + }; + }(wrapper_.getAttribLocation); + + wrapper_.getVertexAttribOffset = function(f) { + return function() { + if (contextLost_) { + return 0; + } + return f.apply(ctx, arguments); + }; + }(wrapper_.getVertexAttribOffset); + + wrapper_.isContextLost = function() { + return contextLost_; + }; + + function wrapEvent(listener) { + if (typeof(listener) == "function") { + return listener; + } else { + return function(info) { + listener.handleEvent(info); + } + } + } + + wrapper_.registerOnContextLostListener = function(listener) { + onLost_ = wrapEvent(listener); + }; + + wrapper_.registerOnContextRestoredListener = function(listener) { + if (contextLost_) { + nextOnRestored_ = wrapEvent(listener); + } else { + onRestored_ = wrapEvent(listener); + } + } + + return wrapper_; +} + +return { + /** + * Initializes this module. Safe to call more than once. + * @param {!WebGLRenderingContext} ctx A WebglInstance context. If + * you have more than one context it doesn't matter which one + * you pass in, it is only used to pull out constants. + */ + 'init': init, + + /** + * Returns true or false if value matches any WebglInstance enum + * @param {*} value Value to check if it might be an enum. + * @return {boolean} True if value matches one of the WebglInstance defined enums + */ + 'mightBeEnum': mightBeEnum, + + /** + * Gets an string version of an WebglInstance enum. + * + * Example: + * WebGLDebugUtil.init(ctx); + * var str = WebGLDebugUtil.glEnumToString(ctx.getError()); + * + * @param {number} value Value to return an enum for + * @return {string} The string version of the enum. + */ + 'glEnumToString': glEnumToString, + + /** + * Converts the argument of a WebglInstance function to a string. + * Attempts to convert enum arguments to strings. + * + * Example: + * WebGLDebugUtil.init(ctx); + * var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 0, gl.TEXTURE_2D); + * + * would return 'TEXTURE_2D' + * + * @param {string} functionName the name of the WebglInstance function. + * @param {number} argumentIndx the index of the argument. + * @param {*} value The value of the argument. + * @return {string} The value as a string. + */ + 'glFunctionArgToString': glFunctionArgToString, + + /** + * Given a WebglInstance context returns a wrapped context that calls + * gl.getError after every command and calls a function if the + * result is not NO_ERROR. + * + * You can supply your own function if you want. For example, if you'd like + * an exception thrown on any GL error you could do this + * + * function throwOnGLError(err, funcName, args) { + * throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to" + + * funcName; + * }; + * + * ctx = WebGLDebugUtils.makeDebugContext( + * canvas.getContext("webgl"), throwOnGLError); + * + * @param {!WebGLRenderingContext} ctx The webgl context to wrap. + * @param {!function(err, funcName, args): void} opt_onErrorFunc The function + * to call when gl.getError returns an error. If not specified the default + * function calls console.log with a message. + */ + 'makeDebugContext': makeDebugContext, + + /** + * Given a WebglInstance context returns a wrapped context that adds 4 + * functions. + * + * ctx.loseContext: + * simulates a lost context event. + * + * ctx.restoreContext: + * simulates the context being restored. + * + * ctx.registerOnContextLostListener(listener): + * lets you register a listener for context lost. Use instead + * of addEventListener('webglcontextlostevent', listener); + * + * ctx.registerOnContextRestoredListener(listener): + * lets you register a listener for context restored. Use + * instead of addEventListener('webglcontextrestored', + * listener); + * + * @param {!WebGLRenderingContext} ctx The webgl context to wrap. + */ + 'makeLostContextSimulatingContext': makeLostContextSimulatingContext, + + /** + * Resets a context to the initial state. + * @param {!WebGLRenderingContext} ctx The webgl context to + * reset. + */ + 'resetToInitialState': resetToInitialState +}; + +}(); + diff --git a/lab4/lib/webgl-utils.js b/lab4/lib/webgl-utils.js new file mode 100644 index 0000000..26ed37e --- /dev/null +++ b/lab4/lib/webgl-utils.js @@ -0,0 +1,197 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/** + * @fileoverview This file contains functions every webgl program will need + * a version of one way or another. + * + * Instead of setting up a context manually it is recommended to + * use. This will check for success or failure. On failure it + * will attempt to present an approriate message to the user. + * + * gl = WebGLUtils.setupWebGL(canvas); + * + * For animated WebglInstance apps use of setTimeout or setInterval are + * discouraged. It is recommended you structure your rendering + * loop like this. + * + * function render() { + * window.requestAnimationFrame(render, canvas); + * + * // do rendering + * ... + * } + * render(); + * + * This will call your rendering function up to the refresh rate + * of your display but will stop rendering if your app is not + * visible. + */ + +WebGLUtils = function() { + +/** + * Creates the HTLM for a failure message + * @param {string} canvasContainerId id of container of th + * canvas. + * @return {string} The html. + */ +var makeFailHTML = function(msg) { + return '' + + '<div style="margin: auto; width:500px;z-index:10000;margin-top:20em;text-align:center;">' + msg + '</div>'; + return '' + + '<table style="background-color: #8CE; width: 100%; height: 100%;"><tr>' + + '<td align="center">' + + '<div style="display: table-cell; vertical-align: middle;">' + + '<div style="">' + msg + '</div>' + + '</div>' + + '</td></tr></table>'; +}; + +/** + * Mesasge for getting a webgl browser + * @type {string} + */ +var GET_A_WEBGL_BROWSER = '' + + 'This page requires a browser that supports WebglInstance.<br/>' + + '<a href="http://get.webgl.org">Click here to upgrade your browser.</a>'; + +/** + * Mesasge for need better hardware + * @type {string} + */ +var OTHER_PROBLEM = '' + + "It doesn't appear your computer can support WebglInstance.<br/>" + + '<a href="http://get.webgl.org">Click here for more information.</a>'; + +/** + * Creates a webgl context. If creation fails it will + * change the contents of the container of the <canvas> + * tag to an error message with the correct links for WebglInstance. + * @param {Element} canvas. The canvas element to create a + * context from. + * @param {WebGLContextCreationAttirbutes} opt_attribs Any + * creation attributes you want to pass in. + * @param {function:(msg)} opt_onError An function to call + * if there is an error during creation. + * @return {WebGLRenderingContext} The created context. + */ +var setupWebGL = function(canvas, opt_attribs, opt_onError) { + function handleCreationError(msg) { + var container = document.getElementsByTagName("body")[0]; + //var container = canvas.parentNode; + if (container) { + var str = window.WebGLRenderingContext ? + OTHER_PROBLEM : + GET_A_WEBGL_BROWSER; + if (msg) { + str += "<br/><br/>Status: " + msg; + } + container.innerHTML = makeFailHTML(str); + } + }; + + opt_onError = opt_onError || handleCreationError; + + if (canvas.addEventListener) { + canvas.addEventListener("webglcontextcreationerror", function(event) { + opt_onError(event.statusMessage); + }, false); + } + var context = create3DContext(canvas, opt_attribs); + if (!context) { + if (!window.WebGLRenderingContext) { + opt_onError(""); + } else { + opt_onError(""); + } + } + + return context; +}; + +/** + * Creates a webgl context. + * @param {!Canvas} canvas The canvas tag to get context + * from. If one is not passed in one will be created. + * @return {!WebGLContext} The created context. + */ +var create3DContext = function(canvas, opt_attribs) { + var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; + var context = null; + for (var ii = 0; ii < names.length; ++ii) { + try { + context = canvas.getContext(names[ii], opt_attribs); + } catch(e) {} + if (context) { + break; + } + } + return context; +} + +return { + create3DContext: create3DContext, + setupWebGL: setupWebGL +}; +}(); + +/** + * Provides requestAnimationFrame in a cross browser + * way. + */ +if (!window.requestAnimationFrame) { + window.requestAnimationFrame = (function() { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { + window.setTimeout(callback, 1000/60); + }; + })(); +} + +/** * ERRATA: 'cancelRequestAnimationFrame' renamed to 'cancelAnimationFrame' to reflect an update to the W3C Animation-Timing Spec. + * + * Cancels an animation frame request. + * Checks for cross-browser support, falls back to clearTimeout. + * @param {number} Animation frame request. */ +if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = (window.cancelRequestAnimationFrame || + window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame || + window.mozCancelAnimationFrame || window.mozCancelRequestAnimationFrame || + window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame || + window.oCancelAnimationFrame || window.oCancelRequestAnimationFrame || + window.clearTimeout); +} \ No newline at end of file diff --git a/lab4/src/WebGl.js b/lab4/src/WebGl.js new file mode 100644 index 0000000..36bfd21 --- /dev/null +++ b/lab4/src/WebGl.js @@ -0,0 +1,141 @@ + +function WebGl(canvas) { + this.gl = getWebGLContext(canvas); + if (!this.gl) + throw 'Failed to get the rendering context for WebglInstance'; + + // Set the clear color and enable the depth test + this.gl.clearColor(0.0, 0.0, 0.0, 1.0); + //OpenGL performs a depth test and if this test passes, + //the fragment is rendered and the depth buffer is updated with the new depth value. If the depth test fails, the fragment is discarded. + this.gl.enable(this.gl.DEPTH_TEST); +} + +WebGl.prototype.getGl = function(){ + return this.gl; +} +WebGl.prototype.getStorage = function(name) { + let storageLocation = this.gl.getUniformLocation(this.gl.program, name); + if (!storageLocation) { + throw new Error('Failed to get the storage location of ' + name); + } + return storageLocation + } + +WebGl.prototype.initShaders = function(vertexShader, fragmentShader) { + if (!initShaders(this.gl, vertexShader, fragmentShader)) + throw 'Failed to initialize shaders.'; +}; +WebGl.prototype.writeuniform3f = function(u_LightColor_Directional, u_LightDirection, lightDirection, u_LightColor_Point, u_PositionLight, pointLightPosition) { + this.gl.uniform3f(u_LightColor_Directional, 0.0, 0.0, 0.0); + this.gl.uniform3fv(u_LightDirection, lightDirection.normalize().elements); + + //Positional + this.gl.uniform3f(u_LightColor_Point, 1.0, 1.0, 1.0); + this.gl.uniform3fv(u_PositionLight, pointLightPosition); +}; + +WebGl.prototype.initVertexBuffers = function() { + // This is the model + + const model = new Scene_model(); // Hourglass + Plane + + // Create a buffer object + const vertexBuffer = this.gl.createBuffer(); + if (!vertexBuffer) { + console.log('Failed to create the buffer object'); + return -1; + } + + // Write the vertex coordinates and color to the buffer object + if (!this.initArrayBuffer(this.gl, model.vertices, 3, this.gl.FLOAT, 'a_Position')) { + return -1; + } + + if (!this.initArrayBuffer(this.gl, model.colors, 3, this.gl.FLOAT, 'a_Color')) { + return -1; + } + + if (!this.initArrayBuffer(this.gl, model.normals, 3, this.gl.FLOAT, 'a_Normal')) { + return -1; + } + + // Write the indices to the buffer object + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, vertexBuffer); + this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, model.indices, this.gl.STATIC_DRAW); + + return model.indices.length; +}; + +WebGl.prototype.initArrayBuffer = function(gl, data, num, type, attribute) { + var buffer = gl.createBuffer(); // Create a buffer object + if (!buffer) { + console.log('Failed to create the buffer object'); + return false; + } + // Write date into the buffer object + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); + // Assign the buffer object to the attribute variable + var a_attribute = gl.getAttribLocation(gl.program, attribute); + if (a_attribute < 0) { + console.log('Failed to get the storage location of ' + attribute); + return false; + } + gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0); + // Enable the assignment of the buffer object to the attribute variable + gl.enableVertexAttribArray(a_attribute); + + return true; +}; + +WebGl.prototype.writeUniformMatrix = function(matrix, u_uniform) { + // Bind the matrix to the uniform variable in the shader program + const u_Matrix = this.gl.getUniformLocation(this.gl.program, u_uniform); + if (!u_Matrix) { + console.log('Failed to get the storage location of ' + u_uniform); + return; + } + // Pass the matrix to the uniform variable + this.gl.uniformMatrix4fv(u_Matrix, false, matrix.elements); +}; + +WebGl.prototype.direction = function(){ + +} + +WebGl.prototype.writeUniform3fv = function(u_uniform, vector3){ + const u_3f = this.gl.getUniformLocation(this.gl.program, u_uniform); + // Bind the 3f to the uniform variable in the shader program + if (!u_3f) { + console.log('Failed to get the storage location of ' + u_uniform); + return; + } + this.gl.uniform3fv(u_3f, vector3.elements); +} +WebGl.prototype.matrix_rotation = function(matrix, u_uniform){ + // Model matrix + var modelMatrixRotate = new Matrix4(); + var matrix_translation = new Matrix4(); + var matrix_mv = new Matrix4(); + // Bind the matrix to the uniform variable in the shader program + const u_Matrix = this.gl.getUniformLocation(this.gl.program, u_uniform); + if (!u_Matrix) { + console.log('Failed to get the storage location of ' + u_uniform); + return; + } + matrix_translation.setTranslate(2, 1, -1.5); + modelMatrixRotate.setRotate(180, 1, 0, 1); + modelMatrixRotate.rotate(0,0,1,0); + matrix_mv.multiply(matrix_translation).multiply(modelMatrixRotate); + + this.gl.uniformMatrix4fv(u_Matrix, false, matrix_mv.elements); +} + +WebGl.prototype.clear = function() { + this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); +}; + +WebGl.prototype.draw = function() { + this.gl.drawElements(this.gl.TRIANGLES, this.indicesCount, this.gl.UNSIGNED_BYTE, 0); +}; diff --git a/lab4/src/lab4.html b/lab4/src/lab4.html new file mode 100644 index 0000000..95b29ff --- /dev/null +++ b/lab4/src/lab4.html @@ -0,0 +1,41 @@ + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Lab 4</title> + <style type="text/css"> + .keys { + border-spacing: 0; + border-collapse: collapse; + } + .keys tr td, .keys tr th { + border: solid black 1px; + padding: 2px 20px; + } + div { + margin: 10px; + } + </style> +</head> +<body onload="main()"> + +<canvas height="500" width="500" id="my_Canvas"> + Please use a browser that supports "canvas" +</canvas> +<div> + <p> Press on key "c" to change the light mode.</p> +</div> +<p> 1- Drag the scene with the mouse to move it</p> +<p> 2- Keys E, A, D, Q, E, W and S to move the light</p> + + +<script src="../lib/webgl-utils.js"></script> +<script src="../lib/webgl-debug.js"></script> +<script src="../lib/cuon-utils.js"></script> +<script src="../lib/cuon-matrix.js"></script> +<script src="scene_objects.js"></script> +<script src="WebGl.js"></script> +<script src="lab4.js"></script> +</body> +</html> \ No newline at end of file diff --git a/lab4/src/lab4.js b/lab4/src/lab4.js new file mode 100644 index 0000000..cfc7dbe --- /dev/null +++ b/lab4/src/lab4.js @@ -0,0 +1,306 @@ +// Vertex shader program +var SHADOW_VSHADER_SOURCE = + 'attribute vec4 a_Position;\n' + + 'uniform mat4 u_MvpMatrix;\n' + + 'void main() {\n' + + ' gl_Position = u_MvpMatrix * a_Position;\n' + + '}\n'; + +// Fragment shader program for generating a shadow map +var SHADOW_FSHADER_SOURCE = + '#ifdef GL_ES\n' + + 'precision mediump float;\n' + + '#endif\n' + + 'void main() {\n' + + ' gl_FragColor = vec4(gl_FragCoord.z, 0.0, 0.0, 0.0);\n' + // Write the z-value in R + '}\n'; + +var VSHADER_SOURCE = + 'attribute vec4 a_Position;\n' + + 'attribute vec4 a_Color;\n' + + 'attribute vec3 a_Normal;\n' + // Normal + + 'uniform mat4 u_MvpMatrix;\n' + + 'uniform mat4 u_ModelMatrix;\n' + //Point + 'uniform mat4 u_ModelMatrixInverseTransposee;\n' + //Directional + Point + 'uniform vec3 u_PositionLight;\n' + //Point + + 'varying vec4 v_Color;\n' + + 'varying vec3 v_Normal;\n' + + 'varying vec3 v_ModelToLightDirection;\n' + //Point + + 'uniform mat4 u_MvpMatrixFromLight;\n' + + 'varying vec4 v_PositionFromLight;\n' + + + 'void main() {\n' + + ' gl_Position = u_MvpMatrix * a_Position ;\n' + + ' v_Color = a_Color;\n' + // Set the varying color value with the attribute color value + ' v_Normal = mat3(u_ModelMatrixInverseTransposee) * a_Normal;\n' + + + ' vec3 actualModelPosition = (u_ModelMatrix * a_Position).xyz;\n' + //Point + ' v_ModelToLightDirection = u_PositionLight - actualModelPosition;\n' + //Point + '}\n'; + +// Fragment shader program +const FSHADER_SOURCE = + 'precision mediump float;\n' + // This determines how much precision the GPU uses when calculating floats + + 'varying vec4 v_Color;\n' + // Varying variable color to get the vertex color from + 'varying vec3 v_Normal;\n' + //Directional et Point + 'varying vec3 v_ModelToLightDirection;\n' + //Point + + 'uniform vec3 u_LightDirection;\n' + //Directional + 'uniform vec3 u_LightColor_Directional;\n' + + 'uniform vec3 u_LightColor_Point;\n' + + + 'uniform sampler2D u_ShadowMap;\n' + +'varying vec4 v_PositionFromLight;\n' + + + 'void main() {\n' + + ' vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' + + ' vec4 rgbaDepth = texture2D(u_ShadowMap, shadowCoord.xy);\n' + + ' float depth = rgbaDepth.r;\n' + // Retrieve the z-value from R + ' float visibility = (shadowCoord.z > depth + 0.005) ? 0.7 : 1.0;\n' + + + ' vec3 u_AmbientLight = vec3(0.2, 0.2, 0.2);\n' + + ' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' + + ' vec3 normal = normalize(v_Normal.xyz);\n' + //Directional et Point + + ' float lightDirectional = dot(normal, u_LightDirection);\n' + //Directional + ' float lightPoint = clamp(dot(normal, normalize(v_ModelToLightDirection)), 0.0, 1.0);\n' + //Point + + ' gl_FragColor = vec4(ambient + (u_LightColor_Point * v_Color.rgb * lightPoint * visibility) + (u_LightColor_Directional * v_Color.rgb * lightDirectional * visibility), v_Color.a);\n' + // Set the fragment color with the vertex shader + '}\n'; + +let webgl_1; +var mouseMovement = [0.0, 0.0]; +var LIGHT_X = 0, LIGHT_Y = 2, LIGHT_Z = 0; // Position of the light source + + +function main() { + // Retrieve <canvas> element + const canvas = document.getElementById('my_Canvas'); + + webgl_1 = new WebGl(canvas); + + // Initialize shaders + webgl_1.initShaders(VSHADER_SOURCE, FSHADER_SOURCE); + //webGL_2.initShaders(VSHADER_SOURCE2, FSHADER_SOURCE2); + + // Write the positions of vertices to a vertex shader + const n = webgl_1.initVertexBuffers(); + if (n < 0) { + console.log('Failed to set the positions of the vertices'); + return; + } + + // Initialize shaders for generating a shadow map + var shadowProgram = createProgram(webgl_1.getGl(), SHADOW_VSHADER_SOURCE, SHADOW_FSHADER_SOURCE); + shadowProgram.a_Position = webgl_1.getGl().getAttribLocation(shadowProgram, 'a_Position'); + shadowProgram.u_MvpMatrix = webgl_1.getGl().getUniformLocation(shadowProgram, 'u_MvpMatrix'); + if (shadowProgram.a_Position < 0 || !shadowProgram.u_MvpMatrix) { + console.log('Failed to get the storage location of attribute or uniform variable from shadowProgram'); + return; + } + + + // GETTING ALL STORAGE LOCATION + + // Get the storage location of uniforms + let u_MvpMatrix = webgl_1.getStorage('u_MvpMatrix') + //Directional + let u_LightColor_Directional = webgl_1.getStorage('u_LightColor_Directional') + let u_LightDirection = webgl_1.getStorage('u_LightDirection') + //Point + let u_LightColor_Point = webgl_1.getStorage('u_LightColor_Point') + let u_PositionLight = webgl_1.getStorage('u_PositionLight') + let u_ModelMatrix = webgl_1.getStorage('u_ModelMatrix') + let u_ModelMatrixInverseTransposee = webgl_1.getStorage('u_ModelMatrixInverseTransposee') + + let directionalLightDirection = [0.0, 1.0, 0.0]; + let pointLightPosition = [0.0, 2.0, 0.0]; + //Directional + let lightDirection = new Vector3(directionalLightDirection); + + webgl_1.writeuniform3f(u_LightColor_Directional, u_LightDirection, lightDirection, u_LightColor_Point, u_PositionLight, pointLightPosition); + + // Calculate the view projection matrix + var viewProjMatrix = new Matrix4(); + viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 10.0); //Profondeur + viewProjMatrix.lookAt(3.0, 3.0, 5.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0); //Position, Point de vision, Direction + + var mvpMatrix = new Matrix4(); // Model view projection matrix + var modelMatrix = new Matrix4(); + var modelMatrixInverseTransposee = new Matrix4(); + + var viewProjMatrixFromLight = new Matrix4(); // Prepare a view projection matrix for generating a shadow map + viewProjMatrixFromLight.setPerspective(70.0, 500/500, 1.0, 100.0); + viewProjMatrixFromLight.lookAt(LIGHT_X, LIGHT_Y, LIGHT_Z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); + + EventHandlers(document, canvas, webgl_1.getGl(), mouseMovement, u_LightColor_Point, u_LightColor_Directional, pointLightPosition, directionalLightDirection); + + var display = function() { + //webgl_1.getGl().useProgram(shadowProgram); // Set shaders for generating a shadow map + //Point light position + webgl_1.getGl().uniform3fv(u_PositionLight, pointLightPosition); + //Directional light direction + lightDirection = new Vector3(directionalLightDirection); + webgl_1.getGl().uniform3fv(u_LightDirection, lightDirection.normalize().elements); + //Update model movement with mouse + modelMatrix.setRotate(mouseMovement[0], 1.0, 0.0, 0.0); + modelMatrix.rotate(mouseMovement[1], 0.0, 1.0, 0.0); + mvpMatrix.set(viewProjMatrix).multiply(modelMatrix); + webgl_1.getGl().uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); + webgl_1.getGl().uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements); + modelMatrixInverseTransposee.setInverseOf(modelMatrix).transpose() + webgl_1.getGl().uniformMatrix4fv(u_ModelMatrixInverseTransposee, false, modelMatrixInverseTransposee.elements); + // CLEAR CANVAS + webgl_1.getGl().clear(webgl_1.getGl().COLOR_BUFFER_BIT | webgl_1.getGl().DEPTH_BUFFER_BIT); + // DRAW THE TRIANGLES + webgl_1.getGl().drawElements(webgl_1.getGl().TRIANGLES, n, webgl_1.getGl().UNSIGNED_BYTE, 0); + modelMatrix.translate(0, 0.80, 0); + modelMatrix.rotate(180, 1.0, 0.0, 0.0); + mvpMatrix.set(viewProjMatrix).multiply(modelMatrix) + webgl_1.getGl().uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); + webgl_1.getGl().uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements); + modelMatrixInverseTransposee.setInverseOf(modelMatrix).transpose() + webgl_1.getGl().uniformMatrix4fv(u_ModelMatrixInverseTransposee, false, modelMatrixInverseTransposee.elements); + webgl_1.getGl().drawElements(webgl_1.getGl().TRIANGLES, n - 6, webgl_1.getGl().UNSIGNED_BYTE, 0); + + //Appel de la fonction render à chaque image + requestAnimationFrame(display); + }; + display(); +} +function check(gl, x, y) { + var picked = false; + let pixels = new Uint8Array(4); // Array for storing the pixel value + gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + console.log(pixels[0]) + if (pixels[0] == 255){ + picked = true; + } // The mouse in on cube if R(pixels[0]) is 255 + + return picked; +} + + +function EventHandlers(document, canvas, gl, mouseMovement, u_LightColor_Point, u_LightColor_Directional, pointLightPosition, directionalLightDirection) { + var dragging = false; + var lastX = -1, lastY = -1; + lightFactor = 0.3; + isPointLight = true; + + //Mouse down + canvas.onmousedown = function(ev) { + dragging = true; + var x = ev.clientX, y = ev.clientY; + var rect = ev.target.getBoundingClientRect(); + if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { + // If pressed position is inside <canvas>, check if it is above object + var x_in_canvas = x - rect.left, y_in_canvas = rect.bottom - y; + var picked = check(gl, x_in_canvas, y_in_canvas); + if (picked){ + alert('Sandglass was selected! '); + // let r = Math.random(); + // let g = Math.random(); + // let b = Math.random(); + // colors = new Float32Array(computeColors(r, g, b)); + // requestAnimationFrame(display); + + } + + } + }; + + //Mouse up + canvas.onmouseup = function(ev) { + dragging = false; + }; + + //Mouse mouve + canvas.onmousemove = function(ev) { + var x = ev.clientX, y = ev.clientY; + if (dragging) { + //Speed factor + var speedFactor = 0.5; + var dx = speedFactor * (x - lastX); + var dy = speedFactor * (y - lastY); + + //Update angle + mouseMovement[1] = mouseMovement[1] + dx; + mouseMovement[0] = mouseMovement[0] + dy; + } + lastX = x, lastY = y; + }; + + document.onkeydown = function(ev) { + if (ev.keyCode == 67) { // c Key (Light mode) + if (isPointLight) { + // indiquent les valeurs des variables uniform point et directional + gl.uniform3f(u_LightColor_Point, 0.0, 0.0, 0.0); + gl.uniform3f(u_LightColor_Directional, 1.0, 1.0, 1.0); + isPointLight = false; + } + else { + gl.uniform3f(u_LightColor_Point, 1.0, 1.0, 1.0); + gl.uniform3f(u_LightColor_Directional, 0.0, 0.0, 0.0); + isPointLight = true; + } + } + + + + //Direction + else if (ev.keyCode == 81) { //Q key + if (isPointLight) { + pointLightPosition[0] += 0.1 + lightFactor; + } + else { + directionalLightDirection[0] += 0.1 + lightFactor; + } + } + else if (ev.keyCode == 65) { //A key + if (isPointLight) { + pointLightPosition[0] -= 0.1 + lightFactor; + } + else { + directionalLightDirection[0] -= 0.1 + lightFactor; + } + } + else if (ev.keyCode == 87) { //W key + if (isPointLight) { + pointLightPosition[1] += 0.1 + lightFactor; + } + else { + directionalLightDirection[1] += 0.1 + lightFactor; + } + } + else if (ev.keyCode == 83) { //S key + if (isPointLight) { + pointLightPosition[1] -= 0.1 + lightFactor; + } + else { + directionalLightDirection[1] -= 0.1 + lightFactor; + } + } + else if (ev.keyCode == 69) { //E key + if (isPointLight) { + pointLightPosition[2] -= 0.1 + lightFactor; + } + else { + directionalLightDirection[2] -= 0.1 + lightFactor; + } + } + else if (ev.keyCode == 68) { //D key + if (isPointLight) { + pointLightPosition[2] += 0.1 + lightFactor; + } + else { + directionalLightDirection[2] += 0.1 + lightFactor; + } + } + + }; +} + diff --git a/lab4/src/scene_objects.js b/lab4/src/scene_objects.js new file mode 100644 index 0000000..23dabc0 --- /dev/null +++ b/lab4/src/scene_objects.js @@ -0,0 +1,126 @@ + +function Scene_model() { + this.vertices = new Float32Array([ + //Down | Top | Back | Left | Right | Front | Plane + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + + -0.1, 0.5, -0.1, + -0.1, 0.5, 0.1, + 0.1, 0.5, -0.1, + 0.1, 0.5, 0.1, + + -0.5, -0.5, 0.5, + -0.1, 0.5, 0.1, + 0.0, -0.5, 0.5, + 0.1, 0.5, 0.1, + 0.5, -0.5, 0.5, + + -0.5, -0.5, 0.5, + -0.1, 0.5, 0.1, + -0.5, -0.5, 0.0, + -0.1, 0.5, -0.1, + -0.5, -0.5, -0.5, + + 0.5, -0.5, -0.5, + 0.1, 0.5, -0.1, + 0.5, -0.5, 0.0, + 0.1, 0.5, 0.1, + 0.5, -0.5, 0.5, + + -0.5, -0.5, -0.5, + -0.1, 0.5, -0.1, + 0.0, -0.5, -0.5, + 0.1, 0.5, -0.1, + 0.5, -0.5, -0.5, + + //Plain + -2.0, -0.5, -2.0, + -2.0, -0.5, 2.0, + 2.0, -0.5, -2.0, + 2.0, -0.5, 2.0 + ]); + + this.colors = new Float32Array([ + //Down | Top | Back | Left | Right | Front | Plane + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 5.0, 1.0, 0.0, 5.0, 1.0, 0.0, 5.0, 1.0, 0.0, 5.0, + ]); + + this.normals = new Float32Array([ + //Down | Top | Back | Left | Right | Front | Plane + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + -1.0, 0.2, 0.0, + + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + 1.0, 0.0, 0.2, + + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + 0.0, 0.2, -1.0, + + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0 + + ]); + + this.indices = new Uint8Array([ + 0, 1, 2, //Grande base + 1, 2, 3, + + 4, 5, 6, //Petite base + 5, 6, 7, + + 8, 9, 10, //Face arrière + 9, 10, 11, + 10, 11, 12, + + 13, 14, 15, //Face gauche + 14, 15, 16, + 15, 16, 17, + + 18, 19, 20, //Face droite + 19, 20, 21, + 20, 21, 22, + + 23, 24, 25, //Face avant + 24, 25, 26, + 25, 26, 27, + + 28, 29, 30, //Plain + 29, 30, 31 + ]); +} \ No newline at end of file -- GitLab