commit 4c104a8c26f28734990f2313b095002b6950ebc9 Author: Bill Date: Wed May 19 02:41:33 2021 +0800 Reset Repo structure. diff --git a/IMG_4326.MOV b/IMG_4326.MOV new file mode 100644 index 0000000..42f41ab Binary files /dev/null and b/IMG_4326.MOV differ diff --git a/hw1/8081_earthmap10k.jpg b/hw1/8081_earthmap10k.jpg new file mode 100644 index 0000000..812458c Binary files /dev/null and b/hw1/8081_earthmap10k.jpg differ diff --git a/hw1/earthmap1k.jpg b/hw1/earthmap1k.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw1/earthmap1k.jpg differ diff --git a/hw1/index.html b/hw1/index.html new file mode 100644 index 0000000..29017b4 --- /dev/null +++ b/hw1/index.html @@ -0,0 +1,360 @@ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw1/lib1.js b/hw1/lib1.js new file mode 100644 index 0000000..33a7345 --- /dev/null +++ b/hw1/lib1.js @@ -0,0 +1,175 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); + +let nfsh = fragmentShaderHeader.split('\n').length; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; // IS THIS THE FIREFOX BROWSER? +let errorMsg = ''; +// +// Initialize a texture and load an image. +// When the image finished loading copy it into the texture. +// +function loadTexture(gl, url) { + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + // Because images have to be downloaded over the internet + // they might take a moment until they are ready. + // Until then put a single pixel in the texture so we can + // use it immediately. When the image has finished downloading + // we'll update the texture with the contents of the image. + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + + const image = new Image(); + image.onload = function () { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + // WebGL1 has different requirements for power of 2 images + // vs non power of 2 images so check if the image is a + // power of 2 in both dimensions. + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + // Yes, it's a power of 2. Generate mips. + gl.generateMipmap(gl.TEXTURE_2D); + } else { + // No, it's not a power of 2. Turn off mips and set + // wrapping to clamp to edge + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; + + return texture; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + let gl = this.gl, program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = '
'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + + let texture = loadTexture(gl, './earthmap1k.jpg') //Texture loading. + // Tell WebGL we want to affect texture unit 0 + gl.activeTexture(gl.TEXTURE0); + // Bind the texture to texture unit 0 + gl.bindTexture(gl.TEXTURE_2D, texture); + // Tell the shader we bound the texture to texture unit 0 + gl.uniform1i(gl.getUniformLocation(program, 'uSampler'), 0); + + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +let gl; +function setUniform(type, name, a, b, c, d, e, f) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); +} + diff --git a/hw10/.vscode/launch.json b/hw10/.vscode/launch.json new file mode 100644 index 0000000..a8c9784 --- /dev/null +++ b/hw10/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch index.html", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + }, + { + "name": "windows", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "C:/Users/sunyi/AppData/Local/Google/Chrome SxS/Application/chrome.exe", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + } + ] +} \ No newline at end of file diff --git a/hw10/.vscode/settings.json b/hw10/.vscode/settings.json new file mode 100644 index 0000000..7a05c85 --- /dev/null +++ b/hw10/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.pythonPath": "/usr/local/bin/python", + "svg.preview.background": "black" +} \ No newline at end of file diff --git a/hw10/RTXoff.svg b/hw10/RTXoff.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw10/RTXoff.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw10/animation.js b/hw10/animation.js new file mode 100644 index 0000000..2c4d2a4 --- /dev/null +++ b/hw10/animation.js @@ -0,0 +1,301 @@ +let lerp = (a,b,t) => a + t * (b - a); // LINEAR INTERPOLATION + +let N = 0; // GREATER N INCREASES "NERVOUSNESS" OF BIG OBJECT + +// TIMING TABLE FOR THE ANIMATION SEQUENCE + +let timing = [ + [0, lerp(1,.5,N)], + [1.8, lerp(2.3,2.1,N)], + [lerp(2,2.5,N), 2.8], + [2.0, 3.2], + [3.4, 3.9], + [4.3, 5.0], +]; + +// EASE CURVE TO ACCELERATE FROM REST AND THEN DECELERATE TO REST + +let sCurve = t => (3 - 2 * t) * t * t; +let jCurve = t => t*t*t*t; +let jCurve3 = t => t*t*t; +// EVALUATE THE TIMING OF ONE ANIMATION PARAMETER FOR THIS FRAME + +let evalTiming = n => { + let t0 = timing[n][0]; + let t1 = timing[n][1]; + + if (animationTime < t0) + return 0; + if (animationTime > t1) + return 1; + return sCurve((animationTime - t0) / (t1 - t0)); +} + + +let bounce = t => Math.sin(Math.PI * t); +let wiggle = t => Math.sin(6 * Math.PI * t); + +let trees = [] + +function add_tree(pos) { + trees.append(new Float32Array(pos)) + createMesh(32,32,uvToCone,-1, 20); +} + +class State { + constructor(idx = 0, step = 1) { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + this.running = 0; + this.direction_l = [0, 0]; + this.dir_sdl = 0; + this.delta_l = [0, 0]; + this.wiggle_t = 0; + this.punch_t = 1; + this.idx = idx; + if(rebuild[idx] === undefined) + rebuild.push(true); + states[this.idx] = this; + this.fig_rot_t = 0; + this.figure_rot = pi; + this.old_figure_rot = pi; + this.curr_figure_rot = pi; + this.target_figure_rot = pi; + this.delta_height = 0; + this.delta = const_multiply(step, [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]); + this.stepSize = step; + this.turnStep = .1; + this.stepLength = this.stepSize*(1.8522); + this.life = 3; + this.damage = .05; + this.dead = false; + this.death_t = 0; + this.hitID = 19; + } + reset(){ + this.running = 0; + this.leg = true; + this.progress = 0; + this.delta_height = 0; + this.dir_sdl = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + this.dead = false; + this.wiggle_t = 0; + this.death_t = 0; + } + initialize() { + this.figure_rot = pi; + this.old_figure_rot = pi; + this.curr_figure_rot = pi; + this.target_figure_rot = pi; + this.reset(); + this.wiggle_t = 0; + this.punch_t = 1; + this.fig_rot_t = 0; + this.direction_l = [0, 0]; + this.delta_l = [0, 0]; + this.life = 3; + this.delta_l = [0,0]; + this.direction_l = [0, 0]; + this.dir_sdl = 0; + } + set_stepSize(step){ + this.delta = const_multiply(step, [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]); + this.stepSize = step; + this.stepLength = this.stepSize*(1.8522); + } + hitTest(punchProg){ + states.forEach((st, i)=>{ + if(i != this.idx){ + //1.15=.5*.23*10; .92 = .4*.23*10 .23=figure_scale, 10=overall_scale + const armlength = 1.15*cos(punchProg*pi/4)+.92*cos(-.53*pi*punchProg) + let dir = normalize(this.direction_l); + let punchpos = plus(plus(this.delta_l, + const_multiply(.3, [dir[1],-dir[0]])), + const_multiply(armlength, dir)); + if(vec_len(minus(punchpos, st.delta_l)) < .65){ + st.hit(this); + } + } + }); + } + start_punch(){ + if(this.punch_t >= 1){ + this.punch_t = 0; + rebuild[this.idx] = true; + } + } + punch(){ + if(this.punch_t < 1) + { + rebuild[this.idx] = true; + this.punch_t+=.05; + if(this.punch_t <= .05) + return 1.2; + else + { + let punchProg; + if(this.punch_t <= .4) + punchProg = 1.2*(1-jCurve((this.punch_t- .05)/.35)); + else + punchProg = 1-jCurve(2-2*(this.punch_t+.1)/1.1); + if(this.hitTest(punchProg)) + this.punch_t = 1; + return punchProg; + } + } + return 1; + } + restoreID(st){ + objects[st.idx].forEach( + (obj, i)=>{ + changeID(st.orig_objid[i], obj[0]); + } + ) + } + defaultHit(){ + if(objects[this.idx][0][0][0] != this.hitID){ + this.orig_objid = []; + objects[this.idx].forEach( + (obj, i) => { + this.orig_objid[i] = (obj[0])[0]; + } + ) + objects[this.idx].forEach( + (obj, i) => { + changeID(this.hitID, obj[0]); + } + ) + setTimeout(this.restoreID, 100, this); + } + if(this.dead&&this.wiggle_t<=0){ + rebuild[this.idx] = true; + this.wiggle_t = 2.5; + } + else if(this.life > 0) + { + this.life -= this.damage; + } + else { + this.death(); + } + } + defaultDeath(){ + this.dead = true; + this.death_t = 1; + rebuild[this.idx] = true; + } + hit(hitter){this.defaultHit(hitter);} + death() {if(!this.dead){this.defaultDeath();}} + animated_turn(){ + if(this.target_figure_rot != this.figure_rot) + { + this.fig_rot_t = 1; + this.figure_rot %= 2*pi; + if(this.figure_rot < 0) this.figure_rot += 2*pi; + this.curr_figure_rot %= 2*pi; + if(this.curr_figure_rot < 0) this.curr_figure_rot += 2*pi; + if((this.curr_figure_rot - this.figure_rot) > pi) + this.curr_figure_rot -= 2*pi; + else if (this.curr_figure_rot - this.figure_rot < -pi) + this.figure_rot -= 2*pi; + this.old_figure_rot = this.curr_figure_rot; + this.target_figure_rot = this.figure_rot; + } + if(this.fig_rot_t > 0) + { + this.fig_rot_t-=this.turnStep; + this.curr_figure_rot = lerp( + this.old_figure_rot, this.figure_rot, sCurve(1-this.fig_rot_t)); + } + } + turn(pt, animated = true){ + this.direction_l = [pt[0] - this.delta_l[0]/10, pt[1] - this.delta_l[1]/10]; + if(this.direction_l[1] == 0) + this.direction_l[1] = 0.0000000000000001; + this.figure_rot = atan(this.direction_l[0]/this.direction_l[1]); + if(this.direction_l[1] < 0) + this.figure_rot = pi + this.figure_rot; + if(!animated) + { + this.curr_figure_rot = this.target_figure_rot = this.figure_rot; + this.fig_rot_t = 0; + } + } + walk(){ + this.dir_sdl = vec_len(this.direction_l)*10; + this.direction_l = normalize(this.direction_l); + rebuild[this.idx] = true; + this.running = 1; + } + next() { + //return this.presentation(); + if (this.running <= 0) + { + this.reset(); + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + } + this.running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = deepcopy(this.delta); + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: this.stepLength / steps + }; + } + presentation(){ + return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0, dl: 0}; + } + }; + +function update_tree(idx){ + //scale, add leaves, add branch + +} \ No newline at end of file diff --git a/hw10/geometry.js b/hw10/geometry.js new file mode 100644 index 0000000..f2bb514 --- /dev/null +++ b/hw10/geometry.js @@ -0,0 +1,105 @@ + +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data) => { + let tmp = []; + for (let v = 0 ; v < 1 ; v += 1/nv) { + for (let u = 0 ; u <= 1 ; u += 1/nu) { + tmp = tmp.concat(f(u,v,data)); + tmp = tmp.concat(f(u,v+1/nv,data)); + } + tmp = tmp.concat(f(1,v,data)); + tmp = tmp.concat(f(0,v+1/nv,data)); + } + return new Float32Array(tmp); +} + +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0 ; i < a.length ; i++) + c.push(a[i]); // a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0 ; i < b.length ; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + +let createSquareMesh = (i, z) => { + let m = [], n = 6, j = z < 0 ? (i + 2) % 3 : (i + 1) % 3, + k = z < 0 ? (i + 1) % 3 : (i + 2) % 3; + + m[i] = m[1*n+i] = m[2*n+i] = m[3*n+i] = z; + m[j] = m[2*n+j] = m[2*n+k] = m[3*n+k] = -1; + m[k] = m[1*n+j] = m[1*n+k] = m[3*n+j] = 1; + + m[3+i] = m[1*n+3+i] = m[2*n+3+i] = m[3*n+3+i] = z < 0 ? -1 : 1; + m[3+j] = m[1*n+3+j] = m[2*n+3+j] = m[3*n+3+j] = 0; + m[3+k] = m[1*n+3+k] = m[2*n+3+k] = m[3*n+3+k] = 0; + + return new Float32Array(m); +} + +let squareMesh = createSquareMesh(2, 0); + +let cubeMesh = glueMeshes( + glueMeshes(glueMeshes(createSquareMesh(0,-1),createSquareMesh(0,1)), + glueMeshes(createSquareMesh(1,-1),createSquareMesh(1,1))), + glueMeshes(createSquareMesh(2,-1),createSquareMesh(2,1)) ); + +let uvToTorus = (u,v,r) => { + let theta = 2 * Math.PI * u; + let phi = 2 * Math.PI * v; + + let x = Math.cos(theta) * (1 + r * Math.cos(phi)); + let y = Math.sin(theta) * (1 + r * Math.cos(phi)); + let z = r * Math.sin(phi); + + let nx = Math.cos(theta) * Math.cos(phi); + let ny = Math.sin(theta) * Math.cos(phi); + let nz = Math.sin(phi); + + return [x,y,z, nx,ny,nz]; +} + +let uvToSphere = (u,v) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [x,y,z, x,y,z]; +} + +let uvToTube = (u,v) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [x,y,z, x,y,0]; +} + +let uvToDisk = (u,v,dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [x,y,z, 0,0,dz ? Math.sign(dz) : 1]; +} + +let torusMesh = createMesh(32, 16, uvToTorus, .5); +let sphereMesh = createMesh(32, 16, uvToSphere); +let tubeMesh = createMesh(32, 2, uvToTube); +let diskMesh = createMesh(32, 2, uvToDisk); +let diskNMesh = createMesh(32, 2, uvToDisk, -1); +let diskPMesh = createMesh(32, 2, uvToDisk, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); + diff --git a/hw10/implicitSurface.js b/hw10/implicitSurface.js new file mode 100644 index 0000000..5615dc6 --- /dev/null +++ b/hw10/implicitSurface.js @@ -0,0 +1,201 @@ + +function implicitSurfaceTriangleMesh(implicitFunction, n, args, id = 8) { + + // HERE IS WHERE MOST OF THE WORK HAPPENS + + let marchingTetrahedra = function(V, ni, nj) { + + // CONVENIENCE FUNCTIONS TO COMPUTE (i,j,k) FROM VOLUME INDEX n + + function n2i(n) { return n % ni; } + function n2j(n) { return (n / dj >>> 0) % nj; } + function n2k(n) { return n / dk >>> 0 ; } + + // ADD A VERTEX, AND RETURN A UNIQUE ID FOR THAT VERTEX + + function E(a, b) { + if (a > b) { let tmp = a; a = b; b = tmp; } + let ai = n2i(a), aj = n2j(a), ak = n2k(a), + bi = n2i(b), bj = n2j(b), bk = n2k(b); + let m = (n << 6) + (ai & bi ? 1 << 6 : ai | bi << 3) + + (aj & bj ? dj << 6 : aj << 1 | bj << 4) + + (ak & bk ? dk << 6 : ak << 2 | bk << 5); + + // ADD TO VERTEX ARRAY ONLY THE FIRST TIME THE VERTEX IS ENCOUNTERED + + if (vertexID[m] === undefined) { + vertexID[m] = P.length / 3; + let t = -V[n+a] / (V[n+b] - V[n+a]), + c = function(i,a,b) { return (i + (1-t)*a + t*b) / ni * 2 - 1; }; + P.push( c(i,ai,bi), c(j,aj,bj), c(k,ak,bk) ); + } + + return vertexID[m]; + } + + // CASE WHERE WE ADD ONE TRIANGLE IN A TETRAHEDRON + + function tri(a, b, c, d) { + T.push(E(a,b), E(a,c), E(a,d)); + } + + // CASE WHERE WE ADD TWO TRIANGLES IN A TETRAHEDRON + + function quad(a, b, c, d) { + let ac = E(a,c), bc = E(b,c), ad = E(a,d), bd = E(b,d); + T.push(bc, ac, ad); + T.push(ad, bd, bc); + } + + // DECLARE VARIABLES + + let nk = V.length / (ni * nj), di = 1, dj = ni, dk = ni * nj; + let dij = di + dj, dik = di + dk, djk = dj + dk, dijk = di + dj + dk; + let P = [], T = [], vertexID = [], i, j, k, m = 0, n, S = [0,di,dij,dijk]; + let lo = new Array(nj * nk), + hi = new Array(nj * nk); + + // THE SIX POSSIBLE INTERMEDIATE PATHS THROUGH A TETRAHEDRON + + let S1 = [di , dj , dk , di , dj , dk ]; + let S2 = [dij, djk, dik, dik, dij, djk]; + + // THERE ARE 16 CASES TO CONSIDER + + let cases = [ [0 ], [1, 0,1,2,3], [1, 1,2,0,3], [2, 0,1,2,3], + [1, 2,3,0,1], [2, 0,2,3,1], [2, 1,2,0,3], [1, 3,1,2,0], + [1, 3,0,2,1], [2, 0,3,1,2], [2, 1,3,2,0], [1, 2,1,0,3], + [2, 2,3,0,1], [1, 1,3,0,2], [1, 0,3,2,1], [0 ], ]; + + // FOR EACH (Y,Z), DON'T DO ANY WORK OUTSIDE OF X RANGE WHERE SURFACE MIGHT BE + + for (k = 0 ; k < nk ; k++) + for (j = 0 ; j < nj ; j++, m++) { + let n0 = m * ni, n1 = n0 + ni - 1; + for (n = n0 ; n <= n1 && V[n] > 0 ; n++) ; + lo[m] = Math.max(0, n-1 - n0); + for (n = n1 ; n >= n0 && V[n] > 0 ; --n) ; + hi[m] = Math.min(ni-1, n+1 - n0); + } + + // FOR ALL Y AND Z IN THE VOLUME + + for (k = 0 ; k < nk - 1 ; k++) { + let i0, i1, m = k * nj, n1, s0, s1; + for (j = 0 ; j < nj - 1 ; j++, m++) { + i0 = Math.min(lo[m], lo[m+1], lo[m+ni], lo[m+1+ni]); + i1 = Math.max(hi[m], hi[m+1], hi[m+ni], hi[m+1+ni]); + + // GO THROUGH RANGE OF X WHERE THE SURFACE MIGHT BE (IE: WITH ANY POSITIVE VALUES) + + if (i0 <= i1) { + n = m * ni + i0; + n1 = m * ni + i1; + s0 = (V[n]>0) + (V[n+dj]>0) + (V[n+dk]>0) + (V[n+djk]>0); + for (i = i0 ; n <= n1 ; i++, n++, s0 = s1) { + + // FOR EACH CUBE + + s1 = (V[n+di]>0) + (V[n+dij]>0) + (V[n+dik]>0) + (V[n+dijk]>0); + if (s0 + s1 & 7) { + let C14 = (V[n] > 0) | (V[n+dijk] > 0) << 3; + + // CYCLE THROUGH THE SIX TETRAHEDRA THAT TILE THE CUBE + + for (let p = 0 ; p < 6 ; p++) { + let C = cases [ C14 | (V[n+S1[p]] > 0) << 1 | (V[n+S2[p]] > 0) << 2 ]; + + // FOR EACH TETRAHEDRON, OUTPUT EITHER ZERO, ONE OR TWO TRIANGLES + + if (C[0]) { // C[0] == number of triangles to be created. + S[1] = S1[p]; // assign 2nd and 3rd corners of simplex. + S[2] = S2[p]; + (C[0]==1 ? tri : quad)(S[C[1]], S[C[2]], S[C[3]], S[C[4]]); + } + } + } + } + } + } + } + + // MAKE SURE ALL TRIANGLE VERTICES ARE LISTED IN COUNTERCLOCKWISE ORDER + + for (let m = 0 ; m < T.length ; m += 3) { + let a = 3 * T[m], b = 3 * T[m+1], c = 3 * T[m+2], + n = Math.floor(ni*(P[a ]+1)/2) + + Math.floor(ni*(P[a+1]+1)/2) * dj + + Math.floor(ni*(P[a+2]+1)/2) * dk, + u = cross([P[b] - P[a], P[b+1] - P[a+1], P[b+2] - P[a+2]], + [P[c] - P[b], P[c+1] - P[b+1], P[c+2] - P[b+2]]), + v = [ V[n+1] - V[n], V[n+dj] - V[n], V[n+dk] - V[n] ]; + if (dot(u, v) < 0) { let tmp = T[m]; T[m] = T[m + 2]; T[m + 2] = tmp; } + } + + // RETURN POINTS AND TRIANGLES + + return [P, T]; + } + + // SAMPLE THE VOLUME + + let F = i => (i - n/2) / (n/2); + let volume = []; + + for (let k = 0 ; k < n ; k++) + for (let j = 0 ; j < n ; j++) + for (let i = 0 ; i < n ; i++) + volume.push(implicitFunction(F(i), F(j), F(k), args)); + + // FIND ALL VERTICES AND TRIANGLES IN THE VOLUME + + let VT = marchingTetrahedra(volume, n, n); + let V = VT[0]; + let T = VT[1]; + + // COMPUTE SURFACE NORMALS + + let N = new Array(V.length); + for (let i = 0 ; i < V.length ; i += 3) { + let x = V[i], y = V[i+1], z = V[i+2], e = .001, + f0 = implicitFunction(x ,y ,z , args), + fx = implicitFunction(x+e,y ,z , args), + fy = implicitFunction(x ,y+e,z , args), + fz = implicitFunction(x ,y ,z+e, args), + normal = normalize([f0-fx,f0-fy,f0-fz]); + for (let j = 0 ; j < 3 ; j++) + N[i+j] = normal[j]; + } + + // CONSTRUCT AND RETURN THE TRIANGLES MESH + + let mesh = []; + for (let i = 0; i < T.length; i += 3) { + let a = 3 * T[i ], + b = 3 * T[i + 1], + c = 3 * T[i + 2]; + mesh.push( id, V[a],V[a+1],V[a+2] , N[a],N[a+1],N[a+2] , + id, V[b],V[b+1],V[b+2] , N[b],N[b+1],N[b+2] , + id, V[c],V[c+1],V[c+2] , N[c],N[c+1],N[c+2] ); + } + return new Float32Array(mesh); +} + +let blob = (center, radius, x, y, z) => { + x -= center[0]; + y -= center[1]; + z -= center[2]; + return Math.max(0, 1 - .16 * (x*x + y*y + z*z) / (radius * radius)); +} + +var implicitFunction = (x,y,z,args) => { + let ret = -.5; + let x4 = _x => _x*_x*_x*_x; + args.forEach((paras, _)=>{ + const center = paras[0], + radius = paras[1]; + ret += x4(blob(center, radius, x, y, z)); + }); + return ret; +} + diff --git a/hw10/index.html b/hw10/index.html new file mode 100644 index 0000000..7946b0c --- /dev/null +++ b/hw10/index.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + Pikachu + +
+ + + +
+ + +
+ +
+ + What's new: +
    + +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + diff --git a/hw10/lib10.ext.js b/hw10/lib10.ext.js new file mode 100644 index 0000000..45a0ff6 --- /dev/null +++ b/hw10/lib10.ext.js @@ -0,0 +1,1363 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = [true, true], + presentation = false, + sRotation = matrix_identity(); +var state = new State(0), state1 = new State(1); +state1.death = (hitter)=>{if(!state1.dead){state1.defaultDeath();channel=5;show_magic = true;}}; +state.death = (hitter)=>{if(!state.dead){state.defaultDeath();channel=6;}}; +state1.hitID = 9; +var facing = 1; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +var show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +var Boxing = true; +var gamestart = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +var channel = 3, channel_disp = -1; +let difficulty_levels = { + hard: { + flex : .1, + freq : 25, + speed: .8, + th : 2., + startTimer : 15, + punchSpeed: 10, + original : 0 + }, + medium: { + flex : .07, + freq : 200, + speed: .4, + th : 1.8, + startTimer : 5, + punchSpeed: 5, + original : 1 + }, + easy: { + flex : .03, + freq : 400, + speed: .2, + th : 1.5, + startTimer : 4, + punchSpeed: 8, + original: 2 + } +}, dl_orig = [difficulty_levels.hard, difficulty_levels.medium, difficulty_levels.easy]; +var difficulty = deepcopy(difficulty_levels.medium); +state1.set_stepSize(difficulty.speed); +state1.turnStep = difficulty.flex; +state1.delta_l = [4, -5]; +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function (_) { + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + gamestart = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + state.delta_l = [0, 0]; + facing = 1; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 200) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + if (!(mouselastX == undefined || mouselastY == undefined) && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(matrix_rotateX(-dy / 60), sRotation); + sRotation = matrix_multiply(matrix_rotateY(-dx / 60), sRotation); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + if(slider)slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + state.figure_rot = (pi*(facing/2)); + else state.figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + state.running = 20; + state.dir_sdl = 0; + keyhold = true; + } + else + state.running = state.running > 5? state.running:5; + rebuild[0] = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } + if(e.code == 'KeyF'&&Boxing){ + state.start_punch(); + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat, i) => { + objects[i].push([obj, mat]); +}; +let clearObject = (i) => { + delete objects[i]; + objects[i] = []; +}; + + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state, i) => { + if(state.head === undefined) + { + state.head = head.slice(); + state.cylinderMesh = cylinderMesh.slice(); + state.cylinderMesh2 = cylinderMesh2.slice(); + state.cylinderMesh3 = cylinderMesh3.slice(); + if(state.idx == 0) + { + changeID(12, state.head); + changeID(6, state.cylinderMesh); + changeID(8, state.cylinderMesh2); + changeID(10, state.cylinderMesh3); + } + } + let lh,lf,rh,rf,dh,dl, head_rot = 0; + if(!state.dead){ + if (state.running === 0) + rebuild[i] = false; + res = state.next(); + lh=res.lh;lf=res.lf;rh=res.rh;rf=res.rf;dh=res.dh;dl=res.dl; + if(state.dir_sdl > 0){ + state.delta_l = plus(state.delta_l, const_multiply(dl, state.direction_l)); + state.dir_sdl -= dl; + state.running = 1; + }else + state.delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + if(state.idx === 0) + if(sqlen(state.delta_l[0] - 8, state.delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(state.delta_l[0] - 6, state.delta_l[1] - 6) < 5){ + donut_eaten = true; + state.life += 1; + pushStatus('Yum'); + } + state.delta_height = dh; + } else { + rh = lh = pi/2; + lf = rf = 0; + if(state.wiggle_t > 0){ + state.running = 1; + state.wiggle_t -= .025; + if(state.wiggle_t > 2) + head_rot = wiggle(state.wiggle_t-2); + } + } + clearObject(i); + const punch = state.dead?1:state.punch(); + let overall_rot = 0, overall_death_trans = [0,0]; + if(state.dead && state.death_t > 0){ + state.death_t -= .025; + const t = jCurve(1-state.death_t); + overall_rot = -t*pi/2; + overall_death_trans[0] = 2.3*sin(overall_rot); + overall_death_trans[1] = -2.3*(1-cos(overall_rot)); + rebuild[state.idx] = true; + }else if (state.dead){ + overall_rot = -pi/2; + overall_death_trans[0] = -2.3; + overall_death_trans[1] = -2.3; + } + M.save(); + M.translate(0, overall_death_trans[0], overall_death_trans[1]); + M.rotateX(overall_rot); + M.save(); + M.translate(0, 1, 0); + M.rotateY(head_rot); + addObject(state.head, M.value(),i); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX((pi / 4)*punch); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX((-0.53 * pi)*punch); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(state.cylinderMesh, M.value(),i); + M.restore(); + + M.restore(); + if (state.running < 0) + rebuild[i] = false +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 10; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + for(let i = 0; i < 7; ++ i){ + sakura[i] = new Float32Array(thicken_contour(.8, sakura[i])); + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + if(state.life >=.5) + { + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + setM(M.value()); + for(let l = 0; l < 5; ++l) + drawMesh(sakura[l],gl.TRIANGLE_STRIP); + M.restore(); + } + if(state.life >= 1.5 ) + { + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + setM(M.value()); + for(let l = 0; l < 5; ++l) + drawMesh(sakura[l],gl.TRIANGLE_STRIP); + M.restore(); + } + if (state.life > 2.5) + { + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 0; l < 5; ++l) + drawMesh(new Float32Array(sakura[l]),gl.TRIANGLE_STRIP); + M.restore(); + } +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if(channel_disp == 4 && i == 0){ + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + setM(m); + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + if(!shader1_inited){ + //initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + drawMesh(obj[0]); + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; + } + else + { + setM(m); + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let initialize=(lv) => { + difficulty = deepcopy(lv); + state1.set_stepSize(difficulty.speed); + state1.turnStep = difficulty.flex; + state1.delta_l = [4, -5]; + channel = 3;state.initialize();state1.initialize();reset.onclick();state1.delta_l = [4,-5]; +}; +let tv_ctrl = new Button(()=>{initialize(difficulty_levels.hard);}); +let tv_ctrl1 = new Button(()=>{initialize(difficulty_levels.medium);}); +let tv_ctrl2 = new Button(()=>{initialize(difficulty_levels.easy);}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Difficulty Level: Hard.', 8); + setup_control(tv_ctrl1, 'Difficulty Level: Medium.', 9); + setup_control(tv_ctrl2, 'Difficulty Level: Easy.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +var iMesh = [], tMesh, tMesh2, eMesh; +function createEar(){ + M.save(); + M.scale(.5,.5,.35); + M.rotateX(pi/2); + eMesh = createMeshFromSpline(19, 32, 4, 1, .0); + transform_mesh(M.value(), eMesh); + M.restore(); +} +function createTail(){ + let tail = [ + 2.37,1.48, + 109.39,18.21, + 96.13,88.3, + + 96.13,88.3, + 126.44,94.93, + 118.86,135.21, + + 118.86,135.21, + 148.54,135.21, + 146.96,164.7, + + 146.96,164.7, + 172.85,168.17, + 169.38,184.27, + + 169.38,184.27, + 124.87,179.22, + 131.34,148.2, + + 131.34,148.2, + 99.92, 148.2, + 105.66,115.13, + + 105.66,115.13, + 63.62,117.02, + 75.2,68.93, + + 75.2,68.93, + 12.78,62.41, + 2.37,1.48, + + 96.13,88.3, + 118.86,135.21, + 75.2,68.93, + + ], tx_max = -1000, tx_min = 1000, ty_max= -1000, ty_min = 1000; + for(let i = 0; i < tail.length; i += 2){ + tx_max = tx_max > tail[i]?tx_max:tail[i]; + tx_min = tx_min < tail[i]?tx_min:tail[i]; + ty_max = Math.max(ty_max, tail[i+1]); + ty_min = Math.min(ty_min, tail[i+1]); + } + let tx_scale = tx_max - tx_min, ty_scale = ty_max - ty_min; + let t_scale = Math.max(tx_scale, ty_scale); + for(let i = 0; i < tail.length; i += 2){ + tail[i] = (tail[i] - tx_min) / t_scale; + tail[i + 1] = (tail[i + 1] - ty_min) / t_scale; + } + let l = tail.length / 2; + tMesh = new Float32Array(2*l*7), tMesh2 = new Float32Array(2*l*7); + for(let i = 0; i < l; ++ i) + { + tMesh[i*7] = 1; + tMesh[i*7+1] = tail[i*2]; + tMesh[i*7+2] = tail[i*2 + 1]; + tMesh[i*7+3] = .03; + tMesh[i*7+4] = 0; + tMesh[i*7+5] = 0; + tMesh[i*7+6] = -1; + + tMesh[l*7+i*7] = 1; + tMesh[l*7+i*7+1] = tail[i*2]; + tMesh[l*7+i*7+2] = tail[i*2 + 1]; + tMesh[l*7+i*7+3] = -.03; + tMesh[l*7+i*7+4] = 0; + tMesh[l*7+i*7+5] = 0; + tMesh[l*7+i*7+6] = 1; + + tMesh2[i*14] = 1; + tMesh2[i*14+1] = tail[i*2]; + tMesh2[i*14+2] = tail[i*2 + 1]; + tMesh2[i*14+3] = .03; + tMesh2[i*14+4] = 0; + tMesh2[i*14+5] = 0; + tMesh2[i*14+6] = -1; + tMesh2[i*14 + 7] = 1; + tMesh2[i*14+8] = tail[i*2]; + tMesh2[i*14+9] = tail[i*2 + 1]; + tMesh2[i*14+10] = -.03; + tMesh2[i*14+11] = 0; + tMesh2[i*14+12] = 0; + tMesh2[i*14+13] = 1; + } +} +function updateImplicitShapes(){ + let divs = 100; + let params = [ + + [ + [[-.3,0.13,.43], .07], + [[-.3,0.1,.4], .09], + [[-.3,-.08,.22], .09], + [[-.25,0,.2], .14], + [[-.25,-.1,.15], .14], + [[-.2,-0.05,.15], .15],[[-.2, .15, -0.05], .15], + [[-.2,-.08,.12], .15],[[-.20, .12, -0.08], .15], + ],[ + [[-.3, .43, 0.12], .07], + [[-.3, .4, 0.1], .09], + [[-.3, .22, -0.08], .09], + [[-.25, .2, 0], .14], + [[-.25, .15, -0.1], .14], + [[-.2,-0.05,.15], .15],[[-.2, .15, -0.05], .15], + [[-.2, .15, -0.05], .15],[[-.20, .12, -0.08], .15], + ], + [ + [[-.2,-.21, .09], .2],[[-.2,.09, -.21], .2], + [[-.15,-.08, -.08], .2], + [[-.0,-0.12,0.12],.165],[[-.0,.12,-.12],.165], + [[.05,-0.14,.01],.17],[[.05,0.01,-.14],.17], + + [[.15,-0.1,0.04],.17],[[.15,.04,-.1],.17], + + [[.45,0.04,0.04],.2], + // [[.5,.06, .06], .18], + ], //body + [ + [[.35,0.06,0.06],.1], + [[.45,0.18,0.],.16],[[.45,0.,0.18],.16], + // [[.5,0.1,0.1],.1], + [[.55,0.0,0.0],.16], + [[.5,0.16,0.16],.1], + [[.65,0.12,0.0],.16],[[.65,0.0,0.12],.16] + + ], //head + ]; + params.forEach((p, i)=>{ + iMesh[i] = implicitSurfaceTriangleMesh(implicitFunction, divs, + p,1); + }); + } +function animate(gl) { + buildsplines &&= build_splines();// || setTVCtrl(); + if(iMesh.length == 0) updateImplicitShapes(); + if(tMesh === undefined) createTail(); + if(eMesh === undefined) createEar(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + gl.enable(gl.DEPTH_TEST); + let orig_rot = sRotation.slice(); + sRotation = matrix_multiply(matrix_translate(-.3, 0,0), orig_rot); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(state.delta_l[0], -state.delta_height, state.delta_l[1]); + M.rotateY(state.curr_figure_rot); + overall_trans = M.value(); + M.restore(); + + + M.save(); + setM(matrix_multiply(sRotation, M.value())); + iMesh.forEach( (mesh, _)=> + { + drawMesh(mesh); + }); + + M.restore(); + + + M.save(); + M.scale(.8); + M.translate(.1, -.62, -.62); + M.rotateX(.8); + M.rotateY(pi); + M.rotateZ(-.1); + M.translate(-.5, -.5,0); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(tMesh, gl.TRIANGLES); + drawMesh(tMesh2); + + M.restore(); + + M.save(); + M.translate(1., .1, -.15); + M.rotateY(-.5*pi); + M.rotateX(-.5*pi); + + setM(matrix_multiply(sRotation, M.value())); + drawMesh(eMesh); + M.restore(); + M.save(); + M.translate(.9, -.1, .35); + M.rotateY(-1.*pi); + M.rotateX(-.5*pi); + M.rotateZ(.2*pi); + + setM(matrix_multiply(sRotation, M.value())); + drawMesh(eMesh); + M.restore(); + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + sRotation = orig_rot; +} diff --git a/hw10/lib10.header.js b/hw10/lib10.header.js new file mode 100644 index 0000000..9b38c3e --- /dev/null +++ b/hw10/lib10.header.js @@ -0,0 +1,809 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +var states = []; +function deepcopy(obj) { + return JSON.parse(JSON.stringify(obj)); +} +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); + +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +let scale = (v, s) => [ s * v[0] , s * v[1] , s * v[2] ]; +let norm = v => Math.sqrt(dot(v,v)); + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + +let uvToCone = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta)*(1-v); + let y = Math.sin(theta)*(1-v); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else if (!state.dead){ + if(clicked || state.running<=0) + { + state.turn(P, clicked); + } + //if(state.figure_rot < 0) state.figure_rot += pi; + //updateStatus([state.figure_rot, state.direction_l[0]/state.direction_l[1]]); + if(clicked) + { + state.walk(); + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); + +let transform_mesh = (m,arr) => { + for(let i = 0; i < arr.length; i+=7){ + const mod = dot_xyz( + matrix_multiply(m, [arr[i+1], arr[i+2], arr[i+3], 1])); + const mod_nor = normalize(dot_xyz( + matrix_multiply(m, [arr[i+4], arr[i+5], arr[i+6], 0]))); + arr[i+1] = mod[0]; + arr[i+2] = mod[1]; + arr[i+3] = mod[2]; + arr[i+4] = mod_nor[0]; + arr[i+5] = mod_nor[1]; + arr[i+6] = mod_nor[2]; + } +} + +let thicken_contour = (thickness, contour) =>{ + let res = []; + let inner_contour = contour.slice(); + transform_mesh(matrix_scale(thickness), inner_contour); + for(let i = 0; i < contour.length - 7; i+=7){ + for (let j = 0; j < 7; ++ j) + res.push(contour[i + j]); + res.push(contour[0]); + for (let j = 1; j < 7; ++ j) + res.push((inner_contour[i + j]+inner_contour[i+7+j])/2); + } + for (let j = 0; j < 7; ++ j) + res.push(contour[contour.length - 7 + j]); + res.push(contour[0]); + for (let j = 1; j < 7; ++ j) + res.push((inner_contour[contour.length - 7 + j]+inner_contour[j])/2); + for (let j = 0; j < 7; ++ j) + res.push(contour[j]); + return res; +} + +let sqlen = (a, b) => { return a*a + b*b; } diff --git a/hw10/lib10.js b/hw10/lib10.js new file mode 100644 index 0000000..f8636fd --- /dev/null +++ b/hw10/lib10.js @@ -0,0 +1,266 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', '#define _NDEBUG\n','float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }' +].join('\n'); +var ns = 5, + cns = 5; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = ''; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + +} +function initVideoTexture(gl, i) { + +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + //initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0., .0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} \ No newline at end of file diff --git a/hw10/paths.txt b/hw10/paths.txt new file mode 100644 index 0000000..1a1b602 --- /dev/null +++ b/hw10/paths.txt @@ -0,0 +1,107 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N +M +M7.97,1.32c-1.2,2.4-2.58,5.53-3.81,9.3c-1.89,5.82-2.51,10.42-2.64,11.39c-0.28,2.2-0.69,6.44,0.73,18.39c0.57,4.79,1.47,11.13,2.94,18.58c0.94,0.01,1.87,0.03,2.81,0.04z +N \ No newline at end of file diff --git a/hw10/shader.frag b/hw10/shader.frag new file mode 100644 index 0000000..352e623 --- /dev/null +++ b/hw10/shader.frag @@ -0,0 +1,64 @@ + +#ifndef _NDEBUG + precision highp float; + const int ns = 5; + const int cns = 5; + float noise(vec3 v){return 1.;} +#endif +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + float alp = 2.*abs(.49999 - fract(id+.00001)); + if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id <-.5) {color = vec3(0.,1.,0.2034);} + else if(id < 1.5) {color = vec3(1.0000, 0.659, 0.0384);sp = .2; df=.4; amb = .7, ex = 1.;l = color = vec3(1.0000, 0.7, 0.04);} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) { + color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color; + if(texPos.y > .3) + color = vec3(1.,1.,1.); + } + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;} + else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;} + else if (id < 18.5) { + color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.; + vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(.8 * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + else if (id < 19.5) {color = vec3(.4,.4,.4);sp = .0; df=.0; amb = 1., ex = 1.,alp=1.;} + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alp); +} diff --git a/hw10/shader.vert b/hw10/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/hw10/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/hw2/1.jpg b/hw2/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw2/1.jpg differ diff --git a/hw2/2.jpg b/hw2/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw2/2.jpg differ diff --git a/hw2/3.jpg b/hw2/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw2/3.jpg differ diff --git a/hw2/4.jpg b/hw2/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw2/4.jpg differ diff --git a/hw2/8081_earthmap10k.jpg b/hw2/8081_earthmap10k.jpg new file mode 100644 index 0000000..812458c Binary files /dev/null and b/hw2/8081_earthmap10k.jpg differ diff --git a/hw2/README.md b/hw2/README.md new file mode 100644 index 0000000..0262c7f --- /dev/null +++ b/hw2/README.md @@ -0,0 +1,2 @@ +# graphics_hw2 +[link](https://billsun.dev/graphics/hw2) diff --git a/hw2/RTXoff.svg b/hw2/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw2/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw2/RTXon.svg b/hw2/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw2/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw2/index.html b/hw2/index.html new file mode 100644 index 0000000..37b8809 --- /dev/null +++ b/hw2/index.html @@ -0,0 +1,350 @@ + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw2/lib2.js b/hw2/lib2.js new file mode 100644 index 0000000..2dde401 --- /dev/null +++ b/hw2/lib2.js @@ -0,0 +1,185 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); +let ns = 4, cns = 4; +fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; +let fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; // IS THIS THE FIREFOX BROWSER? +let errorMsg = ''; +// +// Initialize a texture and load an image. +// When the image finished loading copy it into the texture. +// +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); + } +let texture = [], gl, program; +let textures = []; +let lock = false; +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) + { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + // Because images have to be downloaded over the internet + // they might take a moment until they are ready. + // Until then put a single pixel in the texture so we can + // use it immediately. When the image has finished downloading + // we'll update the texture with the contents of the image. + + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + // WebGL1 has different requirements for power of 2 images + // vs non power of 2 images so check if the image is a + // power of 2 in both dimensions. + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + // Yes, it's a power of 2. Generate mips. + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + // No, it's not a power of 2. Turn off mips and set + // wrapping to clamp to edge + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = '
'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + for(let i = 0; i < ns; ++i){ + loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading. + textures[i] = i; + } + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); + setUniform('4f', 'rot', Math.cos(mousedx), Math.sin(mousedx), Math.cos(mousedy), Math.sin(mousedz)); + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +function setUniform(type, name, a, b, c, d, e, f) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); +} + diff --git a/hw2/shader.frag b/hw2/shader.frag new file mode 100644 index 0000000..8f853c6 --- /dev/null +++ b/hw2/shader.frag @@ -0,0 +1,252 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform int flags; +//FLAGS 0-TEX, 1-RT, 2-MOVED, 3-FLASH, 4-TEX_ROT, 5-CLOUD + +uniform vec4 rot; //ROTATION VALUES USED TO CALCULATE TRANSFORMATION MATRIX +//rot=[cosx, sinx, cosy, siny], x, y BING ROTATED ANGLE +uniform float dFL; //DELTA on FOCAL LENGTH +uniform vec3 fDir;//Flash light direction +varying vec3 vPos;// -1 < vPos.x < +1 +// -1 < vPos.y < +1 +// vPos.z == 0 + +float fl=3.;//ORIGINAL FOCAL LENGTH +const float pi=3.14159265359; +const float _2pi=2.*pi; +const int n_ref=5; //<<=======***********************MAX NUMBER OF RAY TRACING RECURRSIONS. INCREASE IT IF YOUR GRAPHICS CARD CAN HANDLE.**************************** +//const int ns=4; ns is added from .js +vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +vec3 Ambient[ns]; +vec3 Diffuse[ns]; +vec4 Specular[ns]; +float ks[ns]; +struct Sphere{ //UPDATED SPHERE STRUCTURE THAT SUPPORTS TRANSPARENCY.(UNUSED) + vec4 Pos; + vec3 Ambient; + vec3 Diffuse; + vec4 Specular; + int textureid; + float ks, kt; +}; +struct RT{ //STACK FOR RECURSIVE RAY TRACING. + vec3 color; + float ks; + // vec3 colorr; + // float kt; + // vec3 ptr; + // vec3 normal; +} stack[n_ref]; + +bool getflag(int flag,int bit){ + float shifted = float(int(float(flag)/ pow(2.,float(bit)))); + return fract(shifted/2.)>0.; +} +float clampv(float val,float l,float h){ + return valh?h:val; +} +void main(){ + //////////////////////////////////////////////// + // + // HERE, FOR YOUR HOMEWORK, YOU CAN WRITE ANY + // CODE YOU LIKDEFINE A COLOR FOR THIS FRAGMENT. + + // LIGHT DIRECTION AND COLOR + //* I USED LDir AS LIGHT POSITION + //* I NORMALIZED IT AFTER GETTING THE + //* DIRECTION BY SUBTRACTING IT FROM THE POINT + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + + // SPHERE + Sph[3]=vec4(.9*sin(uTime*.4),0.,.9*cos(uTime*.4),.25); + Sph[2]=vec4(.22*sin(uTime*1.2),0.05,.22*cos(uTime*1.2),.02); + Sph[0]=vec4(.45*sin(uTime),0.05*cos(uTime + 1.),.45*cos(uTime),.1); + Sph[1]=vec4(0.,0.,0.,.15); + + // SURFACE REFLECTANCE PROPERTIES, can be transferred from .js + Ambient[3]=vec3(.1,.1,.1);// r,g,b + Diffuse[3]=vec3(.71,.71,.71);// r,g,b + Specular[3]=vec4(.71,.71,.71,10.);// r,g,b,power + Ambient[2]=vec3(.1,.05,.05);// r,g,b + Diffuse[2]=vec3(.71,.71,.71);// r,g,b + Specular[2]=vec4(.71,.71,.71,10.);// r,g,b,power + Ambient[1]=vec3(.1,.05,.05);// r,g,b + Diffuse[1]=vec3(1.,.5,.5);// r,g,b + Specular[1]=vec4(1.,.5,.5,10.);// r,g,b,power + + Ambient[0]=vec3(.05,.05,.1);// r,g,b + Diffuse[0]=vec3(.5,.5,1.);// r,g,b + Specular[0]=vec4(1.,.5,.5,20.);// r,g,b,power + ks[0] = 0.25; + ks[1] = 0.1; + ks[2] = 0.3; + ks[3] = 0.05; + // INITIALIZE TO A BACKGROUND COLOR + vec3 color=vec3(.2, .3, .5); + float ca=rot.x, sa = rot.y, cb=rot.z, sb=rot.w; + mat3 transformation, invTr;//Transformation matrix for viewpoint. + transformation[0] = vec3(ca, sb*sa, sa*cb);//because the matrices are all the same, + transformation[1] = vec3(0, cb, -sb);//We don't need to calculate it for every pixel + transformation[2] = vec3(-sa,ca*sb,ca*cb);//So, we get it from the CPU + invTr[0] = vec3(ca, 0, -sa);//it's inverse, to calculate texture mapping. + invTr[1] = vec3(sa*sb, cb, ca*sb); + invTr[2] = vec3(cb*sa, -sb, ca*cb); + vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1); + // COMPUTE THE RAY ORIGIN AND DIRECTION + vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0; + vec3 W=normalize(trPos-V); + // RAY TRACE TO ALL OBJECTS IN THE SCENE + bool rtxoff = getflag(flags, 1), + showtexture = !getflag(flags,0), + moved = getflag(flags,2)//, +// flash = true;//getflag(flags, 3) + ;//get flags. + // bool hit = false; + int cnt_ref = n_ref; + for(int j=0;j0.){ + float t=-B-sqrt(D); + if(t > 0. && t < tMin){ + tMin = t; //This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + // IF RAY HITS SPHERE + if(iMin >= 0){ + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) //* Because GLSL doesn't support non-const index, + { //* we have to get Sph[iMin], uSampler[iMin], etc. this way + //*Good old TEXTURE MAPPING from hw1 + vec3 tex_sph = (S-Sph[i].xyz); + if(moved) + tex_sph=invTr*tex_sph;//* transform the sphere to original place if view point moved; + //* This is super expensive! plus it's in the inner loop!! + //* We added a flag to disable it when the viewport is not moved! + float R=Sph[i].w; + float tex_x=acos(abs(tex_sph.x)/sqrt(R*R-tex_sph.y*tex_sph.y)); + if(tex_sph.x>0.) + tex_x=pi-tex_x; + tex_x*=1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=tex_x+float(uTime); + float quo=float(int(tex_x/_2pi)); + tex_x=tex_x/_2pi -quo; + vec3 texture_color; + if(showtexture) + texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz; + else texture_color = foregroundColor; + vec3 N=normalize(S-Sph[i].xyz); + //*DIRECTIONS ARE NORMALIZED TO GET THE CORRECT PHONG LIGHTING + vec3 realLDir=normalize(LDir-S); + color=( + Ambient[i] + +Diffuse[i]*max(0.,dot(N,realLDir))*LCol + )*texture_color + ; + // + SPECULAR COMPONENT GOES HERE + if(rtxoff || j == n_ref - 1) //if it's the last ray + color += sqrt(float(j+1)) * Specular[i].xyz*pow(max(0., + dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w); + //*Pushing current color and ks into stack. + //*suppose ks is 0.15 for all spheres, we can + //*of course support different ks, kt for different object + //*but I didn't have time to do that, just a proof of concept, + //*I defined the new sphere structure that could be used in the future. + stack[j] = RT(color, ks[i]); + V = S; //*NEXT RAY SHOOTING FROM THE INTERSECTION POINT + // if(flash && j == 0){ + // V0 = V - V0; + // hit = true; + // } + W = -normalize(2. * dot(N, W) * N - W);//*W is the next direction of the next ray. + + break;// this is only the innerloop, RT is still going! + } + } + else { + // TO SIMIPIFY THINGS UP, I'LL ASSUME THAT EVERYTHING + // IS INSIDE THE BOUNDING BOX [(-1,-1,-1), (1,1,1)] + // AND THERE'S A FLOOR at [y = -1] THE NORMAL IS (0,1,0) + // Because We assumed that the light always hit sphere first, + // It will have wirld behavior when you rotate the scene upsidedown. + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0.&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=( + 0.5 //ambient for ground + +0.5*max(0.,realLDir.y)*LCol //diffusion for ground + )*groundColor + ; + // + SPECULAR COMPONENT GOES HERE + if(rtxoff || j == n_ref - 1) + color += sqrt(float(j+1))*groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + stack[j] = RT(color, 0.15); //ks of ground is 0.1 + V = S; //Same as above, trace again from S, dir = reflect(W, N). + // if(flash && j == 0){ + // V0 = W; + // hit = true; + // } + W = vec3(W.x, -W.y, W.z); + } + else{ + if(j > 0) + { + // If the light bounces away! The color of it is calculated by + stack[j] = RT(sqrt(float(j+1))*vec3(4.,4.,4)*pow(max(0.,dot(W, normalize(LDir - V))), 10.), 0.); + cnt_ref = j + 1; + } + else //If the light hits the void in the first place, it's just black! + cnt_ref = j;//j is always 0 in this case. + break; //The light is shooting into the void, let's stop RT. + } + } + // RTX off + if(rtxoff) + break; + } + if(rtxoff) + color = stack[0].color; + else + { + color = vec3(0,0,0); + float currks = 1.; + for(int i = 0; i < n_ref; ++i) + { + if(i >= cnt_ref)//same trick to use bounded non-const on indexes + { + color += currks * stack[i - 1].color; //if there're less than n_ref rays, e.g. ray go to the void. + break; + } + color += currks *(1.-stack[i].ks) * stack[i].color; + currks *= stack[i].ks; + } + if(n_ref == cnt_ref) + color += currks * stack[n_ref - 1].color; + } + // APPLY GAMMA CORRECTION AND SET THE PIXEL COLOR. + + gl_FragColor=vec4(sqrt(color),1.); +} + \ No newline at end of file diff --git a/hw3/.gitignore b/hw3/.gitignore new file mode 100644 index 0000000..96f05f1 --- /dev/null +++ b/hw3/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +old/ +new/ diff --git a/hw3/1.jpg b/hw3/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw3/1.jpg differ diff --git a/hw3/2.jpg b/hw3/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw3/2.jpg differ diff --git a/hw3/3.jpg b/hw3/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw3/3.jpg differ diff --git a/hw3/4.jpg b/hw3/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw3/4.jpg differ diff --git a/hw3/5.jpg b/hw3/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/hw3/5.jpg differ diff --git a/hw3/README.md b/hw3/README.md new file mode 100644 index 0000000..e3156be --- /dev/null +++ b/hw3/README.md @@ -0,0 +1,2 @@ +# graphics_hw3 +[link](https://billsun.dev/graphics/hw3) diff --git a/hw3/RTXoff.svg b/hw3/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw3/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw3/RTXon.svg b/hw3/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw3/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw3/_5.jpg b/hw3/_5.jpg new file mode 100644 index 0000000..a133a47 Binary files /dev/null and b/hw3/_5.jpg differ diff --git a/hw3/img.jpg b/hw3/img.jpg new file mode 100644 index 0000000..d5be235 Binary files /dev/null and b/hw3/img.jpg differ diff --git a/hw3/index.html b/hw3/index.html new file mode 100644 index 0000000..cab506a --- /dev/null +++ b/hw3/index.html @@ -0,0 +1,341 @@ + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw3/lib3.js b/hw3/lib3.js new file mode 100644 index 0000000..250eac3 --- /dev/null +++ b/hw3/lib3.js @@ -0,0 +1,207 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); +let ns = 5, cns = 3; +fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; +let fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; // IS THIS THE FIREFOX BROWSER? +let errorMsg = ''; +// +// Initialize a texture and load an image. +// When the image finished loading copy it into the texture. +// +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); + } +let texture = [], gl, program; +let textures = []; +let lock = false; +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) + { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + // Because images have to be downloaded over the internet + // they might take a moment until they are ready. + // Until then put a single pixel in the texture so we can + // use it immediately. When the image has finished downloading + // we'll update the texture with the contents of the image. + + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + // WebGL1 has different requirements for power of 2 images + // vs non power of 2 images so check if the image is a + // power of 2 in both dimensions. + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + // Yes, it's a power of 2. Generate mips. + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + // No, it's not a power of 2. Turn off mips and set + // wrapping to clamp to edge + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = '
'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + for(let i = 0; i < ns; ++i){ + loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading. + textures[i] = i; + } + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); + let cx = Math.cos(mousedx), cy = Math.cos(mousedy), sx = Math.sin(mousedx), sy = Math.sin(mousedy); + setUniform('Matrix3fv', 'transformation', false, [cx, sy*sx, sx*cy, 0, cy, -sy, -sx, cx*sy, cx*cy]); + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, // + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + var offset = 0; + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +function setUniform(type, name, a, b, c, d, e, f) { + if(gl) + { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + diff --git a/hw3/linus-torvalds.jpg copy.jpg b/hw3/linus-torvalds.jpg copy.jpg new file mode 100644 index 0000000..8ea1db1 Binary files /dev/null and b/hw3/linus-torvalds.jpg copy.jpg differ diff --git a/hw3/pjsk.mp4 b/hw3/pjsk.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/hw3/pjsk.mp4 differ diff --git a/hw3/shader.bak.frag b/hw3/shader.bak.frag new file mode 100644 index 0000000..d17f49f --- /dev/null +++ b/hw3/shader.bak.frag @@ -0,0 +1,264 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform int flags; +//FLAGS 0-TEX, 1-RT, 2-MOVED, 3-FLASH, 4-TEX_ROT, 5-CLOUD + +uniform vec4 rot; //ROTATION VALUES USED TO CALCULATE TRANSFORMATION MATRIX +//rot=[cosx, sinx, cosy, siny], x, y BING ROTATED ANGLE +uniform float dFL; //DELTA on FOCAL LENGTH +uniform vec3 fDir;//Flash light direction +varying vec3 vPos;// -1 < vPos.x < +1 +// -1 < vPos.y < +1 +// vPos.z == 0 + +float fl=3.;//ORIGINAL FOCAL LENGTH +const float pi=3.14159265359; +const float _2pi=2.*pi; +const int n_ref=9; //2^(hits) + 1 because each hit now spawn 2 rays. +//const int ns=4; ns is added from .js +vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +vec3 Ambient[ns]; +vec3 Diffuse[ns]; +vec4 Specular[ns]; +float ks[ns]; +float kr[ns]; +float kf[ns], kf_air = 1.000293; + +int type[ns];//Nested objects +// 1-sphere, 2-compost sphere, 3- +struct Object{ //UPDATED SPHERE STRUCTURE THAT SUPPORTS TRANSPARENCY.(UNUSED) + vec4 Pos; + vec3 Ambient; + vec3 Diffuse; + vec4 Specular; + int textureid; + float ks, kt, kr; +}; +struct RT{ //STACK FOR RECURSIVE RAY TRACING. + vec3 color; + float ks; +} ; + +vec3 scolor = vec3(0,0,0); //Actually 2^n_ref +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} lastRay[n_ref/2]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +bool getflag(int flag,int bit){ + int shifted = flag / int(pow(2.,float(bit))); + return modulo2(shifted); +} +float clampv(float val,float l,float h){ + return valh?h:val; +} +void main(){ + + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + + // SPHERE + Sph[3]=vec4(.9*sin(uTime*.4),0.,.9*cos(uTime*.4),.25); + Sph[2]=vec4(.22*sin(uTime*1.2),0.05,.22*cos(uTime*1.2),.02); + Sph[0]=vec4(.45*sin(uTime),0.05*cos(uTime + 1.),.45*cos(uTime),.1); + Sph[1]=vec4(0.,0.,0.,.15); + + // SURFACE REFLECTANCE PROPERTIES, can be transferred from .js + Ambient[3]=vec3(.1,.1,.1);// r,g,b + Diffuse[3]=vec3(.71,.71,.71);// r,g,b + Specular[3]=vec4(.71,.71,.71,10.);// r,g,b,power + Ambient[2]=vec3(.1,.05,.05);// r,g,b + Diffuse[2]=vec3(.71,.71,.71);// r,g,b + Specular[2]=vec4(.71,.71,.71,10.);// r,g,b,power + Ambient[1]=vec3(.1,.05,.05);// r,g,b + Diffuse[1]=vec3(1.,.5,.5);// r,g,b + Specular[1]=vec4(1.,.5,.5,10.);// r,g,b,power + + Ambient[0]=vec3(.05,.05,.1);// r,g,b + Diffuse[0]=vec3(.5,.5,1.);// r,g,b + Specular[0]=vec4(1.,.5,.5,20.);// r,g,b,power + ks[0] = 0.25; + ks[1] = 0.1; + ks[2] = 0.3; + ks[3] = 0.05; + kr[0] = 0.25; + kr[1] = 0.1; + kr[2] = 0.3; + kr[3] = 0.05; + kf[0] = 1.3; + kf[1] = 1.3; //Water + kf[2] = 1.5; //Glass + kf[3] = 1.; //Vacuum + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + float ca=rot.x, sa = rot.y, cb=rot.z, sb=rot.w; + mat3 transformation, invTr;//Transformation matrix for viewpoint. + transformation[0] = vec3(ca, sb*sa, sa*cb);//because the matrices are all the same, + transformation[1] = vec3(0, cb, -sb);//We don't need to calculate it for every pixel + transformation[2] = vec3(-sa,ca*sb,ca*cb);//So, we get it from the CPU + invTr[0] = vec3(ca, 0, -sa);//it's inverse, to calculate texture mapping. + invTr[1] = vec3(sa*sb, cb, ca*sb); + invTr[2] = vec3(cb*sa, -sb, ca*cb); + vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1); + vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0; + vec3 W=normalize(trPos-V); + bool rtxoff = getflag(flags, 1), + showtexture = !getflag(flags,0), + moved = getflag(flags,2); + int cnt_ref = n_ref; + float currentK = 1.; + for(int j=0;j 0){ + Ray currR = lastRay[(j-1)/2]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.0000001 || currentK <= 0.0000001) + continue; // We make it terminate w/ kf=0 + V = currR.V; + W = currR.W; + } + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.00001 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.00001){ + t = -(t + 2.*B); + if(t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + // IF RAY HITS SPHERE + if(iMin >= 0){ + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 tex_sph = (S-Sph[i].xyz); + if(moved) + tex_sph=invTr*tex_sph; + float R=Sph[i].w; + float tex_x=acos(abs(tex_sph.x)/sqrt(R*R-tex_sph.y*tex_sph.y)); + if(tex_sph.x>0.) + tex_x=pi-tex_x; + tex_x*=1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=tex_x+float(uTime); + float quo=float(int(tex_x/_2pi)); + tex_x=tex_x/_2pi - quo; + vec3 texture_color; + if(showtexture) + texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz; + else texture_color = foregroundColor; + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + color=( + Ambient[i] + +Diffuse[i]*max(0.,dot(N,realLDir))*LCol + )*texture_color + ; + if(rtxoff || j >= n_ref/2) //if it's the last hit + { + color += sqrt(float(j+1)) * Specular[i].xyz*pow(max(0., + dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w); + scolor += color * currentK; + } + else{ + lastRay[2 * j + 1] = Ray(S, (-(2. * dot(N, W) * N - W)), currKf, currentK * ks[i]); //reflection + float ita = currKf/kf[i]; + float c1 = dot(N, W); + float c2 = sqrt(1.-ita*ita*(1.-c1*c1)); + lastRay[2 * j + 2] = Ray(S, normalize(ita*W + (ita*c1 - c2)*N), kf[i], currentK * kr[i]); //refraction + scolor += currentK*(1. - ks[i] - kr[i]) * color;//stack[j] = RT(color, currentK*(1. - ks[i] - kr[i]) * color; + } + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + if(t >= 0.&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=( + 0.5 //ambient for ground + +0.5*max(0.,realLDir.y)*LCol //diffusion for ground + )*groundColor + ; + // + SPECULAR COMPONENT GOES HERE + if(rtxoff || j == n_ref - 1) + { + color += sqrt(float(j+1))*groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + //stack[j] = RT(color, currentK); //ks of ground is 0.15 + scolor += currentK * color; + } + else + { + lastRay[2 * j + 1] = Ray(S, vec3(W.x, -W.y, W.z), currKf, currentK * 0.15); //reflection + //stack[j] = RT(color, currentK * (1.-0.15)); //ks of ground is 0.15 + scolor += (currentK*.85)*color; + } + //lastRay[2 * j + 2] = Ray(S, vec3(0,0,0), 0.); //refraction + } + else{ + if(j > 0) + { + // If the light bounces away! The color of it is calculated by + //stack[j] = RT(sqrt(float(j+1))*vec3(4.,4.,4)*pow(max(0.,dot(W, normalize(LDir - V))), 10.), currentK); + scolor += currentK * sqrt(float(j+1)*pow(max(0.,dot(W, normalize(LDir - V))), 10.)) * vec3(4.,4.,4); + //cnt_ref = j + 1; + } + //else //If the light hits the void in the first place, it's just black! + //cnt_ref = j;//j is always 0 in this case. + break; //The light is shooting into the void, let's stop RT. + } + } + if(rtxoff) + break; + } + if(!rtxoff) + { + color = scolor; + // color = vec3(0,0,0); + // //float currks = 1.; + // for(int i = 0; i < n_ref; ++i) + // { + // //if(i >= cnt_ref)//same trick to use bounded non-const on indexes + // // { + // // color += currks * stack[i - 1].color; //if there're less than n_ref rays, e.g. ray go to the void. + // // break; + // // } + // if(stack[i].ks <0.0000001) + // continue; + // color += stack[i].ks * stack[i].color;//currks *(1.-stack[i].ks) * stack[i].color; + // //currks *= stack[i].ks; + // } + // // if(n_ref == cnt_ref) + // // color += currks * stack[n_ref - 1].color; + } + gl_FragColor=vec4(sqrt(color),1.); +} + \ No newline at end of file diff --git a/hw3/shader.bak2.frag b/hw3/shader.bak2.frag new file mode 100644 index 0000000..7d8ff4c --- /dev/null +++ b/hw3/shader.bak2.frag @@ -0,0 +1,268 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform int f_tex, f_rt, f_moved; +uniform vec4 rot; //ROTATION VALUES USED TO CALCULATE TRANSFORMATION MATRIX +uniform float dFL; //DELTA on FOCAL LENGTH +uniform mat3 transformation, invTr; +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; + +const float kf_air = 1.000293; +varying vec3 vPos; +float fl=3.;//ORIGINAL FOCAL LENGTH +const float pi=3.14159265359; +const float _2pi=2.*pi; +const int n_ref=15; //2^(hits) - 1 because each hit now spawn 2 rays. +const int max_stack = (n_ref+1)/4; + +vec3 scolor = vec3(0,0,0); //Actually 2^n_ref +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec3 getRefraction(vec3 N, vec3 W, float nextkr, float eta, float c1){ + float c2 = (1.-eta*eta*(1.-c1*c1)); + c2 = sqrt(abs(c2)); + return normalize(eta*W + (eta*c1 - c2)*N); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1); + vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0; + vec3 W=(trPos-V); + bool rtxoff = false, showtexture = true, moved = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + outward = false; + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + outward = true; + } + } + } + } + + if(iMin >= 0){ + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 texture_color; + if(showtexture) + { + vec3 tex_sph = (S-Sph[i].xyz); + if(moved) + ;//tex_sph=invTr*tex_sph; + float R=Sph[i].w; + float tex_x=acos(abs(tex_sph.x)/sqrt(R*R-tex_sph.y*tex_sph.y)); + if(tex_sph.x>0.) + tex_x=pi-tex_x; + tex_x*=1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=tex_x+float(uTime); + float quo=float(int(tex_x/_2pi)); + tex_x=tex_x/_2pi - quo; + texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz; + } + else texture_color = foregroundColor; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + if(outward){ + float c1 = dot(N, W); + if(c1 > 0.) + { + c1 = -c1; + N = -N; + outward = true; + } + else outward = false; + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + if(rtxoff || final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0.,dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + } + else{ + float eta =kf[i]/currKf; + if(outward) eta = 1./eta; + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.001, refr = nextkr > 0.001; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, getRefraction(N, W, nextkr, eta, c1), currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction + else + stack2[k] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { + stack1[k] = Ray(S, (-(2. * c1 * N - W)), currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction + else + stack1[k] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += currentK * color; + } + } + else{ + float c1 = (dot(N, W)); + float ita = kf_air/currKf; + if(c1<0.) + { + c1 = -c1; + N = - N; + } + float c2 = (1.-ita*ita*(1.-c1*c1)); + if(c2 >= 0.) + c2 = sqrt(c2); + else c2 = -sqrt(-c2); + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + { + //stack2[k] = Ray(S, ((2. * c1 * N - W)), currKf, currentK*ks[i]); //reflection + //if(c2>=0.) + { + stack2[k] = Ray(S, normalize(ita*W + (ita*c1 - c2)*N), kf_air, currentK*kr[i]); //refraction + next_top ++; + } + }else{ + //stack1[k] = Ray(S, ((2. * c1 * N - W)), currKf, currentK*ks[i]); //reflection + //if(c2 >= 0.) + { + stack1[k] = Ray(S, normalize(ita*W + (ita*c1 - c2)*N), kf_air, currentK*kr[i]); //refraction + next_top ++; + } + } + // next_top ++; + break; + } + } + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+2.)/3., (sz+1.)/6.)).xyz; + if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(1.,1.,1.); + } + } + } + if(++curr_ptr >= curr_top){ + curr_top = next_top; + curr_ptr = 0; + if(next_top * 2 > max_stack) + final = true; + stackswap = !stackswap; + } + break; + } + } + } + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/hw3/shader.frag b/hw3/shader.frag new file mode 100644 index 0000000..f200d61 --- /dev/null +++ b/hw3/shader.frag @@ -0,0 +1,237 @@ + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION normalize(c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform int f_tex, f_rt, f_moved; +uniform float dFL; //DELTA on FOCAL LENGTH +uniform mat3 transformation, invTr; +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; + +const float kf_air = 1.000293; +varying vec3 vPos; +float fl=3.;//ORIGINAL FOCAL LENGTH +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; + +vec3 scolor = vec3(0,0,0); //Actually 2^n_ref +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.x,tex_sph.z)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1); + vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0; + vec3 W=(trPos-V); + bool rtxoff = false, showtexture = true, moved = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 texture_color; + if(showtexture) + { + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + } + else texture_color = foregroundColor; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + if(rtxoff || final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/hw4/1.jpg b/hw4/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw4/1.jpg differ diff --git a/hw4/2.jpg b/hw4/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw4/2.jpg differ diff --git a/hw4/3.jpg b/hw4/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw4/3.jpg differ diff --git a/hw4/4.jpg b/hw4/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw4/4.jpg differ diff --git a/hw4/5.jpg b/hw4/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/hw4/5.jpg differ diff --git a/hw4/RTXon.svg b/hw4/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw4/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw4/index.html b/hw4/index.html new file mode 100644 index 0000000..dad9e07 --- /dev/null +++ b/hw4/index.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + StarCraft RTX + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+
+
+ +
+ + +
+ +
+ + + + + + + +
+ + + + What's new: +

+     As before, drag on the canvas to rotate the scene, scroll on it to change focal point (perspective projection). + Double click it to pause/resume.
+ I now added Fullscreen mode so that we can scroll on the canvas without scrolling the whole page and use single-key + hotkeys without worrying about conflicts with the editor.
+     Now, you can select a sphere by clicking on it (literally, click the sphere on the canvas!). And drag it around + to move it.
+     When a sphere is selected, you can also scroll whe mouse wheel to change its radius, and in fullscreen mode, press B and F key to send + it backward or forward according to your view point!
+     I also optimized the code a little bit, so that the transformation matrix is completely computed in CPU, + and applied (multiplied) inside the vertex shader.
+     I suggest moving spheres when pausing the scene by double clicking the canvas, otherwise the spheres are already moving.
+     If you found the scene somehow clipped, it seems to be a bug of chromium, please simply click on 'Super Sampling' button + or refresh the page. Otherwise the interactions won't work because the coordinates are wrong. +

+
+
+

+ How it works:
+ I used similar method in raytracing to find the sphere it hits.
+ The transformation methods are implemented in lib4.header.js
+ Interaction parts are in lib4.ext.js
+ lib4.js most contains initialization methods for the renderer.
+ index.html is almost pure html.
+

+
+ +

+

+ + +
+ + + + + + + + \ No newline at end of file diff --git a/hw4/lib4.ext.js b/hw4/lib4.ext.js new file mode 100644 index 0000000..d46e638 --- /dev/null +++ b/hw4/lib4.ext.js @@ -0,0 +1,359 @@ +let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var startTime = Date.now(); +let lastTime = Date.now(); +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = [] +let selected = false, selection = -1, dragging = false; +for(let i = 0; i < ns; ++i) +{ + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; +} +function toggleFullscreen(element){ + if(fullscreen) + { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } + else{ + if(element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if(element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function(_){ + if(fullscreen){ + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + }else{ + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor="white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden=true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign="top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); +} +clrsel.onclick=function(_){ + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function(_){ + clrsel.onclick(); + if(!animating) + pause_resume(); + flags = 0; + moving = false; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for(let i = 0; i < ns; ++i) + { + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; + } + rtx.src='./RTXon.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick=function(e){ + if(ins.value>0 &&ins.value<=ns &&cns!=ins.value) + { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if(typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick=function(e){ + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width)*multiplier; + let h = parseInt(canvas1.style.height)*multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode:"ace/mode/glsl", + theme:"ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if(fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function(delta) { + if(typeof canvas1.setShaders === "function") + { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; + + +let pause_resume = function(){ + if(animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click',function(ev){ + if(!(shift && alt) && lastClick&& Date.now()-lastClick<400) + pause_resume(); + lastClick = Date.now(); + +}); +canvas1.addEventListener('mouseover', function(e){ + over = true; + const mask = 0x8; + flags |= mask; + setUniform('1i', 'flags', flags); +}); +canvas1.addEventListener('mousedown', function(e){ + moving = true + mouselastX = mouselastY = undefined; + let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1, + 1-2*e.offsetY/ parseInt(canvas1.style.height), -1]); + if(i >= 0) + { + dragging = true; + selected = true; + setUniform("1i", "sel", i); + selection = i; + } + else if(selected = true){ + dragging = false; + selected = false; + setUniform("1i", "sel", i); + selection = i; + } +}); +canvas1.addEventListener('mousemove', function(e){ + if(!(mouselastX==undefined || mouselastY == undefined)&&moving){ + let dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + if(!selected) + { + mousedx -= dx/60; + mousedy -= dy/60; + positionsupdated = true; + }else if(dragging){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width), + 2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function(e){ + moving = false; + dragging = false; +}); +canvas1.addEventListener('mouseout', function(e){ + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function(e){ + if(!selected){ + mousedz += e.wheelDelta/600; + positionsupdated = true; + } + else{ + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function(e) {e.stopPropagation();}); +rtx.style.cursor="pointer"; +let rtswitch = function(){ + alert('Ray Tracing is always on. See hw2 where rt can be toggled on/off.') + rtx.src='./RTXon.svg'; +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function(time){ + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps'; + start = time; + if(fpson) + ;//requestAnimationFrame(fpscounter); + else{ + start = undefined; + fps.innerHTML = ''; + } + }; +document.addEventListener('keydown',(e)=>{ + if(e.code.startsWith('Shift')) + shift = true; + if(e.code.startsWith('Control')) + ctrl = true; + if(e.code.startsWith('Alt')) + alt = true; + else if(ctrl && alt && e.code == 'KeyT'){ + const mask = 0x1; + flags = flags&!mask | (!(flags&mask)?mask:0); + setUniform('1i', 'flags', flags); + } + else if (ctrl &&e.code == 'KeyS'){ + let a = document.createElement('a'); + a.href = "data:text/plain,"+encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } + else if(ctrl && alt&&e.code == 'KeyR') + rtswitch(); + else if(ctrl && alt&&e.code == 'KeyN') + { + reset.onclick(); + } + else if(ctrl && alt&&e.code == 'KeyP') + pause_resume(); + else if(ctrl && alt&&e.code == 'KeyF') + if(!fpson) + { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if(fullscreen && selected ){ + if(/*e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft' + ||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS'||*/e.code =='KeyF'||e.code =='KeyB'){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + + switch(e.code){ + // case 'ArrowUp': + // m = matrix_multiply(m, matrix_rotateX(0.1)); + // break; + // case 'ArrowDown': + // m = matrix_multiply(m, matrix_rotateX(-0.1)); + // break; + // case 'ArrowLeft': + // m = matrix_multiply(m, matrix_rotateY(-0.1)); + // break; + // case 'ArrowRight': + // m = matrix_multiply(m, matrix_rotateY(0.1)); + // break; + // case 'KeyW': + // m = matrix_multiply(m, matrix_rotateZ(0.1)); + // break; + // case 'KeyS': + // m = matrix_multiply(m, matrix_rotateZ(-0.1)); + // break; + case 'KeyB': + var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + } +}); + +document.addEventListener('keyup',(e)=>{ + if(e.code.startsWith('Control')) + ctrl = false; + if(e.code.startsWith('Alt')) + alt = false; + if(e.code.startsWith('Shift')) + shift = false; +}); + +function animate(gl) { + let uTime; + if(animating) + { + uTime = (Date.now() - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + else + { + uTime = (lastTime - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + + if(positionsupdated) + updatePositions(); +} + +requestAnimationFrame(fpscounter); diff --git a/hw4/lib4.header.js b/hw4/lib4.header.js new file mode 100644 index 0000000..d40c437 --- /dev/null +++ b/hw4/lib4.header.js @@ -0,0 +1,165 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +let mousedx = 0, mousedy = 0, mousedz = 0; +let seldx = 0, seldy = 0, seldz = 0; + +var cx = 1, cy = 1, sx = 0, sy = 0; +let mouselastX, mouselastY; +const fl = 3; +let start; +var vs, fs; +var vsfetch = new XMLHttpRequest(); +var editor = undefined +let cos = Math.cos, sin = Math.sin, tan = Math.tan, + acos = Math.acos, asin = Math.asin, atan = Math.atan, + sqrt = Math.sqrt; +let positionsupdated = true; +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +var client = new XMLHttpRequest(); +client.open('GET', './shader.frag'); +client.onloadend = function () { + fs = (client.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +client.send(); + + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ= theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m*n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a) => { + let m = []; + for(let i = 0; i < a.length; ++ i) + m[i] = a[i] * c; + return m; +} +function dot(a, b){ + let m = 0; + for(let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} +function plus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} +function minus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} +function normalize(v){ + let res = []; + sum = 0; + for(let i = 0; i < v.length; ++ i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for(let i = 0; i < v.length; ++ i) + res[i] = v[i] / sum; + return res; +} +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function hitTest(pos){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3); + + let W=normalize(minus(trPos, V)); + let tMin=10000.; + let iMin = -1; + for(let i=0;i0.){ + let t=-B-sqrt(D); + if(t > 0.0 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; + } diff --git a/hw4/lib4.js b/hw4/lib4.js new file mode 100644 index 0000000..6629b84 --- /dev/null +++ b/hw4/lib4.js @@ -0,0 +1,189 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); +let ns = 5, cns = 5; +fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; +let fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); + } +let texture = [], gl, program; +let textures = []; +let lock = false; +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) + { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = 'Build Your Own Universe!'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + for(let i = 0; i < ns; ++i){ + loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading. + textures[i] = i; + } + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); + positionsupdated = true; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + var offset = 0; + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +function setUniform(type, name, a, b, c, d, e, f) { + if(gl) + { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + diff --git a/hw4/shader.frag b/hw4/shader.frag new file mode 100644 index 0000000..f436bff --- /dev/null +++ b/hw4/shader.frag @@ -0,0 +1,235 @@ + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + bool rtxoff = false, showtexture = true, selected = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 texture_color; + if(showtexture) + { + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + } + else texture_color = foregroundColor; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + if(rtxoff || final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/hw4/shader.vert b/hw4/shader.vert new file mode 100644 index 0000000..12b100d --- /dev/null +++ b/hw4/shader.vert @@ -0,0 +1,16 @@ +attribute vec3 aPos; +varying vec3 trPos; +uniform mat3 transformation; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + gl_Position = vec4(aPos, 1.); + trPos = transformation *vec3(aPos.xy, -1); +} \ No newline at end of file diff --git a/hw5/.DS_Store b/hw5/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/hw5/.DS_Store differ diff --git a/hw5/1.jpg b/hw5/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw5/1.jpg differ diff --git a/hw5/2.jpg b/hw5/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw5/2.jpg differ diff --git a/hw5/3.jpg b/hw5/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw5/3.jpg differ diff --git a/hw5/4.jpg b/hw5/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw5/4.jpg differ diff --git a/hw5/5.jpg b/hw5/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/hw5/5.jpg differ diff --git a/hw5/RTX.frag b/hw5/RTX.frag new file mode 100644 index 0000000..7c26e89 --- /dev/null +++ b/hw5/RTX.frag @@ -0,0 +1,235 @@ + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=7; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + bool rtxoff = false, showtexture = true, selected = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 texture_color; + if(showtexture) + { + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + } + else texture_color = foregroundColor; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + if(rtxoff || final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/hw5/RTX.vert b/hw5/RTX.vert new file mode 100644 index 0000000..12b100d --- /dev/null +++ b/hw5/RTX.vert @@ -0,0 +1,16 @@ +attribute vec3 aPos; +varying vec3 trPos; +uniform mat3 transformation; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + gl_Position = vec4(aPos, 1.); + trPos = transformation *vec3(aPos.xy, -1); +} \ No newline at end of file diff --git a/hw5/RTXon.svg b/hw5/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw5/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw5/index.html b/hw5/index.html new file mode 100644 index 0000000..762d3b6 --- /dev/null +++ b/hw5/index.html @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + Space Walk + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+
+
+ +
+ + +
+ +
+ + + + + + + + +
+ + + + What's new: +

+     I created a little figure that can walk. First, press "Fullscreen" button. In fullscreen mode, the figure is able to walk + in four directions when pressing corresponding arrow keys.
+      I added normal calculation to every shape I created and then I added phong shading to the scene.
+      As before, you can rotate the scene by dragging on the canvas.
+     I added new shapes such as torus and cubes.
+     If you found the scene somehow clipped, it seems to be a bug of chromium, please simply click on 'Super Sampling' button + or refresh the page. . +

+
+
+ + How it works:
+

+ I used the matrix stack to push and pop transformations to create the figure and move it around. The entire scene is then transformed to apply movements and global rotations.
+ The mesh creation functions and transformation methods are implemented in lib4.header.js
+ Animating function and are in lib4.ext.js
+ shader.vert is the vertex shader.
+ lib4.js most contains initialization methods for the renderer. You can also see how I passes more attributes like surface normal and + object id to the shaders.
+ index.html is almost pure html.
+

+
+ +

+

+ + +
+ + + + + + + + diff --git a/hw5/lib4.ext.js b/hw5/lib4.ext.js new file mode 100644 index 0000000..ea27917 --- /dev/null +++ b/hw5/lib4.ext.js @@ -0,0 +1,593 @@ +let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var startTime = Date.now(); +let lastTime = Date.now(); +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = [] +let selected = false, selection = -1, dragging = false; +let overall_trans = matrix_identity(); +let rebuild = true, presentation = true, sRotation = matrix_identity(); +let facing = 1, running = 0; +for(let i = 0; i < ns; ++i) +{ + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; +} +function toggleFullscreen(element){ + if(fullscreen) + { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } + else{ + if(element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if(element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function(e){ + if(e === "no") + ; + else + btntoggled = true; + if(fullscreen){ + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + }else{ + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor="white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden=true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign="top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); +} +mov.onclick=function(_){ + movescene = !movescene; + if(!movescene) + { + mov.innerText= "Move Scene"; + mov.style.width = "170px"; + } + else + { + mov.innerText = "Move Lighting&Texture"; + mov.style.width = "280px"; + } +} +document.addEventListener("webkitfullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;}); +document.addEventListener("fullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;}); +clrsel.onclick=function(_){ + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function(_){ + clrsel.onclick(); + if(!animating) + pause_resume(); + flags = 0; + moving = false; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for(let i = 0; i < ns; ++i) + { + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; + } + rtx.src='./RTXon.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick=function(e){ + if(ins.value>0 &&ins.value<=ns &&cns!=ins.value) + { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if(typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick=function(e){ + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width)*multiplier; + let h = parseInt(canvas1.style.height)*multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode:"ace/mode/glsl", + theme:"ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if(fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function(delta) { + if(typeof canvas1.setShaders === "function") + { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; + + +let pause_resume = function(){ + if(animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click',function(ev){ + if(!(shift && alt) && lastClick&& Date.now()-lastClick<400) + pause_resume(); + lastClick = Date.now(); +}); +canvas1.addEventListener('mouseover', function(e){ + over = true; + const mask = 0x8; + flags |= mask; + setUniform('1i', 'flags', flags); +}); +canvas1.addEventListener('mousedown', function(e){ + moving = true + mouselastX = mouselastY = undefined; + let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1, + 1-2*e.offsetY/ parseInt(canvas1.style.height), -1]); + if(i >= 0) + { + dragging = true; + selected = true; + setUniform("1i", "sel", i); + selection = i; + } + else if(selected = true){ + dragging = false; + selected = false; + setUniform("1i", "sel", i); + selection = i; + } +}); +canvas1.addEventListener('mousemove', function(e){ + if(!(mouselastX==undefined || mouselastY == undefined)&&moving){ + let dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + if(movescene){ + sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy/60)); + sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx/60)); + + } + else if(!selected) + { + mousedx -= dx/60; + mousedy -= dy/60; + positionsupdated = true; + }else if(dragging){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width), + 2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function(e){ + moving = false; + dragging = false; +}); +canvas1.addEventListener('mouseout', function(e){ + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function(e){ + if(!selected){ + mousedz += e.wheelDelta/600; + positionsupdated = true; + } + else{ + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function(e) {e.stopPropagation();}); +rtx.style.cursor="pointer"; +let rtswitch = function(){ + alert('Ray Tracing is always on. See hw2 where rt can be toggled on/off.') + rtx.src='./RTXon.svg'; +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function(time){ + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps'; + start = time; + if(fpson) + ;//requestAnimationFrame(fpscounter); + else{ + start = undefined; + fps.innerHTML = ''; + } + }; +document.addEventListener('keydown',(e)=>{ + if(e.code.startsWith('Shift')) + shift = true; + if(e.code.startsWith('Control')) + ctrl = true; + if(e.code.startsWith('Alt')) + alt = true; + else if(ctrl && alt && e.code == 'KeyT'){ + const mask = 0x1; + flags = flags&!mask | (!(flags&mask)?mask:0); + setUniform('1i', 'flags', flags); + } + else if (ctrl &&e.code == 'KeyS'){ + let a = document.createElement('a'); + a.href = "data:text/plain,"+encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } + else if(ctrl && alt&&e.code == 'KeyR') + rtswitch(); + else if(ctrl && alt&&e.code == 'KeyN') + { + reset.onclick(); + } + else if(ctrl && alt&&e.code == 'KeyP') + pause_resume(); + else if(ctrl && alt&&e.code == 'KeyF') + if(!fpson) + { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if(e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft' + ||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS') + { + switch(e.code){ + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + running = 20; + rebuild = true; + } + if(fullscreen && selected ){ + if(e.code =='KeyF'||e.code =='KeyB'){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + + switch(e.code){ + case 'KeyB': + var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup',(e)=>{ + if(e.code.startsWith('Control')) + ctrl = false; + if(e.code.startsWith('Alt')) + alt = false; + if(e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = new Float32Array([ -1,-1,1,0,0,0,0,0,-1, 1,1,0,0,0,0,0,-1, -1,-1,0,0,0,0,0, -1,1,-1,0 ,0,0,0,0]); +let sphereMesh = createMesh(32, 32, uvToSphere); +let tubeMesh = createMesh(32, 2, uvToTube,0,1); +let diskMesh = createMesh(32, 2, uvToDisk,0,1); +let tubeMesh2 = createMesh(32, 2, uvToTube,0,2); +let diskNMesh2 = createMesh(32, 2, uvToDisk, -1,2); +let diskPMesh2 = createMesh(32, 2, uvToDisk, 1,2); +let tubeMesh3 = createMesh(32, 2, uvToTube,0,3); +let diskNMesh3 = createMesh(32, 2, uvToDisk, -1,3); +let diskPMesh3 = createMesh(32, 2, uvToDisk, 1,3); +let diskNMesh = createMesh(32, 2, uvToDisk, -1,1); +let diskPMesh = createMesh(32, 2, uvToDisk, 1,1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 5); +let head = createCube(1.5,1,1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => {delete objects; objects = [];}; + +let delta_height = 0, delta_l = [0,0]; +class State{ + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5*pi; + this.lf = this.rf = 0; + } + initialize(){ + this.leg = true; + this.progress = 0; + } + next(){ + //return this.presentation(); + if(running <= 0) + return {rh:.5*pi, lh:.5*pi, rf:0, lf:0, dh:0,dl:0} + running --; + const steps = 100; + let dl = 0; + if(this.progress >= steps/2) + { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-pi/5, 0.5*pi, 0.44*pi, 0.55*pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if(this.leg) + { + if(this.progress < steps/4) + { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } + else{ + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf-= delta[2]; + } + } + else{ + if(this.progress < steps/4) + { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } + else{ + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf-= delta[1]; + } + } + let delta_h = Math.max((1-cos(abs(this.lh - pi/2)))*.5+(1-cos(abs(this.lf)))*.6,(1-cos(abs(this.rh - pi/2)))*.5+(1-cos(abs(this.rf)))*.6); + this.progress++; + return {lh:this.lh, lf:this.lf, rh:this.rh,rf:this.rf, dh:delta_h, dl:1.8522/steps}; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; +let build_objects = (state)=>{ + if(running === 0) + rebuild = false; + let {lh, lf, rh, rf, dh, dl} = state.next(); + delta_l[abs(facing)-1] += 0.3* Math.sign(facing) * dl; + delta_height = dh; + clearObject(); + M.save(); + M.save(); + M.rotateX(pi/2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.translate(0,1,0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi/4); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.4); + M.rotateX(-0.53*pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0,0,1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi/4); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.4); + M.rotateX(-0.55*pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0,0,1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0,0,1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0,0,1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.restore(); + //rebuild = false; +}; +let state = new State(); +var M = new Matrix(); + +function animate(gl) { + let uTime; + if(animating) + { + uTime = (Date.now() - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + else + { + uTime = (lastTime - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + if(presentation){ + M.save(); + M.scale(0.3); + M.rotateY(uTime/5); + M.rotateX(1); + M.translate(delta_l[0], -delta_height, delta_l[1]); + if(facing !=2) + M.rotateY(pi*(facing/2)); + + //M.translate(0, -delta_height, 0); + overall_trans = M.value(); + M.restore(); + }else{ + M.save(); + // M.rotateY(2); + M.rotateX(1); + M.scale(0.3); + M.translate(delta_l[0], -delta_height, delta_l[1]); + overall_trans = M.value(); + M.restore(); + } + if(rebuild) + build_objects(state); + for(const [obj, mat] of objects){ + drawMesh(obj, matrix_multiply(sRotation,matrix_multiply(overall_trans, mat))); + } + M.save(); + M.scale(0.1); + M.translate(6,0,1); + M.rotateX(1); + M.rotateY(uTime/4); + drawMesh(torusMash, M.value()); + M.restore(); + if(positionsupdated) + updatePositions(); +} + +requestAnimationFrame(fpscounter); +//pjsk.play(); diff --git a/hw5/lib4.header.js b/hw5/lib4.header.js new file mode 100644 index 0000000..1fb7f6f --- /dev/null +++ b/hw5/lib4.header.js @@ -0,0 +1,332 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, mousedy = 0, mousedz = 0; +let seldx = 0, seldy = 0, seldz = 0; +var enableSelection = false; +var cx = 1, cy = 1, sx = 0, sy = 0; +var mouselastX, mouselastY; +const fl = 3; +let start; +var vs, fs; +var vsfetch = new XMLHttpRequest(); +var editor = undefined +var cos = Math.cos, sin = Math.sin, tan = Math.tan, + acos = Math.acos, asin = Math.asin, atan = Math.atan, + sqrt = Math.sqrt, pi = Math.PI, abs = Math.abs; +var positionsupdated = true; +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +var client = new XMLHttpRequest(); +client.open('GET', './shader.frag'); +client.onloadend = function () { + fs = (client.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +client.send(); + + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ= theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m*n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a) => { + let m = []; + for(let i = 0; i < a.length; ++ i) + m[i] = a[i] * c; + return m; +} +function dot(a, b){ + let m = 0; + for(let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} +function plus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} +function minus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} +function normalize(v){ + let res = []; + sum = 0; + for(let i = 0; i < v.length; ++ i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for(let i = 0; i < v.length; ++ i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function() { + let top = 0, m = [ matrix_identity() ]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_translate(x,y,z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_scale(x,y,z)); + this.value = () => m[top]; + this.save = () => { m[top+1] = m[top].slice(); top++; } + this.restore = () => --top; + } + + + //------ CREATING MESH SHAPES + + // CREATE A MESH FROM A PARAMETRIC FUNCTION + + let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0 ; v < 1 ; v += 1/nv) { + for (let u = 0 ; u <= 1 ; u += 1/nu) { + tmp = tmp.concat(f(u,v,oid,data)); + tmp = tmp.concat(f(u,v+1/nv,oid,data)); + } + tmp = tmp.concat(f(1,v,oid,data)); + tmp = tmp.concat(f(0,v+1/nv,oid,data)); + } + return new Float32Array(tmp); + } + + // GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + + let glueMeshes = (a, b) => { + let c = []; + for (let i = 0 ; i < a.length ; i++) + c.push(a[i]); // a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0 ; i < b.length ; i++) + c.push(b[i]); // + b + return new Float32Array(c); + } + + + let uvToSphere = (u,v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x,y,z].concat(normalize([x, y, z])); + } + + let uvToTube = (u,v,i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i,x,y,z].concat(normalize([x,y,0])); + } + + let uvToDisk = (u,v,i,dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i,x,y,z].concat([0,0,Math.sign(z)]); + } + let uvToTorus = (u,v,i,r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta)*x; + x *=cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), ty = cos(theta),tsx = sin(phi), tsy = tsx*tx, tsz = cos(phi); + tsx*=-ty; + return [i,x, y, z].concat(normalize([ty*tsz*0.5, -tx*tsz, tx*tsy-ty*tsx])); + } + +let createCube = (w, h, l,id) => { + let mesh = []; + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,-1,0]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]); + mesh=mesh.concat([id, w/2,h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]); + + mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]); + + mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]); + + mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]); + + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]); + + mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]); + + mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]); + mesh=mesh.concat([id, w/2,h/2,l/2,1,0,0]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,1,0,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]); + mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]); + + mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]); + mesh=mesh.concat([id, w/2,h/2,-l/2,0,1,0]); + mesh=mesh.concat([id, -w/2,h/2,l/2,0,1,0]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,0,1,0]); + return new Float32Array(mesh); + + // let verts = []; + // for(let i = -w/2; i < w; i += w) + // for(let j = -h/2; j < h; j += h) + // for (let k = -l/2; k < l; k += l) + // verts.push([id, i, j, k]); + // let mesh = []; + // let n = 0; + // for(let j = 0; j < 4; ++ j) + // mesh = mesh.concat(verts[j]); + // for(let i = 0; i < 2; ++ i) + // for(let j = 0; j < 2; ++ j) + // mesh = mesh.concat(verts[6 - i*2 + j]); + // for(let j = 0; j < 2; ++ j) + // mesh = mesh.concat(verts[j]); + // mesh = mesh.concat(verts[5]); + // mesh = mesh.concat(verts[3]); + // mesh = mesh.concat(verts[7]); + // mesh = mesh.concat(verts[7]); + // mesh = mesh.concat(verts[6]); + // mesh = mesh.concat(verts[6]); + // mesh = mesh.concat(verts[4]); + // mesh = mesh.concat(verts[2]); + // mesh = mesh.concat(verts[0]); + // return new Float32Array(mesh); + //CREATING CUBES THIS WAY REDUCES VERTICES BUT MAKES IT HARDER TO DO LIGHTING +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function hitTest(pos){ + if(!enableSelection) + return -1; + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3); + + let W=normalize(minus(trPos, V)); + let tMin=10000.; + let iMin = -1; + for(let i=0;i0.){ + let t=-B-sqrt(D); + if(t > 0.0 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; + } diff --git a/hw5/lib4.js b/hw5/lib4.js new file mode 100644 index 0000000..2753dc4 --- /dev/null +++ b/hw5/lib4.js @@ -0,0 +1,211 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); +var ns = 5, cns = 5; +fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); + } +let texture = [], gl, program; +let textures = []; +let lock = false; +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) + { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = 'Build Your Own Universe!'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + for(let i = 0; i < ns; ++i){ + loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading. + textures[i] = i; + } + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); + positionsupdated = true; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + var offset = 0; + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4*7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4*7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4*7, 4*4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +function setUniform(type, name, a, b, c, d, e, f) { + if(gl) + { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +let drawMesh = (mesh, m) => { + setUniform('Matrix4fv', 'uMatrix', false, m); + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, mesh.length / VERTEX_SIZE); +} + diff --git a/hw5/pjsk.mp4 b/hw5/pjsk.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/hw5/pjsk.mp4 differ diff --git a/hw5/shader.frag b/hw5/shader.frag new file mode 100644 index 0000000..b0fb7d1 --- /dev/null +++ b/hw5/shader.frag @@ -0,0 +1,253 @@ + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 apos; +const float pi=3.14159265359; +const float _2pi=2.*pi; +vec3 LDir=vec3(.5,.5,.5); +vec3 LCol=vec3(1.,1.,1.); +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=15; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} + +void rtx(){ + + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + bool rtxoff = false, showtexture = true, selected = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 texture_color; + if(showtexture) + { + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + } + else texture_color = foregroundColor; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + if(rtxoff || final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4((scolor),1.); +} +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4; + if(id == 4.) {rtx();color = gl_FragColor.xyz;} + else if (id == 1.) color = vec3(1.,.4,.6); + else if (id == 5.) {color = vec3(1, .9375,.7329);sp = .5; df=.8; amb = .05;} + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + color = color*(amb+ df*max(0.,dot(norm,realLDir)))//specular for ground. + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),5.)*vec3(1,1,1); + gl_FragColor=vec4(sqrt(color), 1.); +} diff --git a/hw5/shader.vert b/hw5/shader.vert new file mode 100644 index 0000000..eb1842b --- /dev/null +++ b/hw5/shader.vert @@ -0,0 +1,20 @@ +uniform mat4 uMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying vec3 trPos; +varying float id; +varying vec3 norm; +varying vec3 glpos; +varying vec3 apos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos * vec4(1., 1., -1., 1.); + id = oid; + norm = normalize((uMatrix*vec4(normal,0.)).xyz); + trPos = transformation *vec3(pos.xy, -1); + apos = aPos; + glpos = gl_Position.xyz; +} + diff --git a/hw6/RTXon.svg b/hw6/RTXon.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw6/RTXon.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw6/index.html b/hw6/index.html new file mode 100644 index 0000000..d752cc8 --- /dev/null +++ b/hw6/index.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + Spring Break + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+
+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + + + What's new: +

+      I corrected the Phong lighting, added perspective and implemented both splines.
+      Since we're learning splines. I figured it would be interesting to + do vector graphics.
+      It seems that most paths in a svg file (see path.txt) + are a combination multiple of end-to-end Bezier Splines.
+      I created these shapes in Adobe Illustrator and output them to svg.
+     I processed the path data in (see lib6.header.js) and + transformed them into multiple Bezier functions and then interpolated them in lib6.ext.js + so that they appears to be smooth shapes. +
+      I also made animations with Hermite Splines.
+      As before, you can 'walk' by pressing arrow keys, it's now looked more like it because + I added a ground and the scene is now perspective. Again, better used in Fullscreen mode.
+

+
+
+ + +
+ +

+

+ + +
+ + + + + + + + \ No newline at end of file diff --git a/hw6/lib6.ext.js b/hw6/lib6.ext.js new file mode 100644 index 0000000..3624450 --- /dev/null +++ b/hw6/lib6.ext.js @@ -0,0 +1,795 @@ +let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var startTime = Date.now(); +let lastTime = Date.now(); +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = [] +let selected = false, selection = -1, dragging = false; +let overall_trans = matrix_identity(), overall_ground; +let rebuild = true, buildsplines = true, presentation = false, sRotation = matrix_identity(); +let facing = 1, running = 0; +for(let i = 0; i < ns; ++i) +{ + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; +} +function ev_supersample(e){ + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width)*multiplier; + let h = parseInt(canvas1.style.height)*multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} +function toggleFullscreen(element){ + if(fullscreen) + { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } + else{ + if(element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if(element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function(e){ + if(e === "no") + ; + else + btntoggled = true; + if(fullscreen){ + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + }else{ + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor="white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden=true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign="top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick=function(_){ + movescene = !movescene; + if(!movescene) + { + mov.innerText= "Move Scene"; + mov.style.width = "170px"; + } + else + { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;}); +document.addEventListener("fullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;}); +clrsel.onclick=function(_){ + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function(_){ + clrsel.onclick(); + if(!animating) + pause_resume(); + flags = 0; + moving = false; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for(let i = 0; i < ns; ++i) + { + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; + } + rtx.src='./RTXon.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick=function(e){ + if(ins.value>0 &&ins.value<=ns &&cns!=ins.value) + { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if(typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick=ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode:"ace/mode/glsl", + theme:"ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if(fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function(delta) { + if(typeof canvas1.setShaders === "function") + { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; + + +let pause_resume = function(){ + if(animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click',function(ev){ + if(!(shift && alt) && lastClick&& Date.now()-lastClick<200) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = pause_resume; +canvas1.addEventListener('mouseover', function(e){ + over = true; + const mask = 0x8; + flags |= mask; + setUniform('1i', 'flags', flags); +}); +canvas1.addEventListener('mousedown', function(e){ + moving = true + mouselastX = mouselastY = undefined; + let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1, + 1-2*e.offsetY/ parseInt(canvas1.style.height), -1]); + if(i >= 0) + { + dragging = true; + selected = true; + setUniform("1i", "sel", i); + selection = i; + } + else if(selected = true){ + dragging = false; + selected = false; + setUniform("1i", "sel", i); + selection = i; + } +}); +canvas1.addEventListener('mousemove', function(e){ + if(!(mouselastX==undefined || mouselastY == undefined)&&moving){ + let dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + if(movescene){ + sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy/60)); + sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx/60)); + + } + else if(!selected) + { + mousedx -= dx/60; + mousedy -= dy/60; + positionsupdated = true; + }else if(dragging){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width), + 2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function(e){ + moving = false; + dragging = false; +}); +canvas1.addEventListener('mouseout', function(e){ + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function(e){ + if(!selected){ + mousedz += e.wheelDelta/600; + positionsupdated = true; + } + else{ + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function(e) {e.stopPropagation();}); +rtx.style.cursor="pointer"; +let rtswitch = function(){ + alert('Ray Tracing is off due to global silicon shortage. \nSend me an RTX 3090 to enable Ray Tracing again.') + rtx.src='./RTXon.svg'; +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function(time){ + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps'; + start = time; + if(fpson) + ;//requestAnimationFrame(fpscounter); + else{ + start = undefined; + fps.innerHTML = ''; + } + }; +document.addEventListener('keydown',(e)=>{ + if(e.code.startsWith('Shift')) + shift = true; + if(e.code.startsWith('Control')) + ctrl = true; + if(e.code.startsWith('Alt')) + alt = true; + else if(ctrl && alt && e.code == 'KeyT'){ + const mask = 0x1; + flags = flags&!mask | (!(flags&mask)?mask:0); + setUniform('1i', 'flags', flags); + } + else if (ctrl &&e.code == 'KeyS'){ + let a = document.createElement('a'); + a.href = "data:text/plain,"+encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } + else if(ctrl && alt&&e.code == 'KeyR') + rtswitch(); + else if(ctrl && alt&&e.code == 'KeyN') + { + reset.onclick(); + } + else if(ctrl && alt&&e.code == 'KeyP') + pause_resume(); + else if(ctrl && alt&&e.code == 'KeyF') + if(!fpson) + { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if(e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft' + ||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS') + { + switch(e.code){ + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + running = 50; + rebuild = true; + } + if(fullscreen && selected ){ + if(e.code =='KeyF'||e.code =='KeyB'){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + + switch(e.code){ + case 'KeyB': + var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup',(e)=>{ + if(e.code.startsWith('Control')) + ctrl = false; + if(e.code.startsWith('Alt')) + alt = false; + if(e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = new Float32Array([ -1,-1,1,0,0,0,1,-1, 1,1,0,0,0,1,-1, -1,-1,0,0,0,1, -1,1,-1,0 ,0,0,1]); +let sphereMesh = createMesh(32, 32, uvToSphere); +let tubeMesh = createMesh(32, 2, uvToTube,0,1); +let diskMesh = createMesh(32, 2, uvToDisk,0,1); +let tubeMesh2 = createMesh(32, 2, uvToTube,0,2); +let diskNMesh2 = createMesh(32, 2, uvToDisk, -1,2); +let diskPMesh2 = createMesh(32, 2, uvToDisk, 1,2); +let tubeMesh3 = createMesh(32, 2, uvToTube,0,3); +let diskNMesh3 = createMesh(32, 2, uvToDisk, -1,3); +let diskPMesh3 = createMesh(32, 2, uvToDisk, 1,3); +let diskNMesh = createMesh(32, 2, uvToDisk, -1,1); +let diskPMesh = createMesh(32, 2, uvToDisk, 1,1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 5); +let head = createCube(1.5,1,1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => {delete objects; objects = [];}; + +let delta_height = 0, delta_l = [0,0]; +class State{ + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5*pi; + this.lf = this.rf = 0; + } + initialize(){ + this.leg = true; + this.progress = 0; + } + next(){ + //return this.presentation(); + if(running <= 0) + return {rh:.5*pi, lh:.5*pi, rf:0, lf:0, dh:0,dl:0} + running --; + const steps = 100; + let dl = 0; + if(this.progress >= steps/2) + { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-pi/5, 0.5*pi, 0.44*pi, 0.55*pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if(this.leg) + { + if(this.progress < steps/4) + { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } + else{ + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf-= delta[2]; + } + } + else{ + if(this.progress < steps/4) + { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } + else{ + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf-= delta[1]; + } + } + let delta_h = Math.max((1-cos(abs(this.lh - pi/2)))*.5+(1-cos(abs(this.lf)))*.6,(1-cos(abs(this.rh - pi/2)))*.5+(1-cos(abs(this.rf)))*.6); + this.progress++; + return {lh:this.lh, lf:this.lf, rh:this.rh,rf:this.rf, dh:delta_h, dl:1.8522/steps}; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], star = []; +let sakura = []; +let star2=[], newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3,.8,.6,.5]); +const curvey = matrix_multiply(hermiteMat, [-.2,.5,.7,.2]); +const curvez = matrix_multiply(hermiteMat, [-.5,.2,.3,.8]); +let adt = []; + +let build_objects = (state)=>{ + if(running === 0) + rebuild = false; + let {lh, lf, rh, rf, dh, dl} = state.next(); + delta_l[abs(facing)-1] += Math.sign(facing) * dl; + delta_height = dh; + clearObject(); + M.save(); + M.save(); + M.rotateX(pi/2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.translate(0,1,0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi/4); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.4); + M.rotateX(-0.53*pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0,0,1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi/4); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.4); + M.rotateX(-0.55*pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0,0,1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0,0,1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0,0,1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.restore(); + if(running < 0) + rebuild = false; +}; +let build_splines = ()=>{ + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, innerN = Math.ceil((paths[0].length*n)/paths[1].length); + for(let j = 0; j < paths[0].length; ++j) + { + let xf = paths[0][j][0], yf = paths[0][j][1]; + for(var k = 0; k <= n; ++k){ + let t = k/n; + star1.push([7,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1]); + } + } + + for(let j = 13; j >=0; j--) + { + let xf = paths[1][j][0], yf = paths[1][j][1]; + for(var k = innerN-1; k >=0 ; --k){ + let t = k/(innerN-1); + star2.push([7,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1]); + } + } + for(let j = paths[1].length-1; j >12; --j) + { + let xf = paths[1][j][0], yf = paths[1][j][1]; + for(var k = innerN; k >=0 ; --k){ + let t = k/innerN; + star2.push([7,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1]); + } + } + let concat = (a, b) => b.forEach(e => a.push(e)); + for(let i = 0; i < star1.length; ++i){ + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for(let l = 2; l < 6; ++l) + { + adt[l - 2] = []; + for(let j = 0; j < paths[l].length; ++j) + { + let xf = paths[l][j][0], yf = paths[l][j][1]; + for(var k = 0; k <= n; ++k){ + let t = k/n; + adt[l-2].push(10+l,dot(xf, [t*t*t, t*t, t, 1]), + -dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1); + } + } + } + n = 20; + for(let l = 6; l < 13; ++l) + { + sakura[l-6] = []; + for(let j = 0; j < paths[l].length; ++j) + { + let xf = paths[l][j][0], yf = paths[l][j][1]; + for(var k = 0; k <= n; ++k){ + let t = k/n; + sakura[l-6].push(10,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for(let i = 0; i < star.length; i+=7) + { + star4[i] = 9; + newstar[i] = 8; + } + return false; +} +let state = new State(); +var M = new Matrix(); + +function animate(gl) { + let uTime; + buildsplines &&= build_splines(); + if(animating) + { + uTime = (Date.now() - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + else + { + uTime = (lastTime - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + if(presentation){ + M.save(); + M.scale(0.3); + M.rotateY(uTime/5); + M.rotateX(1); + overall_ground = M.value(); + if(facing !=2) + M.rotateY(pi*(facing/2)); + M.translate(delta_l[0], -delta_height, delta_l[1]); + + //M.translate(0, -delta_height, 0); + overall_trans = M.value(); + M.restore(); + }else{ + M.save(); + // M.rotateY(2); + M.translate(-.3, -.3, 0); + M.rotateX(.6); + M.rotateZ(.35); + M.scale(0.3); + overall_ground = M.value(); + + M.translate(delta_l[0], -delta_height, delta_l[1]); + if(facing !=2) + M.rotateY(pi*(facing/2)); + overall_trans = M.value(); + M.restore(); + } + if(rebuild) + build_objects(state); + for(const [obj, mat] of objects){ + let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)); + setM(m); + drawMesh(obj); + } + M.save(); + // M.rotateX(uTime/4); + const _curvez = matrix_multiply(hermiteMat, [-.5,-.2,.3,.8]); + // M.rotateZ(uTime/4); + + M.rotateZ((uTime)); + //M.scale(100); + setM(M.value()); + drawMesh(new Float32Array(star)); + M.restore(); + M.save(); + let t = (sin(uTime/1.5)), mat = [t*t*t, t*t, t, 1] + if(t < 0) + { mat = const_multiply(-t, [1,1,1,1]); + mat[3] +=1; + } + + M.translate(dot(curvex, mat)*2, dot(curvey, mat)*2, dot(curvez, mat)*2); + M.scale(0.2); + setM(M.value()); + drawMesh(new Float32Array(newstar)); + M.restore(); + const __curvey = matrix_multiply(hermiteMat, [-.3,.5,1.9,.8]); + + M.save(); + M.translate(0,dot(__curvey, mat)*4, 0); + t = abs(((uTime%4)/1.2)), mat = [t*t*t, t*t, t, 1]; + M.rotateZ(dot(__curvey, mat)); + M.scale(0.2); + setM(M.value()); + drawMesh(new Float32Array(star4)); + M.restore(); + M.save(); + M.scale(0.1); + M.translate(6,0,1); + M.rotateX(1); + M.rotateY(uTime/4); + t = abs(sin((uTime%3.1415)/3.)), mat = [t*t*t, t*t, t, 1]; + + //M.scale(abs((dot(matrix_multiply(hermiteMat, [-.3,.9,1.7,2.8]), mat)))); + setM(M.value()); + drawMesh(torusMash,gl.LINES); + M.restore(); + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore() + M.save(); + M.translate(0,-3.155,0); + + M.rotateX(pi/2); + M.scale(10); + + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + + drawMesh(squareMesh); + M.restore(); + if(positionsupdated) + updatePositions(); + + // setM(M.value()); +} + +requestAnimationFrame(fpscounter); +//pjsk.play(); diff --git a/hw6/lib6.header.js b/hw6/lib6.header.js new file mode 100644 index 0000000..69dc16c --- /dev/null +++ b/hw6/lib6.header.js @@ -0,0 +1,454 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, mousedy = 0, mousedz = 0; +let seldx = 0, seldy = 0, seldz = 0; +var enableSelection = false; +var cx = 1, cy = 1, sx = 0, sy = 0; +var mouselastX, mouselastY; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1,3,-3,1,3,-6,3,0,-3,3,0,0,1,0,0,0], + hermiteMat = [2,-3,0,1,-2,3,0,0,1,-2,1,0,1,-1,0,0]; +var starColors = [0.9921, 0.5378,0.7109, + 0.65, 0.56, 0.992, + 0.992,0.7994,0.2402, + 0.1760,0.5094,0.5378, + .1164, .1274, .2289, + .9784,.71,.4482, + ], n_shapes = starColors.length/3; +var editor = undefined +var cos = Math.cos, sin = Math.sin, tan = Math.tan, + acos = Math.acos, asin = Math.asin, atan = Math.atan, + sqrt = Math.sqrt, pi = Math.PI, abs = Math.abs; +var positionsupdated = true; +var paths = [], origpath= []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, currY = 0, maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + var currShape = [], currCurve = []; + let i = 0; + let postProcess = () => { + if(currShape.length){ + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2){ + curve[j] = (curve[j] - minX) / span-spanX/(span*2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY/(span*2); + origpath.push(1,curve[j], curve[j+1],0,0,0,1); + if(j%6==0 && j > 5){ + let X = [], Y = []; + for(let k = j - 6; k <= j+1; k += 2){ + X.push(curve[k]); + Y.push(curve[k+1]); + } + + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y)]); + } + + } + paths.push(funcs); + } + } + } + let read_num = () =>{ + let num = 0, sign = 1, accepted = 0; + + while(i < text.length && (text[i] <'0' || text[i] > '9') && text[i]!='-') ++i; + if(text[i] == '-') + { + sign = -1; + ++i; + } + while(i < text.length&&text[i] >= '0' && text[i] <= '9'){ + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if(text[i] == '.'){ + i++; + let multiplier = 0.1; + accepted = 0; + while(i < text.length&&text[i] >= '0' && text[i] <= '9'){ + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], c_idx = 0, prevX = 0, prevY = 0, getC = ()=>{ + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if(delta){ + currX = prevX + read_num(); + currY = prevY + read_num(); + }else{ + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX? currX:maxX; + maxY = currY > maxY? currY:maxY; + minX = currX < minX? currX:minX; + minY = currY < minY? currY:minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while( i < text.length ){ + if(text[i] == 'z'){ + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N'){ + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c'){ + + prevX = currX; + prevY = currY; + + for(let j = 0; j < 3; ++j){ + get_next(true); + } + } else if (text[i] == 'C'){ + for(let j = 0; j < 3; ++j){ + get_next(); + } + } else if (text[i] == 'M'){ + get_next(); + } + else ++i; + } +}; +pathFetch.send(); +let matrix_inverse = src => { + let dst = [], det = 0, cofactor = (c, r) => { + let s = (i, j) => src[c+i & 3 | (r+j & 3) << 2]; + return (c+r & 1 ? -1 : 1) * ( (s(1,1) * (s(2,2) * s(3,3) - s(3,2) * s(2,3))) + - (s(2,1) * (s(1,2) * s(3,3) - s(3,2) * s(1,3))) + + (s(3,1) * (s(1,2) * s(2,3) - s(2,2) * s(1,3))) ); + } + for (let n = 0 ; n < 16 ; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0 ; n < 4 ; n++) det += src[n] * dst[n << 2]; + for (let n = 0 ; n < 16 ; n++) dst[n] /= det; + return dst; + } + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i ++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) + { + ret[i] = -ret[i]; + ret[i+1] += ret[i]/fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ= theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m*n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a) => { + let m = []; + for(let i = 0; i < a.length; ++ i) + m[i] = a[i] * c; + return m; +} +function dot(a, b){ + let m = 0; + for(let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} +function plus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} +function minus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} +function normalize(v){ + let res = []; + sum = 0; + for(let i = 0; i < v.length; ++ i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for(let i = 0; i < v.length; ++ i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function() { + let top = 0, m = [ matrix_identity() ]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_translate(x,y,z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_scale(x,y,z)); + this.value = () => m[top]; + this.save = () => { m[top+1] = m[top].slice(); top++; } + this.restore = () => --top; + } + + let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(m)); + } + //------ CREATING MESH SHAPES + + // CREATE A MESH FROM A PARAMETRIC FUNCTION + + let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0 ; v < 1 ; v += 1/nv) { + for (let u = 0 ; u <= 1 ; u += 1/nu) { + tmp = tmp.concat(f(u,v,oid,data)); + tmp = tmp.concat(f(u,v+1/nv,oid,data)); + } + tmp = tmp.concat(f(1,v,oid,data)); + tmp = tmp.concat(f(0,v+1/nv,oid,data)); + } + return new Float32Array(tmp); + } + + // GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + + let glueMeshes = (a, b) => { + let c = []; + for (let i = 0 ; i < a.length ; i++) + c.push(a[i]); // a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0 ; i < b.length ; i++) + c.push(b[i]); // + b + return new Float32Array(c); + } + + + let uvToSphere = (u,v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x,y,z].concat(normalize([x, y, z])); + } + + let uvToTube = (u,v,i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i,x,y,z].concat(normalize([x,y,0])); + } + + let uvToDisk = (u,v,i,dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i,x,y,z].concat([0,0,Math.sign(z)]); + } + let uvToTorus = (u,v,i,r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta)*x; + x *=cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), ty = cos(theta),tsx = sin(phi), tsy = tsx*tx, tsz = cos(phi); + tsx*=-ty; + return [i,x, y, z].concat(normalize([ty*tsz*0.5, -tx*tsz, tx*tsy-ty*tsx])); + } + +let createCube = (w, h, l,id) => { + let mesh = []; + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,-1,0]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]); + mesh=mesh.concat([id, w/2,h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]); + + mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]); + + mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]); + + mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]); + + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]); + + mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]); + + mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]); + mesh=mesh.concat([id, w/2,h/2,l/2,1,0,0]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,1,0,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]); + mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]); + + mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]); + mesh=mesh.concat([id, w/2,h/2,-l/2,0,1,0]); + mesh=mesh.concat([id, -w/2,h/2,l/2,0,1,0]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,0,1,0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function hitTest(pos){ + if(!enableSelection) + return -1; + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3); + + let W=normalize(minus(trPos, V)); + let tMin=10000.; + let iMin = -1; + for(let i=0;i0.){ + let t=-B-sqrt(D); + if(t > 0.0 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; + } diff --git a/hw6/lib6.js b/hw6/lib6.js new file mode 100644 index 0000000..80c4a30 --- /dev/null +++ b/hw6/lib6.js @@ -0,0 +1,214 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); +var ns = 0, cns = 0; +fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); + } +let texture = [], gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) + { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = 'In Spring Break We Did Homeworks.'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + for(let i = 0; i < ns; ++i){ + loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading. + textures[i] = i; + } + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); + positionsupdated = true; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + var offset = 0; + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for(let i = 0; i < n_shapes; i++){ + setUniform('3fv', 'starColors['+i+']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4*7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4*7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4*7, 4*4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +function setUniform(type, name, a, b, c, d, e, f) { + if(gl) + { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +let drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} + diff --git a/hw6/paths.txt b/hw6/paths.txt new file mode 100644 index 0000000..dc26bed --- /dev/null +++ b/hw6/paths.txt @@ -0,0 +1,63 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76 +c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65 +c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N \ No newline at end of file diff --git a/hw6/shader.frag b/hw6/shader.frag new file mode 100644 index 0000000..3a65d4b --- /dev/null +++ b/hw6/shader.frag @@ -0,0 +1,39 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +varying vec3 norm; +varying float id; +varying vec3 glpos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5.; + vec3 l = vec3(1,1,1); + + if(id < 1.5) {color = vec3(0.,1.,0.2034);} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) {color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + if(id < 0.){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), 1.); +} diff --git a/hw6/shader.vert b/hw6/shader.vert new file mode 100644 index 0000000..e5d222d --- /dev/null +++ b/hw6/shader.vert @@ -0,0 +1,16 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/hw7/RTXon.svg b/hw7/RTXon.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw7/RTXon.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw7/index.html b/hw7/index.html new file mode 100644 index 0000000..edb3d60 --- /dev/null +++ b/hw7/index.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + Magician + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+
+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + + + What's new: +

+      I made a 3D character with Bezier splines and surface of revolution.
+      Because human body is more complicated than the shapes I've created so far, + I first made up a simple and highly symmetric character and drew it on paper. Then I used the pen tool + in Adobe Illustrator to trace some segments of the contour in my drawing that I can use to create + the 3D mesh by rotating it. The selected paths are in the end of (see path.txt)
+      The character is supposed to look like the below image (or schema.svg). + It would look much more realistic if I use another spline to make the radius change over u. But this would require + a 3D model, which is hard for me to create.
+

+

+
+
+ + +
+ +

+

+ + +
+ + + + + + + + \ No newline at end of file diff --git a/hw7/lib7.ext.js b/hw7/lib7.ext.js new file mode 100644 index 0000000..1e27b31 --- /dev/null +++ b/hw7/lib7.ext.js @@ -0,0 +1,783 @@ +let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, startTime = Date.now(); +let lastTime = Date.now(), rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, selection = -1, dragging = false; +let overall_trans = matrix_identity(), overall_ground; +let rebuild = true, presentation = true, sRotation = matrix_identity(); +let facing = 1, running = 0; +let curr_mouse_pos = [-10, -10]; +schema.height=screen.height*.9; +for(let i = 0; i < ns; ++i) +{ + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; +} +function ev_supersample(e){ + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width)*multiplier; + let h = parseInt(canvas1.style.height)*multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} +function toggleFullscreen(element){ + if(fullscreen) + { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } + else{ + if(element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if(element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function(e){ + if(e === "no") + ; + else + btntoggled = true; + if(fullscreen){ + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + }else{ + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor="white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden=true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign="top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick=function(_){ + movescene = !movescene; + if(!movescene) + { + mov.innerText= "Move Scene"; + mov.style.width = "170px"; + } + else + { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;}); +document.addEventListener("fullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;}); +clrsel.onclick=function(_){ + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function(_){ + clrsel.onclick(); + if(!animating) + pause_resume(); + flags = 0; + moving = false; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for(let i = 0; i < ns; ++i) + { + SphTr[i]=matrix_identity(); + SphDletaR[i] = 0; + } + rtx.src='./RTXon.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick=function(e){ + if(ins.value>0 &&ins.value<=ns &&cns!=ins.value) + { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if(typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick=ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode:"ace/mode/glsl", + theme:"ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if(fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function(delta) { + if(typeof canvas1.setShaders === "function") + { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; + + +let pause_resume = function(){ + if(animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click',function(ev){ + if(!(shift && alt) && lastClick&& Date.now()-lastClick<200) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = pause_resume; +canvas1.addEventListener('mouseover', function(e){ + over = true; + if(e.offsetX >0 && e.offsetY > 0) + curr_mouse_pos = [2*e.offsetX/ parseInt(canvas1.style.width)-1, + 1-2*e.offsetY/ parseInt(canvas1.style.height), -1]; + else over = false; +}); +canvas1.addEventListener('mousedown', function(e){ + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1, + 1-2*e.offsetY/ parseInt(canvas1.style.height), -1]); + if(i >= 0) + { + dragging = true; + selected = true; + setUniform("1i", "sel", i); + selection = i; + } + else if(selected = true){ + dragging = false; + selected = false; + setUniform("1i", "sel", i); + selection = i; + } +}); +canvas1.addEventListener('mousemove', function(e){ + curr_mouse_pos = [2*e.offsetX/ parseInt(canvas1.style.width)-1, + 1-2*e.offsetY/ parseInt(canvas1.style.height), -1]; + if(!(mouselastX==undefined || mouselastY == undefined)&&moving){ + let dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + if(movescene){ + sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy/60)); + sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx/60)); + + } + else if(!selected) + { + mousedx -= dx/60; + mousedy -= dy/60; + positionsupdated = true; + }else if(dragging){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width), + 2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function(e){ + moving = false; + dragging = false; +}); +canvas1.addEventListener('mouseout', function(e){ + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function(e){ + if(!selected){ + mousedz += e.wheelDelta/600; + positionsupdated = true; + } + else{ + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function(e) {e.stopPropagation();}); +rtx.style.cursor="pointer"; +let rtswitch = function(){ + alert('Ray Tracing is off for now. I\'ll try to add it back as some sort of background or texture later.') + rtx.src='./RTXon.svg'; +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function(time){ + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps'; + start = time; + if(fpson) + ;//requestAnimationFrame(fpscounter); + else{ + start = undefined; + fps.innerHTML = ''; + } + }; +document.addEventListener('keydown',(e)=>{ + if(e.code.startsWith('Shift')) + shift = true; + if(e.code.startsWith('Control')) + ctrl = true; + if(e.code.startsWith('Alt')) + alt = true; + else if(ctrl && alt && e.code == 'KeyT'){ + const mask = 0x1; + flags = flags&!mask | (!(flags&mask)?mask:0); + setUniform('1i', 'flags', flags); + } + else if (ctrl &&e.code == 'KeyS'){ + let a = document.createElement('a'); + a.href = "data:text/plain,"+encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } + else if(ctrl && alt&&e.code == 'KeyR') + rtswitch(); + else if(ctrl && alt&&e.code == 'KeyN') + { + reset.onclick(); + } + else if(ctrl && alt&&e.code == 'KeyP') + pause_resume(); + else if(ctrl && alt&&e.code == 'KeyF') + if(!fpson) + { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if(e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft' + ||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS') + { + switch(e.code){ + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + running = 50; + rebuild = true; + } + if(fullscreen && selected ){ + if(e.code =='KeyF'||e.code =='KeyB'){ + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + + switch(e.code){ + case 'KeyB': + var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup',(e)=>{ + if(e.code.startsWith('Control')) + ctrl = false; + if(e.code.startsWith('Alt')) + alt = false; + if(e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = new Float32Array([ -1,-1,1,0,0,0,1,-1, 1,1,0,0,0,1,-1, -1,-1,0,0,0,1, -1,1,-1,0 ,0,0,1]); +let sphereMesh = createMesh(32, 32, uvToSphere); +let tubeMesh = createMesh(32, 2, uvToTube,0,1); +let diskMesh = createMesh(32, 2, uvToDisk,0,1); +let tubeMesh2 = createMesh(32, 2, uvToTube,0,2); +let diskNMesh2 = createMesh(32, 2, uvToDisk, -1,2); +let diskPMesh2 = createMesh(32, 2, uvToDisk, 1,2); +let tubeMesh3 = createMesh(32, 2, uvToTube,0,3); +let diskNMesh3 = createMesh(32, 2, uvToDisk, -1,3); +let diskPMesh3 = createMesh(32, 2, uvToDisk, 1,3); +let diskNMesh = createMesh(32, 2, uvToDisk, -1,1); +let diskPMesh = createMesh(32, 2, uvToDisk, 1,1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 5); +let head = createCube(1.5,1,1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => {delete objects; objects = [];}; + +let delta_height = 0, delta_l = [0,0]; +class State{ + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5*pi; + this.lf = this.rf = 0; + } + initialize(){ + this.leg = true; + this.progress = 0; + } + next(){ + //return this.presentation(); + if(running <= 0) + return {rh:.5*pi, lh:.5*pi, rf:0, lf:0, dh:0,dl:0} + running --; + const steps = 100; + let dl = 0; + if(this.progress >= steps/2) + { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-pi/5, 0.5*pi, 0.44*pi, 0.55*pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if(this.leg) + { + if(this.progress < steps/4) + { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } + else{ + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf-= delta[2]; + } + } + else{ + if(this.progress < steps/4) + { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } + else{ + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf-= delta[1]; + } + } + let delta_h = Math.max((1-cos(abs(this.lh - pi/2)))*.5+(1-cos(abs(this.lf)))*.6,(1-cos(abs(this.rh - pi/2)))*.5+(1-cos(abs(this.rf)))*.6); + this.progress++; + return {lh:this.lh, lf:this.lf, rh:this.rh,rf:this.rf, dh:delta_h, dl:1.8522/steps}; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], star = []; +let sakura = [], stars = [], nstars = 25; +let star2=[], newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3,.8,.6,.5]); +const curvey = matrix_multiply(hermiteMat, [-.2,.5,.7,.2]); +const curvez = matrix_multiply(hermiteMat, [-.5,.2,.3,.8]); +let adt = []; + +let build_objects = (state)=>{ + if(running === 0) + rebuild = false; + let {lh, lf, rh, rf, dh, dl} = state.next(); + delta_l[abs(facing)-1] += Math.sign(facing) * dl; + delta_height = dh; + clearObject(); + M.save(); + M.save(); + M.rotateX(pi/2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.translate(0,1,0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi/4); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.4); + M.rotateX(-0.53*pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0,0,1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi/4); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.4); + M.rotateX(-0.55*pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0,0,1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0,0,1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0,0,.5); + M.save(); + M.translate(0,0,.45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0,0,1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.restore(); + if(running < 0) + rebuild = false; +}; + +let build_splines = ()=>{ + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, innerN = Math.ceil((paths[0].length*n)/paths[1].length); + for(let j = 0; j < paths[0].length; ++j) + { + let xf = paths[0][j][0], yf = paths[0][j][1]; + for(var k = 0; k <= n; ++k){ + let t = k/n; + star1.push([7,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1]); + } + } + + for(let j = 13; j >=0; j--) + { + let xf = paths[1][j][0], yf = paths[1][j][1]; + for(var k = innerN-1; k >=0 ; --k){ + let t = k/(innerN-1); + star2.push([7,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1]); + } + } + for(let j = paths[1].length-1; j >12; --j) + { + let xf = paths[1][j][0], yf = paths[1][j][1]; + for(var k = innerN; k >=0 ; --k){ + let t = k/innerN; + star2.push([7,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1]); + } + } + for(let i = 0; i < star1.length; ++i){ + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for(let l = 2; l < 6; ++l) + { + adt[l - 2] = []; + for(let j = 0; j < paths[l].length; ++j) + { + let xf = paths[l][j][0], yf = paths[l][j][1]; + for(var k = 0; k <= n; ++k){ + let t = k/n; + adt[l-2].push(10+l,dot(xf, [t*t*t, t*t, t, 1]), + -dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1); + } + } + } + n = 20; + for(let l = 6; l < 13; ++l) + { + sakura[l-6] = []; + for(let j = 0; j < paths[l].length; ++j) + { + let xf = paths[l][j][0], yf = paths[l][j][1]; + for(var k = 0; k <= n; ++k){ + let t = k/n; + sakura[l-6].push(10,dot(xf, [t*t*t, t*t, t, 1]), + dot(yf, [t*t*t, t*t, t, 1]), + 0,0,0,1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for(let i = 0; i < star.length; i+=7) + { + star4[i] = 9; + newstar[i] = 8; + } + for(let i = 0; i < nstars; ++i){ + stars.push(star.slice()); + startz.push(.6*Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +let state = new State(); +var M = new Matrix(); +var buildsplines = true; +let magician = false, magician_neck = false, + magician_houki = false, body = false, + leg = false, hand = false ; +let sa2 = [ + 1.6,0.4, .9, 1., .3,.2, -.4,.8, + -.8, -.1, -1.4, .5, -1.6, -.5 +], fsa2 = [[matrix_multiply(bezierMat,[sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat,[sa2[1], sa2[3], sa2[5], sa2[7]])], + [matrix_multiply(bezierMat,[sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat,[sa2[7], sa2[9], sa2[11], sa2[13]])]], + random_rot = [0,0,0,0,0,0,0], random_rot_pos = [0,0,0,0,0,0,0], startz = []; + +let division = -1; +let changeID = (id, obj) => {for(let i = 0; i < obj.length; i+=7) obj[i] = id;} +function animate(gl) { + buildsplines &&= build_splines(); + magician = magician?magician:createMeshFromSpline( 13, 32, 4, 4, 0, (i,_,oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i ==16) return 2; + else if(oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck?magician_neck:createMeshFromSpline( 14, 32, 4, 5, -.2); + magician_houki = magician_houki?magician_houki:createMeshFromSpline( 15, 32, 4, 6); + body = body?body:createMeshFromSpline( 16, 32, 4, 7, 0, (i, tmp)=>{if(division < 0 && i == 25) division = tmp.length;}); + leg = leg?leg:createMeshFromSpline(17, 32, 4, 8); + hand = hand?hand:createMeshFromSpline(18, 32, 4, 9,.015); + + if(animating) + { + uTime = (Date.now() - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + else + { + uTime = (lastTime - startTime) / 1000; + setUniform('1f', 'uTime', uTime); + } + let curve = []; + let n = 8; + // for(let j = 0; j < paths[13].length; ++j) + // { + // let xf = paths[13][j][0], yf = paths[13][j][1]; + // for(var k = 0; k <= n; ++k){ + // let t = k/n; + // concat(curve, [7,dot(xf, [t*t*t, t*t, t, 1]), + // -dot(yf, [t*t*t, t*t, t, 1]), + // 0,0,0,1]); + // } + // } + // M.save() + // M.scale(0.3); + // setM(matrix_multiply(sRotation, M.value())); + // drawMesh(leg); + // M.restore(); + // return; + M.save() + if(presentation){ + M.rotateX(uTime/12); + M.rotateY(uTime/3); + M.rotateZ(uTime/6); + } else { + M.rotateX(rotTime/12); + M.rotateY(rotTime/3); + M.rotateZ(rotTime/6); + } + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0,0,-1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0,0,-.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1,0,0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save() + M.translate(-.06,0,.85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save() + M.translate(.06,0,.85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save() + M.translate(-.35,0,-.1); + M.rotateY(-pi/6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save() + M.translate(.35,0,-.1); + M.rotateY(pi/6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); + for(let i = 0; i < nstars; ++ i) + {M.save() + let f_idx = (uTime/(abs(sin(i*1234))*8 + 10))%2, + t = f_idx - Math.floor(f_idx); + f_idx -=t; + let curr_mat = [t*t*t, t*t, t, 1]; + M.translate(1.2*sin(sin(uTime/(i+2)+i + 8)*.9 + dot(fsa2[f_idx][0], curr_mat)*5 + startz[Math.floor(2*startz[i]*nstars)%nstars]), 1.2*cos(i + sin(uTime/(i/2+18)+12)*.1 + startz[nstars-i-1] + dot(fsa2[f_idx][1], curr_mat)*startz[(nstars + i)%nstars]*.4 + i * sin(random_rot_pos[i]/30)/pi), .65*startz[i] + .3*sin(random_rot_pos[i]/20)); + let epsilon = 1/(i*2+20); + let random = () => { + var u = 0, v = 0; + while(u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while(v === 0) v = Math.random(); + return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v )/pi; + } + if((window.performance.now() + i*(2+abs(random()))) %69 <2){ + changeID(Math.ceil(Math.random()*20)-5,stars[i]) + } + random_rot[i] += random()*epsilon; + random_rot_pos[i] += .5 + abs(random())/(pi+i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i]/10)*pi); + M.rotateX(.1*sin(random_rot_pos[i]/(i+50))*pi); + M.rotateY(.1*sin(random_rot_pos[i]/(i*2+50))*pi); + + M.scale(0.2 + startz[i]*.2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore();} + + if(over); +} + +requestAnimationFrame(fpscounter); +//pjsk.play(); diff --git a/hw7/lib7.header.js b/hw7/lib7.header.js new file mode 100644 index 0000000..d7997ad --- /dev/null +++ b/hw7/lib7.header.js @@ -0,0 +1,537 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, mousedy = 0, mousedz = 0; +let seldx = 0, seldy = 0, seldz = 0; +var enableSelection = false; +var cx = 1, cy = 1, sx = 0, sy = 0; +var mouselastX, mouselastY; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1,3,-3,1,3,-6,3,0,-3,3,0,0,1,0,0,0], + hermiteMat = [2,-3,0,1,-2,3,0,0,1,-2,1,0,1,-1,0,0], + catmullRomMat = [ -.5,1,-.5,0, 1.5,-2.5,0,1, -1.5,2,.5,0, .5,-.5,0,0 ]; +var starColors = [0.9921, 0.5378,0.7109, + 0.65, 0.56, 0.992, + 0.992,0.7994,0.2402, + 0.1760,0.5094,0.5378, + .1164, .1274, .2289, + .9784,.71,.4482, + ], n_shapes = starColors.length/3; +var editor = undefined +var cos = Math.cos, sin = Math.sin, tan = Math.tan, + acos = Math.acos, asin = Math.asin, atan = Math.atan, + sqrt = Math.sqrt, pi = Math.PI, abs = Math.abs; +var positionsupdated = true; +var paths = [], origpath= [], path_misc = []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, currY = 0, maxX = -10000, maxY = -10000, minX = 10000, minY = 10000; + var currShape = [], currCurve = []; + let i = 0; + let postProcess = () => { + if(currShape.length){ + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2){ + curve[j] = (curve[j] - minX) / span-spanX/(span*2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY/(span*2); + origpath.push(1,curve[j], curve[j+1],0,0,0,1); + if(j%6==0 && j > 5){ + let X = [], Y = []; + for(let k = j - 6; k <= j+1; k += 2){ + X.push(curve[k]); + Y.push(curve[k+1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]])))/2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX/(2*span), spanY/(2*span)]); + + } + } + } + let read_num = () =>{ + let num = 0, sign = 1, accepted = 0; + + while(i < text.length && (text[i] <'0' || text[i] > '9') && text[i]!='-') ++i; + if(text[i] == '-') + { + sign = -1; + ++i; + } + while(i < text.length&&text[i] >= '0' && text[i] <= '9'){ + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if(text[i] == '.'){ + i++; + let multiplier = 0.1; + accepted = 0; + while(i < text.length&&text[i] >= '0' && text[i] <= '9'){ + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], c_idx = 0, prevX = 0, prevY = 0, getC = ()=>{ + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if(delta){ + currX = prevX + read_num(); + currY = prevY + read_num(); + }else{ + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX? currX:maxX; + maxY = currY > maxY? currY:maxY; + minX = currX < minX? currX:minX; + minY = currY < minY? currY:minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while( i < text.length ){ + if(text[i] == 'z'){ + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N'){ + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c'){ + + prevX = currX; + prevY = currY; + + for(let j = 0; j < 3; ++j){ + get_next(true); + } + } else if (text[i] == 'C'){ + for(let j = 0; j < 3; ++j){ + get_next(); + } + } else if (text[i] == 'M'){ + get_next(); + } + else ++i; + } +}; +pathFetch.send(); +let vec_len = v =>{ + let len = 0; + for(let i = 0; i < v.length; ++ i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], det = 0, cofactor = (c, r) => { + let s = (i, j) => src[c+i & 3 | (r+j & 3) << 2]; + return (c+r & 1 ? -1 : 1) * ( (s(1,1) * (s(2,2) * s(3,3) - s(3,2) * s(2,3))) + - (s(2,1) * (s(1,2) * s(3,3) - s(3,2) * s(1,3))) + + (s(3,1) * (s(1,2) * s(2,3) - s(2,2) * s(1,3))) ); + } + for (let n = 0 ; n < 16 ; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0 ; n < 4 ; n++) det += src[n] * dst[n << 2]; + for (let n = 0 ; n < 16 ; n++) dst[n] /= det; + return dst; + } + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i ++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) + { + ret[i] = -ret[i]; + ret[i+1] += ret[i]/fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ= theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m*n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a) => { + let m = []; + for(let i = 0; i < a.length; ++ i) + m[i] = a[i] * c; + return m; +} +function dot(a, b){ + let m = 0; + for(let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} +function plus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} +function minus(a, b){ + let m = []; + for(let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} +function normalize(v){ + let res = []; + sum = 0; + for(let i = 0; i < v.length; ++ i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for(let i = 0; i < v.length; ++ i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function() { + let top = 0, m = [ matrix_identity() ]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_translate(x,y,z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_scale(x,y,z)); + this.value = () => m[top]; + this.save = () => { m[top+1] = m[top].slice(); top++; } + this.restore = () => --top; + } + + let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(m)); + } + //------ CREATING MESH SHAPES + + // CREATE A MESH FROM A PARAMETRIC FUNCTION + + let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0 ; v < 1 ; v += 1/nv) { + for (let u = 0 ; u <= 1 ; u += 1/nu) { + tmp = tmp.concat(f(u,v,oid,data)); + tmp = tmp.concat(f(u,v+1/nv,oid,data)); + } + tmp = tmp.concat(f(1,v,oid,data)); + tmp = tmp.concat(f(0,v+1/nv,oid,data)); + } + return new Float32Array(tmp); + } + //Create a Mesh from Splines + let createMeshFromSpline = ( idx, + nu, nv, oid = 16, additional_offset = 0, f = ()=>{}) => { + let S = paths[idx], meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0]/nv, curr_s = 0, curr_d = S[0][2]/Math.ceil(S[0][2]/ds), i = 0, s = 0; + let tmp = [], ret = undefined; + while (s < meta[0]-1/100000) { + + for (let u = 0 ; u <= 1 ; u += 1/nu) { + tmp = tmp.concat(getSurface(S[i][1],S[i][0],u,curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1],S[i][0],u,curr_s+curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1],S[i][0],0,curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1],S[i][0],1,curr_s+curr_d, idx, oid, additional_offset)); + if( ret = f(i, tmp, oid)){ + oid = ret; + } + curr_s += curr_d; + if(curr_s >= 1) + { + s += S[i][2]; + ++i; + if(i >= S.length) + break; + let curr_n = Math.ceil(S[i][2]/ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1/curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); + } + // GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + + let glueMeshes = (a, b) => { + let c = []; + for (let i = 0 ; i < a.length ; i++) + c.push(a[i]); // a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0 ; i < VERTEX_SIZE ; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0 ; i < b.length ; i++) + c.push(b[i]); // + b + return new Float32Array(c); + } + + + let uvToSphere = (u,v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x,y,z].concat(normalize([x, y, z])); + } + + let uvToTube = (u,v,i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i,x,y,z].concat(normalize([x,y,0])); + } + + let uvToDisk = (u,v,i,dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i,x,y,z].concat([0,0,Math.sign(z)]); + } + let uvToTorus = (u,v,i,r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta)*x; + x *=cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), ty = cos(theta),tsx = sin(phi), tsy = tsx*tx, tsz = cos(phi); + tsx*=-ty; + return [i,x, y, z].concat(normalize([ty*tsz*0.5, -tx*tsz, tx*tsy-ty*tsx])); + } + +let createCube = (w, h, l,id) => { + let mesh = []; + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,-1,0]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]); + mesh=mesh.concat([id, w/2,h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]); + + mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]); + mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]); + + mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]); + + mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]); + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]); + + mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]); + + mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]); + mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]); + + mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]); + mesh=mesh.concat([id, w/2,h/2,l/2,1,0,0]); + mesh=mesh.concat([id, w/2,-h/2,-l/2,1,0,0]); + mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]); + + mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]); + mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]); + + mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]); + mesh=mesh.concat([id, w/2,h/2,-l/2,0,1,0]); + mesh=mesh.concat([id, -w/2,h/2,l/2,0,1,0]); + mesh=mesh.concat([id, -w/2,h/2,-l/2,0,1,0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function hitTest(pos){ + if(!enableSelection) + return -1; + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz)/(fl+1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3); + + let W=normalize(minus(trPos, V)); + let tMin=10000.; + let iMin = -1; + for(let i=0;i0.){ + let t=-B-sqrt(D); + if(t > 0.0 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; + } + let matrix_transform = (m,p) => { + let x = p[0], y = p[1], z = p[2], w = p[3] === undefined ? 1 : p[3]; + let q = [ m[0]*x + m[4]*y + m[ 8]*z + m[12]*w, + m[1]*x + m[5]*y + m[ 9]*z + m[13]*w, + m[2]*x + m[6]*y + m[10]*z + m[14]*w, + m[3]*x + m[7]*y + m[11]*z + m[15]*w ]; + return p[3] === undefined ? [ q[0]/q[3],q[1]/q[3],q[2]/q[3] ] : q; + } + let evalSpline = (h,t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t*t*t*h[0] + t*t*h[1] + t*h[2] + h[3]; + } +let getSurface = (S0, S1, u, v,offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v)-path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), yy = sin(2 * pi * u); + let x = r0 *xx, // POSITION + y = r0 *yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx*yy, + nz = sin(tilt); + nx*=xx; + return [id, x,y,z, nx,ny,nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); diff --git a/hw7/lib7.js b/hw7/lib7.js new file mode 100644 index 0000000..67e2aa0 --- /dev/null +++ b/hw7/lib7.js @@ -0,0 +1,216 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;' + , 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {' + , ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +' + , ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);' + , ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);' + , ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;' + , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' + , '} return .5 * sin(r); }' +].join('\n'); +var ns = 0, cns = 0; +fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); + } +let texture = [], gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) + { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0+i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} + +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { throw 'Sorry, your browser does not support WebGL.'; } + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = 'There is no real magic, but Computer Graphics is a close one.'; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line+1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } + else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + for(let i = 0; i < ns; ++i){ + loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading. + textures[i] = i; + } + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); + positionsupdated = true; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + var offset = 0; + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for(let i = 0; i < n_shapes; i++){ + setUniform('3fv', 'starColors['+i+']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + //gl.enable(gl.BLEND); + //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4*7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4*7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4*7, 4*4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() { } + +function setUniform(type, name, a, b, c, d, e, f) { + if(gl) + { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +let drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} + diff --git a/hw7/paths.txt b/hw7/paths.txt new file mode 100644 index 0000000..b331549 --- /dev/null +++ b/hw7/paths.txt @@ -0,0 +1,104 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N \ No newline at end of file diff --git a/hw7/schema.ai b/hw7/schema.ai new file mode 100644 index 0000000..b1dd94b --- /dev/null +++ b/hw7/schema.ai @@ -0,0 +1,1210 @@ +%PDF-1.6 %âãÏÓ +1 0 obj <>/OCGs[21 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + schema + + + Adobe Illustrator 25.2 (Windows) + 2021-04-07T19:53:38+09:00 + 2021-04-07T19:53:38+08:00 + 2021-04-07T19:53:38+08:00 + + + + 116 + 256 + JPEG + /9j/4AAQSkZJRgABAgEBXgFeAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABABXgAAAAEA AQFeAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAB0AwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYqoW399df8ZR/yaTFVfFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqoW399df8AGUf8mkxVXxV2 KuxV2KuxV2KuxV2KuxV2KuxV2KuxVZNKIoXlIqEUsQOpoK4qgNNja3u7qJjUzP6jEdPUEcfqH/Zc hTFUyxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVQutzBH2klWp/wBQGT/jSmKqD7Syyd0uo6D/AF40 jP4PiqOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVQbe+ip2ikr7VZKffQ4qoSgtBqQAqTyAA6k+gm Ko4EMAQag7gjoRirsVdirsVdirsVdirsVdirsVdirsVdiqgn+903/GKL/iUmKutv766/4yj/AJNJ irrD/eG2/wCMSf8AERiqvirsVdirsVdirsVdirsVdirsVdirsVULf4prh+vxhFb/ACVUbD5MW+nF XW399df8ZR/yaTFXWW0JTp6bugXwUMeA/wCBpT2xVXxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqFjvA SNw0krKfEGRiCPmMVdbf311/xlH/ACaTFXW+09yDsS6sB/kmNQD96nFVfFXYq7FXYq7FXYq7FXYq 7FVO4kmjj5QwmdyaBAyr9JLHp8q4qhPq+qTf306QKdjHCC23+ueLV+jFVK60tFhJ9Z3kZlQSSLFI w5sFrV0Ymla0xVGwyKtw1qo+GGKNh1rRi69f9hirrb++uv8AjKP+TSYqgmSO+Swuiw43KCoCIw+J PUBHqK5FKH78VVv0ZKm9vdyRnoAwDIB7IOKjFV0UmpxyKk8STRk09aJqMPd0an/Cn6MVRmKuxV2K uxV2KuxV2KuxV2KqV2jPAeAqylXVfEowYL9NKYqlcF2yazdTy0+qNEqC4APFVi/efvD+zT6wfo+R xVXa/ij+u+kwlnqZIok+NiBCnxcV34/59cVUtGSb6pZQSAA2gZSBvxEQMKoT/MRUk4qm+KuxV2Ku xV2KuxV2KuxV2KuxV2KrZJI40LyMEQdWYgDfbqcVSm7vLd0uRao01QyUt1Lh5XXiWZlHH4V23P8A DFVNLpWv5eVpcLFIWV3ER+FnjiIJ41P7HXttiqPsb2yZfSWSIT8iJI1KqS/2iePWrfaxVG4q7FXY q7FXYq7FXYq7FXYq7FXYqll27Sy2zrvznKWyncbRuTIR0NKVHsNuuKoyyH7o0P7vkRFXrxX4a178 iC1ffFXW399df8ZR/wAmkxVAXwA5GX4gk0Syk7H0nlBVh4cOTLXwriqY27t8UUhrJEaE/wAyndW+ kdfeuKquKuxV2KuxV2KqUMjNJOpOyOFX5cFb9ZxVVxV2KuxVRuWYgQoaSS1AYfsqPtN9FdvemKpf cgTehHH8PrMiQcduEKMGdh4F1Xb2xVNgAoAAoBsAOgGKqFt/fXX/ABlH/JpMVUNUQL6Fxx5LHIBM vXlG4K0p3+IqRirlf0vj5cvQADtWvOBt1cnuVp+vxxVHYq7FXYqslmiiALtSuyjqSfAAbk/LFVP1 5n/uoDTs0p9MEfKjPX5riqhb/XRLckem59UVX4kp+6T9r4/1Yqr/AFtV/vkeH/KYVX5llLKPppiq uCGAINQdwR0IxV2Kpe8iyGRmNEkqGbwgQ0PT+dq0p2+WKr2jZWhuJBxYyryX+VWVo0X6C+/04qjc VULb++uv+Mo/5NJirrzdYox9p5U4/wCwb1D/AMKhxVRdWgkHEE+nVoQN6xnd4x7rSqj5DxxVVsmU RmEEERUEZHeMiqEeIptX2xVEYq7FVC0AeNZ2FZpB8Z7r4p7cTtiqviqhbf311/xlH/JpMVV8VUAB Fcqkf2ZQzMnZSKfEPCpO4+nxxV11LxHp8uAKl5XHVY16n59h/ZiqhbRcpaMvEIFZk7A/7rj/AOea 708TXFVe9P7pB3MsVB8pFJ/AVxVXxVQtv766/wCMo/5NJirrqoltW/ZEvxHsKxuo+9iBiqpNF6kZ WvFhuj91YdDiqBhdlCuq0ZVLCMb/AAA0kj+cbfZ+7xxVMFZWUMpBUioI3BBxVvFVBf3NwU/3XMSy ezgfEPpHxfOuKq+KqFt/fXX/ABlH/JpMVVyQoJJoBuSegGKoeFgqSXUvw8xyNf2Y1qVFPlufniqH LH4nkWpUq8q9y1f3UQPtUE+/zOKouJRBBWRhUAvNJ2J6scVaiRpH9eQUPSJD+yD3P+Uf7PGqqtiq hbf311/xlH/JpMVVpI0kQo4qp6jFVOF2VjDIauoqrfzL0r8x3/txVQuI2WYFNjIecZPQTKKUPX7a be1PE4qvtJF5emu0br6kIPUCtHSnbiT+NO2KonFVk0QljK1odirDqGBqD9BxVqCUup5DjIh4yL4E eHseoxVZbf311/xlH/JpMVdL++k9Ef3a0Mx7HuE+nqfb54qp3coc8AOaxsOafzyGhSP8Qx/pXFXW 0RMu55JCT8dKc5W+23yFeP3jtiqoP9IkDf7ojPw/5TDv8l7e+Kq+KuxVQtv766/4yj/k0mKq+KrJ ovUUCvF1PJH8G8cVWClxE0cg4SLQOB1VhuGU/iDiqEQOWANFl5Er4LOoPIU/lkX4vvPU4qivrcX1 f1v8rhx2r6nLhw8K8/h8MVV8VUbgFA1xGKyRqaqP21G/H+mKqCzESTCIgtNMFR+oH7hW5e+y4qi4 41jTivTqSepJ3JOKoRrZbi6NwjGIxHijr0dhsxYHZgPsjuPixVU4UVbRGNacppOhoSanb9p2r+OK okAKAAKAbADoBirsVU7eUyRliKEO67eCOVH6sVWW399df8ZR/wAmkxVekpaeRAPgQL8X+Uakj7qH 6cVVMVUZ0YETRiroKMo/aXqR8/D+3FVKeyiu0LLM6xzBWohABK7q9acq9Oh7Yq71I/qPD0l5U9L6 vT4efTjT+X+G+KovFXYqlOmAfXZF7JJcBR4BTEoA+Q2xVNsVW/u4ouyRoPoAGKqdqremZHFJJjzc HqKjZf8AYigxVWxVShkZpJ1J2Rwq/LgrfrOKqdvIsVpLK32Uedmp1oJGOKqGmmYXd3FK3JkEJbw5 mOjH6aYqibb++uv+Mo/5NJiq61kd4izGp5yAH/JDkL+GKquKqEP7uaSD9k/vI/kx+IfQ2/04qq+l Hz58RyrWvvSlfnTauKrsVdiqU6b/AL3Tf8Zbn/iUWKptiqldxvJaTRoKu8bKo8SQQMVXRTRSgmNg 1NmHQg+BB3B+eKr8VUI9r6YDoY4yR7kuK/cBiqg3/HLu/wDo5/4m+Kut/h1S4p/u37X/ADzSPj/y cOKq0BCy3ZJoBICSegHpJiq6xBWytwRQiNAQeoPEYqrYqh1dZbxHjIdI0dXYbgMzJQV8fhOKojFX Yq7FUp03/e6b/jLc/wDEosVTbFXYqpy28EpBkQMw+y/Rh8mG4+jFVn1Uj+7nljHccg//ACcD/hiq hNHeJdW7LOrBucQ5x1b4hzqSrIP9106YqoSpemO9T1YqSSrF/dtt6qRpX+87cq4q5EvV1Bn9WP4p Gi/u26tFG9ft+EeKukS9aC7pLFxnl9IH02/a4wk/3nZgcVR3oXB3a5YN3EaoF+gMHP44q76lbn+8 DS+IkZnWvjxJ4/cMVVwAAABQDoMVdirsVdiqU6b/AL3Tf8Zbn/iUWKptirsVdirsVULzaJZOhjkR uXgOQDH5cCa4qoSdbgdzdQ0HyERxV3/H5/0df9iuKuj+IQp1BuJmZf8AJV3NT8n4/TiqOxV2KuxV 2KuxV2KpTpv+903/ABluf+JRYqm2KuxV2KuxVQv/APeG5/4xP/xE4qoN8N7Q/wC/lkJ7BXhaNa/N lp92Kuk3adR9prqGg8eIiY/cFJxV1r/vRH/0df8AJ9cVR2KuxV2KuxV2KuxVKdN/3um/4y3P/Eos VTbFXYq7FXYqoX/+8Nz/AMYn/wCInFVC72mnbtGlvI3+rHK7N+AxVy/FfFR1WcyH/VECqfxcYq60 3lt37SxzSgeAkkRwPo5YqjsVdirsVdirsVdiqU6RvfX9f2JW4+3Njy+/gMVTbFXYq7FXYqoX/wDv Dc/8Yn/4icVULz/j+/5hV/5m4q6H/jqSf89P+IQYq6z/AOPH/mFb/mViqOxV2KuxV2KuxV2KpTo3 +92pf8Zf+NnxVNsVdirsVdiqleIz2k6IKs0bKo8SVIGKqLvC0wckGCe3YyE9CilafhIcVQ+n+p9b Hq19Tg3OvXl6dvWuKqtlv9Spvxtfi9uXp0r8+JxVHYq7FXYq7FXYq7FUp0b/AHu1L/jL/wAbPiqb Yq7FXYq7FXYqlMm1oE7xWtxET4mMohP08cVXvKYry5lAqY0lYA9KiOA4qibSIJLIqmqwpFBv1JQF q/c4xVFYq7FXYq7FXYq7FUFp9qsN1fOGJLyio/2If/jfFUbirsVdirsVdiqU3Pwtdr0RY7gL83SO Qj6SWOKuvdjdlftn1VYf5Bt0JP8AwQXFUfabiWQ/aeV6+HwH0x+CDFVfFXYq7FXYq7FXYqsSII0j A7yNyPzChf8AjXFV+KuxV2KuxV2KpZcxNK8sS0DSTsoJ6VNpTFVJpVnN7KlePoOxB6gSwxFf+IHF Uxs9o3U/aWWSo8OTlh94YHFVfFXYq7FXYq7FXYq7FXYq7FXYq7FXYqgLi1cXUTrOyCWblxUJsfRZ a/EG/lxVDtZBFuFjmkQF4Ld6cDyTjGu9VO9HPTFUzghMQcs/N3bkzUA6KFGw9lGKqmKuxV2KuxV2 KuxV2KuxV2KuxV2KuxVZJEHeNiaem3Ie9VK/8bYqoJF6r3K1pSeNq/6iRt/DFUVirsVdirsVdirs VdirsVdirsVdirsVdirsVULb++uv+Mo/5NJiqvirsVdirsVdir//2Q== + + + + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + xmp.did:1f11ab5d-adaf-4248-b844-d9f5cb5f62ba + uuid:4a121c01-23a7-4908-96cf-83074d4e1f43 + proof:pdf + + uuid:dab6724e-c618-4184-9b2e-7d44879e5e5f + xmp.did:008add62-65b7-3547-8416-6472cd533b2c + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + proof:pdf + + + + + saved + xmp.iid:1f11ab5d-adaf-4248-b844-d9f5cb5f62ba + 2021-04-07T19:10:31+08:00 + Adobe Illustrator 25.2 (Windows) + / + + + + Basic RGB + Document + AIRobin + 1 + False + False + + 800.000000 + 1600.000000 + Points + + + + HKS 88 E + + + + + + Default Swatch Group + 0 + + + + White + RGB + PROCESS + 255 + 255 + 255 + + + Black + RGB + PROCESS + 0 + 0 + 0 + + + RGB Red + RGB + PROCESS + 255 + 0 + 0 + + + RGB Yellow + RGB + PROCESS + 255 + 255 + 0 + + + RGB Green + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blue + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + HKS 88 E + SPOT + 100.000000 + LAB + 33.725491 + 2 + 3 + + + + + + Cold + 1 + + + + C=56 M=0 Y=20 K=0 + RGB + PROCESS + 101 + 200 + 208 + + + C=51 M=43 Y=0 K=0 + RGB + PROCESS + 131 + 139 + 197 + + + C=26 M=41 Y=0 K=0 + RGB + PROCESS + 186 + 155 + 201 + + + + + + Grays + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + + Adobe PDF library 15.00 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 5 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 26 0 R/TrimBox[0.0 0.0 800.0 1600.0]/Type/Page>> endobj 23 0 obj <>stream +H‰lWÍn&¹ ¼û)úZ#þèïÈ) ‚9äAN` ÏŸªbÛ;ƒÅÂ#}-‰d‹ä·¿¾^ßþüÚ¯?üñõzùý¥_6{¿6þ¿õ¯ÿãåï׿^¾½~ï×ë÷Ë®ëûë_^¾ý ËþçåwltügWžÓÆ>û²ž§…»~üöÂß~{¹­­q®»73»îlÓ6W¾üºw³ÅEì¸~àÓl{êÛ+`Cs×DZ–»­9ñ·Y~îÞæƆ·È¼¸\¿KËÙ6V£­Ôå~š¼†mÇÏÑÛ\xkµÞñFœÖyjµcƒŸg¶Aãv×À¯ÃËOË„-c´ìZ–åÓšsßàXÂô9ZZ™«>·ÀvêÀÆEt&›ÓL¬åº6'/xìA Æ™<±à&½Ñ<`ز6–çn‡'Vo}Ó´ÝÜ\o¶-œ³åÀS ®Xè ;ôÁÛYö|x.¶|mþtîàÅ(›DúgÿèeÔq®w›Œ2A6ú0âñ¡†Åñݨ+£pb…A7D߇p@覓«Y„Ü8‘†3 /«(¯ÈÃøðÊl‡îþ±Óµ±œ`ð¦už3|d‘z[øÙØÜà¿ê–¥/ÆûCàòÖˆ¹ë%3PœziÛÞ×ÖKÓiÞÆé>äÎR”°±r‰+®˜ìõFtl­Á°lÒOg6‘´ƒ‡È+òK.KëöœÙNë€æ1`2CÌ[ô"Œ“vf+R½¹é56>¼“_#5Vâ!mÀ¥CðžÙ×®ÅOðõ?1äãdá’Ä‘À=:ØdLX|BܶÖÁ¡Ÿë<’]ºàUá#3H~O“`:b¢Là™É]~ñܤ4`/Ì :›ÌõÀ-Û 9åL„xÇ#{€^üôrZe ÿê¼d•Uÿ}Q€:!<6dbƒº²w‹¡.ÿÈPƒæd1w’Š˜Øòá÷si`ð0|/ D"ûÁÉJ*r "–0iù+´`Æ~ ¤ ß 7”¢¸ÃÂeé +—̆/ß_þöEÒIl9-$ÙO’•ÆŽXñ]â&eñ¶2h«‹÷ ÍÞJc ã’; ¿–TÌŽô.[']‚ê>*Wº‹% Q)Nqy`ã0hpÆ¿ àKâ¬xË/íh‹`m}â”I‡ì-æ +œŒ y²K(™òU[ÊNÔ’gç@f‰E'`I_®´¤è0ôÑ)¹tX0—è¹^é¤G—…f0žŠ"ÃV¦â™K;PZ‘¹ã5²‰;îÅÌÎ4y¾êüœa¡6ù¡t¸”<‰17¬”M[þ±¥ Mml²yÏâ”#-'•a#¹ç¨ gÞ!.u Ðóù”ŠIñuÜ× (ópÙ›16–™K +h4ƒÁ á9Tù±@n­M¶a= ZgfmAN‘m¬=©wFd=ì¹ÞªqÌ'z²±¤0™ñeÈ°dŠš¿UäÁ»^ñ–R4uhñ‡™ÓæS:CIg‘àìA¥ëb +˜jË%«¶%CCÚP94NÆ=‘´„ÅfU;1e4õ s€‰Dk; Óz0–³‹ÕÉ  ¹+[ ­­l›,#‰ð/©Ë¢4LÙ¤dà:j7eH ¡Nt Æ,â†K7næ‘Q"*ÉIýAOV•iœR :^E= KOŒSŠïl5ˆrîQk¯P;:¤Úb—Èp¡Q”P‹I‘±0Òg=ÝÔCð篱‹ Qq>ú>ÖœÓؤТ<ɦýeƒ) €qÄUEÍÛsf<‰ÏlÀõ.f#Ç™eïAÐFI"åÍüÙ:*ÈS÷ƒ—… á‹1‡º$Fͯª +±6b(>Ð0ZçH¢œO¸ç™åä¦ tC·#± :uB ñ:Ol™j'Z_Q +6¶Œ(ÌVá +êM…6„°'lìÙ©^KçV«/í 6‚ñ‹ª³‘¨ðR‰–JùQt?’¹ÙY¼d-ÒÞàÍQS€J8ËŸq=‹"ú³@¦ÎøøpiÀ‚-/J.ðù‰8¨“@1Õ{H‚Ão§Ô¥«?{¾Y¦ëÓ©jƒËLåj3€½îpŽJô¡`‡‰ZÙV¶Œ[@tÕ„]P°hTbBy'MÜo +køC H"RÌbÈ-ÛºôJ*VöY +’b/€\%<«ë‡:°ú_˜µ<ÿØÀÏ}»_ v‚ìþ„ÙMÐÖh7£˜o¨ÝŸ`»¿àvîþŒÜýºû3v÷gðî/èÝ?ÃwÂïþ + É,Ù)?hÕº¼ã÷>¢—oèå“Foè±À­–ä‘*„nüÝ©áÈœ£R÷•*Ú\ª†|A½™;89‡ž¡J ‡‘ÔDˆ ƒˆŒiÁJZãYWÙ׳†VM`Êä…úïû¹©jˆ‘^UšÂrJi(K7ÃÉ¢£¾¦2““)‹)”,® hlâÝÑA½ 6¢eô +®ö¨Þ¨KpåhdÅt0.¾ìZÉ\¶ä?:8LjêQKÄŸ#”ÆÊx” ý@êë”Èc¹I ~‚oó1ƒ³í!ÀP´PUç(¹®Eô¶êåÁçÐ?õôƒ"‰6©õ;•…S¨Ù©úˆµ“þ§zµ³¬¾R ýתÇ7£Çvãrùü6fPl¥!-˜l9êÆÁcÄSÓ&í–ÎS‰5KºiÖÃaSc F»9 rÐMŶ¬*ë–L’ í*R†º Ag«ò“ðëiš|&›õÓàÔfRº¢ú룖•¥C »H«M˜lhêÙxƒ„ÍQ½ŠîSãÜÚzÃدÙîêâùÔ°-jLÀÔ\Û8‰”™³Òˆë…œƒ‹NÍ3hZ±;‹z…¬à­ ¡ í`ÉãZ#Ï»Õ,xá ã`•® ÃÃìJÏl¢Û¢bH§p<\ŽÊ@4Àº(ΣÍÒ zˬñŽ9‚¾)y<í[ýýŒnTŠí;ÖÎ^ìÿù+‡7´`/40yv¦¶£’2NÎVøߥ&]öÅ „A°bZMM^er2»MÌE|ŒîÃ77v%E©>!pgWOá,6`ôRÇ£ñí`:a<:Ñ <3``êŽ1¶Åà†?÷¾g·*ýà•#­¹‘¦ ˆ'ß% +ÇXD€/åPaˆ˜$l0“ ::)JR²#Ž]€íCf[ ¡0É/iªàuëå¿èuÅÔ0ôúðyó!ÀQIéˆ^%DôœØˆÑ%ð ÆV/ÉΗäb]@ûIû´Š“˼‚Z<*º“ ì=E¶#š,Uéà„aÀQ³ÛŠ'c¸C¬W±KÎZX£!8R0ƒÈ…-8ý<ìØè ’ÿlMgÌø®v¼4~P6’Ú)“ SÚj8h(®jAIN³Ðp€dO_M¶B—j (Ž¤)&¼>â]´ë¤þÿ/“$Éq+ˆîuŠ¸@´aî¤u›éöòç‘É «¥U™AøðqÅ­¤3…4?ݵ¼Äâ¶O¥Ì%ßÉ¿w¸^ýs½„ÓuÖZD2‚ÈMGkŠ’Æ°ÏŸ³íoù#Œ ZÍüY„D ñöFÀG M-g€›CSBŠÆ-¥6^…§éNn†ë§UÊc Òaõ꣉ô. ïƒAéÛÕÇË鎨ÎßPÚO!”•ÜUÿ—+tÁÌ÷c + _“þ}{ÌÞš¬”Àõ^bÑïc³ÿï5p‰¿L N êò-è•ŽäÏï5ͯ­Bï«$Évù  +d)QQ‘Rã<¦Õ£lfBš+—”£5G¥S°{4{8ÁÃ#+Ÿ'85ìJhn5ªRáœ%h¥ø¯ç$ Û±ç-·j¢ÑûC¬z5ßx…Ô²‚MÔ„áo”Á Z†N5y3ùOØ3H­=î™ÕN g&Þ‰Ëj"6.¹³aWÚâNrîD2š’»dHÜ ?‘ØçÉÔÜ]Èøò5¿Õueº£&HéÀÛH£¾a–ùGÊ™hP#°&)Ÿ>HƪèiÖQ¤¼#“ŽíÝ-s–Ì9ª*ǶÄâzZ‚YœÆIå Åœñ3?#Ž –î YÝ©ä#6eѥnjÔæ^ÅHÚÏI²ŸÓ¼ÒòàDÇ„$H„W95¤§ÔE ñc3Æ\ˆQûŒwÚ5Dÿ'÷)šóŽãÒIë+Ø]-:Jö(Úê4NKi~·׬Òsàê“lš›î8êqÇÀtÿÁ"$…ÇÅt¯FRÔ;§³aÿJÒæ6SS ‹n>·ïpyV-¢h*/eTèÛÜ+G®J•rµêÔ|ö:§á²ZÑ+xª8¿ 'eQ¾§šuð@KˆÂ«ó÷ôÅJ+|Æ,‰…›Ašr¸ GPl.¡¤ümZv¶LÇ\.vpÐgO‹å!k“³×GPúIÞÎ3Jw¤òlßEÓT%‚‹|ÜbVr$S ²ÖÓ¡'Jic–ÅdºúË0À°ç NÏXh´f)}àp‹G–CÐ&N,×Cƒ;n‚: Àô]¦ úÉ ÝÛ°½ñŽv5ô‚·KªŠ¥oæzä³¢À¶¸³Æü°g÷ìÉhå¦åøQ ^ßmL»pè1òׄ³5R ЭË3EšcA½ýr¾‹E‹œÈŒø¡\ɱBGÍ¥Uܲ; £Â‘KŸþyúË'¿mòá’7“´G®|XäÍ!oyóGÛãø±Ç›;>Ìñæ¶ÆñkvÆruÆ»1Þ}Ѷ˜¯¶xsÅ»)~{¢{àÍà 1ü°\üðn‡7´—7¼™áÝ Ã +×Å +NF¸~0|p}|Ðíônƒ|šàÃÃó¯þÁøð¿°¿«û…ùýzßÃúþà|ã{ø^Ø^þgÛëÚ1™PhÑvƼP­Îv=ʼn©Õ¶÷ºš¯üò¼oËûr<~‹á•¿»Úm +»Û»kÑ>nw3»o¯ãí¶ºüñºüú¶ºù²ÓÍ«Ó}]{ÙçÆÇçD)ln›k°Ô6×mÎ.W]îirß·^ß׃SHg8œY÷åoB¦ímÿÚ[¸[þØ›YôínâÑÝÜnÞfæÕ¨³…±å³ ª_Æfú26¥¡‡±…¯í«¯ÝlM°«Í_W SWS»yZ{=,-­mV|ÚÝÏÂÎæÅÎnn¦µ|™™>øͬS‡ó£N¹O¡ð·ó1~æSñ¹þì‹%së8RO—ì9]ueeYQ k¬ˆ¯V«÷ èä"݉„+´¾¿òÒ—–OZÐÒœ<Šá|9“"ÁXGç²O½›(~j j,(•ˆcH™ð·NFó!‰°ç-‰/öI¹EuÓØo¹°›4Ê,vpÆdÒèƒPIˆºãB(Åü’1Ãi3Œ-á"eÛJ J€{CvÉ’Æt_÷|2a®T)~â§kaX­,„d,å¿f6¦Ù¶õ"ÿ^›Òâœ;–ˆU(aöC<¨úâóœÅÐ%è•)ˆvÁÈ;Ó+šÎgÂŽš|×GõDE†3ùBÌÛ>¡’Ën¸¾‰š“dpà›–ª²—²_Gî¿Ä ]Îø«Ö.hZòv0’$k˜M'ŸÖvG!éŽOP&}SÝR·µ¼“?KÕJôT c†»¦8Tw \½x=¢ƒ8Û­ÖæÊSþØ™KÀ𾺬®9Ÿ¤d=}ub±‹êID®ë.E9ÖGÍÂ¥kL":E@jpJ2ga}Tâ¸Ñ²$CÊ_rdc¬]ØmÝYGÝoêÕC"$éxÅaÎú‘nµ°Þµ‰¡Í¨²]ÎŽ#夰E¡Óp¶¶ª‘±ziá¨LµÙ'_’”£¤ˆÝUsú[ú­¹P£ À ¢5Ñ{=mÛNrµfcÙ¦Û›Pf‹ÐQL2Ò¢á( G~O©¤»z¬J§Ë:ìUÇcÁ19¢êƒÝŽ]Ë¡±LOwPR4Ž1LëbgÀx:ž0ðj„VÞi£hüÔŸìî,Œ}09HZ,¾BD¥‚)ƒD’ß3Â*v÷ÈìÚ Æ¢ž¢œÐ¼&BHÕ3›Ò?WªFwSÔø'8¬›¨-pôàݘ㦹(noÔ’liõÒYfë§NrÙËi '«#Ù@Jî[âFGÚjFSã:”Þúè%IP³Æ¨…&h'Y€âÒÐÏ =»ÔÀkzX" t,mT¬Íµô³x£« <Í^ÂÆ{ˆ_'ÕÕøýôP„AÒE÷\ÙJ13® Í#S\ç'€(,šJkmÌõ¥}Õ!fô6¶üÆŠ¯È9T+OŽ@rù™§¿ÏHºÿKî8ÁÏ7X4Ô%À„:">„Î9­#–kGÍZ4Y±'“ýS;þ›õ! ò¨µ‚˯k?Ý*G|ãD:%qË~lŒ:ÂŽ¦eŽ-á?HnG‚"À#»€v»Š ã#î#Å*u|â_Ì~ +ƒÎÏ€ðÁÒ´ æ–)~BÂÇD+é€QÈ„Jßég=ç Ç—²í -RrÕ;i[šjeT °9ÁË7ÇÁ{õFäXa0‚Ԉ܆IY²tmÆ蜛¯;‹u6¸Áy+ó·`Ä2Ît×ö£%t˵FEÄhXƒÞ- åàu˜z‘R¾EbáOJ›úëÕŠë.å'¥´ÇdÀX3#Iö‚)6ö×}&°“·ÎïØq½Á•xåPìs?#¤v–¦ÎXB¬%Á¹†æêHpµ?’›êSÒ<“çyMŸZù4¹ª¬ÜíL4çbµÂ¹¯×ûáL3-J.U@ŸXVí_¶Ñ°paÆm vÅhh({9kJkLÈVˆªí$Ã"Å"{•`W€ ýù/×å’7 ÄЫø)~ÕÔÚ奔۠“ª¬ìaI ›ê†Ìp«•¿ø³ÐÓX7“9GÏ ‹Þ… ¯Ò+P0ºêÀ{X²Ú¶uj“Uç«Û%H¦º4v¡ÃY©3ÔGYeN–üFÿ 6£óü7è“Ô° ¦áãZÑ4ïµ @ÈrÑ7úpëx±Q°×ö÷h¥æM£ÉLV¹MXDÑšN×¾:zSb´3ÔÅtE6us¹»––jãɹI‘jXa>Z ‚!B +§­]ÑÐ3Ã5ÇÁs™‚ßóœèhÔÏhýXú}ß9gšë¡¼$·”ÙŽVã>ec œ__Ù/ŠßÞº7‰s¯¢¾¾??ôç¿Ê,„ +endstream endobj 26 0 obj <>stream +8;Yht>EU6T#Wtp)`\4m6f9oK(`#rXd?_T=;K/Mupb"_,9d`+A$hP +/+WQU#8@Gq72ObhW\<3pojunI7O+m.W/bc4irlkQXt\"Ui9@aE-9AYqFH\LlPkE;U +f*03)!9pt>NFO +kf.\'+R2YIh*o4%'-lpMVQ/YDh4C$0]`9MBcugd3l'NB3lsn:\E\T=aqZR'h`5-7Q +Mo+\]0gn1<"a::`dq@4$e>foKQ;I$dGi&p>`NiPkpJRKji2jaPS;,Y&9c.b)Tmn;T +5Tn37>Lh;2jQs.tTZ'](m"g>$=tdV"PKe)mO$g,]q;i5>""!!'>Fu>0V@fp*?k&gR +#@pQ).ok!d1X9/+&5OI0]^`OBSu2MmVpr`IZ`=UPR]en;)u/^i$0T+EHt(aiNRccb +T5q4U"4@e%)#B9c0lT$rX/^-Ig!hBn)qP#oSFnQ3KX85=Sf1@>;sgLYRQf56ZhTBn +NFVS5)L50mcO4'K(E?B3%4(tbHrB`P*-Vb<[L7S"I1602$U32~> +endstream endobj 27 0 obj [/Indexed/DeviceRGB 255 28 0 R] endobj 28 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 21 0 obj <> endobj 29 0 obj [/View/Design] endobj 30 0 obj <>>> endobj 25 0 obj <> endobj 24 0 obj [/Separation/HKS#2088#20E 31 0 R<>] endobj 31 0 obj [/ICCBased 32 0 R] endobj 32 0 obj <>stream +H‰b``ßâèâäÊ$ÀÀP\˜ì“˜¤ÂìçؘÀ 1¹¸À1 ÀÄÎËÏKeÀß®10‚èË: ³0åñÖä‚¢ }ˆRR‹“ô N*/)Š3FÙ"ŽFN@v›‡“‘#œ]’ZÒËàœ_PY”™žQ¢`hii©à˜’Ÿ”ª\Y\’š[¬à™—œ_T_”X’šT µ8@~u15 ÑÉ„ADd”„…›Ü´C‹™™‰"†°ñ™˜YXÙØ98¹¸yxùø…„EDÅÄ%$¥¤edåä•”UTÕÔ54µ´utõô ŒMLÍÌ-,­¬mlíìœ]\ÝÜ=<½¼}|ýüƒ‚CBÃÂn‹Ž‰‹OHLJNIMKÏÈÌÊÎÉÍË/(,*.)-+¯¨¬ª®©­«ohljnimkïèìêîéíëŸ0qÒä)S§MŸ1sÖì9sçÍ_°pÑâ%K—-_±rÕê5k×­ß°qÓæ-[·mß±s×î={÷í?pðÐá#G?qòÔé3gÏ¿pñÒå+W¯]¿qóÖí;wïÝððÑã'OŸ=ñòÕë7oß½ÿðñÓç/_¿}ÿñó×ï?ÿýõÿ÷?üB†ÿÿÿƒYÿ!x¤‡Ë¨ÿG´ÿ í,<ì +endstream endobj 33 0 obj [/Lab<>] endobj 7 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 24.0 +%%AI8_CreatorVersion: 25.2.1 +%%For: (Bill Sun) () +%%Title: (schema.ai) +%%CreationDate: 4/7/2021 7:53 PM +%%Canvassize: 16383 +%%BoundingBox: 70 -1541 740 -53 +%%HiResBoundingBox: 70.0306889924905 -1540.06382978723 739.877601468255 -53.0851063829814 +%%DocumentProcessColors: +%AI5_FileFormat 14.0 +%AI12_BuildNumber: 236 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%DocumentCustomColors: (HKS 88 E) +%%RGBCustomColor: 0.328318178653717 0.305199354887009 0.291489213705063 (HKS 88 E) +%%RGBProcessColor: 0 0 0 ([Registration]) +%AI3_Cropmarks: 0 -1600 800 0 +%AI3_TemplateBox: 400.5 -800.5 400.5 -800.5 +%AI3_TileBox: 94 -1196 706 -404 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 2 +%AI24_LargeCanvasScale: 1 +%AI9_ColorModel: 1 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: -142.148936170212 -524.404255319147 1.30555555555556 1859 967 18 0 0 56 107 0 0 0 1 1 0 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:0 -1100 +%AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 18 0 obj <>stream +%AI24_ZStandard_Data(µ/ýX´L>W†Ø (ÐØ´b@º #;´˜Pñ0AÊn".Ø¥ÈÞ´³ô |¼à +sVäþÖ Ë t1÷:ÃRe¨'žÅëé4Ç—ì'–ùÌIŠåpEejN/ë†u£*8"•2â+Æj%u+~xÄñ,ë5¤1Dq¼ ªhÅf½nj”LJ‚GdNŸÎ0ÅÐ÷ÑMd†gÆQØHyôÊÙ «aR¶éèDápÍÙGÊá›äHG—’yç¥i°â:å£_ëá*›+lnÌ]B ¡&úþ¨N¬ì¨.g2hZ5l¢z¾ã˜FMIr}zb +»©!9Uö]4¬÷îbã%:c¶n¢›;*A)›1%†¼9£Ž¹i9®iÝr7diŠ.<“bÉ´~Â^hfÜ7$ÕÖ‰ŸqS”Xõ!Ö…ÜQ¢8™v‘22󨉻S£q'ºýjn¼'s½ä8ÊعA'n+«é31¼¿Ä(ìV£%ìõýÎVÿQ3r#Ò®Š¿êÕ½g6ªn£,h›32Ë”œ‘ËV§ ³`ÍÌèuëA$®8®(Jú°Wâ):©7jR2jbèS{ÌQÔ2ÊÄÉIHŸŽ99çúù3•Q—wuwvÃ1%|c7¿ä’+}óiy«xέ¼šO<¦„™Õ™Oª%&í ‰×Åó‡ç ©hÝa&Œä<§½qVrúù6| +“uÜÄù2÷yTtq” “ôEEÚ/Dg%RóD¦^4ÿ®—:×÷/áÓjî"CLb~͇˜òb— +úÕ8fœš‰¤\çûÕ1U%¡[$6|‚–qRa#3΄GÆÈgõ†ïGåû®|§ßÍÑL>Ý7hXSÚä—ر¢ÎÓš|ãå¦W‚z2ò¡q%XcC~o:Š%ŸA'êeb_h3ØŠ¢Ú¨Ÿ¹¤EŸèɇ/ììD:ý4xŠsÇbB¯:骣Q®ªµuX²QãtêH‘{ua$f5»H}ߎ"5‡] ;ß!‡HEÝ7~¡We†o㕲KgÝq¾’’úÝèQüR±›«+½b4Š<›‘K’Ž;““M‰\kH%ª“!*}ŽGV2*Ý26ì¨~Ù8‘*™éŒHM­ÕòÍ°3‹úUéö™aGqÿ°PŒI2ô"ß £°=´½îfT„¦›­¯jµÝèŠ*z‰~¬_ˆ»M}Eb¢O•FÅØ[qÍVE¿ + ZéSÍ‘AE1V=+ÉJ÷îè5e£¬1;ÓÒ˨è´ÛEIáòXQ;wÚk9ÑåˆXfEböœ«dOw2±¡³ã‰TrR;éFÄ×`Q°ÌX»y8„V)½vE}¥Ú¥lã„¢ŸÊÑ¥ÅQ562öáØ(¡(³Üè6S£Š¢ägc?"#´¢¨ýdû•iˆP”ØG¯’†«hübfªjÜ‹(Œ>~Aâ ëB»Qº“¶·lj™HÌŠuR¬5Û¹æìg­±*‹\l·‹F…âŽÊ8ƒÄcYÊ´Ýêc¨æ¦Ã.ΤÐH]†>Šî½ ëèmoLQ¸îd¤N#‡íŽ91<·NFê´7RýlŽ6Ä»XÑHäW¼fÆœðݱ*ÕýLSÖ°¹³Œ9q2)Õ‰jÆbŠ8n¢îF¦náŒ2…/B„#ýÒôŽÄn„$[ëŠØO_U–ßÈU•C DˆÒ–:2Æ¢!"tB,ˆ +" €%¸€˜€  ðÁ20`ƒq8\@&˜ð,¸À $dðATÀd 0 .0!DÀl0A ø …:PÁT` +\@ ` 0Á6@'0>ÈLÁ„``P 8àpÀÄ<@A BB#8ˆ  4À€LØ *ðÁ`0Ø <ø@„0PÁ:ààƒ @à 6è`Â` RÁ&œÀƒ&Tàh0!8˜ B0¡AbbBhð0€d€Á€€6È@`@@ à Hð +LhApð„ „„`œtÀ€ 2 +4¨À7D°AlAØ€ƒ \pL¸€8Ѐƒ*°A"˜àÁL0 8`Á„À‘Ãå>ø .xÍ#–ý½ô¤Ü8QA0à# d ‚ : À`PBSÁ„ 4°Á„P€&EGauq:Š"™Aaæ9Dµx¸dQVŒ5?sÔ4€8ÀÀN@ îAX€AL€‚ Ü(Ѐ&Xàƒ "è \@Ä@ƒ &€€ œ4À@Â&ˆ  "p[>È`ƒ t0AX@ľÀ(\@6à`*˜A +&XlQb¢N@ødp4D<á 48 &`à‚q€‚È ‘¢„xP‚00Á 8è`Ø€ƒ 2Á t@"0%$6ìÏ|‡ˆ‹Øi~’Ìa´¡:ãq®3D `pwâh€<0x€ð@dðÁ.0A˜À˜@À`àA.˜š +t0á&@Á@ A(X¸ t€Å˜ &hÁÃä…@ˆ€:à@<ð`‚:¨ÀL0 +$pЀ@ˆ`éd @õÊ”2»ñ11µ³÷¦dÉÝÿ®ddx0Á4€ Ä&h¢„˜@À +8XÁ€`p°€@ˆ07E€ -ÈÀ0HÁH b&Xéì˜ð pÁ2À```‚ #à&hðA@ D€Ä6@Á„d° €8x@ Dàè  +lÀAt0Aœd¸b@|€Ax€ 4p€考0˜ÐC!‚1˜ð@ `€ T="‘×vRC›OÈšmÖDl³‰X«–Í&Wöö¹aR£¥b&Ý€ƒ >Àà,`H °° !‚ 2èàƒ @ `N4Ç@! Œæ¦'B‚„46-³Í×öJʾ-£%lâßVG‘ÏhL$>§­q5UªñMHÆ´WÓö^ã;Ú>eÈ‘ÍžDQÑ­$üÖ?Ìu² 9jé­û‘v©«–\}øăĆåùþð »Ä3I}g’™’gF´Ï–ž±þ·t´ÿLgOÿEb®ª|ߊŒrTE³U‘™•Íì$\É¥Ä훹¬øžW$ºU™HpFÜŽ‘hoäè&á(YFŽˆÊ[y×óŽ>E–øÜçµXbÊêZ2†DÅjG¿ó]ùôrIG¶}­ÔMãaÄ‘²T‘¥n&4Š¦¤¨Ÿ×<“p•Ñ‘LIÍÔŒ2oõƒXص44æ(Œ|£FTIiÈј½ÎE5¡ûš©¨æ—!*²²xDIEûà˜QÜE¨¤¡[*ÃW.±ô)—×¹èd$ÃGñxøLâ¡×öðu®CÃÇûhgè6×ð}´ÂäÎÐÉeó +O7»á£¯óá£øª(zÔQ”Š_Qâ×cçÕ–©öëVCÚR[[ÚŸvÇ+^4ÚÒÖª†7Ôñdã9vÅü.â δt3ñ­öóÙgácMl›x†L<šx.dbOK5„„Å®Šd»‘’ ±›*+Îe†â¢Ô("½„ä¤Dñiê!BÞ!/š"s#¦ˆÇH¶w<#` )YFØÒ‘³’U]YlY±Ä>q<ØÚ˜ºbL¤Y’™¾©8£¢ÝŽ ©Ff=Âì† GÔ]Ž`‰XÉr\ÅToÊF±"ß4±¤‘Í6»º ³vFì°]X6óA–zî÷¥!b%{Él,wŸ¿Í0/Zwt.ªšßý"ϸ“æó’4ž#*e$ݯÓ&#f~$ÝÊc6FÔuœ ªRìcBþ™\J? +3Ñ ¶Ù˜DD˜Wd½¡]Œ&‹"flX˜˜‰Ñ¯%öñ¢Íb2Ä(ƒ„‘|XÜê¬Ê"£01šèvB£Ì¢Å+ÛÝ SÐŒN=Ž«aºäd”µ*F/Iª)Fbs¾!«M/*«ÒD¤)U!‘^GGÛ 1Îy«äµ«d3¬Q‹’»ŠnK šœÔÇQH3ßzcÉôKÂt£¨þITÄÊt™ RÙãfã,Ž.wrÓ9÷8£‰ÏôjÙ8£(ª³Ó‡xCŽ‚ÊEöÐ¥®Ã''.ñ¬q rÙ”nzöô¬ãµxJw÷oa•OéîÄè†ý!¾q#¦ÒG^cÎŽ5b¨f$9-F3ZwÒZtüªð}F†¹'S7¯÷£®¸ã«5v'*Û鎉‰jHõ˜Å4Í)U¿³‰oh^ž âI~rÆ"²ÿ ‘ņæGsƒ¨&D×›áÔ”*HR£isº7Î6b·«!Z]˜TFóë°¸øªä‹¡ò´8|q­Ù _͹èÐðÆ\\ª<·]ã¨rc^ž£ŠŠÔô5wÅ ù´2lâø1Ñù§¨ˆÿL{<"»ÍïUUrcùŒFŽŒŸR1åùìÎ_¥bÆb¶‹yÜò±‘akx5¡¸OêÅŠám•*må‰ö*ù‹Tu/™LÆ¥HRzcEºÝ;xùÙ;.Tœ–ËŸÖE²¡á[#.ò‰xÿÝ(Ö=~ +s¯µ×iåÙ,4[IÙ†FÅJi—úÇ”¨ÌÄèDWÂbmEº¡¾&vyÝ žÇŒˆ‰)¨êèŒVb/2:ßRTE+3oc5FnTb!Žâ©˜Û'F¼˜O3¶Ÿü§´Ò«E†±Âv¤œª:f·ÿd8ÜÊØkpH¯Î] >"+"ÒèŸÎªn3Ž¯fPæŠS£ejºª ™ŠrΆ I}êg¼|™V·³ ™ú3ç¸v\b«—¢ÖQ¥»Â×I×ÑšÆ7ßYÔ!¥ *Q=¨ŠÕ¯ºÖ +²+¢9G}±zâ·¦ÃSÑëP|O“k;Æ&ïr†Ä\Vq*“¡øœÞÙê*ß#tÍle3³¤U|£gØ´Hñ)ô°© ÛÕ”‚Ìççµ×“·˜Õ¸¹Îé6}Ólp*êCÄ’ý¼ì4Äd•½^r5N +Ž™DV–RIû“Ò ÉÍ­XëÈͯÞAîÔ Gäæbš½yÆŽÆÊ¡²¹­nd·yK5È5KVŸ*²!—Êù¸óºõf°>s%›OUø¬Zgé0UÜ?Š5?:R¥Œ9¤8Û˜ª%œâò“oX±¡±†‰ÎŒKUVŽiXLyd$ߊ²‰Sp,V3Em2>lvª®ß”m.’Ã(ˆî¸e¤q±<ÌcÒÐ0Jë|7¬TG×Ìú’áÔkþ S¼å1jkêw«<%óZ¾LÑï¬Ø+ÊËTÍ V”)SuÓdzV½Ç ‡UXíºK7ÄÔO½*7zsµ`Q•[ô ¶ãØ´ +z¨Þ[G «bÈcV¯Ö†•/bó³á }6Øò¸Fþ¸ç)ëIìÚm.õ‹>Ç¥ŒŸv¼ŠŽ5óLí +±ûZ2âȤ#4lD_s42¢S7W§šƒd8«:ùVùÊ×g\mƉe^;Í0ÓÉÇfav¨'Å–i"W~ÔâÎe´Ó• ’C,^—ŠÝÜ;#â»Ð Eq(êžQžs´ÂþM5¤)¶jd(%ŽBe׫Cezj¸< ²^®ªW:²º‡SÕ³¸)=BºÉ<®F_î슬~g3®u²ºJµ¬ÞÇ•âŠ6Ì¢Cÿë3fãîqç1‰Ý#5Š/Óp ë”x´óz‰ÝÐìè«Û•b¶]ëãh‹ûø¯SÙÇÑÄ¢ŒXµˆÇ ï¤.v’>Ì ]Ý„h¸urÓ©ÛðÈQn̼FI|lqM1u'QÛžº˜¢ÄðC]5¾PÝÆxÒ:uuBC7r$ÕENÆ 9ô;x$’›§¼É QM¸(tC1ÒTâÖ"i<3tÖǤ†P=har~ë÷ªQ&ùIë“!Da$ìFŽuþ$[ãH\ÍÙp ¿™®Vq%¼ õG·9–ò£ûW£·kÕgèÎŽê7®ÄÜÌ‘(•õŽZv5®„ÉæŽ>ôGDÙs§ ý6”Q§šŸnrG1½Ó gx„ñ#¾Éù˜2¡´Žn”32<‚å::ÿ²’”\Ý-SbðTìèTæþqžoGÔ¢Q‹9º‘ºËÅÒ¸ + ±Ii̺·ÔI>£ˆhLT}uQñÜ£JQí„C¥Ø ‡7¥Ý¤Í”Ø»šS¿N¦Žb›Tm£°ü˜}›è·EìÓê=FÞ¯æß²]ø +ï\óŒÑÑ%éhT;áœçZeVbbÎKl.²¸ÃîÖí$I*ŸüßþÿÿK<6Ÿ­êg¶“ZX!‡Ì4'ËãñZ—ó^YÎØÙˆLÇHx¿ÙÉY}¦Ðþûõzý¿Ïr±Á:2û0*###ÞÐ0¢adj½ÊUeÃÈêdÃd«a2"&2“É2ˆ†jHUÝ 2;#Ý}.}˜ÍvÚˆlFU»)™"r)ZlLÒ¯âÕ ã Í0;+3Ñ2;2;{¯½ÔÎfÛF“Lèä]ëó¡Mh¬cÙ°¿>~™›–h¯ÒDZ´’}4"[ÚÍQzò‰Y¹£“QŪúªªôÉ«*o"q…'»– +dD56®±ÌŒLM›"ÓÈß §¢ÝÜLúó„?YfdèÍt“9µ„wCçÙ˜ÂÏÉ駣Sªóý~uŸÏ¥åúeUó]¯7ws3æønhN]ȳQÓðÎÆφÙhÔƒþðff¦3:ì\ÇMÕC\ÆS*Õz2¨®qP‡4:ŸòðÔäcfH>:þ°ÞÜŒËMņˆ"uÏB™“Á¹ñö:¤D6r£dŒ ieˆF†HuHyÈø-§ãfPÙŸÞQÆÚÇC*µ ©É|üåñŽ3‡?,fSC„4ÄèfH‡Œâ)ÂÅŽâŒ* š™QS37Þ£m¼ç—~ã­G†ŒL몣&gÆÑØ -oùò}S‡Ih†Éd¦k˜LRÃ$$DDDDd©D5í ¢¡³œKGõQ¢›’o”F©D-ÒIÇm¶qïwË0R}‘•ˆ>ˆŠªÇQ$¡s¡Õѱ±«Hƒ. ±©hˆ§†øªvCL{,N.#çÃÆ:¦#¾3ŽˆŽŽJ4NSçá"#טÛMl7Ö>ì÷×Ouý,r›M¡ÝŠ”Mþûow+á¯Z"_Í„r³Úo¨.dR›Mé¸ßX»:^ÜÉ¿Áó<º±a;»Ù­ìv;fcWEƒˆõ0ÑŒ¡v ½È¡#^o½ãÈE¤ž8ÎíÏ›c<¾ú¨Ç¬qöœÑœÙ8Û‡ø§ÝlÆK$¹ã5v¼.ëx_£}ØÜÜ’IЮç×ÕaoTÏh£:õUl€b°à0\°AH … @0á &œ`‚.ðÀ ‚.Æf%E2¥Ä²IV#Ïú‰ÕNìø*‘£Åj%$«ŠUE+¢‰ü âQ~¼±1:Uií í%æa²R¯]h4âÐ\)2ßJGh¶£‘HhÓ¥lEµZuì-VÑ„è1ͱV³‰U’Yåu5v¥óœˆ¬®=ÓY‘]o¦»²o˪ÊL/“ÐÌ%UVÖ3ßÙŽ%2ckôxbD;IÎÒ%Òÿ©3»³jÙݹxw>‰oUéÏ»•]ýsëCf£zfu›÷)†îeEŸoèfE³²7× Ù7þ™K:?—\éÇ~'J,rS\¹)¥) +íˆä¦¸jä&V9“ôVEáÍý´óg¼"é¬#©%ÝJêo§»ò&Z·’ÞÏáùÔÔ\R‰È¤Gc¶“:}8ÒV™Ys'hî44×eV4Ç9A/3»:éIrNµ¦t“¡á ÚÑt›fðhÈ},•YY©ŽcÂÚ›t:ÅY¬ôî4º/ŸÁ4w‡tÿ¤«Ðý_ÿÛNR?IB ë°¢ ©æ‚¦\ŹxèÌÝâUOåž®Wýü=]‚,3')ʩħ6—"Ùœ¨ª±Ô(w(áçŸTMTN16ªŒ›j'—©¡ y÷z§fœT“Ÿ ÇUЈD5ý…ê5qÜx¬æãfì‹õ•fØÖ.v¡ÁÚ¥Z»pZ +ÅN¼ +ªÚèˆÖ¯WTZüb?&±"Iñ‡Ä~rçHI±RbE±"Ç4jFÜäúKŒw5•³yõ²Ë¨~Ԉלb36âFBGu#4†6ä"µCûüì利*­d¤‘×Ù±1ÖXRe$U£é¥Rõ#1:ÕPyLå«RÙe™~nÔ7‚¬Ê.±ÙL‘ÕL¢Ê¤:¢9R¡{«Ì¡·Šæ­2I‹EÍ´îq÷’e>O¥ˆS7¥¬‘²ÈŒðy<±n/ã©>âˆÆSùîxºu£iFXI «gë4ÃÔª”gQÂÌtD ï'¡#:Iw«qF©Qd•¢Ä4Wo颫ÛjÔøêíňløŒF²C;²„#=,FbÕº×É‹ëÊ4V)‘ÑíL|Ùì>T,>=±I6Ej­¤[qé•VVTëYôUh>ÍGh,OqJhÒbY¤95i·bÑsž5ûR66Bö±:Ò˜¥×Õ’ivZVNì7$Ó¹)Ë!,£i'w}ªuk7ªÏiu㦽q^}ÎJ§˜hÎýR5~Â8?Ǫ̀ qQ*5f¡%ÌÌ ·7vÇJx4CýEÙé‘à Ò¥™¡ +iæü;5ͼ+š-¡ÔL«}™èhÎ!$q1K»Ží"F6¾x¤ŠS;3TSu²™ÏÌ¥Ucªê¡OjÜÑsñ=¤Ê ßÙh~ÖÈéÏÉÒÑøyjNÓt\Ûg~³»¡Ku27ö˜É kÇœ0:uì¾Ï}ñÍÉÿÜŒ+ò¹ZsãÎYgnìŠÒˆ§N3ÕQ¢iˆ¬D‰~6nŸ:xÂF~!Rj†ìÄ™)5»Ó\¬wš;a’N3$*ÓÓ ÑõÄ ±fÎéLY‰‘ñÌÔí÷½µÍOJÿç-‘©¯$cžeeƒ&lLªO¥”‘`× K´5º‰Oì9KÔðâ×I]fl=A“ˆýšj°ÕrQÊØn¾›–†œèM´ Vz§GÛ#ÿ%ÆZrd4F'£%Ê$4—ÖäФ2¦„ËÒüJg\nÔ„‰ËòœðY©1´¡=ì¢flÌ?©]üScž›ñ8±*Ç|·hœÔ.ˆg5»¨‹z6Žhôe’¹Ó1Ç‹ÏH©Ø»°©2™Ø»Ú½¯bëjÒ_¯v’*û?«ÙËgÙËÕn2£S™Bc§þ†…Œl˜g<¹ln”Œ•PLí£*—yFìõz˜OÎ{h¼™'cNV•kõQôº³×d–PåñMu÷ß*ƒœ//­èWCB¤ØÝ_+ù+d¬/%ÖjèÕ1,=wC3ž1I3õ:ÛÜâ +ùÒöDöÁªÛáÐC«Ê|Ní‰I;S©Kþ~Ä•*—Êè(®iÅ5%W|iÅ¿÷RSJ‘P(guµgÔ+Êé¡Ü*F±)vÞ—ÛV9sö"öXc¤ÐøñxÊi¦Ð˜ŸÐÄ!štÒyäèIµššÔSŒjn‘²¯©o—Ú¹lŽ/ÏH6ÒI¾UÉrNMÇ1ÖÙ¹RÓsDW]ìèˆnãêÉmglÈê%$ÉêZ=³Õµì³žÍÊZ"ÍI•‡5^–ù¿ºººÊ±ìËÎEsŒwf4tÖÚE“ÎÌnêUväG-WÊôy¤e³Úz&¼*©õQN3+Z™UYYMªe&‰x†ÔnedÅ*£b«¬îN&ÃkÊ$åÎP§È”19n²Ói)®ql¢ÔEZe?UÏ/v–ˆ«¦=%s|¶¾X'!‰Ä£7cTC2}«È# U©±’²[ÕŽ<³ÖQ¯¨TãµZ¦ "­±­¯533n…ìÍÕXÙ³ÇÓÚœ’òíXvfO²É>K²o)³öÙªÜÉÃÞf;KõŸS7ßf$•‡Y›’É„ã³ô§»ÏH?©¡‡:¦ ~;ƒoªÌÎÏ?™ê….t¡“X¾ÜwžRâLYȬ&ôûHkvÍ#µJvrÚ•7*>}þDŽccb5æ¬+‹é%ùà±CTÆÛr$ÎI‡©Ó*T'UêlY±Vèc’ 3íE›29Ö.6Ò‰ñ®¨ÄXVóè%Æ*ãÝI¬xcNºšõHe<*¹hÊeÿEÇ:iféœ “ÔÙ‹Zæ2ÿÜŸ…f·Þ"%‹ä|Þi¶z!K’:Œ•#Ÿž22¢Ì(Ö/M†µÃ"ߎÄW¢šjË,#—HÙtcÛg%ôF5ÊÙctÅ9ÒèEϵŽ-“ªêG÷üö[¡˜Ô=uenN|ÒMxB—ÓdèÈ"ãQ‘¯úÍÝäºmfΨFH(6t5WšÌPÛAÅVQUcE/£X†RF4ZŽ¦X$Bœz{­ûŽ,E›©qÈCì[e^ÇSvãùÉó^Icój>{¥hÊ&õÈžzM±±ËTgrÕ°j8WB6ßFNÅJŒ%7¼‡X Š½üªO‘ø\¼Ó¼ô9í#MjÔ«í~®Þ¾k(A®wå¶sÝH²éÞ­Ó Ýôl)K# +¥>D2³Q¤~ôÞyK¤®¼Ù”="GçÜ.W‘L£¹^fk¤.cg̲]c#ºçu1ÎX?¤©jµ7¿[•Õ$3»¹©zÏŠÉt^««ÓQ†{¹å"Ëk)/+vÛo)m]J×n« !iRhnž¡ÔDx•éÐpkÖ§äÑ uN +YC,#MÍ™v¼6EG"›§Âñux3O7bñ§ÇèwZˆc$Q<Œ(eoÙé%Õq"‘ÜNoïtâñÞŸYFÓ~N‘ùÞÏP†Ñ™]œ9r±>fD2"ºK—âý»ñb•™ g¨u:]·×]X3Ò¬»46YÆŽ2Žðèt6º†Í##L6gC£­PÇnÇþ¶Cî´C¬}NoHÛ+ËìˆÍË”üjM‰Ý~xúœµ]²}óÝ8ks¶_RE’íH4…l¬µÊÖåsæÕÇ¥U“|Èe-±‘¡Oi½<µÎ¶—oÔDs>yÊSšGJ:¿FnŒ´Lg"©#©»É+mË»ª1GnM©æÊ"ÎÅxT³¦t1:ou~‡8FzŸ=Òt ÍQ¬¦t¯¡ÓW›:kCs¤MÇgŽç£”~$¡ûÅp>ÇhtÈÎÒÿZ—oDš;²Ðë%=Ÿ¤¼×ÈJÌ7lÇ:óIÉèæ»Z‘‹Zª±IËŸú£,¹åšŽUqj[¤Ù·–/ûÇÎUÕørëˆ\iKE¶Xï[hü²žÉLÝ jGÆÔÞ¦Eï jY’­Qè™~è즿£k?|\9’ÏÉǥȠ[å³"~^®–ŸžLMŸÎ7h:BÓœöôÛÚڒƵÂaoÄ4%sŸ›ÛóWó÷gãš÷¹KúÒ–Ç´SgÇm[ºÏïjŸW™ì㌛ÞÔ4·VW+bgµºŽÛJÛÕ¾ºU?\ßìLÚ‰Zõ1ÞrXõkцõ>¿UÕú–êb…ÃZ;ã°V¯¦JßË,veWEåšQÈ›*3ž‘UýîhpÈëMY¬ÍÐTÃ9±Üõ£L¢fö®†ô™,¹–™Ã©¹Æ‡·ž9Ì,×ÇfãÃ9ž«–«]ÙLÙ¦|i}þBC’:ÉŽ~ô:úYhŒ”G¢^ÅRE<¹R²ÌéÆáx±~¢ÛØ»ï^aÝDaÉab…œ’oÅê±H<ÒTÑtF‹,Œ¶IŽ¶iì± ™"O"GôãÐÙ\É Ñ¾a]‰nFêèê,F¼³Îvg<ºÜÐ8cQK—wÆeª,Q‘kŒyUSïycÌ›©©1fÑŒ½Oë&¾q&EÈR¶Hýˆb㹩1_V]Ìdxè"ÖÇ Y5Ã^®ä«ªÑÜuC#6='¾ºšrØ3«×¼,?™»ª›°®ê'ÆÁ"õjvUgQ ݌õÔ±ìg³Ô,ú·t~¹£3ÕéšÑÝêD5„×Ë)-ý†?RIjÏ1:§¬ŒÍËR%çÏ™Å\K]X5ŠØ³éÍMOÃŒ¼‹‰G¢ã‰~4EE¥©òTqŠ~ÒéZ4Fi«t›»QGÖøqÇ!–\™^B7žEòEÓã‘ô9Iö)—JïÑvŠPɬŽÐFHˆÎh2ÖdLdÐY«3ïÏtŽ†¥½ÌÍ%å]¤îc'5JZëçRf¾ÜÙ(éù¢›qi™MÊ©+ ;'åKCg×KüñêqìÔœ5#›ÊØÜPÉ(Öí‹mÕ‘L ‰Ž,í;¤åYõÆ–ön¶Ïžý²›ql±å¥|ÛG|7CŠLèNãÒÖ岟òÍŠ†s)!º|OÉš·»ëo–†>5bÒ^ëÝÌ1'Kz÷™Í­c—!í>B™ÒÏhCÎ λt³dܱÎ8ý ýšù1yÔµGM£®kÚÏtCÊjîÆùÌseÜwq˜j¬¤hP‰ ±ßÑ3“UãÊ6w2'éÏçŸË¯·Sç¾yO<æs‹ø„ç»»“çGœzÐÑÍÝȦ¬sd3ÈìΓ³±ßɸ;»qÇóüàùÁ›éÇ«½ý8"Qé£DfR‰Î£DµŽóáá:é1ñ“A® ŠÝ(éÕ$3æAÚŒÛcŽS£jW4ÇÉ8G'ãÆ‘l˜ùRlqquPÉî¨ÖU­8ªÚñðLÍøØØÐLÆÍM]fÜüãò\ii2î1Ä14ŸŽ¡¡‘sÈ4åagÐãaUèƒEÇb “ùÃtk*«ÕlÔj5W‘‡Ñ?Cʵ‡””PJêþñ£l÷6>#3õuÌÌÌÌÌœ˜lÐÜFÍçÈC|SÓº/o4¬%£JúGµZ­VGuL©åøŒéŒ³j;¡qÅÏ8ZÇ]•Ô(Ÿ1$"¡nÃæj£°¬d_ä/·Üˆœú;‘ýz½_µ¤¹«ú{'C1óæÈQÌjHb%ÃÎåWm…bõõ2žÈëURIövŠ\EEýÉÙÕT݉‰B”&VÚþ2Eç-.¯,eZÙKŠ¨ò“"–U.¥2]õò¬gu—'¤•Œ]nÕ;³¢:‡¬ò\Þ–ÓFvUFfFEC:+“ú MC{ÎŒ‰]XæcKôY¯yttöÝNj:733Eæ™/¹èæ.e"ñ|R&žI"VíÃâ«^/_,_?_,‰ìõ®¨¤ïg6Ã"Ç)¥ÍÌH“8(B±€pï€À–>XšEá°Š”¥2† …:þ*ªñE°¤€ÙU®ÌE亮2T-¢Ó¯òR(ü:jAç°²qS*_å3¢ˆ^; ŠT&â{¯¢,%‚yZˆXþ#¢ׇŠéDËŸ~ˆþè!X3W‡ˆY®’z8An×Yj•!ª…U>¶BÏ—“ç…&ð,„‹«BPÂ*›…BÀ‚U~¢¯äU1¬ò÷"¬²fupú <øG}N&Ñé ¬i fïxæÀÀ*bCi•«Âw 2î®l%™®Pð +w%…ܼ’"û^1 ‘ã}eL`á?Q›`±ÙHPon@ Ër,î‰õh–ûÔÿàï…2Íщ ÖI¢÷äÀ"šx3ÿJ,/Ãi‚ðüÏÚ_Y¢‹ÄD®ŸA XÖº´ÀòwéJž 'R`‘nbLËâWÎt ÒAב:µÀr¨#A{7X’‚ˆ:XLF®i°Dq΃å:*ƒTE¸âϲNÁJ­WAª fXR9GÃñå/ _X¦A$8XÞv Twaùœö=v° “?½ (~|˜³2‰÷Ñ +9@ðqÿ!úù‡‹Mú(×ô‡M1–ÄÁåzýÀÛ:–)ç‡#"‹>Å š,Cyîñ×Ù„•e}Õ€_–¡õD³ìŠù€Æãƒ!ÎbÍ`= ¿t%âqsÐRM÷ðù{í5FËzØDJË7b=´¥JŽõy©eïél«åÝè!ÊÖ²Æó€º_ËÿÌC,²e]åT´å=ä¡zlË>ÆØpË›ÃCôê–¹wº­yKÜ~5ù-ìèj\ÌÖ2£á26x,×µ¸Ð•íèqÉÚ˜Éeõc$].Ï€:6—EÜ: îÈ‚®¸G—w¯E¨Ër©°³.O9:%vY4íîÚ%£ƒ”è.Lø.Æé9„¼Ls¨TðòÍA~âeTæP"QO…™—§XC/#jÕ ×“C¹g/v6„÷òÞãÐ\|Ù3²å|)Ì¿¾Ì5pˆ%xˆéðeÌ ‡ýñ¥tàþ³€{ _^3Ýæ„I`²´ ˜ëì œ’`†Œ7Œ[0JÚ ÄJÝcƒÉAn aVü6 «$ÌwÛ†¦R˜¥­ Xman7ŸÉØ2Æu³AóH%ÆnåÄbßa†^b´î(_ÄÌukØJŒö²N;1C¸^Ë£´ |Å`Wj`àb6-5 c~5¤°2f;N(iÌ×—†îý1i¸ŠO©*,@ŠEÑ°³Ç¨æÿÇÈUh˜CÈŒhøGdtÜ3¬‘íÎ0„$£ˆ¢Ûè’¹ÜÍ»ÉüK ÉOÁ2Ga†¼2sê¡2`t²2Nz‰e>x2¨­eV2té2ôêËLd¨½aæW&á~Ì\Ã1d3“vÆ€šÙòb€¢šùªbˆx6³ÎÄí›ù9ÄP8άÝa€/tæ CLvf-…”xæCnöÌê ÜúÌG†¾f…×æ}ƒÍlÿB†æ7z!šûõ…0*š4ñ¨ÍzöêóhÞϳ‹4¯¦{…“†ºwá‰+RÚ0ÍìÕ…qšFItØä4[>Zqo¼Ç«˜ÆfáB@6j¦úŠ,5ÏéÄA5“ײ¥j‚´²šƒX ñ ?žè”ð„¯-ì&ÔÌîY¸¶Öh@³@é·fôÊÂ]£Qdo¼æ×gÅ´bô5ø dÙB lÆ÷+änؼnÐa‹ÍŠÈ:òº‚ŒÍ$q….(›‚Ý·¢0›¤Ï +9kc…p¡Í KÞ^…fLm.$ɵ!®(§ló4TA·Ûf¦Bènóô¨ l€›QÛ]q,ÐMÜüíSÐ"ŽÂ‚%7ºzÝ3£kI7þVÈgëfd˜$¶ü?­¼x·›¥`ožÖ(ôÉ›ÑÏ^žôf&‹Â`ƒ†ªÞŒ +yã› +\Ø7Ðë7ÿz¼fÓ3vŸPÚ=Aꇻæ!ÈtÂ!tèÐù?C{ÑóBˆé¼W!¨ž”ÎŽL‡Æ²ÓÉe1Q'O¶©“î„eÕ1£þ«ã‹Ö‘…6ëÜW÷ÖÉK„@Ïë¬ v~r±ã5^ÙˆõìÜái'Ï ÚÂlGúÜŽ>¬pç6z¹“.ƒ@º³ Øu+ ]½ã3p|G:G¿ó¸?àIY„ÀÍàYçKѧÂ#‘8'ŽDüžÉ¯&ž:h%žƒ¹|»xØo< s:y,hGò°o @åªV\©¡<†´,L²<÷aŠyÒ% ×àÃôŒ˜>øêѨ|À¶ªg²°I“³žñàƒÕy=j·Š=S®+ÎíÒ_{æ pÏ:çätÏkûºMÞ“û辧x°é++¼©CœƒUHÜÈ |¤Á@Í°‡Ò„ö sº +н#/¯‚¼‹Ñ7.ø™ùy®4õÖ™ûÏf¼²L¡Ù½ï Ö„eøÂ;ˆ'³È&i¾PìwßÒµ‚S‘„ÅA¡GÂ|×A s®Ü\[pr™KzUÈ^Éë ñÕ–1‚ºf¯ƒqXÊè[è6rð: ò1Kˆš3¯×µoãÚòrõ Èϧø8G=»÷:€‚@­ª.½xä—e¹a^‘Îë %.¥(Ê$6–%y8`j`•ÙF}ûÚÎE#6aÙT¸ºøn›yW5ÓC¤ë@}mbw×Aá-O&éµâêƒ?àv¿Ë°0¿g@ôêô‚勈—Ða÷²œì)ø¬-ä3ꋆK·§³ÎsVë",¬(ÅŠ n€Å¡¢…“ôJçKQ&Šê€ÖÀF¬0qN©â¨÷ÂÂ0…éÊË—вRDLÚŠnléÀF.ÒcþÖõk¡è—/9ôAÕ9&®‰J<Ì8ùcp7ÏÉA;Ìêã×*„N(>¸»NZ‹Ü:ëØ8˜ˆ²ñ"KYÙÖu´î°Îƒ «D96t0ë'‡”¬…ä‹)†]„ä /]»¯[ÚKrÀúcld›?µk’å$ +4”[k$—¹Ž¹Öö}§ƒvéÞRRIì… ðì&Lô + +了mj7ÿ¦?šËu'8R{[ÿæÕ]·7ˆŽ°@" ɲíÌA@<|È znIq¨ðl, ©˜4º ñ+mÀñéó~­zN~D1Ñæ$m@£F/m ^[B‹û« À…dƒ^¡+f`Bîþl>XTFQ•· ¨'ÿl$äèj <ˆ‘êxcȈÈšg5`[xñO*Oç£:#¦K À"Á~Ó€Îæ{Ö Èö¤IÏ`ð@ÆbêØ^è\35†ì³@EQ40/,+ ƒgÐyüYÑR8?Ð{3^?c\àz3:Õ g›4›Ýe ›»Òû¨ ÊBPð,´®تd:2XQ;’BàžcpõÅ»‘÷ú»ãñ2½çUåvh-_@½ˆä7aô…Aé,²ÑÓ\2:ØsCÍ “Ç ÔœˆÛ½€3¡Ñþ)TI] Hë-˜W‚˜ÎÜ(#D‘Q¼ÀqT£át_›ì‚^û£xn™X@HX s9v«”m™| ê@Àç:Npµ±}3I•ÿÄZ@Ò“%(Ç<-h‹i·ª‰=ŒîDJ9¦Néô! œ’~ÓPƒcl‡q½eÕ7ZÓ©©Hôe›:à0Õ}‚Äȶƒ¯A Ót"ŠsR.p‚®@0WƒOb%™w­ ô„-€Ü©X$M—Û;¹VÑ þ"Y¼ª´¨ÿ©à³"—Kà´²Æ0*¸©ÈwñGxOçð]ì:øƒÐܼPÍ,ü?*ÐDA»4DðŠÐEˆHAáÒ•Æ jÊ–Š~¥ÊËéå„(˜wÏáçSpm œƒ‚rÿ5OÍ—JŽ€?ºž$PKµ5ßz‚,þÕ¸ÐÈav·V¯O;Úf;NFû˜Žn)gÈÓîö8R.ƒ&pUkâbö!^ã_7æåñÕQ +x ˜À`hæ3–Ï*·9–½Íº%8 :é{ +dÓ• ð‡‰%¸:J !gi¼” »Dݹ“=VSp,ÙÓ–o÷8I MÂQàoFJÊŠ@“<Úü,_A;)È€ñž­òˆü4õïÙþœšó Ž`P4ÔñøáRBÂÔ3‚?­1 -îÑChRøÆ‚)VîÛ5Zž˜Ä›Ô(ÐíB(܈"O¿¯º©b–«C |P†*† ] ÂÈ­}ˆBpåj€øAP3`ZK* ‚Z±›˜Žêêbº.ßA‚>SVAæ;‚Ô?€"XÂ*Gx:D¥X"Þ$–ݵ¹Œ ²>@–kŽ§B’“âä5¢åc•Ïe”[ƒX€N†+˜ä‰ãJ^Ÿt³­EÔø¬§t€&E­…<‰ø_„ØIRë@]‡Ë™ŠPd?[†½9s`×íò5S=ƒ Ìù™QÿÈÆ×æø¶0äD((ì3UJ| öo`ö<0Øäš©lfãð  Ô‚tîJã¶{‘,E„ó “ ˆjãÙ€@nïth"MYo @ ++*«ÐÃ0R™,¼P—IhŸO…nR¨›?èü‚^À›„Ås-J±¶Ë¶PF2Px:SÂ$ÅÝM¸h,1pˆk|‰uÄÝ0ÆÜ·áh)€棹ñ#¿i/°(–8âÛ.P(žR¯¹@².w{@Ëj¹jT+-^O ä„™˜¢bð²€æá+œÄ‹íh.¼¬í©!W (¡2§ØZ”2°MÌX{¯átªO„½-±è©@ ÜU¦S ¸BHsÇSð;|@Lá|°Ö|Žf›R +ÈÙíäOã›S(Ï¿_ "iìMy…ÓÕõpÜñ…›6²;pP4D.hÁ^£j'{ xÏ_,lgê9ˆæK`×#«ïIÆ­ÊÉܸÇþÎÌ•K›*Õ<—˜©úG$@ñŠUÖå>Á(”wÞ¸ËJ]U÷¤W2ª<`Î>E¤Š:K$.@J< +\DÞ¬…ÀöR>ÙŸu°ÞÅ~Z!¾n•ÇnD„þh¿-úkŸòèYö…+þ€þά`"H¤Û3"RhÒ]UÐ$5Jeõ€{GmìØ‹GJc°ÞÂzðØpÝ4Vƒm° Y«§K6ŽŸç€Ò 𒸈Šx>`@^pÝ›1È/J!èƒ;š 8MW@ô@a9ª¿Y.D +<ÿÉ0Fòì®Vò¥% »°fÀÑ­…oTtwiØŒ ›\ mJƒp˜ˆá/ 7„6òè‰ì®OLä1°¶öhýÓkˆ²å³ÈÑ\Y @ML:ͳ«Žä=|.ÍÐÃÉ Jèyjì“5ÈahŒ³ßTž{’Íño.¢ÖH{g'ÅK@]a©¡Ðz|_; PUì@¿ýõÔ¾#Œp¤¸Æþ›~­ø[ã<úŒr +b,œEƒ*“Åä°<µà –äW¨×Qó<ÿhgwfôµªWH,:S2)û‡˜GÜ:œÑà}m£¼' @xH íI;$` ­@¤÷ÁzúHš[½K¿¡Ã`/3ǘº+€3òb‘ûsV4Jú%¿€*'@»Ô¹\žW“ÉÄ·Õ@¹ŸšKÉ_«m‰ +‹ÛrÞÔ-Ê‹ÕÏÜ<8_‹P_õmÊ€º :ç‚HpÙ~ 5g.ÄÙ6ÓÍ[‡Pñpë.š*€Ñ‰ ÊZ¤€™i¦ŸX_V,†¤ oÙÈ“qÐA€¾²34z‹UI—@1“Á¦ åpE(pRªÛŸ>±ñ`Ó+Wü ‚vxHcÜXÌ„½Smx.W)š•h|á{líôcU«;Aïÿqéã$ú°YñÿI8ù&8ÿ€xЂéì_YÕbþ?›iú->õÿ«u+Ð2µ{yWø¿ˆÂÕîØþÏûUò@V¥ƒ¸èþÖÓc/'ŽÿÛÏ‘ôÔ!øŸª(^*ë Òÿ›ƪ +Z1š«“8(ÂBü_c¨ÄÒ¥½täã7øߪU9ù-íW7`"ýé,MÊœs½Âë-ðëÃßvý/DÒ·<£…˜ _ÿI7#—‡ÜÂnßyJù%:^ûúKàl`Ú_ÌÏ„™ÀKÖ2:ûAÔ\=NQö <ªæ-g#—b¿R0É´¥Ø¶»¾/ç¥ ì°¼;‚(oÂëK"#>ø(«·þe4=@Ê +šõ¹­^ + Œ­„_¡×‡ª/=·Ÿor¥Î¼GKýúln¦L¨ŸYåÿ¸Éé‡zÏÀϤÖ`ç$¦/m¡GÝkj”~c|ÅPˆÀ ÷£¯9)RvÔjÌ([ôc:}(&5ôIG’Xg{r,ú”@Ù¤…˜Üç?>F26bîóŸçÉG§'¬¡-Ï×ù„Õ åù<•á¨KM¤ó}R …ãà|pÊ°@†B\óßÞ=ÖTÚ•Ä™ïA*ÊŲ½­Ï[ÌVÄ0RÎËsÆ]¦žÙ.€–_f;øXµ°Ê¿=´ÈA‹jŸ(æóøfòõv/Y-¦eò}¥Í.åyÞññQLZËo +‰/ìÈo…nöi^ …œÌ G~råBÐÆ‘ÿœ(•÷ˆéÈÏ +cÀ–TRȇ¦‹:¿QbQÈõXâ"…|’;àЪ;„¤Ž|DBõ]!öÃnZx†7¹‡/Á/]Œvvpå|üÚ0AÿùøÞ•$l‹üv¢0_6 韉t€l‚:‡R”—¨8þðïZÿ|üKéî,tŸ¬TÉù·ÇÑpÎrâwÍ_ûU·Õö¡BZŽïd±úµìÓX¸ù\Ÿ#G`|Õ‡6$dURà¼+¾ã±û··A8ñ…ß#A᫈¿'8^3‚RááGÉCBÞ3|^¨r'&^‹“·tÿÎZ _÷ÞøCkÕƒr:Ö•ú§ÿ¾hi3\ öÒ=[ вjw¦ÿß;r÷¥Ãþfß3°ß?Ìc–¦§çVÜö½ŒÄh|ï¯Ñ;x‚Y`ÿÚûaŽ†ø®èýsú¬–4¸;*î€e(ùká¦ÃGô÷¥™ë? PÚp½Æ,¦(4 nlÕëžCìšyçû@|ĵGÃsoVÝOÒ×ä>¹ÑUýÍ|Žd™IÜÛú<(r‡ºŽpßèeklÀÛã6!ØO*áíçØ@•F€õo{Ç2LõåƒÙ¾¿åqó*l³}…|_˜~ÕµÇúN’=Áao¨ö ðÐŽy®¨K{¦*Œˆ+´ƒùy Lœ½IÕ´[=j yÙŒLÄ“û»H0Ù;žÍœÏúÖë +3­kÈ9¤=Åž¸MšÎ bż°‡!Ù±üo3DœÀ>ÂCÕXБçë{?4FÆûáûÄ/”ïib¬0Z=ס嬑®‘”°ì&{ë§\·Z­'.žt‡¼.U•3ëç@J>hL­0¬4‡ü]ˆ®þÈ;ªï_ FÜX½PÁ”BVúTý*ËÉ`ØIê iÝèÆ¥>D¤Ò…Ã?Õx¹£¾ ­´(Þ õÔÞ2òÑ Íûôåÿ¸­Aøy)¤ÓS¶@ÝðxMùýG5ÓÏŸàaBãÎÒ¯hGû¡ôCA·ŠÀÎeÅ‘¾°-|Ï٦Ň|ôO¬³Ï“`P5zó|,z­§š«,úÉ>mÔÿO¢/p©¬g®ßziÍDÊmUÄQèñÖ7JSä—cÛ‚>yzЧˮ6 ©‚ŸUr$æþ BÞóãa—MÕ¿¯˜diºu”²ÃÕdÅò6_Ÿÿ‘)I¿Ìx‘_)ì,qF9ŒoˆÚ,ö!¦ÌŠ.~ TþhçG¦f,>G‹î'ƒ+*¾%$ÌGEp-b?ñFú.€¬:jK<ìjV»¤EªŠ ©Ôí*ÃßêV!Yw7zø”à;Î,¼N¡cý{Fi­)Ïð8™ÓüBq»—ž`Nýõ|´6”¬ðj9ÀÌ?ŸMxæ–­p þLú[)a΃imäÓä)•¦ Þqü.K/„ƒá“LïŠ<¦½_â~S¯¢ÖVï璉G¨>C9©ª‰Þã ˆ`½ëqK r>H›y/6u‡%íyŸˆÌïí“û\†À;@&ž北ŽwOA_ÄNZ²ÌäwØ)íÙ]K#‘G^÷Zöè8š”U÷ÂZS‡V÷ÚÂ~ Ó¿ñb¹Ó=éß⊅ªew©0+º“ƒrˆ£ŒñÜk±b=DG¢#=tlwøÊM¹++Þ‚ÖkAÕ¤‰r w˜þÑùºL´X ^ÜósËû:Óv¸+H.¦OÜ)›àN +k j'-°|>¾öVåµË4w¬Öšâ¥Û j=p:à!¸Ý½"Jœx{m_Ù©gÇ®E*ßê#'ÛÝ7¶“ÞWqålcÙ^;@!µ³ö*mºq”Y{¸Ü‚öªö0)7 µû®´cÚ÷ŽÑ±qã|ì£[à+¥‡aA»ÐíçÁçdfv3 o0î²;F§—Š&bËሗ#d_²++ª¤É-¸+d‚y=W8TÈŽéù/dNÓ9v®‹,S;%GÁ€±“`"SWŒ¹Ï"Ï­àçÄî„!Äþ!ëµð©Ã!/ì‘÷Ê(^PRø 9î`G}@8c§€q`_)¸Trv‚þõõ•OßT›ÝóëëlÕ44l·á–zýB¡R¯óÜ’iµâ,x ;ÝUgB_×ënujº¼ç\–(H¦+3ô.‰ë9™/AÎLíÖ#¤;9ƒtÙ:a›<³u[‰•k8ž9زjxüºÑŽ|h]Îá>¤pf]Ä ÐõX7K’?·CÏ”Jjɸ¸þúêi+ S¥„ÿ@WgâÇ ZØv ku([Ò>º8½V¿.>í.Åê_J—¨ŽžUg]Ø ýˆ¶…ªwr°òÕ*b‘êÞ8H“|õö§]a=¨ðÛÔ™L žßR?ꢬ˜R‚O³n’/j£î4Wô´­ÿõ(^’Ú–…q_êÑÍ—²#&¶ø9@¾-Ÿ§=6KöcÊ)ãö2u<¹‹@x‡Êõ«˜ÿ%À«â2]– e·éŒË4Cª-g¹Õ°H¦· Äõ…Ò‚£É÷ÖˆWdÎ>Ýu0“û:é *˜Q:?îÎBï7ïÑÐf?ÒAlQ¢#ˆ†ô]¶óZX­\råG¿-Ãrèèæa]ѸF óšƒfó»Ãèð:л«^\¿¢—†]ûHkÙšènæÉúŠßí‰èäÍŒ°Z‡žu-†.£b…B¿ZÑÆ_Ì5YP3’®ôcd¤Ž¥Á@_eû7=( —Õ,€5°ñHæBÏúsÔÓºï òe‰Ÿ¿™oü (’gÉ@ŸÏ†J·ßsáû<{=ØzNõ–Lñ/|³( Ì5Ïe¡áÚ<ˆD˜~6;⹩ (dyî3Ù_»É»Cu‚Œoq ëNΙŽYž™VD +|Î[Ù¡¢œÎ‰M—%žÓ97F­ˆãúœ¶i}Î?&”gäϹYm³] +ü|ξÏÐÂÔ9žä“jSyLEPçƒýÑNhæÂÔùxc䑧Ík£²s"i}€A›:oë“ÙRçe«f±Ô9 ›3]a,L©seÄX¶ßIƒ2y$*t‡â?u.ås x¢ÖSç`|5RÜRçÜdN¦N§›cêü‰×ÅÈJÃtt ©sìY§¤8ü(èü¶BKPŠËùÇ:}æåœBjÂá… ár.Û‹1i=Äå< ³­Z!;ηlKÕ` ˜Î㦖UâG H„×÷æë VÔ"¹¹22Ÿêû ¥"@Ú\¿€o¹©€Í©ö%ñ+Ps5Ï'̧4è4¿Õ yÍt#¢¹'ÞòÌ—å“·eíu3:ââ™9ú¦¿­2_©d¿PÑVÃüÖÔ¡d\Œ¹}9â, éýÃ<Òy½,»ô!âTƒ9Å~ªQ~9rIoOžŠs^>3ÅÙ ²J2 Xí•· —¿„S%„p9Ÿ’ïecˆ…-ÿ>/ò ÍrIž5&–CÕW­S–ááŒ;šÏ2Š²r¶ö+›¥Ê¿¢V̨œ«§Šøæ3—òÛÔÐä£Xåc<§YJÔˆ¡Üeü|Íq3öÉÇÊS—)Þ0þ‘ŠÖä,ÛŽŽ˜üdz"bŽƒ%y@‰º!Ö¿˜Ú“\ ;JV$Ç“ëò(p‘¯N+sþúI3ò"n‡ŽJwù?kÝ¥E +o!ò<%gINy0 y¿3·) ‘íAînzÌ™AžË@N¯õ=+^ŒŽFÜkF¹ûãuéÖL° ãèÇݽœžÖ•ÕòqÜå?Þ>mGŽâÞ¸7‰y_"‚ŒD¥UUã³óÁ#)q»g• +×s[]fœ~îÿ ÝœåÖ ìиüD¨p]¡q­8Õ=B4%g¿‰ÏbkhÜ)9_y‰[&ïËÙ»ÙO:4Þ——P/ï@˜Xúÿ€›vBJh| @E>éƫÇ×ñUã¿vÝF€®W«B€{çf’O£(¬–…óPø«ÞzįҪñìôŒl©ÌÒS?S-)þæÞ TŸDüØè‹Ô8$ Ç:6ìÅÆÏ¿ÃÆ<˜ØøŽM…O˜áç_ÊÆ+¶†ßPI'( +dã^E|'ñ—§Á&Ÿ2~Ùx5ÂtFÙ8‚è›lÜIïe)•Ùø`#¦¿ÈÆ/>rdãi=?v'ÿ"NdãEs7.›¤XïÆI‡…\ü¡”=,¾Ö¢K"*kèÊn¨qùœ»Kl|ö”jŒÓå¦G ¨qnLðUÔ8å 6΃ìd£4ˆ ”_Žw$V§a†‡Ø8+HF¨à?UcãT‚œÏØ8§óë@†+£Æã$®¬®Sº¨qì:aœé*ˆ Æ¡öZíþ@Püâxçk£Ærå`8K—eãBoø´l|,´c7þH$ y»ñ­¶'²áøw†Yq….ôsƒ#Ý85 ]êÆÅA$[ÉÆ·K§Ã˜‹ +]lóeãæ +ÉMg5©qrY€èÍö¢<0lR¸ûø7–вŒK_psжQÊ`þ‡M1Â8m2[\­âp Örñ»æJZü@Ñk¡ä +O¶£ÓÀ7¥ö*î!U*ãXÝéH–8ÅOß»3ŠâñbïøYÊÅ‚Ç·²òÏ.Õ­‰×¦§˜z+.-=•¸ O¨ó¦“ÿ«!ñ›áÖúÞG0âÅFÙÜ¡ñ¡ÒrÂÝ >RtÃÝMš‰m?¼ ó·1±•ˆ³®Õ'2õpLxåqEëȇÑ#ü¿QÈo]䀆på™W¬s@œˆÉÏϸ§¨ ½œ^ò¾M5ߺekp +ß㈺þˆÁʉg9ÉeÁkT¹•*vÑ{ÐPp~'äÁÚmà³i=;j®âŸ +•i?™ºî¬»VzcøX ¾—Àa'€ØØv(8õÑ'Wkm6?„ xN| ¸RÍò/R–¦õßu]î÷#ñdâßà@ØSÇ|nâ¿1±Í¥p%,õÏŒjHyHmý¾!ŒA(}>ZÎq~g}üüVE¸‡3WÔ€düƒ¼r¼{Ésí}ëu€¡±{ßá¯t‡&QsMV°X«ozÓ+êÝÕk2Ö IX‘Þ{lßËÏÛdü#p¯·y3|Z uLËûºì“ÇÉ0k|öˆh°ñ&Ùv/A‰wŒîx 9i®Ý$¼ñ®Tx@21Ã6~°Ô†E`ß»[MtÎ $çO:ÂÝÍÚÁí*MHËÝ +à`˜ðô†¹ÝÕéñ—CÞÓnX}Q‡9Tvû<@ Ùww—"Ø]bNœ¯½iâºåƒ¦’ë®’Ç-#°ñW÷õazCëQ£^)5uûDÒî +Ú%Rî +*ê”nW‹nÂ%G¶LyÝN&&8yå¯D1 žÛ]L¾¶‡CSľ¹Gom_j%T™ûZñS¡Ç,!&9ïr¹»4*íjSnä)£ŸÁs,f.¹QàÉE$N!·ÜÄ­‡ƒ·ƒx³Ÿuìü5 ÒÒóÝ‘,ÔyâöæúmçóÈÃ}Rc+õy_áVá‰Ìå¿ÜÜu´‚-pã‘9wœnD.è·ÝONÊ">N|;]ÀÛÝ«ÑÛhp¬ónÓ‹>ÐùË«nÿ§ðbnsB»EˆŒYqn?|ÇX_­ºíºTjWÛ6ÆîX*Y™Úv&w!¨V@Û%ýQö¸Y¹l;J“ç”ßÛâ•J¼dÛ[ؾYE©u¢S¾¶£E²Œ¹¶Ï4XÏ0×6eœ8W/.᯵©©á¢yPcÅÚMMbÌÔ Œ4ë¬6¨òÌüª &š‡îQ›øl:l…«ö¥¥,ßÚG»QÖɦYi3Öe1$Ò†´‚[e´AI¸œLˆ¶â¹CbìѶ_*u?{~áõ4Ï&aa0‡æ[pìäû×ÙG¨ø™àžãÛ%ÎÖûP»ÍFÍŸœ©?³3MnžÙ‚)ÞÕË~}qiÙ§”,Îa•íwG$Ê\ªG»à“©1“-]q+HLEHvØäÜ´ZY>d“ÁÖ•”òpðcƒ`øÁÀ™—7 +’kl>O¨{¸áïW‡¤Aq[ÊvIYPìÊŽ¡™LfFì[Bæ@Ü;>A½‡­×gÏ4l‚…Gp·ÂæñûJTœ^aV Bt†·B±Â6n}þ¼À5×c°s­²”!ìÇz‰Yo ØÔŸ¨oýë0¤Ò +-|è׋©`ü.†¬¯u½6à`êb4hÒf*éÝ0³½>ä»êäÏÔ-£ô:MôØÖ±‘×8ÃWξÓßµ¶Õ:K±C-8b}hC¶8û@¬>XãåböÓ_½V&é¡"Ž¼^]Üõñ¥ñPgWüÞ>>®¶‰»ö—ïk `³Ú굈ð©/G+ÔI© +‡AíGÙÆÎBI ~wY\Néi‘~z6Cƒª˜ÍÕ<ƒOÿ'v=DÞ“žêq¦ó4™ªÓ;½]g|(ªÓùýHv¦&žÌéŽK@6Ípo‹kùI™xÓµ‚4 Þ´Êæ]ëf0+yÓ»pÍ­O‘e+1ð¦UU—%Ì›žÁFÙ´ ¾_˜É¦¿y°eM=²iÜL¢ -›Öí/•ñd ”†Ë¦ Ì¢ˆÅzSÓ ƒk-☦¦¡#Aì ýªÔ´3¿ôYÉì—©iv«è/€¥¦_‡ZøY*[¤¦ÓÏO–Óù”ŠÕmäê¡–rÐô€Úù6R AÓ\‹ø,êÕ5Ð4ÅN}G2W@ÓúA +ŽE姦±ÏÙt¸X*j6½FÅŽLgèèfÓŽçiÎäÃÝaÄlZk_±móàM{c5O¸Õ]@"ÇxÓN¡®<¸!Rò^‘ªaHÍ“lúI¿þ>鸚~§]Í)¨iÇëÃl°a_wšX˜~šv‹T¶£ ¿Óô=~«²»š>Mg9 :xØ*uòÍé¬z Ï>ÂƶÀô]ÐÏfSqýSÀO…&®zy‹éúôÍi¦yC­m­ó»ô{Ôêc­è”U˜º¥ßÎi…ÎÙZË(KW„‡/®4x‰¬ï%÷…´L¥÷Sœ¬”Ò¸©¡;Ai I[ëß$ZqÒ‘i«A +«w&ìú’æ?\žÒ,"xÈS’®î¨Y8Òñb|W%}šHKW@"Z%¿Q…ô´°RHŸ)N!QÃ]Í€t7/ÃÙÑ­ììŠ(æÄC8-‰f õaF‘äò&JajG.ŠÎj¾7oäL_¸´¨R– spfêE8_b£‰eÑɋϨ¿ Åg”;!Ρ±Qä¿X•Ø¨ÈpÔšBQ—:ú%–h½(&ò»i£ó£ ÷H-H3AuŠÑÒ¢auFêËp&Û•oHj¹SLÁôª:]¡¤rh &0©Ñ “’<*ÀI—ˈ7é—"žmèé\mÒR¹tõ“Rä@Rú•æé’^+ŨÛXj<É Ê–vZM§Œèh6M07oØ`擶)ítŽµ½îMig5áߎ㾊‡bÊ~˜“ni¯°"¶)üØü7åSî×Pªó1Ûªè4î¡´SÕ%—ÇÓÝJ­z)áðói`½¶¼þÔö”Ø4M¨´2"õ rz#£€¡¶”Ý¢?¹“¨¨è9ÈœQóŠV#ãQe‘š[O<ꤎß:\Ë©~…6M£¸NSË‹µZNMé«M{ªEëD¨P‚FÕ–}œÂR²„;ž*>Æc¤3¶8U\Vu)/檕 Ía˜åIŽ[ÿ*«¹ª˜²Z§ûšD«Ô”œ¶ÚÖ\Y…p§~rD±ikƒòª…ùP•¤çnÉÀjÀ«,`XËYGaW¬cƒ•<ÖLcö“u™€Y3}oË’ñ&£õl­Ñ¥(ŠU³Ò‘¤Ö(ñÖÖ:óD.Ùº6.kÇêI'dÖEl·Æy÷5Ó\Fþ­‰OUÔ”÷¹åBTh®lNqt…XNÎíO±®9|…Õ…p‹ºfSMìNêZ¡X€šçÕѲÛz$Å#|}wʯ¾Flùuƒ:sÿæR¡ÅÄ¡_œ›ØâÀv–v]°ç²@Ø¿—’†ҖeXŸDÑ¿Ãöv[F&b™Xwwr{‡SĽùŽÅU,µÀ1v±L‘ãÊXàÓ’=>¤AÞ¬±êÚ“9ö§ ÉäðëþÁE# F7²Ÿ:dgÉ&IXˆ‚²ˆq6tX¯C(Ë9Ç5uY‡Û2kŒ¼.œ†µ-C>¬U³·T{ß¬í¢³Q=ÏÚµyôYhgHEÜÚòŽ—{†ö‘mÀ¦¢ÅèÄRÿÑÖRÚàpŠ%ÓâP(A;í1£åD-%ð}ljÛ§ö³«v½j!*²•µo]Kkç$]rmˆ×¹.lßkÇÆf#sÑÁØ>°$œ*[¡Y<Û©‡…N[W¡_[²#rƒÛâ‚g·v¦‹ävh¶º[ÔŒt›zm¼¾Ö¤>œò™[Ûûpo `|ß‚Ë—(h'2'¸y·ƒ+ YqJÄ®Ùe”qH%ë¦Ö¼â‚˜]ü»‰Æ]uO¤pkØ"W† Ûܧ“ò¦Ê%=Žx¹­ R‚æZœ LAÏ%JØ º‡šA8Ý*ݘçZOWŸ–:ußûë‚®ŒçºDصAÙD3_v³«”)¨]·ÿ v»Í´¹kËdBÿîâ,¾Ë å>.·¥Â[e6 xŸ pIÃʧw¼‚ƒ\LÞ»Ú€a®ÒéøFëæÍè½ùA´'’kI“ÞÓ ½`êíÞ=,&×+€ö~`²§¸—ØàÉ¡÷2» ïHMË“ñå,¾@±Ó¥\E-Ï7ÿïí¹Ó—qp~™&O Gs߯=ËoTJÉ[îâƿK9äµÅÃÉõLß…´gƒ!Þ E*Vn7‡WKÀ5S±Ç]&[•©x }$µTq‚¨æËT|àÉ)ÿNÅ`6E¦b«¾>çÄn7FÅMÅ9¿$,ÍS]Äò‘øÕ©¸ñ-vŠ ©XÝƺb œŠõ©¹…MŨb u*nŒ—9Ÿ*J^Xè⤊ó©Ñ‹ ž¤ŠûÞÎâ`EÃýh_&=6…U±W&£›‡ëšz”¼XMŒY‚óHõü©Ì·Õ­¨u)þs:°—b¿Ý|ŒÅe>ô%Ô‰ódt½Í>ã”LÅ9=Äd§br˜ µôLÅ ÏÛD:ìB¡=Р9Tq\ +#yéçîG +ÖöŠw€ª)»×+.„'L)å½W\:—ÕàS+‰Ÿö…/Lh¦ ’úWÌÀ $Û"hö`P󪓬Ž­Š“qe>O%79÷Ù­FaT×X.'»©Š¯ ¯øò)ÞÔ_¼â¿¿“ò^Uü8a|á1l³ÊZç2%S\‰áØV{h6r_«âÒð›E¡²{®Š—4«ÌÐIŸS¸px#¤*ö(TUÅ[D òŠQóS +ðŠ‚_Ý™^1[ú›(¥X4â`ò,v‹%ÀCF2]/éóŠeÆ~&ŽÔñÔl¯˜ÆrÒ%h *mgàœ§Œg±oWf ÇÃä,£8¹dpqGŸû“ Ë?F>Þb=9LxläR–V»AÊ>´(Î#t,±Ú·GLº NO´8c“ÅÅÒÌY[/F¸6¾ÁX—FÆéb3•1—õÏòŒGW§j@q¨ÎÐð½ÄéÇؘ Xàª@å4–ñIÏ%ÍiÔŸGùÉØþèR»tÒ¾6±ËídŒc AÉØŽeÑÜŒãct›XË,Hcÿ—Ô;¶Ztlc¯î®Ì_5ζñe1~ãbÍ}r\J…‰¢ãŸÇ±ÒŽc¢×¯ËS™Ç@í"Æ‹˜ÅwS3ß­jÃ?dÝùj!ÇhÌócåZDW¹õ‹ÈU§´{En¿f\32Û}),J!‚‹dMay;Éü¢y‚WrCÛ““‰¾C­æè:®tUÝꬌ2Êóé({›K9õSƇ˜ U–ÿ¬\hÄxe%ùRüXfs•-Ÿ„¬&Ê–Å)5léò©Ë¹è—²Ÿ—åe‰©ùe0 3)˜xÛaîô ×.f˜ì %Ùß­¤ÌœLà º|Sf®¶S¿íÌoÍ +}hâi~(ÇðŸ5;é®ÄfåëÒ)åZ›¿ÓÍA¹æní›…ÎÁÚŠ×ÇySPRþr†C¿]3¬?=Sg•@?¤ìŒ +ÍÝ9 fA<÷Õw©ó.ö${ÆZ|¶­¬ÇmŽ”úÙÂü’ H@O´€!L„`~®&±‘P}ÖmØJ§IVŸ-K]°©þÇ,ƒDWHDã´aŒÂçe+¨Ï ÿ/#õ9Y FþyBáóñRÕêóéüloîÁÜ?_;…‡º­âòD@¯ˆÍØœà9â„¥|†úgæq5“”eáó ü‡¸.¶Gh˜ß*ÚrÜdhFk¬GÅôððËkSXa ßÝpÌ€¥SMKYÒÀÞºQÿ™ÚI¸:(}ÚÑÉrR ŸÿÂTgA;ßOÍ_a: º”ö¹÷m`Q¯WÜåM“Õ( ÓWÐŮس`ÌWÐß5ÕÙ0Á¢ÝÛáÚ‚na@¥ôÙQ4Þ á¹r—ËGmýÉÛpØÌ7Ñ2±tú žª¦vÐn÷ÚDd±ð¥ƒžu¥L2j‰ +õ/£ë é>¦|BlÉ;貨eVÙA_b á“èW«3ÇJãŸÉ„…[9M˜ð OR¯¥Ï ÙÞAÓ´4 +«ƒ~œ\®<ßRP“;è~qQ»ƒæ·‰€^k½ƒî+>ª4¡?a·ŠGÒtt¡’†©óU_èúÒ5ü…æ@@'®;^h^ZnÄ/t’ ~ú…öþO8´³rKyð™:±â뚽Р©¦Ô/ôa­óÄ¡1*Ds{Nâ%úÓÇ=è¤tÑ›]lFwaë!ßèdÀ¬À£\»Ø}XQ[¤ãœ pŸÕ­HÌi’N'gœ´ÅLŸc=ù`NW!QÇÜéP$:Îí?™¾¡½7iúómºý”šœ~ ’Ýv]ðìih¸þÓ©O +Y„ºn[iŽ¨ÿ¾kÔõ jmÝ;¦ìm„Z6Ôp³ÝE¨¡©|_ë©sP "Q†étB³4L§Yþ„u‹_u­…$ZMý…<®v”qìU¬¥?©e¬¡·ò“‚èY'ÌsËc§µF½[sc¬sk¼QYp]ènµ‚k¨8[¹vmÓÁ˜®Ïá® ]µ +—׊c÷zÍ(ïëWuY50 ×( ´§:d×Pº„ %F¦¶;¡’Änž ,Iìሧ·Øi¾66pwß³±Í› ¢çãý²;hU•ìed™ÊÎÏ•yÙF ¡ª„™Í\.¯f^[oöÒYîö§–Û.•Ì[«%rËƧٓjl§Šv¸†²´“9Xûn‡ù•ÚPmDXÿŒÇ³©·v•'°arÅÞöWšl›4ÚönÅò ãöÕ&'ëCt3èöï§C·{´üfñ6d“섾ÍV7\Ò·åÔÐY#q o¯6dð_ÐÄ€|{±¦êñí”ñ§T|{Ær,dÛÞðít’"Å·k tú‹o‡ò""½÷‡3ŸõÈ®7q±o|$a½0áÛמä,ûÝÆ]ŸEỽ¸iµOCªïöŽ S¢hßmhBRÒwû\ä™–Ä!†y·ëÿõê݆¢šÇL)aﶷ·qÂëRóêÝvIšÛ¼Û @Ç$’¢Þm1þ²ßFiYÈÞÛ„Q‰™Ñ`t·:¬•„Qw;{eÙÅ»gûÔ¥nÀþn;Mz4¾­ÓWY+I•‚ùÞ'”DœÂx-00½·ï'ÜÙÂøöa&µ‰o#r½ŸúYä݆HÙ,wÛäÍc†Ï #×r·cƒZmy¸Û}Å ªÓ¨v۳͕ÞÞoäýÿ +.ìí=eŠÉDâ‚ìíyåïÑGŽÛÞN›­ò߶œ~Ñ}[*1*Æ—¾ Ž¿yœ¼=}Û„L¡‰xz<”~à Xôm|RòñŒ< Ö·¥µ!`3bú(G“ÍÄßfAUá¾ šb-§Ë q·çÕy`MùŸ:¸ôžPk· A!×Óëf\Ûwd¥Ð~\Ç¥¢-r»ëLʦIä6J“‚Ù›Ü^Êq]î¸ x2-|Øm§Â*÷ƒ ¢z›åÅ<R·Éëá'›ž!ÝŒäT§Êtû09Ú t[N+òù‚ypÛÑ.xକÊT nŒ€Èp{"•£+`h ¸}Þ%ͦrp»àÛ•'EÁí+Ï}‚ÛÛ:ôašˆmC½'_ۮ˖õ:@&žmÉ”ž‚gÛǹ˳PY]gçÏv 3¼ù>žm3î1þùÎö¼…CiÏvI7, „;ïžíX#!ó ‘Îö(ÌÿŠXʸ¶³W®†Õk›»|Î×`Öáªkáò?#h;rÕ\Û¬nÆîœL×vÓÍmAWñ„³]hyäälGd9umóä.¸þxd‚nãÏEqñ¶½ šR躂¾mš3kwBÁ2ˆ%ÐGUkঘÉ,¥ë6×·˜T=Yßí‰aÑÑ݆èÛÒ¡-ÁíÞ± wôZQqç^!ƒäq·h(X:¹Ùed³J´¶3ÁÜÙî°VÎÝÄ +Æ ÝùÞÞP—îr64ÉÇK·¾@ +¾P‚îûúH¥•îu±Gu×—³¶ë.Ä J”ÝReän·rM¿»ž‰¥­‡wëÞÜMÜZ÷:ÉÞ¹BÞ“d]ywÛC`;îýÝõÑúAÑ(®»ƒD,¶Žß}ñª«Iüî–‚ì´¸}ñö»í^Þ`êÿã¶äwãÏ|…ªî¦“hªJÞ]Ý]ès{oWSo +êî2û <`b»o›ÁNlwÝ&3ºwíf(ÂÑ̽vsOÑ¿v_v€}ºÛ/[çÌ-rºû%éµènƒÿd5øqîŽx¹ hÞKévs·o¢{7Ð}©s÷/:¶0f¯ytXÑ.»÷ÊÖ>C"Šps7»T-tî¦1ÃäOÒüš»©{¨‰‚ôôïbÁ4N´©l{÷zYš«ùìÝñ &*Mã»Õ“¨Rà ï–òKSNý=Z÷Ýï5:g©}7xU“H±r= †Ò¨i~{x®˜ûݶ`Y]œÚï*$‹Ã; +J±ÿжsJ‡÷»öjG^qxç æõ.“"ïGTQ£HQoW-ºUÞV6‘ÅÍ+²ì¤ðF`É2±1ùî‡`‚‰…·¦¼Y·³ä»!*u© ožÎ˜é?$Àã‘ÜñÞA…óŒHþiEˆOFI»” :ýeÔò6Y9ÁçíÖ(E½á˜Oõ¾)€`²·wS³Ý{æt·ß® ‰o§I-_“ÂxfØ7âCÊïF¬Ž~¯Ù‹ßAQ°&6;éßÓåQï²Ã0|8gÐG¿ßÀ¡bä"Á㤔'Ö‚ÿ<°pЋÎÖîm †ÈDT±0\­†WyXfÀX'½K{x]ŸðñvÔ·©ÚG*p´¥Ä_h€#pfûS¸üö¸1Ü=îž”>ŠÕ°=ü$ +Ý/Ò +ñTw/¯wd‚ð<ïN‚ˆK-Þ”á¼çàõ1ð}fòf ïF‚S]{‰˜›¸¿Ó@>àÀ?ÌÎ0û‚ÿ8ŸÔ¦oýÈðsQÀ¯xN”ˆxtfh{M|w³ÓØŠ'$%¿xòô†ºg4Þ~ç+ti§8þˆÍbùO6BÞ…ã ÓÈ›PÜ^[ò4 +†1O>|Ê/' :,—âry@,ׂyL™w²ÆUaš‡]܃îÜya€*ðHüØeÍE_æL?0kÐâ#…OaãY,vÑ[q‘|‡y@Îñ;‰ vgùíÅü¼³‘¯ùw"ÐfÎSñù ¯8{¡oK8Dïè—*ÞºK¿+²ü€ú^›Jªß,$S¬_ Þ&ÚõÏä¡(b_¾[ÝìÛ‚9™ªoaQ:“™Ú¿.g`·ïÆĹï§!$rÏ÷=þÀïj„+Ô |‹“¢â·‹¢=~Ÿèwß”*­“_³¤ßÌoÖûçÊùS|7:èAè/û--Ó„$_eä*[ýôÇ€¯_¯4´ßDÖ“…‘¸¿ŒÎ*™ÞzRþf“´ýÿ˜+Ñ©¼:úÛÑÅ_Û:Š(ÿ ÇzÎÉÿ!}V (ç9f+ýá:?C®¿> +íy–Ðó.|ÿJ}ü' ô6Ù?U†ÿN[ß?鿬ÖKµáè¦ìêÿ-Ü÷`ïKF @PBîUàUßý  tw3°=‹[íh€W= à‰C! ðuH€Ð;z˜¶ +X>;2Àf1ÕLàÃÀâ‚Iˆ¼÷þ!²ŸÑQ"C£Én”ÑBŸ&”&®€§zä î§k\ÍÙÿ 5Ðn|F¨W»† +ð /„àDµÎvAÅÚÖÆÛu@lZßR9^xU-|iE¸r±é°ÓORYþ{ nZ¶Ñ©«ENl =’»’E›“ú$59çà,Û!Þ\ªÍÏQÃ0A,ºö%öžžÔ,òÕ¤(¦kÀÈ×!çÈ¢?µœVtE‚ž”[g™b‡•\k%YÆŽE™ÏI·GG€Ä<…ˆ\ÓÔ‰Pw>>¿™Öž=×ç„è®—HAÿê àêui¨ “I/ª -}M§· V(+M7ƒóA +â»[ €·Õ¡}ÚXpM¡Q¤ð21l©1žå6ˆ()U&AɆ´–¤'¢ºa7ûÜtœ½³ûw”û’AÝ ¡¸ßíQ[Ä© #¸~ú`šs‡y¾æphZ¢Ù0ÐRÑCž jm:Á™Ð€Ìi˜†&-ùvaãªpXBë¿òÌã´ .èÇ–‘èÖx"àÝ uŽDréRÓ‘ÓFBñ[ +6ùQª†ªasž¿ßêÖzØà «âFÄs,W`Í8‹Ú_ÃÕ@m†ð·3#˜A†}xI÷2CpY¶–äqÐ@:ÙüBkw­u0óe¯"õE,j í2SD׶0Êu®tõ[bݨarïä•:gø+™ä›Ñ½ÑîÓ‘¸¤Š@F…qfˆ8[;’'ÐÌ<¤°0§¯+ã$qÀ905ËD4‚3y"èœHœîB¾%Z-af‚Ojs*#R,_z §q8¸iÿÕÂŽ,ÀšŠuÙƒò7'±èÞÑ +ókŠ3¢¶:µò†¥öÑïꬽ&LéÁì0d$©1q»+çªèÞ|覨üÎÆ‚€G'H„6 µk{b5S†Žâ“7ê¥ìW/‹ù­ÙæÜ2Xà·db†c~›îÒeÑ“â»ñNFžðŠf#øKØ~率L’4f¹Zâ9¥B>ù u-áñ–É©ØýÓyÁ˜gåð#äi±*bu`3ïÝFEªÛÏü¡…Œ(oµþûó¸Æâ–È Ióìœ#Ú\RHÍ.ûžFX<ÇŽKÄœpðÂt*•Ñ:¯í€ƒ{e+ü{„•qñŠ«PšBCK4¯ö÷Œ›FT’ˆ{Ɉ cÐúv1šÏ=Iä§qcà·"ÀóîÖ"3g•®P¬Ÿ ›}—¾ ^tžyÖʆ۸ŒP f¿‰šzåEGkW§nfrm»Ç&ð!À«“g¸-.º`»'Gq9†lPÎug¾"œÓVÀÃÿ‘J~¹Ódô¦Š $åAIÙŲÑØ•Hë¦'èMMÍ@3»|ײԈ·û‰äÌ·g€ê"¼¹Ã¿€{\°Ž€¸ˆŽ$É—ÓÇ‚ŠGF€>‡ãJ|ï³`¿Óbªeîk^ü¦½azÓÛ¤°ˆ|b¯AúJúlÞ±?–‚ÖæÝ1ê„9öö”>‚ý»—6R õÅ…Ei > ³´£€âíÂù ¯Õ|œL7ŽWùm@^yE‰šÉ ×©­ÝôP |³!¼’Qá²-دäZ± ÈjÖclŠzóÌRNéÙ“6fF‡œ%Ä2²*:Îä·ƒõŠÿ¶ùüî`÷Ip¦(ðr bÑö•ÍÒ›Ä~f™´bçp‡bæºÀ)“{“*|.§‘Ö3¾"x#[©¢fZ“ ƒ T¢4ne¹˜kˆ‡nðu;—;ð¨ÜïÃpÕV•Ûˆœ˜ ¦%À´Æ©*¦-y©hô:¯8M ,áã‹aø˜Céaäv³‚òÛ8 …¦–ôgû´ÊÙ3b[À(]Qnj«é–F/žR ÞÎrE§‘kˆ#—t +XŸÂ³0if¾µ~Q_=…§w ÌŠz¢¾y'$†Ð²+*p ôj&Ÿûèz,´ìµT¡…­_ÐâQÆ<ßç…ÐJå|=«~ɵ2¥ƒÍÌ]1üàkœgÓkb„É<•‚4ÙƒS›—ŽÅZ*X“F‘ÿkÅ^ ›8£†Slm{3ª¾ýÇ Àåä'™áíò]Z;ÃDÞÙäþ.¾©L0ñÛ<ˆ…ϺÙ&@¸7MÛ·0µâ^Õy[”„òMÏL#Aûn7þF\B{¦MTC©‚ì‹ Ö£ÙP[ùš§•ó¢:ÔkI  úÿ̲¡WJ§£„|·ÔËU…ÎÐ ÝFrßU×>¬º”Úg!aa'*_͉¤, XH›R;ЩÑ;Ób2:\Š +[_ª«Eý*ck®Û±yÔ®h—®Ê&W¢LÌL+èJ`V¬˜êãÙo³À$­ìÅ»­š±ÒMèÅÕk eáѼ?~~BÀ\@8!ŒBÆìtœ×—ƒ5îŽF§À¦ÁxA#êÄȬ Æ d¢0Y|rá‹CíQÝlƒm» +>›wiäMb´`ô#ŒÜ–Eï¹y +¾9 Q9¯éžUg +§Ø=ý>82;•¿__\b yצ÷Ìâ͈N…/x{!{BËx.”ÁÞæÅŸ`E%gÝŒ44cô0n|¸CÀ¹ïô¡Š!„w·Ø²°¥Á0Ëé.ÃÞ†€' +FlE1ëÜÓi' @êzg@Ö;¨€@øß1 ¥»Ñ˜²6 7ùQö¯£wH‡Ts‹êÁ’Iæ[j ˆ¢o£°ŒYUN51•.pQó«“0ꔃN0pÂÜtxÚ{Uuâ^™Ò¨¾¢+,-7+¥ƒG+Ç—E(ÍH=€«¡ÒUù€`ø© pÍù’ qY‚‹ÄP¦w²d_ärvµS1ÝÇ|‡ZÛGWz(P{…ûçLübRt ˜ò–/H¯†hK^š»SO­J·˜Mp)øð ð}²âzª-Ó’vÓŠÁÙj‰$²Yj( °§!H½x~”ìÙaëxÊv— ªÕ9ÐA³“ú)`GeN÷xb¹¹J+Ä£fÑ„9‰P˜50œ®|¸±Âçv\DR©ÚÊO`œ±y_µ™&goDÜi +- ä·* ÃO¨ÅZ0ö¬lëv0O‰ÁŠ^‹?îuöÛ%}Âg zälbëTN©‚ÿ·ìм‘|Ü°ðiÏŸk”ð=b^⟫OûáPy1ŸÚ öИZ£ÍÛ[bƒ©5²TV†±pM§¦èP¬§oŸðhsÑÂøìžjÆ9PÌ9s×Ïü»ãýŠ¦Â7v‚çË¡) 7¦ Ç’¥Ë^|D”ª¢Ú\h\ŽÌÊ[EܹÅqý÷^ªxŠÚü\¬ø}9H™Rsç!“÷­KcyVÀÇaŽ„²ë·(àh„ÕÀ3¦4—k;ÕB‘±A«\!ÝæEÈF°±ÿ.Ö|Uý¾P]uÍVô¶ÖÑBCæúÀ¤šÕ$´THLUzL𠘶[Ÿ¾ýß)%/E5¶MX,¸ÉväÓGÊf.…H­ýD§°–0‹r'­¢2­†<³Ÿ’M5W£3Œs"Mª¬lØî~(ÌT̹•ÿø­W°æ/w¡GΈà_¬kÏÓwF¼²¡ÍÒÀÖÖþHª—­ ›¥íˆ%AÂãðC˜Í)÷zª“"Â2œÒ"wßls1+‰“é™WíºÃí²£>˜± w®‡‘™L—¢bœ#½±aÔÕéDf¡Šü¦Ã‡8o¬X¨`Œ–æ”YqòÉÀáweµXlM ÅÿyaÒ +G#¾vk¶ +`³6ÏI­ÿ&ÿ£³¤óÒ@ÁÛU)Àb@Í ‰8„-Ip’ycXñd²íLd’†-þPí +3 •0¥\¨[Ä,×U +¨Fä!‡ÿ8åÆDeÎ&$UÃv¼ƒäµ)ô&µZbÊå÷4^pGÛ¨Œ;ëp;5Ì—ØÞC Æþ0pt;œ„ïbÚ2+»A¡ƒ$‡vù™È0Jp3yWáêœÂìa\m½8‰fѲO÷a_8 0]>L€:æhqbµ„&~àU0³˜¦ÝW'Ò![íTÝ|åun9b«ˆa€êæ!Ë ¿V"êb6Œ•Å©1+ÔH†«Æcv2x¸E‰Ó6¢£´É¥ ÚÐ,Öe¨Ÿ!˜}zzÿ áèF‰žßF™K6 +MX£Çmˆ‰êIGG¨Ô +“ÖF²ºHîŠp*Åyê37;§ýÀ¾B*üGû5`b´)"¬é<à? çS‹­çåÍO¾È±†¦2á)à-X5».0Zjgn—0‘U‹ÃY´r&¨’3’EçUqe͹DÀˆ4FI´ùÖšÙé§Ù㞸S—þjÝ ëýÂlN £¯„‘_C„~.µñƪõ_EÆÁÊ£Ws#?@NTGˆ~ÈŠ‚„n¾ÿdË8ól¹>ܹ•gÁ‰(XÛoi‘v.„d?N¢÷ìÙ©~OAô=Ø©]ÑÆD°dE¤¥@ +‡Ã¶bgk¯âWy+lØQ°ªÔŽpÏ3q<*d¼áC¡d¿3 À¯Y†­R&4 ÂòèàÒµ¢Áòƒ^Ðe²9¦«×S>ç^ËÑ®}ÈQLy@÷(9»~‚í°‰b 1“% Àl ¤¬qÇB äÎ?ò½¡ãWvwÒAÄS:Vm®¤R^hqƒw”üÑ>.÷Ì‘ õç]šÉ$½iÉÈ™ˆæ›®ì q]š<%QzÄ R½嘄ñQ.²Ôõæ}ÈÓ€ÐQFåX¶y³‚.hðí™IN4gª~¼­; 2Žþ¶ĵÏ=éíü£Û…ÑŠ4m”žºƒyB ž¥È¡{IÓMÑ«¾—9.'¥z•éÚ‡ àôL?ÿ†^Ûõ’7E„ûr%UÀ•*ˆ4Ä7¶Ã{ÖηI )·â¢3‘NiÄ™2ºœ" ÚDùòî?gI·48Ê~Œþ0(ÊU9­qÏ>3KŠù÷–cnÑÑYŽùôdÝì­!=ýÓü§<¹õ¤ïîš¼Q25²µ]Ä$(´Ý‚7EÜÔ6‹[{xªr6‘&墔¥¯‹@Ù¨'Á¶S(Â3õ³VÁŽ|» +GùZCqØúe”©|‡-C­Tî\×œÄ bꆉoBK÷Ž8н»NÊœÏð& >n¨<Ñ‹Ö’ö4_€Æ3QE6‡Ig'M¬Û¼•^)C÷Ëâ4Ù:ª†ki˜nXý d­ˆÑ}æƒIœÅj1„¯ÍuÕ(í8RâÑ5 ݸûH½Àϼƒ³®°Æ¤:J*†·‘PÀÓ͉W‘O6ÂúŸe%*¨SlDb¦v\Ö._}¼²„[¤ÆœUæœïÅ *,õðZ0àcœä»ôÉ)œô„P¤óA ȶÏÈuq¨cÀëØð_õœ""êþ§½'Ÿ ©ó™aá&gnÏ4P2ˆ«Œ¤Rsƒ}grù‰u¯z>êN:Ï7ù 퇂rì©oiÏJ|“‘ ´¿w• ãàèýECA[5( +LMÏï +†ëxd7e+ñ8’äœ@ˆN]r‡« ˆ)U{£ QüvEŒ˜)§NóS.3¦ÆÁOã0}㺇ˆ>2ØnÛ×|Çv<¼cŸ¯3z‘|~"ª}Á^“£nràÆø%12¦pCTƒ$,&îV_I)+TÔ‘ðï·àsuZ«è9Ó7Š;é{Î0‹Uåm™oGcEyúSed“ZŸ÷3a.¸£ç&ø^—AQ9g 0Ù„3€,·Íò¹’\T¹¸ä(¹Îð”¯èn0ÔÖƒÛ­ÿ×’ƒYfeÚ¨«òSØ—Ki˜èƒŒÛ«1QóX¥ˆ)ÿ¨Ê2¦*  ­Ìá÷¨5æƒüY)×–î¤N 2倭 ( +¾áÏÔõÊ0Vª“m„ºíâH_»öÙ`n\Ãn*}DÄqWÖˆÖ%ŒË°çk¦é†XZ¥Ïd¹è.ÈÇO0㬋œ!C«Â|ªyCYÙV<ÃI@ŒæÿŸÒë6‡6X=¡QyÔ Ýe“Þ¥ø˃œd1ñÛÚýšÂ¢R• Z2(ý&†6.AÓô&âÂ⾿eÛ%ýýzPIq¾æÇuáêF±'ÉŸ4ø:O ;fÈ8Lé Žz°P +‡¢“<Ôr˜Œn¼£D候½0ze/³bUdŠÀ¥`¿½Ò©œ¦Ì09G £ÄaÞ¸käù)cs‘ç ]Þ²*1KO¾…ˆ­))T4%šÜÜ —•nHJ!…5{Õ9W3ìã^ä9 ÀÖÃw Ì›Bü#$B–Ï×RAúFÿg«_wïÅSM^aV,]ðrÃ…£] gÍú%£®ƒ ˆIŽÚ1r"âê¬,éiT¾eƒ@å–Éö=ëÈUš“댂#7" dªBg+(P:™L;¦óJTí,áÊ„‚¡´„„¡\ AejO:Œ2>M²<†ÄaÃè‚¢Ö7cà`Éšë³Ý«ÎößøÈŒÀµ¨…˜¯'×ê¸pêž«WëW©*â<²?H­‚v£¬ÇؽD±®ð#ai㨕'm·¬ˆ½ ak8ødð±:ó•þVŒ°PÅÙ7Š±7´ô͇$Âh6Í“*" Þ§ÿ^C(´´#~9 Œ¤ô·_J˜u5}VÐ Ü-Ð~áº\N("ºNÃÆ‹C: èjǪ¢¹)å§^9Ä=¡zcÀQ{—N©<öÙLí1ZüR?#ùÂã¢ÀJ°0)ÍC‰[§D6hZ!ÀÔVv¥' +fDb©4I®hÈäØ_ÖLx…c¥ØÑB“òBNCÍ›Ñ~µÊO_V·[övÖjÄ%ÍzÄô“ÊŽí£:ycJØ.‚ë3e`~Ú•gX›—x¼•ž«ó„µùËÙ†—•7ËÀ",«ylÿ¹Êáæà2ÓˆÄ3ñé¤5‡`j¦¥-°1äKx$ŠMñ†0ñ(²’Si‚&ró8:V*’öŒÝÒÛøîÕ²T´}5Øb¥ÈñLœÃ¿É²¾ÝÛä«}FcÝfÅ‘“`h»OßÇDX£—^ë +|Lþ"úN§2äÕg—ß#Îr‚=Z4Ò3ÈÐ$Ð~"òyx('ÆYÃHÀtJ eAtÅÏø“Cu3 ××ÚTÊë_={”Óñ{SWܬsRã•&xg2eJóìJL‹búG‚œÕ˜“è`ŠhµÝôkÉþ‘@•iè9a·«9fx£lè¦ +M<;¥u†º_ ̶¥¼™pò3 æôU¾¹€#ò×L LAŠ€Ñ“Žrè|,’Ú‘?ô˜Ô¯,Ÿ?aœâyÌ®2‘¡ßÑۉ͔öyòv3«'=¨6¸ÈWo4±5‰»K¨.hN?ëy‡ÍS¸aÙ•Vå•–…Œ‰ÉÛ&:rÛ%`‚mÆ`ZÆnúš ÞÌÖéº>/ðz’Vׂ @ÛïùTJ7 ÷B&»V¹ªþ<ê¼ôØ)ÌB š»žRGë«ã 9ãVàÄo'Þâ$`Þ|¯ê:SÊ ø?\&$Eî5¾DË1óÓkÛñþ‰…ûøKz=Í÷ox¨„>»±2ÊÈáƒwS\\$-UmoŽìb–hÆmõ¨M…óf¤ŒSmÆ$s#PKQå–È­÷5é}ä~kÉ¿y¡‹ŠQïUŽ¬„9æçÿ‰A©kù_a*Û¡2RÂky5 Zù™è¯þñ|[¨iF²ºëNü*áÆ£±µïŸìø§yèmTEáUÛR§*)Òþ¦àlu0šq¡èŽ«òWÎ*KjƒAor~P…—3²å$)–„5R•ç»'ج ‹ô*2>5ë½èC7üì*y +|Wó+¾þÊ6h;}ƒO`&†OÞ‚l +‰Jï´f©U¹øqÔ.—©c+Ôt2 ÓTÍCAóþÞÈÄÔÏSb8žŒç¬$"ocõ’݃„ž¦æx©âþo4Z‡þˆ"kâÖÅ”k?Òjß<·¼!\`?‘‹#Ž ¾ê +‹È—DQN‰Y +E]«BQAð˜‚úž9U‚2õç%ÁR÷u7+Ðvý‹¹æzÇ1Ú¬ ǵËU& m{-$é7;yw¯žHV¥Èè +‘¥¢L]—‘ëJæn *­\ÕÖœ©öΖ)R/Aç¿üSÍŠ’AÒœ±þ˜äî_6Iߢ⣢Ȉ#îÿ•³^•Èv2/Þ2 +¬íê¢Èd“ÚXÞ%îêæ*íRÏÔâRʧ17dã {¥²Éÿ´¤€Ù9ÂoIژã#Ø–¤0¢,0i ¦ !.ÖD4Ó —¯yoZÑà›4‰ã™ëqCÿ”JE|xˆœãUͪ…ËþåQ·®áÕˆÁ0]®—R‰û©?l‹¬4H°™ aõr¥ÿÚ+u0óË<ëJRgsÚ49–3âNÂ5òÿâÁ~óÊýøÂÅù"†öç- Ë ‹;oøÕýûÍ{À†Ì«o¢ƒúÄzÜ5÷“(Ã\@ÞA„ =Š€¨…5‚Ö=9GEàL6öœ&zÈ,éñT&.´‹;Ð`±!â¤c(ŒY•SmWäŸN{šHê©ß¬Tû^ñ¼;h–ÞVÝwS:¾ì/1³%ÇyÆ‚8øÛt‚DÛ ïdF?cæ¥JRC 6^uëàæ/ P¾˜e¢µ;àA.Hÿ,»~î Æ+#¥!È<‰:Ѫe9Ãx™¾áìwM·'”­é­>œ[š›}O(¢l׿†¶ž4ÑYƒáW˜ËfÆüPè÷Ù­N_6e¸’ͺ#9ØÛŸ¬TõºEM2}Èö>cùXÌï!êàÉ$qù(<¹P¨˜2¨Wjÿ.÷‰töåîùn\‹§ü‘œèNr£á²è†fQû\ zÈŽ¡ÈH[©Š‹´rãÕLü!¤ôÇ9îÞjƒ…@Õ/ŠÀô¢½€R:î}±-ÔGÆ›'Uó²NÑÿáckAjLµÓ6°šIí!höî+Àõæ¤ì¬nû!±w0Nñ™¨‹þM¸ì°ØàÓ ,ÔÙW`^Œ!ƒC6oKéM¸GžÅ“}ݧB¿–ù ol†¦t oK…µºyq:e_Ã_e6ØšJÎxûx—Ë8jB ‚+»"Úª™{Žw\`w,?1N² :Ø£—Y¤†þô=Rl˜ˆûWÂ33Ø#\¤lQÔ´v `Üâ£G:v‰}@à0%ÿd¢NnáÈYC.ÿ¸ŠC)‚¸8t$b¥âL¸v˜9xÇeÅ9½~…לQœ·-U`•Ê Á6”Ka¶Ì0Yù@Pêç/ózœfÓ¦prû_€ÄΈáo¨ŠÓ””Z—º{„ýõêéÒËæXï³Á†ØUÖäë_ˆŸ£¼‚õ¤õõé”Û-g¤t3wfÐ_FN+SÂR&¬dŠFlÈlî nLÞ¯P„¡xCóÙn@´ÓGt#Utœ¡¬›“r¼‰ñ^›¹°G¶eXÈj˜—·×ùGŠ[$3&)â™Tý$ÜØf¢¬jϵ£{îZ©S¯€'·ëË<ï²È Z(ed¸ë‡eéœù?¨ÂIm +¨·%Þeë¨R®NâÝœKc3©*£V*Íæ:&4_Ú!WŸ€y©±Áׯ¦(]xT׋Ëñž±›Ž°ÃV~«8–¥vòj¼Àôùî…M}šú‚o!ïiõÄ_·ó®2ž¸êO9Ù¡“¡ŸAkÏÇ0Ì Ï±›³Ûd‹ñšÊY`Š‡)T>?¼ +Of,3wý‹zÅ”+܆1æü Ñ4/ïM¤7¿ytK¥Ò¤?»Ú3?:¼¯x‰QºìÿÇúÞÞËë}wzÏ—úXÞÇãIÛàEO’QaPâBÖÚ(€ê`™FµÛ@bÜn·æºe[¦çÖárGŽ;­&Üp<ÛZÉps¯¿}énûnûÒÿWÿù¢QÆ;ŸÁöÅkx +³T½«‹Æ|œ¿ø^©>ZJó-÷ˆòÛfÒ/…ŠQT@ÕZƒ`RÝÁÍ\v¶Å)©ZƒjwaØqżÇ*ýçî'eþ5Ž' (L©§‚l…` ¾"0ÔåÌü|²2”uI™qÊU†aeo+;M˜IÅ€U M™À°(Ðî7ï4I™#=1ýŠpd>¡ÍQô¶þ#D¯Ô³ffI!‡_³jÛ!ÂÚÅyÁ´²G©”WÅ…_âœPÖÕ˜SjߎHçdóH[Ý+±#\óRU²õAÝZ%o;oRùÐV´¨°Ò}­ …´L`]íl]N‰{}õ ™¢-À{¾ x¿åÐúßè?{zzŽgcè||8›ÐdG3ÛÀyÏejj©³”LÛŸÅ.E¿‰Šyû§ùC ,Çü}üPþ%ºivq+š‹’ð?:*›bw™ºfÙþ8~¡Ö(Eñœ_MyAíô£_‚±þ8G¨ž9³$³~ñû˜U^p62êæ¯.W}d¿þœrE]Dozå°JŒ¢Ÿ ,äði÷ýbÛRÖíTy)éQî;‰­buì+:z±*öÄ’V¼÷|lÇ}=úI|ì@þ“ÝÿÖa£Eì¿—âG>ˆ¶ÔN“wJ|Öh^ü½}§Ñ‰?£QŒ¿Y¿z÷V¬ª×¯Å§\Íà. µÍ¸Q4žw‰LøVaõÆa›R^r +&ñ¯–À‚yú©C3X!Ëh cÅþNÿ…dòˇÀ¥"öÛÎêÊFÌ >F£¢)Ù£ú~ü$´8Š$§3I÷lš¿¹øäÀ­$j¶õ5¬7…Ì]Æׯñ?o”ê¿Aüä S0 Â:¿|Y»¹Ì‹Ótµ…}(¨ýÔ6~»‹ˆ+ú‘(aeO†ŽÝuϽ}câ\/Àð÷±û¯¡(‘÷WiëÿWöÓØþQsHþÔgÊ=--ðŠf$1F7€[¥ fOÔJ‰$‰|q£ãx™)}zJ.?áÈi"[aÂV4¥ekƒ©V“¹ØÛ©Ãó˜+±<åD¼š®GO«hePÍÓÖž¡Ý6*ýŠ~³g¡mõ”áìò7g6¤Ú<Å´»-%µ]\œ-´Ú:ê qÖŠŠ¨w©¾Ÿµ¥T#[Ô:ºô>ÞÃ4ÛÎYÃ2ûД_»SµC&âí†|úK3yÊŠ:Wsvc.ßîŒ×Üˬ!S-&&îÕŠs¹Ò¬V\Ý®²ÖØmkf£5jˆû¬ræÑIÇpÓ›J;»³{©¿[ÿμöqÊk9åó˜–ÕÈSFT³t֘Ѩ‹Fß53£ï1w×c÷Ø.ª×6MÓÞúŽt÷ÿ¥kå˜oШt°A»OÿsÅSÎ ÷Kù±]#"4­Éëë&K‹|d¯úV"M¢7«0Q34¹Z4yySvæÝnâg·*OÕv”J‡”A w¨Ž££õßO;š:O¦oó^Gié× ¦ÑÎ)¼§oEcHx4µ¢8.Ë_¯—Sº4W_We+õÆ””ÆvýØ^)ËZkÅ]Ä‚HPBÐDe¥,• +3PqX*Ž‡®ìQ‚X†‘ƒÆd±@•‡ƒ“L(h y4x˜,ÀÁå&DXР"Y”‚‘ÆA :dÂÐÂòx°P ¨H@âyµ 5fVNÞj¤±Ñ{?Æ-§e6,¥u:cVÎ9S³lÇÌW3[ëØ·í±Í9ÏwÒßþ.Ípn¼ïÿè©Ë>—‹;“Î¥é.ÕÒW£Æ—®½ã¥õzè.ót·öï‡7§;¥ç«ŠxÌ¡× Ý‡ôlþº¤¹N]žZq¾º»ßåê÷7wjÅ`ÍΑvç.Ï”¦+úüÔŠûÝåÚOS3ñGt4™§Š–w•¿ÑÏïjôôfÏ>TêéÑÑ“ ót¼Z±øxÏ7{ø£Îé¶ïЧ~¬Ÿvê ´Ò•ÝøÿXñûA¢¹Çÿý”î¥÷ÿw4³«ùo?ÿóæÞ>ø›#^OhƒE³ÇU+æž½Ñ Ξvÿ½Z±OÂ2+E3¡ZQHZ5UŠe÷©R<´ó Þ<¯Bûæ ×æð7gƒˆ¯A/ RîâÚÎ븻«…6¿›ÃD›—}u7ÝánΕˆ:öáj2ÌÖ9ÞÕ +DL¼Á«¡&ÞóºÍ É4'3s°twtÉ&é„IYYyš\O¢å`Õ8­ƒY—ª:¦VÜÆ©V¨Çy6y7¾óæà)=Õ9wG<5zn))êà?ˆyδœÌ4N&z4ÕŠÑÍÙÜœMó‚„,z®‹÷ æ~ ±öƒøÓàÍ·Ô<ß×ÝѼg骗9=÷ÑíÔmÕ©Û9××Id¶´Ev£²Wiæœ/r˜ø9;s|¶qÌP·ê–yîÖ`måZåXÚ`í¢žRWOikêÔ +:cX›º¼«Ûº÷¹ù1L{Ò݇îî²Öš7VÔ½{æˆx<<_}ïP-5õ°.ç0—ÔT§^;u2µ¢^¿›z>ŸuÏóhòùú›sŸÙyc³Ï¶ù̘Ûô92ùnLœó™Çvš^´oo~7¿KµˆÑ^ ê2@d“‹›»¾å Ñ“ùçôX¹"hŸï/Ñt®‘cÞœYv=›ftIŠ„9IZùT›Ä1Õ,OZo¬©zª“©ÔD½¢&Ò ¢**fZÕ¤¦îÕŠLMß'õ6uRS¯&U SS󕪨© \eXÍÝÓ¬!ò 퇒ÖV¯’67sÓVS›WkkkæµUDï.з£¹[¼UªÝšï"~‘èæ¾ÇEüê÷ˆµƒ¾þˆ;¹¹ººŠs›Ýï\W­pn殚¿z¢Ó~5qô*Ī/Ú­íÕŽ¡Þx´—›¨äÔ]3¤Ò^é(Ñ"Õꦭ?…U{ÖÁª%Z¬¬Å»±v×Lk׫[ãã–i絶Ve«c¦µ"kY”“´iIkÅžOy?:£=íÑÒÚhii½´W´¦]Ý\ÛÓçÖsÏrÕ +ãâXíylLéæ‹»iÍùÃï­ÅÒªÞÚ¡%UU¯ZÕŠU_ïpRñrTÕT­P½ªZa%µzLíÆ¥¥U§¤º¥æÌZK¤Â±§/ÆvK—³,Ïr§ÇÒ¨ÒÒW™KéE=ZüX1Q1í´Ö$zïD£ãžÆ~Ùæhš£mîTêl£Ù†….ª4á˜×*ljG¬Áû?Òøjvÿ{¤„ß_Íý|óÏÆv;Ý1»%}QÇÊÖžS¿{êV·©[Ù\7˺±«©×•ÕÉ. k¼T³G6ë™é$Ãà©u[Ìëg¤©Z”Í6v7ékMm4–¶¦w²2Í^­»ñ¦ÿΩ;‡‡‰IvV·QµP( H¤ÊáX " ˆ<"ÀÀ dy00(“ÇBY¨,ÉãÁÂd‚‡CòXX" ’e¡L°a,¾@– [x‚²Tð€À•I™¨D-$ˆ,Ad ¯ÙB%êÁ$²P$‡Å!y(0 <  +DP™ + Ð1¡0@YŒØ€ÁK¡°d2ah4j4 m4Õh4TÒ¨û¤2©D`@€0(Œ +ÄB‰À²P ‡ÃB©<c‘< + 4 Œ†’‰$"Ñh0h‚¥áŽÈ„âyH‘Jï” +£ÅA4È"d‘H*0„ T$Kdi0 Ô 4H‡Ca*âá`PŒL ”I\S„‰ƒá@Èò`YÀ1q`™€²4àòÑQ@T–ˆƒÊƒÂ¨<  €2L +H„Ê"™(¨ +‚„CÂ@E€XÀ%UÒLLµœÌ¬QËë®­îÖþ°ª~|}êiÄ×ë‘Ô©gêÜîÎï¶Ñ^/Ô:¢{û=—÷Jqkî|·ŸqÎæ…-^cªOš-¨dZŠÚª€ p0$ ŠFdRÁD]~`r@¸d ƒÁ€XÄA  "@Œ1†!•7Ïø[PÈféb—K÷;R‹l Û|.êO+­T$J¢Y?ƒ&‚À08v›Wß`%×0ƒZýÀñEí!®Ûp0˜QI;7gW(z‡ñ¨‡3¸á_{àOÞh|«èŸbÕ ¸f<¬Tñ¡AK·Q5‘~I +kϽž +µÞ'5Ôc&˜…ã•B($’!¶¼å°ÝñZ0¹]¼ì†é7!kù½Ñ¬šÛÝ!d¡˜ÉåƒøÆxHÝ<wÙ ƒ«¿ 5ê—¥E7sq˜¸£qŒb%S§†LÇù½4F‘öð»—Á Íßeªt”™yón\,yå°@ÇR£ ÄŒÿ3Œº‹nr¬ =€KI3™}Í¿ââèzßIñ{4£óP\¢v°S·Üè!úYGóºJE¿†ko³Õ uærBáÌæu T&ÌÐv;4IBŸ×H‚½ûËÂG1œˆÌ]¸OÀjñá)ò‚înð-%ãÂrzÉähTñùÈtÏb¢}ÛÀ-–|½£ ´ësX ýAV ™$ õ.$ œ|r½ç®ˆmÜSPü=SýBêx`äÿ?\Mn€šÌ¦½8sܸf{Õ ñ-½@Üa¯ø£ah>ÒW†yQ +&Ïä; A1ƒÖÊrÈFù&´Áf3üÞØb 9a%L;2ãeF,ý®ðÓn~fªñ”ã쬯¤YãÿB k ¢1çøÙí¯ÊÆBñwë®^§qòB]¹Ño98ý 8¥–¢%+ˆ#r‚ÛY›°ÀYh¶MKÄ>»iÂèhë=©¢!|ó¡.G‰”cGƒ>ô¥äÆ;:Øò£«Pº—™¸¢8€Áf/U”¯º«{w4*êÐûqcaz3b´½rá½}Ksä¶Pçµ + 44ˆgoìÜØퟆì%h´zémMnžW5›f×’ŠœID«UmñÕÕ7E'7©ã%J£;=}ÜbC©ó¤Á—&uê„«|0ŸÿÞÐÃm¬qèmŠÞMïSDãžñ”³+0Ǹ“¹uFë¦ñ=Wé÷Ìýiè«Ñ}¾ÒŸÌ›'éN‰^QêÕí“Òý“«¦tCö—todóÛ½ô%Qæa+s]Ì'ÝN¹’OÖ£—8 QÿUûéy€Úþ ùùµâã¥Ù_”ÀJryîJV˜¼É™ôh!¾a§?û×Â_:hß»š“ƒ©ÝÇ‹5PÿÙK2í'ˆ…ÒÁg\48|å¢p.Jå;2àD#½7£úbX[#YP;ʽy²dkû‰¹ØèÝ"ñëÒ †ò|'_Z}U$Ý!æ„æˆ+‚«1€ôç’íÔÓJ¬þ tl)ĩЈ”wF¬ Æ þ’BC÷Á¹“±^w.ÄI•ÛáѽǨ‘ÅK(‰ð¯42$_9_¦WIÀ<ÛI±L¥ÍÿÿªfÒOò×µÅ!iÅ“w§DWl;èöBæÊEª„!Ià ¨ßbÒ èIúcóÖQÌdßqõܔµ¸‘7µÕN?бˆ‹/ #.­w¬‡»>_©6 ‰U: £8öÛ$U’½k¼á4áŽÄßÃ*ÓÓ_3¥ŸÙíÆe5üøüˆ.jùp÷<æÚÉ•OF t˜àÕ¡ä ³qø^sðñàµô±cìÂè‚BU!³;Œî*›¾áºq–Ëê^¢y¡GË +꨻ËGˆ¨õOð}}ç”Ï&ÌŽ>sCÑû…Àqù€ÓÃ7§èK­»®&¡nUp ä; ±Êo:vÅ@þ&”"úöF7rˆ‚ÑšoúöéÇÀÊx ;ƒ/û̳F¸Äëøz–n +KçÀì¼ÅãÉdl-J±bÑŠ4²>AûéïSÑ°ª uÐ[šÕ]5~ÿ‡WËÁsª£f×åJusº„ü½Ô¼Y¨|hM•Ý¿²âÀòDl‡x2!3³ º¢ +ȈÿÍDI¤c«ç¨Ëá]ðÃ`ßK™Ý‹{ÜKݯ +{¸V4‚-Qâ>¨Ò5ßív>”$Æ8M¼ÀÌŒœ¹u¤*S)žA‹gy{˜¶Ž8ÒYgM’?Œìªšˆ£wù„ø~–@g=]ú("cª"ŒíFuvü÷öBJ€8ÖÖ^¿ÏÍÊÈèÞ±glÕ‹ˆT1Ý¢2Žˆ¬óÕøÉ + h÷k*ˆÿ+cHÁU!çìù—Òbú8ïZ5zbj誤væÓö}¬©¢©•bí”Ç8¡ôžÑeãÕáIÅs zb7Ø(?jµj}¾61¥¾"“ÒÎ ÄyIñ¿oº”ŸEš–¬ÏÌ@›å¼Ö¯=®ZÝÈ•(DÖÄ]v[ã+ÄX2¹øVç/DïÃA$ÀªX¥7–þ¬ØgêÏ ¨©}_*‘ß·? ’@aexkK¾ œUpñR_zÞóLE¶«ÒÄûø»mô=» ‹ ZÚn¬œÛbžù«Eˆ)y08àº\Žàô‘N¦éî’³ÿÉ‘øpÀ¬°~9B-u¨ ”.Ž­¢V}šu¯ü‰ËFÔÝ{›¼I3[áÔÞ Ž›£ ñÏɵ†@ã3lÊËó ¯µÃBÃíÚï@XÐ(Qå°©áJTMª/뙺@ƒOMŽÜrèÞ[± S‘éT¿ž/¾‡2ÔÀ\SD„€šx-g4 #ux³©t<à¨÷ïÃDÌÏÙ#²ßœ»L1æËèPš1I1pu£æCR„ 8#ösÚ #î ¯ÌAÞe¥èî*”``O£çÞ÷0«@øÞWŽ¨ýÒ݈)²‡Æ+7Wmb÷Ö/™¸®4·?ਣg|·ÖZqƒõß ž ôæZý‰ó²âž¢áŸLâ7kÎÀñÒ‚»oêÉpŠÛóxB®Ÿ:°ž Ð:d8¢KÊ TVk+·Õ¨.‰M/ÑŠ³A>/QìKòâHÝiO$=ÿXs“óÉÜ O‘`û±T!LJPí®¬5{µÄ}€K5}MƒÓÿQ!¯§ÞDòÀÙc4šH ïú8ëÑ›¬iü%Ø€¶¤,‚íùå ,¤VÏ'¦T²Ãí&$„ºÈ§‡[Oê› E𯞯&jÜTÍÁÉ»ådJiFòÖô…`¶ÿ$_éíÀ0«·O,Ê i-wûq\il‰KÏÉ!†tºSòhÁ…ßo,Rã×üüd!ñ.NØ,Æ¢uúrð2€êfZêÈ·;ÒbO¸¼+©¥@—ïÞáÎ19\/¨MÇ銓ÐÓÃ×J£Ch‘{çø¤BGšfF À,k$éFÙ¼¯®$è^¨cÒp‰'žý`ê5»ÞqiÁúô¤ÙLg‰põ¯Ä')_ùE|ø eÖ,@_ì*àþ7#zXw6ÛýUÞmÐá7*½#5ÐJª9áÇ;)Ö A^ɽ +ŸlKɹ³/Ä =<åZðÿÈ'îLïZƒ™ +õj¿í_M;&I 61¡‡É.*û9 ’´íeWtÀ[`IRòy5F[ÙÆÿgŸî˜'Ñk úa¬&þ+%ŸÍä_8ûœõ²ênb:HŠ8Ä7gp±œ;á&V*6Ñ÷Â. ¤¶V¿™¸Ø\ÝxmBŸ1ÿEiJlInÇ9áʆ”F°ÍãK ¹2£2%`l¡¶$W‚AoÇ :I1¸kóðT“ÓãW84¢¥Ðûlà*ùkñŽ' +¼²‡t*ù=¥|aæXNqh™ÇÊþ“bÌaê>Õû¯Ð‡êÛW•ev†ü^¯¤›c2wèÂå+*p¿dpÃú>T ü¥3¾½¦Åþg–Çdv¥¶ƒ¥#|s¾æ °TÖ«š)f ²¬TÞ_²Úeš„Ð8«|jߪe|h²I4(HòÓg¨I¾qAAX74KE’@m$‘/‡@ÀTLÕJœ4Ælqêaõ6bûRB‡b™Áô““ Ñä‘%Ò‚îVÛ56^G}iÀ¢B‹(ˆÇôÐ6KzsVm H± †Ìg¾SÀÞ‡µF³Ú‡†ÖGÃÁ«µ`>F>@ö]52y;˜‹1 +딢¿ÈÏ3ñ—¾Dy ˜cN'4­ ëÒf3÷¹¢µíýˆú½’PôÔVv2ÉŽš?µ`¥#j”V€‡çÆNimǞʎ@²±¦é#·“3\ì2ûÍì¯fŒÿf0w¼À„Œ÷/©³Ö¾:ð&HŒ‚ûÆ)¡`‡Þx…°Q‚bYÅz fT™6í^7" Á¹2úEØçB_¡ÚÖµµÿ"¨dæ!ñQõÄp¶}PxŠñTŸ¿œšvîA¥»sêqþ±Rñxé™Ð|]çízÉ“uéÛ–j’ÿ?š½ËñFÔ†)weÕU0ÄQ`òÏ• óŠ€ÁéEÁ\FR¡öDAŠ/îq#qŒy·tZcœÝŠ§,›Êø{Z¨œ´ò¹Þ¦²£pÐM¢D+¹JÉ’×M:÷›~nûpݹÁld‚Þüä#µO,'îx |•ÆBÓ¥IIvðhdÏ4\HA¿¥³ Ê¢»`AE©Yò·#ÊL9ÏŒ,ïl1ÜË É$ðÇ#V’^‘D 0² `ÕÕ€•†ƒCe“;uÒ¥ƒ×N¯ºîíÊ$·²Ì§X.nt?}oÞ¸ÛêM-G‚€Xvâຕs@ªþ,ç• +õÖþBÑJÊ2èl5ÍC¯ +b*Ò¿Ç‚R¢rn:1z…º>± ½=Ï2óù¬Û糶ÅÌ=Ð+Þ {PG Ë›çÃŽ 'úþ»ÃmL™˜sñ|—¡Zô÷‘X‚§IhlZ +>©Ššý0ÁL‰ám3QG‰´ãàÌ‚µ–³QŒ<¯}`Môò0œ¡LACôá(K(Pdc]PQRp Œ;û¸¶ÞªhNˆc¿J‰âpüºÔj<\ØÍ(–SÖPŠ„†Æì.ÛùÉ"øÙý›»á@î$UûᇇðÉÒ´œKu¸sqbvfO`â³ìq=¶¶Võr³ï±®Mð%Š%i–lñýÍÆUÝç²5ú*ŸO<(¬ý Ò*ÍóWH ¯÷!¥òvCÀ9ýî£>COkã’½@£þFP›€¡ xp§žâÃKó î|U³iã8­ñ0¨ï™6eþâ_V{ùc6ãü.—A»†amƒAä¡3#£€}Jø{P³þé!tŸ¿•beH§óÁÖB tâÓp27ˆ4d”6 V¥T¶¡j¨î˜â FúRVŠ‰u­¿ÀÈ.´á÷ð3'¡¶ÎFŒ™þE´úUßDzu[Á챃O $RuûÒôwѵâ‘ØHŒA'~óë™ â2 ™´£Í+Ý;â-ºi'-Óå/š,qqcY´„. –ûÿø¨y4==ÐÏ—]FeðGú}é‘NñõÃ?ÂØ2Î~Ø*ë +»ç)ì+o:æÇ’’ØØÝÕI°ÏÚ‹DO140fYTOñaçc½O®¯o[vgâ$§­$õŠ5Î3_¿h"¿ƒ3½~lå»=Pbw¶L(ž•E³>WY]©½êqµ,cÄÖ¨ò]Ÿô¥›[^^Ékçø çÄÅ©Œí¢.sç ’7ô'Îu’ÞÆ!ØÉ·ëiñDÙ¦§ ç«åÛ‡~•=•ªŽߘ6eÇÃÅ|¼½,j`lEÚd°¾ù·¾_ðv¶ÿí……+^šm[—Þ,h_.¥>‰ŠlÓ¡…‘25ãÍm‹ø]6HèöPU +öÔÍÀQ7¯šÀ*%djNöÌèÞ†™Ç Z¸„<æîcœƒ)C³šoÜ^þ첃`åÇõ‡éâ2¾ùÃ{¤×C¦8…g9jˆýœÂË—ìÒxÓÏ?úAžªe½Œ(‹|·¤bO+lÉ#— ü—÷M)ÕÓ*L‘Ä¥Yœœ¸8ì5ë°2‹°td–èÎ#h‡ÒDo™)„¤:4 ¦EŸ‡¼­+ÓêS‹ûÁµƒ59J9ƒd‹ÝN½×‚‘#0'äGš1§ãI)Ó+ý-_”ù¡b"}£ý\¡€Ö†–è@_ +ˆÓZ+sq¡cÜoN²ýÚ:拇R­5ršùÀöœíi"F°³ ±ÔJ9\FLp-8Ï"­Rö³ËG³Q¢>_c®RÝF²a`6·Ý|¼|¦óÙ"‹|ÚÀ6”eJ£&ñh+@¿¯Ò)£@º—J^¤A€³\ClZ…ð¥έ“UÀ žp(2¤¡—Ñ€2ƒíöøÆz› Û¢ÇTf@° È!;Q©T +ÌLÍœÄn‘D"iÝÝhUUQ7É¢Öâs:‹¨ÕÞÍf ŸAúMy•‘è}.¡«º{v7§Â½“ùIò çWˆc½-)jÊßÎÀhkëä£C€ž¥¤“u‰uàº6ÍÈblEp­¦+2û+Çêoq³²µE{¢“”˜õ¶v,sZò¿O¤ô¸^G×½80fâOÑ^ËRÔEîëfUÎ.¡ºµ´ˆÂŒ¶ÀáÝÀåå&êxãäO=îCE¦YÃ{ÛhA‡FóÃÿu‡A¸E_‹CµÁ*QÀœx›[?*J8Ë>qkgÇÖÅj–çHŒNàl䟈'¶SdøÈ`Ž#Bµ¨ ¬t`3ƒFò£á¯¸˜ó ž\B¸·ÀU?\mU‹—^Së–L™íYÍÞYü@š¥|ºUºa‹ ]cô€ôŒE\V¨Øpv´¸"!, 2€© Ç:îiÄ0…¼®x® úðô&÷®æž0ÃôêU7ªŠy}tÖIô9*§äñNÄ3¦×nÄÞÝÅv—¼‰\*p +æ (¸4»]MvD´pmNãÇ{Ê"w”ˆ1¤N7Tb!Fà~^‡ò NúÚ÷ËcStoS½î.¯þ’!Æ>ºcM–€çª4¹÷…*‚òé’:F€úó¦×á-^°Ü@x ^8” Æe fjjS“Œƒ–ÁK€·¤s¥qâ"šS~ã ^ÍU ân‹nL®X{8W©èÙ€êf[VJñJd$, ±c.hïyاÞ94οn>{-U0> IÖ//Vt¿01^¬XLÉœo…n¦!B³„^8ŒÄLË™XöfŒ°ñ(4« üŸn^ë_™[…ÊxG¯ûlÖÛ N†ÈäøcZK¤ÇcÁ÷Ká„dR +wuH¿\ˆç=ÒöË]¿»‹ìêÒ Ã؉L]‰Œ!Û||#‘ñ¼Àz—ÔF°.kSüK+õ}pŠšÈn¥È±"è·XÕIth–­þš¨ªþîWzáÉZðÃ{#›Æ4MØÓøˆ5eîã}¨* Ï7ŒÓØßË€ðr¬´jñâêZŸ +SûÊlrUp‚øàøqZˆ ËdáÐlqÛŨËâ•ú '–)[ И¤ÔK^ƒäÌ)÷Ý­O3Fd¬L¸z))b8Ñͯwj⎥3î²Ö©ÉÇïÛ¨AC,ïD›ùhô„„ïˆ.ƒ"K´8?¸…yÑ¿í-¸@쟛iÐq\ÐRø…=öä€øŽœ_DzmŒ0ø- ‹ÅQ³i?…| +þéŸG¡.C]©E3ÖIia{pçvWŽên«ä"²BÏ7’wÌ.w~6ºWðL~7P3% Óä!ä«ûHS`1DÚãÍêí'˜ë >Îw–"b‘ЉoSñ“£³¾«¢gŽ¨ìÑo@4ÍŒÎSe5ùÁYF0E( Ôls„Ú5-mD·‡‘Q/ 6#¡õ…P(Œ§KX +e{³ŽÊ/šHø?I…ËØÑVà\1å_«ñbq’ {¯â<¬¥Þ ¤ÀZêfÝúNÏ•|·ò¼Z×Á[¸à7_Õ/d¹Ï4vCz +Ò¸Z>Ř,.kvÜm†&™àYCŸ:MZÈ™Gf|˜?$²zïÔæL‡!U"Ñl:Ñ_=×ú_ƒkéq}²ûíöҚŽ?Öñ†X¡ÝxÎ×`,Ì ÝÌqÞ/ +¦·Õãf÷?\tD--±¦1iå2œF¾®o”cR +wŒìu™Jhúô‘j sÖȆq ýy¼ÒNù/iÔ1’¹«MøiÉz­Df‘ +.ÒØ‹%Ê€}­ÿÈ"$#£¥éiq€„LJKžp¶gtÄjšýÇ`'C—Ö»åÿFÙ +¶ÈÀáÓmÓËðÅØW_v3¤ :tݼ—á¿Ä½[Ñð¾C|a1¢‰œÙï²–ÉK Ó ”3pVׄI9ÔQ@Ì«ÜÐDœ…÷¡›"^§„ÉóÙ¡ÅÒü¯rî^ç[‡ÆŒ­Å ‡Åà:ÇÛWB8ÈS5ߨ9ˆ¦%?òµ6@€°i¨Sé´c[@àÈ]ßÖ¯å’xƒAÏEH„˜ç,Šl5Ý[¶˜Ûï-¿ˆíËEÓ$øG‘­a‹JV1OÄÊ`cª†Ç\ØÖÕ6hÊb3¡a0GÏ°ziÍ’‡9g|”àóè,”rOÙ ¡“xä8Ôù™öÛZÍŠ½JG RŠ—*^qfQ·ét:6Ìg¯IóéxƒY/ÙÈA·µvcÈ3~ü| +VsŒ‘½=¶:|#ŠÍˆ] ÒÂý?Üq”îü#!qÖ×shÁyÍÍI0S sVQnwÙ²‰’0&$SB#©aV2†DNã„©y]Ù@}Øýîá-œW7,j`à5`1dÐá¼9_U9´ä70¬†uMP! ¦’Æ?0ŒVÑsa‹[™þ²ý8f\ÞTŸaÙ¢M­²Ì¥ £;ÎûOàwʃgÝEõ[‹Y$pì–ßa‘ +Vn‚GN—Ûb?Þ{®¯–Á¹@¡ +[Ù‡ËÐÞtƒñ®R§-Ìq‚Ëô¾#cÀGíf$«Åsœå˜×.¸ émfQ¡ÕPèuø'¢ðíY@Þ÷½&b= Dˆ–½`¡QÊh®bŒæÔv{潆HѲ2þp¨iù̪–S«í±°¬[+´ÝEºÈU­z`RZ¦w5ÇV+L²ÕY±œwÓ›Vc–ºø‘ +¾&¤·¯°¼e¡ðÒ²9¸Å~é’ñ¢Pkß:ŠyÉM¹ë^Q2ñÿv©ÈRó÷ö@èæÃi¹Ì2¦éAlžÓ+ÉûÌ Ôyê‡}ëkÈÃp” nÇßùŒèKCܺHΕÁ–CMÈû×D—†·à—pŽ¹ Ú±iß A0Ñ3ƒöÆx$öË3šš eTõºð…+RZ¹L4cº+¾}r413I‚¯ŠÛ¢Ù9xŸë0("tÐfi¦6ŸÍc »”0ƒø×Rcö¯Lƒ†jñ‡ÂaqÖ˜ÒÊ^H®>W Ý\0ìx¢,Ráé¶ ˆ‚JRÂbð,¾ìü ©ˆÒx Í´iè+f#aooCiá¯~c‚œ”‰ëýη¡Þ~„*ªïêzWa’]š:šUßG›C4–VE'“* 6¯ßîMèT–ÓmÙ–àV=à&ÍèÆ¥„#² ªi§ðJ£aO§R'£â¢§ì&Pš*7Ôç+,MÝø;¡ÿTÉGS äE¿:„3Ñ×ÎQê¯ÿ»CiBéáq¢­ _ ¤Unê²ß¦Å[êy‘ë?/O²nåÐiL¡nb_…o¬!‹çÍŽ©z#éR+ŒÏq~OÓþ«ÉIð +‰Ðòªê¬Õ>ýxs ¶¸@D˜Õ·&è0=ˆŽU ø#ƒ£¢AÖÏ.\†OöŸ³@Ü·eªèsqÓLØ"ìW ,è‡Su^;“.¯ùª(ž–Â÷Ç=r&ú¯?šê¾WÝ dg5à‹ñäÖ’ËMcjëYï$’zé• SJ÷°<…n¾5E·cë²ÜràéUFÜ)«É9vŽc5cõbŸ]Ì3½µ5¡ ‚5ô@³ƒ·÷Už‰$x1§M)dÇ^ÆŸGê 䞶7„pKÕr<ôyÙŽµ+3%dÀÝ•FRh‚ÈôPeP¨„‚}Iºe8uÕ— âÀ`lB‘‚L—¹h]ݤxç‹ +Ý Í·¥ðt³I­YåŽ-‹ªŸ¤zW¶( óÄVÖø©™4]A•Y'{•#~s´9 j8neÇ2Öì|æ:oè ¹$I ÖíX,^ÕIa§!PýS3Å +¶Ž Î>×éäOÇÕÅ«²ƒ¥Þù ‹ƒð‘–Í—íÏ;öêw—²NŽ1­SØʹLÛëü/Ä3æ–ÈÅQ#g!ëª:vpGª)À®çâ9t!ö¥‰­ÎÒfíwÔ’v… + ÉÿÎ-nk°{ÓD‚÷ÛšoO™)ƒ5°-Œ M´‰È×Õ`î”çªàÍYà9.òdá.lŠa¨ ÈòéJ(–œ8,´Ë;¦»ÁÕAɤÒC“HИ[U±Í[¦½tZËa2òðŸ²ÝüCÐ9xQ¥Š»®q÷³+ïíã‹Ú\i6ƒ• (EúÁ„Í¿ fg !Ï 7‚V¥¡­ú ÝajÆZà¸à ¢ +†"?š†­ +#Ó°ÑòBœ~ŸšÖûtxe„¡W€q‘4À§2>U°C®ÜDb ½¶+ßU E´Ðæ<ÅÝ=L¹eIùm9ãRxÓ…A(Ó~NÈ +G„P¿÷40ÆÇN!“NÞ,†Û.WöA¦ß@—¢.'¬æ“á~3l8ë’©•ˆyà€A0C t³¨ ' µ—º£æ¼ð,ϸŠÍ…Áõ ´8°¢²\㎠+ÖVãÆè!»hç(Zäü5um·Þºc±/× P!M{ßË:54Ñ7›¯&”üé8Ü`úF ÈõsDö—¹[G^aš7˜Žx%{3| D¬äÍ—ì쇀é¹—^£(íŸ/ž\7üÂÃÛƒÎÐÄí+«þAá»)XÜ͵ì ÊYì‘ŠB½d¤§˜fç\’”¾È˜ gôsÆwNÌ,‰óÛÚñ·§K<ð22Ò†ÝȃÕćë¼WŽCÆ™qáv²Am׆|#"É›ð¯>÷ܧäc|“((ìÁÖßšMxñR»¡°Â9Wø/Ž8áÁÉ&ûq_@É(•©LJÍÄÇA $ÊŒPx;€AsÊzsŠò1sïI&æhHÑ/š¥¸Ð®hG!Æ?<51ߤ°Ÿ`ÀøÊ¡§ˆÍ¥6‡©…¦ˆúHguõ°”öµÄyMÿZÚÜC£[ tŽ§4*NX;v­#Àa…F{ô6 Ã!¿”§_”Ë’ð/é,q Q1ã€ømz°ì÷,P.íÖÌ!«’ ¿xZ#ê$¸¾ò…¾l¾ÖŠçùƒÐ-”+™…¨Ïòûjò%«HÓ4N©‹;Á%þiÍùÂtÚ¼ðÂØ0AÒ¡Ýgv÷èãÎa¸­NåÊÜ_í‘H®j™ÖÍWK~éú +Ë+âñ‰%KžHEó´‘Õ¢†¦JÇãàý5“¶ Éþ +ORëUO_oFî¸ÀӂùÚ#dá2œM!¾¾”ívÈñ¡H˜?ЕQÑÖQÌ'z…Tñ©rý×°.šˆà;ªuÕóJ•÷C¼Zž.;4ÉóÐD´·åNg½"3k qêÄ Ñ¸T|À×®œ­c¿D 4YÆüØÝ[øåþÊ(kzæà]t0§G"îUzv÷²5 +sþU½âñ¾Ý¸¾QÔð’àg…Â;ò#®ŒÑÈ"œ*¿“¶ôÂ:¶ãQDÆ&-£ÈM¸ÔyJ€(tºâ!•çY›[L5º"MƒC±sí*…¿„V¸F­C ²¥¿;é¹ÎQÕÉÙåèë•»çÓ@sB'%™ª±\Þ:À^²…÷{Ás”$÷鱂Ò]–n½Þ¦Òcí¼d¢‚¶ ǃ¾ r¢G!Ö†gë »°{ì{­êhG ËS\4ýˆïn.D„ w}@Í#¯§;DiÓÐBˆ|ôÕ~²aQ…ªqÄú>Ëy]Y +}VpQÌf58Ì+5|c]§HŸIU#œÂE毡)+*¹€W‰wöÞŸCT’l°…U€€8ß26Vdz°³pã¼Ò]t•®wªk¼¾råÉåt õª;,À²ùj"‰×á?ŸƒÉ¼ÕŸ.íXDÜï´?uð²ºéÒ¦4UìVÊ—ô¬MPhŠ•Èo”¨“†Nú~oJ/ß*•ö2Qê|ù} …£”Ûÿô n’ü2èg³ÖlÈõØ\õ(¹kÇL‰4ùö WóŸˆIä¦ã»”/ÏiS7DV´‚?MBÿUÍðOŠ½Èi¸cÏ +ª¢‹O`„1RXÜþá–À‰¨Lšßáo‘F¸•ÌØÉ9«@C ãXV1"õŒbAÞ!fbU·Ü@Ÿ*^ÞBÝž˜ÔRnŒ®Z8T`B‹«Me…#² ?ÆlƉd÷YP€|bV\àEÂrù¨ÛÁÚ䟯wΊ{­agÚp¸Ói ùŠÑ­…U»˜iã׊ E´Êšr“Û•.’c*.ž"y7ËÃ[ô´_ãØ´A…ï½A49¡/Ù¢eÝC¿úÆÍ€`e¨H'1Fú«†ù/¦ü呲Þ/ìjAGÉÍ4½drle•S°&šŠ:QÕ̳µòéÁì>6üŽÍïaÙœ©ºÊÙÕƒ­|‘SmÄE‰ÿ@ÔYwæ ›Ë¯2KQÆ(/8ÿßûŒA­æ×ÒøeëûÍ4Ñ8>(MŠp¿BÉÀêÿœ˱UáÊìyÎ îEmäsœ™‰Céõ€tÕ,„ühÉhŠ¸ œ'!eWïÓ8æÚ*ŒU‘‹›Ý`ÂT°5ÍôAÃ&@Ÿ1òYGù­ ë»Sz[Äš&B<:y‘ÿ™¼3¶[Û½eU'¼Y"¾›{ñÂÚö&&§ ¶ +ërÀ)OÆ‘ 6“Í9=ëPÀ.€r³K9«ïyëLêR«k +ïfa}A§B<É°;;°-ÝK¥}q¥^ê_âq=—YhühÅø’¦±`«<¨„ÚbÖkŒÊRë°öôtß +­(¸'àEÓ;á[M—¸<ç9zUnûÀ© ó]&Dº't8)¾È(…´‹\öAÈ%Ú¹*¤nöq“û](²Xh墋{ÁìÈÓ‘å¿$œSzêk§.@Š´®‘/k<3ŒÝ5´¾!bxÛ~8ÉàŠygm-îׂ/cuíYßìŸùÃÈ`p­ÈÓôÈ.£Ð‹ð…ŠÒN ÇÇMsÚ0™R( M¢EÑqvù±^Ñ–¯ö¬d¾@äÄ0貈©”a«Ã¼»íŽ’3Oµ+HvymÌ–˜_.eD’eÀxbË„ Ï]°rKÒØYYf\×V¼ÉAáŒÿ,ÈT­Œ|!x$pœ¡VÜ]¬ÚØÕ§höHTÉl} G®B< äAMl~„î +d»ì?èGý¬GÕË÷2”“ qä”’äÓ¼U7<ÔGºà¶l¥¢»Ïöøö v$G"ïSS¼ëóîwÃóC%Ù~!¸Õ\[Oƒ„BþÃæq*Ù‰ï=i™ÍL¬€T·²ÇcRv’W~WkJÃ|Ý¥Ê6>å(’ñÎËÝŽìæH÷(‘’b`B8¹L:\‡žˆó쑣Ң­óØ©@ÒÀ}è5—9ÑœBah8×”,ÏEÌ?ˆO>ÖÇ Í²€m—ZDCÒòulNp…HñvFÿÓ¾ì4 $ôFæ-o‘3i×z’uîn˜eU­VHKŽ‘Àò­¡f“™¹Ïu$ɺ ÄB+žIf0xBM (þ@$“Ô âÎíÍmâ¸ëЩ!z›ª +Å\Ë@θŸ%è·µ'ñOº¾‰ÈÐÄ•–Ü ýÇп¡ÎYLYä]7Õêàç>0¢iÒ ö#»‘ðßõ†’Ðóc•ÆvM…à^#üwr}žh¡†#ÕÅpÙõîIœ²~çV€29—cª”–‹nÀ‡ÖõeÆ1Â;ñK4ê|a|w¡ˆîøëÄ;ÉŸ¹pÞÁÜm€zI9z=¬J)µÛÀï+w €¤ ¯°"ü98®Äù¿¨£`xþI^sX«¤_AR‰caÐ ²¸±•s§&è»—1Ycˆ¹(-'aS7švÛTF)ø˜;f†âþß{² %ô­nñkøäiÊÞú‡Zl2›-¬‰g€zKf£ð·_ õ,X¼×!cWç™n¡8˺oRW.þ¹& –íZ,%ÆIô×’,‘ÂìVÚ…P:ëCÔ¼>ÿhÕP–Ðû[º/JKJ`Ñ˨_h(’BQ‡——òï=2)Bcš~´4®“Ï`6¶þë“àÈ? 4(! i1@dž¿Û0\Ö  +ò@âäãoüWÂÒíI3=ÓÐB¹¹Ù Aqái‘µ+rG¨4îãY/ç&ñ)cÉd%}™Md?bÀZ¬Ç€8Û?ˆ+½ô1VøA|µõGÙyždF¥±|°d½_Àë _æ^x\@AG³Ì‰²É!ƒŽ&{[ô‚ biÌvü‚ ,£Ã‘žš‚‰o¬C`"1ðKÊ°Cû^R©›_ò©vÐs‡àß¡døO–ãAF^ÒΡ‡J’ê„/ÁϨAìa¨{Ð}c +ð{ ·Ëà†•Ê‡œ—|¨s…æÔ¨èƒDÕ>dw ’Æ@]âÏ~(Ý%zý4/Fÿ™@¬&<̈#¦ATÐlbjèî’! ‚Gê Üg<.QÜH=…ÈrKp×B€×Ÿïç±!ÈÖrë5›AM-DHà="&¢·-é„"ÀÖrªNZ‚;‹àš%nxY¢j1‚é9#±ÄáQý•ƒ#ˆv%KÓœ>BÄ)‘Ê#hÃ÷R„ QÉJPjH §Ñ7Y$>æ#a¢JjPðQ ˜è;~N¢ô))F(jJx\JPK 2§PRâ®Dä(Qi%xýXN”8À‘À§BÉ„©™øˆÿZ){ !>‰&„ ÊÆghc‚6'Q­LÐn’<…š¤ï4—I€M cš›¬ÒrÒ7K­„'Y’{ð§’ÌR&8( +y‚>еö„cæ±OÀ04x}$´†'N䬑Æb$È +B =@‘<# +Á‰^ƒ'â>$z‚NC‚1VH–£TߘðHc™F·Ì}Œ–ðªöA; öÀÛ õñóÊxëc3Ƀ¥>ÐVÒ|˃úÀgó`o>t?R—"éÑgAêA–|rÒ¼'>HȠ¢íÁ,|8ÞøHþùƒÚ=°2á=¨, :÷x§‰}oA%\Zµ~mDÇæ#cöð,é#–ÔîÖG×Õöc=¼ø}DT=òƒòé±Iôƒ±ôÐp—Pu?Ûè1 «bŠûÀ’,¨Ža¬?è!þáÄ ý `@7A%Ü…Š >&dú¢âœ‡'\(=@]¯‹dßÉ™ˆpú¼åÿýú$9fÿÑNUÔÓ<43 jt”ªÿHüò€¬Ò"@¸[J ÿ®b–ýƒ]ÏájdF:$$P²Ñ1¼Eùƒ¥D<ó;¡CgrM\È{ŽÏÀcä,ç` msüÇ Jšc°!™ƒ[þàæ@îÝr€w\9ÔN)[óGƒ“ÃÕq’#ïüA*r(ëä äý ï8ðݨ‡…¶2Ƭþ(›¯…|ŠƒWâ˜Zq¨–?*âpTÆŽ¯™?bÎoxIÅZŽ¡í ]ªÀÁÿ¨ à0>ÄßÈÄ?ˆ÷ ‡ù-¸{ã>¡ÞØM—7qþàÃйîTø+vûùƒ‚º¡²|n°0ÞHû+ÒÒ¡qãöü¡Š¿ùÃŒpþÙÛHÎt¹ ½ðâvþ`Ó6ÆØƨ‡ýö}„€ÓF×ó=6œz6Bý“fCmYÙ Fùí6:ÆË1³ü‡ìw<廇` ,@\¢üGl @Z= +@O„7nRþR†}ˆáúÎa;A?o…’7* fÕaqa“0EÆÜ7{Ú‰~B¡þjÀÕÄìkÈpå5jç@Úéý\ãR_kHd8k”n ÍŠ5šÀ x“$ïY}¢ªñÑ $ÕPÉÂijÌ{‘»%,ˆä"Ñ<î6;—MƒA‘@ŠÁƒ¢Ó C-ÿ6!JCña:dð£yæF#A‚\4¤QºDƒA-H¡<û 4Ç HBƒT6ö˜VÐà6ê ù³ëçùPq1ø½üžAÏ„ÀæøE!@w†'&ˆQg€«¢r†fZÎ`ë…4ÜŒú}Íøo1¤˜fèœ!DgÆp !Sf(‡‹„tH!˜Ñ”‡yœìC “p5R}1úA6‚l1ÞW ´«)âÙ‰gs„[h)1ôÄŽp;DJCÞáÞ±Qmë>†ÒxÃàLa3 ô·f„ªÂ@É@²ÂP$„& –’º£kˆâÁ`¤Hh¥Œ9eG§`°%’à!¨& ᢕ¤yÀP“`´ &i˜Lè]“ ë/8: Ï~ë“ç6”Àñ 6”}ñB¡DOô…^vðè!_Lî_(ù$m·s¤ë%Åôâã“(`.w^ŒÏI埄.^è[(a¼ %}¼‹îR¾]cÍ.î³”^r]Õ….¸ +߯H‡.$ΫJIsáXP.âjÇPKÖ{‹Ÿ§DT† æ…q¾ o‘¬ƒn1èý¶øû”ÄÖRQ%λeE5ä[Ä£J¸] ]OÖ‚á©„SªžMî_€ri¡ƒ*©9Z4¶ +-c%Õ|"}ÎÔQâ©Yˆ˜ame, ~Éð‰ød±¨YH2÷Xð€á«D…KXI»b¡?bѺ߰àÚtÂbЪ`¡ì +Xp|•„ù¸’ _!&Î+:—…^Á4€à»‹é½‡ë +ÝÁ’‚sEÁÊ`ļKµ[q‘­˜‚%üÔ +*eIhE—Z‚]VÐÖ-zØRsI蔼KV° +…âK˜z¥ #,&—\ÅBaBÒ* +X(nÚæbUÌòSŸcUøBɤìBлLxžŠ¹ûLÅ -ÀG¥b`ÂŒv,5(*ÄÖ)7 d“ŸbŒ¶ x§@0» ¢ÇÑ¿‰ÝMa@œ†FNZjŠBs‚2¤ ŒN 0®¬Ká˜ÚIËRˆöNèTŠÆ”Bõç ?“‚àzR!)úŸ C +žô 0â'>Š~ý PŽ‚ `iÈ3PÀ½+(¢…àA!6Q,N(…f +§ˆ‚mx(˜r(u†Â%!JW¡Ð.QøŠA±ê=PðrQÀ€¡Œ~Æ‚ôOwŠ~HÖ'|M¤ñ a%)ܦÑM +=!ƒ”B"O°Z)ÝïDïÛN¼mKÉ­Š_ +K:1„Å91LÁ•œ Î'>Ò½Ö¸)õwjN‘ïva.ܶ‰óÈZd͹”°5áâ8îcn@ؘp8\3Á’•‰ÊD¥„L´sTŒ º•T(Ä 6˜ n*°è— ßSà¼fR§K8JUºo ©­ +ÓZb«0™%4†3,qU„?£t%ªš ++ P%0׫pO ìÁ +¯”õ0Jg¬ÀX0(Á+cE–‡ô$ž öb¥‹“fG“`¢Åþ—Ä×ÖJbíM2 aC榋P¬͸’ô±ƒ‡É‘`Kh •ÌÅHL-ïŠF­ò:°ü¢cÝp" Ec‹†ÁÔCI$Q’bôO¬K6À +1ÂüÎÀ +èZ±‚ì5L·+$¶L¶b…²#±X!Wž,ÖTž3ÀʾI‘ØýUx \ß ‰éÛ ñ‚Xé*"+Ü~ÄZc…+!ó¦GàÀ#žuëˆ fçꈱҋ#P¦¼(+äÙ¸U5bX…F01#¾á#4šåËtÀúÓš,À´Ð}¿uk‚u¢à§fe=XNl²›6ù "äÞ5C –4 ] +8Ûÿ¹¾çÍíý×˽Æ9go1ÇÝjîuÇ|õú‹ Õ¾3î8ß|õÞ÷[kíïž{k7ï?k{ñ¿?s®;Ç|Õÿ‹ U«ïÅÝóÖZ½w¾×æß¹í—_k­Ý_Œ¹vó¿ï¿;ãŽw÷þz9cL¨âü=[5Çx{8㛯þóUÞïö{ö{¸óß9æ|oÜíï[óÍõö6c¾ÚõÕ¯_k½½cËý÷lýÛíY{íÇ„êÕ›ï­ïîw‹ñåø{ÏÞïo·üþß=[yÆ|ÕcL¨öŒwÆ8c»½·¶{|wÎøßþ=kwçÝ{ÛmíÖ˜¯þŒ Õü/†ÿì1þ¸s{¹Æ¾ãÝ1ïýæ~¿çLÉ0–øþ¼sËoç˜_/··ã°6~ß߬yþÛâí»ÏÞƒt¼Ý[ó{}¶}ãëqÞß[7†gèZüïõ›c¾€ûÆzwm³÷ùï}ïý±¿Ü¾BTýB¨ˆb*xm¿q Åb€Bë¦i8‚§ùaý‡»£ISéXa¡§ÛÔ¬» Cõ ÓšÉ{{ßòŒ=ßÖ_ß¹í^û¯·÷WÛœ=³>üíoï¸ÚÚµw»jª­¦Tˆ >Rê¢5ô W5•_rl¹@³  8KYŸàËu¢.4ÏÑ…‚ +»šåé±»B‹bay‘áø£z™È0À'DU”ŠµYvÒöúbëáž»î﬽Ý).îá®^»ù÷½ëjWùí*ç6kOóü±µÞ滫»«ùzŒy÷;ž»z·Þÿúnßq%±8Çâ6¦UÔU­ý:ëkñÖý»;óëoÏÞ³Z÷z;¿ÚÓúëìóÆ^wÜÿ÷_{öúÞÕͯǹ}³í6g޳ƜoµÆÜëzgëiïë®Zïå®J4ƘÿN¯ïj®·÷ê{íycÅ¢…=¿ûj¿ùýÚ÷ÞéÏùÖöúýÏYïn­gIÝ=ï¿bAR,:{kÛU®‹Žß{½«|+Ÿ‹N®œw/׫ûÍ7ÿÌ«÷¸«¼ß½½Üí®fÝÙ~½Ýµ¿öŠÍ~ŠEçbl;ï*Þ]õº«Úwóºõ[±èÔ»·¾Þ¯þÚì]¬óí:Œ‹Nݸ»{_± mÜi¯sPÕ*&Å¢6!K’CŽx[ÏßïݺÿýÞ­üî<ÒXœbᩞŽT$Œ-i{Ýó5wÕWþ·Öx{ÞæÚvþs¯ënqï0þØû÷züíïÜW|;ìÑð½ŠH±èXo={;œíÝ|o/ãngÜÕ}³÷¬÷2ƽsï¹æ÷ÛénýÕÞÅ^ÿëáîém½_3î|Ï×ë=Ï=ÝÕ¼¹ç»§W¹çÞïò¯¼âësÎÙ~ë=¶ýçuö,æýzî=î÷ûì/ö¼õ½ë2î[çì­bûªµ]•ìÛé:Ͻý_½¼{ìé®z¿~Üù5÷|ͺ«Û{ž=_3Ç{ÚïëyíéÚqW}Õ¶ûºgîýN±påïýn{¸n‹=Ÿ=÷4®7cëÙkuöpƵç=»ª·§qݘûŒ½;ëýÛuÏ_»k9~)Ô[-+HÿˆÃ % !4ât^áë,]!*- ,µ®½Ïy¶ÛoOw9¬;çöß­yݽ_ìy\öµ>ÅÂek¶õoW×uŠ…ËrÅÂ$s`˜ê·ÚŒ¿ÞÙ³üoìýþw_½ïöŸ{þ¹öúbîYì±ß^½÷þîw·ßíi¼½~5þjo·½¿½¿½_­ç·S,H4{¶þš€ò+[´²“ž¥) |‰µP8¶@òóÏPŒÅ1õYœbQý­–WÍâò½>kœqWïõœwÕÚëªövcµ·Ómkÿý¾j ˜õùs}µã»Ši +µïÞ»ª­µÖó®bÜÕ«XľzWm½ŠEëS,Hjo×U{»Úoîîÿßû÷ͽoíµ÷c_o®7ÛÛÕ{;’bÑ©þ×{ëö¤ê±go½¶â\1ö~Wqî:Å‚ä±b«Ö^²LÓÚQ,ƲE @ßùaÙ;âpv·Á3ö*ËNxŠ 3„)ÀP<R2$ïgk÷Bxå"k vMgmüLZÙÙH“ >stream +"[È^ §Xlæ±m²Îm- +vlžJc»<<ÛcÛ¶ë¶màctC»>Àú2jlÛ¶=D¶Œm›üËd›P0lŒ‡‰„±mÛ–h¶­s~¶M'x¶MBôbl4—¥±uWÒØ È”JŸ';t0Ãë<§él(Ýy9!þ|ÙVKqJR%Å!8`lÛ€F¹#×ÉÉ0ÔÒ{‹Ûã—ÇÍøqŠdôy~÷Q$&8°ÆB§V±­I„ŽJªÛ@ëš+°Ì$ .›cøt z¼Œ­sŠEúŠ‰rSIq.d?Ó e+ ˜Y pÕ™€HJwi&6—TúP."ŒØY¹ +87@ Z¬¤q&f§X˜ ð‰ ›Êë2û"¸Ãš(”f–ñP &b^„S, Ð Ëç{h^(Šaá ]ÏÀ!{Mêàë¨dÛX¦mÛ. q¶mDdÛNǶ à eÛ6£±mùt0Ì&0•dÛ 0`§XP24™åë¶fXèxL$à|s:ÌÇ€¨»Š°—‡—òȃe[ƒCá‹ ‘kNy.ŽÖç!bºlðäû€8tsê,_žhHÙãÔ< †Eh}d&gà0°ˆ¶9Âfÿ³%ÃßþÛ¶‘‰LħSMœaô¹Ý¶mh‡ò<û´  bç³mÛfPÙ¶-̸Š±m›9Ù¶­Âñ !N±@ÀH¶mƒIœ¤í9Å‚AÙ6d²mžɶ 0À$… YmÁä8”@âO7¦Ë%ÒÍ&œÛf I,õaN |‘ÉâÉ™ÌÂÆúN¿öÙ¶{•®Ð³ýhÔIqÄÏRñÕ7§.h¦P ’©If‰0ÖÌ5‚­r ¡c $ÕRqk5FŠÄ)MPÁ*¦$ÈK1žU@8šâ躴†¤8¦µr[5@wª½#ÎÇV Sp,QQ††¥úGœ{ÆjZ+g®’œÕzoZ3Óš)£õ%ž±€‹ OÐDIðLëqZ7VqõXã¦Îb +Žáîª}…áøõ8"ZÏÐÍúGœ3LQUË´Æ©#N¤ +¼@“ç +ÒJGÐÍWK×™¦Æ±qfÖ³ž¾XHº½»b¤€P\iYLäûwXHc$'t #E*’àªÞÎ=YºÂzŠ á‹cØá7ûz³6KXHºŸÖ~ÃNþRXHò¿VvÆ1ìåñ,EËb"*L|RXHò»û­Ï²Ab +š´²òœ^a‹ö¡•paåy’¹DYOó ˜j˜Š²„Š¢áišµTíwÄ)ÅD¡)Y‚ÒmÕRè¦! <ÁÓˆ3³h¢('[ %u‰ÑŽ-Á3:Ô¡! 4AWIr•ža*RRqk* D¨a€Ó,O5Œµ ’j¢áyªgXk½wôPi5ñ–",.Uo*’*T + I¬—x†Àðä˜åÙ fŸ9ŠsòŒT*ÛEX‹+aëY\ø:Á²•žttÉRLE´<¡J„1D¥¥™bá;ú¯ÆSð¦! 0°’heš–-xß]ãû¹öÞç<¬}ÿ÷úºÄ½b+ S'ˆY\8s•c*À=Á.X€V¦hÍÅBÉG„iÒ0¦ 'Mˆ^DXœ‹»,®Š' ÂâÀô|I*ÄJQ‘ô°ÆëïÙêóÿÛzÌõ¿úwÛ³õ0æ«Öfü¿çý¾{û»=æ|ã¾íåöþ®ïîøí­ÅªÚ~ùî6ûëñï×î«í¿\ÿûûï¿onçþïÞû¶Üû«±ßºÿ®íþÜæÍ·¿>kŸûÕ]÷5¾oÏ~ëï½]ïn·ÖÝ⬽ÇÞbî·å¿cÌïÆØ^m{Î×nëï;w¿õ½·÷¿{Ç|µwL¨öþýæ}ãkýîù_ëwÇž÷Í=»1_½óUŽ1¡ÊõÇ[g컇ÿÿc÷ÇÿöíýõœsŒ-öÜÿÏ-Çý«¶Þ«1¥Ê1¦£1ÆûÚþ1Æ™cÌBÍSªwãßõýÛžm·÷nSP-Æ,T‹)U¾1ûñÇ]ûÿÿåþãë÷>Õ[%¯±ìD  +Û¾íÕ½ç›ÿÞ=sîûççkýÿ}ûÏq¿=_}/·ÕÞ/cvÏúzîmîsìû¾=w;ç×æ®1¡Ê?ÎÙnßû¶Û›;ïüö}µw±g·Î9_÷Æ–{œÿÅÚo›¹½YçË1ö¬þ~{œÿÏ>ëœu·šsyÆsŽ5çÝÛkmÿþ[Ï~ËwÖÛ^Ý=ë-ï6}¿îû[Ïoþ|_L¨úßµgkæüÛëóæ™{¶_î³æ–c¾z;&T³ÇÙ³Þæ«ñ×÷¼=çZkËóßycŸ»åÝc¾ú?&T÷í:{Þêͱå9ÿ3îöw»ïÅ|Uç‹ Õ¼ïÞÖsë·¶{ã­íÍßcíY¾·ÿ9ûÿ1÷_Œõþ?ç›»÷ßþŸ;öìæÝc½Çøâ¬wÎûb¿}¶Ø³™çÝ·÷öãk/çÿzÏVÞùþÿ~ÌW-&Tý¾Ü[}s÷9[o{þß‹ýåUk¯Ý[{õÝûæë¹õY_œ¯ÕÚ{Ž¹¾ÿïyÞ­Îùb¾º5&T±½?{V{¶âþ½÷çÜ·ÿÖz¾ï­ýÿÚê½u·öb¾jñ·ž­_{kÿÕsœ7ת\ïÛï×v[ï×ãn/Ö×÷ßóÖü{öúÏsÿ÷ï/æ«ù^›³Õ–s¼=Ëí×Ý{c¾zûµüzù[ïýï]ßÞ³ßýó¾/æ«ŸsÏV}³ýk7î_ëÏí·7ï{µ¿Wç|=ÇݽßqƼïùª¶ÚjÏÖŽõîßjm9Ö˜ûý3¶×[ó¶^Ûì}Õ%Ž¦ Ö´VŽk-Óú +C²ÖÊβVÄJŠ`,FŠ©ø ++YVÍZ+QTU=çD‚k8Š°J‚5FŠ@´’¡ ‘bš†h­¡,­á Ö e(ÈsºwÄ GSLÇZ) ¦lŠŠ¤:#G./EÇ)ÊRÐ i±eÅH1@Y +GQ–`‚dª’b­%YÿW-—3†¯’ö2pÃq,]cŠžai¢iŽ)JÆD4|y­R"-Ís”™àx†¬÷:]gêµJ ïO2ÏÒ{.ÜCRaá˳0R c  ÇTL[¥Ä ¶ZøÒR±…šâó^§+ Q5]¥'Dk VÞS––¨g +€æ:cž) „æØÉSôLé K—g +ðL¥+Dk/³ôL.²{y§i^ŽSRôh.’MÞišVfz4wϨ~! 0z¨™[(’)8Â<Ô¼Þišd™’¥ËCM{Ž¤ëžá ”¡jŠ£Ë3\æòbgŽ-oF‹É¼p]èu¦ õ^§kSïuºÊÓåÍh±ñå¥$ì¢çëtÝÑCMï4OÓ žž@«¾BR„…žˆwš¦ Œ…¤‡š­ê¥$ìE‚¤HJ>¨N5űÃS×"˜n-aä¨UÔ”Hpl™â c Y†èO;Ž`dK—×*%,‹‚âÈ©bJrøˆSqêô©÷î ;C‹GJ™%Aó80=5EXyžjJ†´š«ôT|GœH`qŠE&ÆIÐPŸ°ÎÕƒ·9C/o'%LìL„Ÿà ‚Rš¬ÔÉl˜,62-Y™EeêõJ‰±ép!ò#’„!h´ÔÀ†–¡)0há­-Œþà30‚¦:ëÏjö†ÅL Å!%1r¸§X@H¤ØpQˆ-žçl”­h–“L€<Ȳ­Dg1Ö#eŠ2α¼Í`5…Ì5!Ê0”@g ?Ì)Ü–@‡’†íuZ×ÁK"Vá/pTPº.šrqŠÅ*5ð.Tkr¶N«×ªu:¡Z§Q L6 `Âj£Z#èJ "f—ìDÔFZA‘)¸XýFF¬Jå±T“.BÀ6Dƒƒ¢å)u zh] 8Åâá=l³åàÀ r‡Í…‘8dZ¯ˆX.h˜ °Û@’|h 奂À !É. 0ÜI‚Äç[•2²Òêb öj&€pª6)Õ¥ áS5V®‰ªKc¦Èå„0 AðƆ`JL©ÊJ©¡%E 2 ¡4¿Bu‰¬AÙ“%‚’p +ì€S,¸È70J?¨Á¶ƒ"ejšÑ©Êð 2ûL˜*5Fƒ [ITòL‡šAc'4|8Ç@ëƒMà“(aQ…Pê|b¦´Ú®¤$Ñ°’‘&HŠÄ)faH §RÑ!+ø`ˆ*XA0` +ÐS¤ ®d2„¥f pMBp†¾Ä¨ãjP$$Î(S€PÅÆ M”ùÈD³óäu +¾ÊD¨äâBœbA1ŒB¾Û!Œä²…ÐÔÔ©f¡èË, T"Z MÄgsP}$ÿ}ÄÌÃòI•"LG–ðàñ<50uÏ«¦àð¬B-X‡ MS s:ybàu8Å‚â:ͯú ¾Óš­vð?‰Ùä“ ,!–À‰-¼>±ÌœÙÄÆ1g´×fã"© $wÎÆ2Y64O§5'’iÒ<œͬÓuš‡)šN±è%Vf4)0È<´<˜ÌYªdò&01Ž ã’Ék’Át4&‚61 qðàÀÈ%; ˆÍÀnDì@‚SAÂK€ôàA@!(ÔÑåXàà A¼˜²páÒ0.t¹,ˆ“KeÀ›ÿÁFüÙçà›«Ç÷O!þxcj|‰Mè 2 ^oŠD>ÁTê<§õBû¤<_}†3«®PêœbzI~çÚaÒ ‚‘±ÅY)OY>E\Ï5ßdÂû¤2±m¬sm‰É¢Ú\§ÚŒžðw:™Q6êȨF ¡NÍ ,èGÀhJ'µhN±°¦†ö1’DÞ8þC&øú)«"v'GH4ÏdÊ#Éi÷TøÂ!o¢€ :xü”L`0æˆ^Dä-;·uq/U·dÁÑ•IR˜k‘ÜË)¤Èˆ»³®DÚZ6Á[ÙÆ)%° pk쬡Êv€‰ØÄi²!˜‚@Å`ÉéÅdÕ:–ÝxyK …ê,™€aI‚Ñ© +@Ï1@Zy ‘UÇ)Ý”m¼±õà^©á£x–!Vëd(oÖ•"ºì°W]  º„ NÇé.½£d¸®›!Œ.aÖ¢U>9ÂUɧ‡…Ju™*"ýTÊn b0áÊ×`E*œbÑQŒ”âù’…C åT(%PFOÅ€³J‹! °J)1‰„ÒÀ*ÓIÃ8›8€ck– °|LBGMfZ¤™Lxø¤Ñðð&” 7Ù~œpŠ…!#J +¶2B’Ð!P¼fÞI‚z&B…ÐH2$R—œ(‹Ä~ £àK#Ü~c„d¼".ÐÁ!òH‘‘Œ‡RŠ(€>‘ÔK`"þyDáüȆÉ@"œb¡ÙÌRkD¢A( )Éj3"&@4‡ä•2 º,ø ïa¥! +–°ƒt è’ éj/ÁC>v{hÑ‚Vè€á©<äL¡ô(XñЃS,"qóh¸§ËC€Ñõä=¼ÇÁkyhðµ=h.˜É"X%^ÜÄl„àRÆ·ñzŠ“` +Jœì@âR0ûq™‡ÁGᤜ#AƒË9@jŽd ðà8ÅSØ8/”šŽ1sÈ +J0GCã:N+‹ƒ#tñ´‚rìJÉÑq²È¹žŽc3 2Ž –ÃwÐTÛvHX‡,AqÄ:ˆ¦111ÎmÈ:(²Á)X¢AKÅÂÆ žÀÃü ˆ¤±Ðè0 }àpj`f ¤ȦD §Á Ú4` ¤ù%?vÃ/äÛñT×Hh‰’†L£ Ç™b4¸ +’28ÅbäO&ãÁlÐ *‘G0†ÓZ Ö–b1#Ç!ÚÂ*U1ȃÅÐ¥—‰a9ãèaÄÐ F +%ë0&Î c„däE€xÁEb38Å‚Vº™!–,#‚C;†Õt( •‚,Âx4¾£ô™ F m ®¤ÛD‚kb y¦r›X¸Ð6‘}̶,e)ó‚mSáµ¥P«ÖVÙ›Ãx±u•‚ÃÆ)2 ‹aûTJ…!Q[m*«Úti”ÚDNë`ñÇi“P4[•D¨Ñ‘ +îgå𹕅 %Ô-d(Ÿ„D€3¹“H #à9©ð9e(ž²Tñ4Dj Ç3¹Ÿl›>$­A{1t$?ô% §XÌ<ÝÃfkð-£BO Äð¶Y„UD%¤QcÓPŽ7°d1&¢v.H4Í1‘ɨPÖx<^©ÏÀ£/‹à>4iªð94©(¶iÒ å öRøð„¼¬(VÉøÚÕj…ànõgÊÊBËoÃ'5V¨àÔÏ2 ‰orC¯†Râ”ä–E•†vÃ)'+”¡€P ¼ +|l¡LåÒ-5Üâ %Ä >Ç'.–&òUÌÊÂC±À¸0û@yB™åa.„2‡Ï}„²Yñ(<$¥ƒ‹¦[È_í "•‚!5ÖèÏ2”ÏÃ;…cè¬,|&‡Oƒ×YOÃÉR,<úð¹e ídJ„–v +eZÑ€pŸ ©@À‘9Å£|"ŠÇÔpU•[¹ªÊU,PPYªpŸLFq€—S,< ¡Ÿ9˜4<¡ÌÂyB™åCË‚'6œÉExEÌ Çm»I™SfRc‰,÷ˆÙ“*ô3[(Cñ å'ÙœÉ=èLºNåSÉPJ9¥-vd5ÜQý ëÌ'-/N±à\&Övrí´`&µ®rÂ@°ƒTŸÏªÀÀ£4XMœÈ34B–;B ÖB„Ç@s ~(YÄ@9ÅâA¢ÇMD"Ûù|tHã÷É;RZ² Œ¡˜×A\ÅÐYN±È&ˆ:,,TB†2è$Y‚‘qJ¼Ãªˆ>ñt$L¾MÂ@gÈ|@§X<8‰vKª<4h"’Ë`à)ݪ_N°‚ +©e6,^Û +éÁ0P +Ú ¤#zðI0m¶Ç¨3d®OÓ-z0õ“ÌBrN±0e"~¼ìu€:'[¢‰Çñq[b  +Þ ØÀIµñßydŽ¢"[œó”àÇËŽRÆãx{jhm´¡1@"©4yàÁ}*Ñö.‰F¬/+ñ õîª"j§…# ¨$šà’>`ˆ ÄÙ¼ò‰h~逞èæñ² ÞpØ<ˆÑ5bèé‰".Ù§XÈöäϨIĉy¼,‹Vƒ4Lž¢j§f¥ d 6‘çħƒrNÉÆ40qÀ²œbRxPN´/}˜ x$b&Ê)ÓƒÕ )‡Åš´4 œˆˆ§XH4›l®lÁ²„Tå%­,§XøW+–ª5H^²²@ä`À)V*­„¯‡® /)+iàtX³Mƒ‹ç¤Ä³ŽNˆèdHvàA3‡ÏC{5#¥xƒGº……¯“¬Ù/[Ú6]3xtâ8™èiÙL›X1¨UB©•eÆ   Ê)(ÑÈ>,œ«”£Á3Ÿ²à$a3‘`"jg'd‰,§X¨FÊ‚D3\kD Ïl ¥ŸhL> +×Cœ®†€5à"óS,:•—êò¨ä´ò<œ Ÿgre–Õ™!Ó…)õ㥚ytá …ìkA4~48jñ8ÅBä‚ìI “/b &þ,Âæ‹mÀ°j4¸‡„((G+âð"j§Y: èƒ1pžœb1ãp_‹n‚Gq¢x«ò(§X¨Ò‰‡3âœåƒíáªÓ}¼ìä‚šò(§XtBV$¥-¶ä‘<œ +¨Ày¢$ÏÂãeÇÐÎPZ 5ò%£ú±…2”š'eO7€§Œ£0l€–…S,ðå`@÷™‚ÅŠ}ž(‰b±©ÌÃlC‹XÒ—aáð`‚.eQ©8²»¡ (GHIº•q`(<âC"›‰0i4À˜™œÊP“®C ˆœ<² Æ߀“7à¥À~Ðò£L"K#ØJç¢h<ͦ §°AÄ× £h<Üe‹8¢ bðL|Zãæ ®…ÐÁ¦‡j„Må÷0ùð ,œ$¶7 7±)7ƒ™9 Þ0¼í‘\CêȨæ‹IgÛdçL.H‰høxÆÑC'õ I>Ï_€LãaðüÒéá2C-Tø%|0!„ˆ/ڨśyv†¢!ˆÙ”â0² ­-ÅñðNž(Í’%"¡Êã ¶7œbA`éÒÀ§uW•nÜpÖ@Ùq4bmfÁÂ< ÕÍBæùRM8È—ƒK‹Í©LC±”(ki­4”±3Å""Ü;â<=ä9Eª€š`çÓÁü¦)Ø*O§$Ë’abJ†¯Ñ-[œ0ÒFÃRr„ÖF9Âwv–HO­E#˜%Æv&tÎ Œfá yQgÉMàð×A‘Z‡Ì«\–%ª5êü ”5Ä Üù`íHsÀ­ˆ)<Ç5\ØFý‘»hWÿÌ*ÔœÄRãY¸,ˆU@=wl£­:eád(¦¹åI”Á;qr‰&`<r+‡ÇzœÞ¦ñM‡¤w‘rz§J^`«ò4|œªª³BcZî›Ë“Ø(é–jï½7™66óìB¬•X÷6htÿ8ÈËÞÀ­§ôù‡.Æ‹$䵈ÚÎ?Þ}aj¥ Ùô7 Bnà’1v3 +Ÿ «Ö¶‰³lM/›˜‘iUêp׬2¼½|xGîçû=ïe÷ͬßÐù‘_l€= ©Ãbl†Q³±û"\n»{f’œ¡Ÿ£Kl5D\ÛãCŠ}?ìŸ;‰%ÞÊ +äÍóAIm +¦5éAŒÍèàK/ݬ×'›5z§Žx—ÚrY2?#;ç1ãµ6]´OVð¥JÓ“;Åq’‡ÂG¼H½ué6ruLæ‘uÀ?ýb…ÜßF@bMKуv‹ŒASáœ0¶oÀ8¢G,•x±`©ºâ±Gu ÀQ gˆ?·É*W‰Ã·,Gõ+ì‰ric¤„Bâk²)!ΊÍÍ€.>QÁãB;ö>aDÂùäöZaD·á“U^£(ÞÔ$’ËãaÎéÑ4”²'%*ú.Œ˜bMĨɺ¨éêkT'M"VvЇ4þ$JÕàüHø•“Rt$¼Yl3Ox7uºDi^ìÀãl’ÚÝm*uW p±®Óf°9ÜÞWXîÏð)_´9jË¥Q9Ø´ƒ¿ñß*hBM Á-Ì¡æÐ`èÀá˜b˜¨! ¿¡„{\I‰Ûç=µ‡È1øÈ ªË€~£Qîá}À:9f¸×x»4êÄ!D õKœ<¢¦ëEÙéa­<–oß”- Væ±õxúŒ 4{Y,ŠÔ“vc>h˜ô=¦@+`Q6ÕUF‚ÜêyL^ìa'®ÍI8µTœÚ¦&Ú©·Q–@m +Z»ŒˆGÂMg­|ؤ΂6j{$¿¿ËBØ—SÅ5÷ûά‚ÅΪqžö‘¸žuìCT£"¿ÓÖý3CìäºOoÆîØݟອ]5„£«¯ÖÁißsß±Õ"sº;ðÜþ›@…¯G9/è&N„¿ïŒÓÎQŸ~_Z”3û‡3È”hãµï/¸Ÿ@t( %b^®n‰ïõbí3Wjy‡~fÀ!B½¦<ºÆê²>׳Ù¼ÚþÕA·©LÍó†eÔ}€Ì;°Á âßpþÚßþºñ$å@ü‰\ä|=–оcé˜R¢Œ¦RÜ}ÌÜ*I…fžÆ[lÁUÜ01væ„Ò˜æçÆäE\,ÌSeŸFN˜+‚R‚_ ñ—Fï!F½®ðMépHoßN þ40­£Qæ èuÒ@=¬†V UK%¶ØÙÍšýb6,÷K(g ¹s'Ñ›þ¢ ?M ò>·SØ®`]k%چζh ¢Ù°2áîcâ®È4.Íçº5eàÜ©« «‡%¢Ò.¹æÖf1™1& qC[Ú”>£`g¬Ç9½¢•¡,̬²*{L§J™gÌ~Oÿ ”=¦•4áÀù4§ßÓD4Ó07YÇÊÌßÓØŽ‚!‘—÷ÊTÔ{.v@«ž|Òî¤ûËÚÊjë 8ÁHt@ŒyœHRâý¿…FÓÖÍÅV—öŠfFÁ¿úðÍYMÝĽíëc²w©=Z˜vö–—è³Eª„p¹:£qºµÃZæ°Œ‰%7h´ÇG"êyKPì(¢Ô‰Ö:uN7Yõ +ðÀÊ3ÈÇKš;×¢pÌ|rãÓ#Z:”ï>i$Ћ©9²PâS'ž<°ƒ““=» ‚-ÐêŠ@ðÞàŠ˜5t0ìøjŠy*çÁC»=$7¨—…aH–Pè´³¤ì€¶ŸY{4ùUcxETÈ%þ,@@«Ù„ U4Ñø7f'—3…D6‘x^ø ‹lÞ¯ã‚ÉKžÛdPqÌ!˳pVKùÀ6X„plH+#ˆY{A®z~=“¬Îç=7 †(šÿÖ_ Éó7N~n~êîÔÈev}”¥€ä?ð@0¤Uf*WjrÖmö è‚êÍËùY冉ÊÍñT™t.6Q¾3>,`mÌ#B<C†¾ló2–-U5Ðs&ð½jHõ’ËZéÒ¯K_¢*“÷ËÏóºüH±r~N—¶Ö—Ý¥rÝR”¶YQqp¾ÕØžaRÞ‰ &d%[áÞ vñ5 ©—Q2¨mÞ>WàFÆ2ß\Ùe»ë°X kjª&Â3÷ Û~öØPƒØ|~*ª@Bñ(W=8˜!îRµh{GY z¤º?MÇ°PE˜ÂÊEËÕviŽ†rÎ^«Õˆ²îIµñz>OïÇ:tacÇM(ÇAuE'Ö¡3qžWÁuë…¶1Ó˜+Q]ÖŸ¡L§«Šÿ4F`õ”m„œ_L‹Ÿÿbºl_%ºîéMÔâ3u4 +a‚°ÛÜ[÷¢ F[Û>G·Ii\âˆB…·¢\Ñè1qÿzH@0“ÿB_Ƽ_ÆK§"®)ëõªŽÍ`˜ÄtN/ì;þñÌ”ø"Ò!ëk¯ŠË Zs¼4ª›añ€^ HÌpoH[„Yòn¥\²fÜ + +¤«Ùv.e®áÔc³7QB‚äÇá +ÇIC àUH—ÿÓª6‚Tvùåt]GèQ"’fã@®¢s‰~‚ +æÅö¯Wþ +»0 Û¡@AÜÛ‰žÄ‰ÎY¼p X‡½ɽŠÈ!{¯]86† ͼ¯™Ò+—Ð×Ü™FåÁó¢×š{ ëÿa³ëÞ®ÎYw¡(Æá8%çx²J¨•ÓÚÑÌùw›Í5`2t +Z{l4Gzm‰‡/]ÚÄ\gÆ Ÿc»•úq´}PáZ›I©•ú¼‡¦ÃŸx9—þ +DV\¨SÖb{ŒµCC›}€þ¤l4‰}Ñæ‡Ø°"b*é¸àá¸ÏŒ[g¼ü-µÊjÒî"\lƒ1’:sžò¢Ã*=Èi–LuR”ø³@~0ò£  tä±:l§)mBPLB8Ö“õ3A/jÀ£êÐvó@"ÏG:Ž”?…g£õpreGYÉoiFI×-–uˆÂÂ>¦ t#@tu²^­s^.@Rí2(1(Š ÑVo‰ +aÚ×ñ$ cQ!>ÊR#z=sWÚL—`È R±à…” Y‘lâ̪ÙBâ¶Æ(Tqd~ÃùT¼ÑæX´®liñ›Š¡gI³ ¬î²§šÑåäÕ~v²}é4Æl ãKLŽ6rÓãɼ«(Z”@'hR7EW¦åŠRFív»óUÜŲÐé:ç’Ôç‡>.É¡žã­0N½ªi=5úVx >GH[o¢Ê- ¤b‚È ý4Ùçãº7+Î…l/dL;þO‡ü;æÁœûr¼ÕšÇèS’ÁN’¹ÆIÃÜÍ¢ã½B™LÓ´Âð/Ì…5ömúà=¸TPóú³Ñ3BôöÏÐ7# ö ‰W¨Ìa§C¦ZÐMSCäd®„ý‘Ä£Û¿+[µàbå…~UC„ËÂ/µ—×M€ØZ1W˜R¿IA°íIOÄr·Ê®š@_)ƒ$ør«(´cÿ©`¶U” Ä©E½K–l*6AKQ¤¤Ò»2öT]µ0_i pYzj‘Ÿcp8ÂtiWÅ¥jžLˆÏ’2p6£T@xŒZœUi ýUÉ}.Œ'd,TÀ„Ë:d“¯×‘v¤c=‰ùä×Ú¡¨X³²îEãöé4ž+t`yg›j=©ãÃy5x 7H› š¥êgPUn™î{‚>?¦p2°p÷·jÔì£À1/*Íê¦ÿÏ& +?ÆàfŸÿýË6žS”šÎi‚ÍÑ™ÁùŒû§åÇ<'®G†àâ;xÆå‹ *L3¿ÇßÝ1”DÒ^zÌ–ü¨Žoõ|}<\EËóú>m˜Ñ,R¸¾M ¶‹xjýöà‹äc%%ïÌ;¬ãšØè‹?A@H5<Âq²…ª†ÀÐÐXiàúØ[¥F:ê¸b¬¡Œsmà»R¦ÆÍ4ÝšíMI 3~ÿËhGßï+"ÌËŒ‡½ô§ˆ‰Û´ìºz—&9ñ÷!ÄNäøW +6—Ü°9m‘át†…Ä9×t<Þº£†H ŠTYd×7c¥xÏ8É5V¬ŽÍÀW¯0åïó¢sÞ["ÌËBxmÒò&6üF¼µÝŽÍH‚ýêNè"Ä*‚[èóÖ!®ÀGs¯ŸÈåÐ_Y¦Âû®cBB¸¶[Ï&@K;†=ŸÌÈ·½m‚¶°ØÿL¯Üû]{éÕÖ÷ÝØâ7¦: ðUÕe’@¯Ð‡"¥| öBà’‰x² +(NžLNÔ&-Z;\?Þ¡¤ ³Ð—¬Ô³8#­CN°œ×£¤ÁÜe*âsãM¤¥Â²¸ÔÃêRÌ=º¼ãlTÒ–¢F#WÃî³ÍòÿÅÁë +õÀþËÕœðœ¯X¡ÂoÎÛåLR¡SI" +±u÷ÒÊ 3³b½“§P‹.U ÐÕ\ù[AÛËhÐÌ­ëܸ7W`Àß +¶­ ae­À;b± Gõ¹{ƒ?2ì)%ÿöZ Ö,+n+˜|6ú̽?rÅ‘J°¨åâm¤=š#CéÐ1N´·/cGä '¢”sç’lŠRñ…*Ûç·+Vlæص ’‘óN±Qø}þWc/ˆ’>á\U8Šà­(½ƒÒfr CirÌJÙ؈Ü_CŒýšn:œÌà3iô²íÃ(Šºóñ•óª'‹m`%°ü©ê¿Çê#8¿â«_œù‰¡,‹}*ì[ªE:Ñû}ö«É \¤Àʇ +!|…UÒç(ÊÀa„A<Ê×ü‡duè Šy/ÏQVxhcE'ö M)5¯ÚJ*ï8ÖÊðjªÓŠG僚s +”XQ”M~¤1¸cL¹Ã5 ¨x};8ŽwÉiÉl?°lÈDzÎG·«j aÿõpÛñU@N=Hx_8J¸Dé››Ú–yŸ½W»–bi·Èc7 iùL'8Û)UqÉýÛàÐ8·Äê™±Pe¿ümy(áú¾j£Þò€µI}¡æ̧À7ðeø„oÓä Ø+ÄÊ5ÁñÆî࿨UgÏÄÛ½,ÌzÀíó×­XdIÚ?4;)1•ÿêÎl¥I¾´HjJÀ[Õ38{K + »€Ú1VWlìs`1Y³WHÔ_ ­ÌˆgÉYW9ªW‡[®»Y«.¢@IG?Ý͈øÁÅ„u@žNÀD›„0Ícj¬{;¨»µèE:Ïé,Ãð#¦lÒtÐårYSÀ×59[L}® G‹w/yPy ÐgºÃ +þ´Ì›™–§ ¥aâÌ‚âñøÙ¤‚i.’ ‘&¡Ò‡» ‘b„a‡ÝÔ³ˆÕq DEÎÖ®Düí"AÞk1†±ÛÐ#ì-‚dÎΉÍÏFdì¶ ËŽ¹0®È|qxí}#Zv ÁwÇ)¢Ìí (Ú¢˜>:X ¤zU4¥.%ÌÒ,q B'±8'³zà T7ôÏò5¥Íª‹„332 ÔáOï%¶’|£HGbwÔ)•ñ‰öþƒJ*Âú‹Deå2û.!ZµgEX¦ðhFi8]º‚Ú Ø•NôgבÊw‰QWx€d(h‰“lÅáÝÇ…R‹Õ?ÄÒü?Å8÷ä}ö/Hƒ×‡w0 ÜKD”;[bÙÜÊÃé@i|‹ãÀ0¢½‘!´HO$ü¹Ä4éOHšÆ<;Êç§3dâ=E`Atì’¢ +yÚ@´³«—¯˜ÐMX?BÚæ9TAE#‚Š'aåBÿD‹+™Ø“Ðåü}¬“·å¶ àž¨ +¸‚ƒ&ðäcJÂÛ¨kÏ{h¬¨ê¹Eg`õQ…IÊ2†´¬¨bmÜ¥JZß×`¨¢O“óÆ–úÐ +©ñ ”—ãÚ¬-b°L¢ÒÖ†ãÔZ „¨$¬­J„SxŸ祋oF­SE!°~ÀäKÀ"»ÈÊ.…ÚSÀ„½àY„»!ç7‡ê³7¥°`I´Q¸íe!÷,¥ +­ÀêÀàÞþ¤¹¡“Kêùˆ¦LšÏê’DwcóÕkFäÀ,>õ/ŠÊ:QÖ¸Ü@«¸È5k“~6죺EV¾Ÿ®z>+pÿ—«A^0ÄÕpL‹Éµx´{–&×Ç}‹‚Î`Š€w¶5¬ÅNÑF’¨ö"rþ’†‹Å@È0öøE<…Æ–âÝ=ÿƒWǃÚ3t±YB6dZlt! îÜm}R$µ%àXµä Ïí¡7 £P+UÚˆRØÔ Ø¡ h<Ýqº4-Æ k™¥° ',¾#X¡¡P£á.VàôK£Lr {µ_ØûUƒÁÄÚèn]ã +›ê™ÏQcÒÚD¸ æ5êHY–·›õ°w¨àcM^—÷E {ÖN•KM”*«ŠmaÕtÇòú+C$½š@Ž^kl¾ÍJÃÿ¸ÁpÙ9ºb¯ó1sRJ'˜äŸÏúK<­U‰Ï,OPSÝáp $T¢ë‹Uøá +8¶7FJNF@ +ï‹5dø®âjákmùÁG]Ðò¼}†.ÐCí&–V‚ycZK QFY^ÿƒ"Ì>h>rü7ÂÝ„Ì|Gqêu²£ ¸¢§—¨"xQÆdè5˜é ` ìëþº˜Û¹49®ý”"²º#àu©ÁêkÌ‘OÞ˜%Ä$›ø§WðKnü·+Ù¯"í]%2JQŘBëÿUnm÷µ†BY=od~4SÄ­srD7Ž·ØáRØé-§C¢·N¥íp#‘WOHÕC#šjlzÖçʧp¥@9r]î½m^J±•Í™}Dxó¥0(Κ¸8Ë‚yyàßÍ:Þlð{sòUaÁÉú§ ш$ílC¶¨¼8e.à«Ûeü £Ä:5`1(ÆôÓùÌmŒGörU`F3ª´#ÛÈ<½Ë|£ó.YªL‰Ö¤¨šròZh …WNkHˆV!¢"ÇÕ  é +¡ƒûy‘Ä2Þ¸^1‡»ªz +›zÆ£"Ù©¾¼‰”X4Y>pk º…”ë[ÙuÐ'Þb_즌xç’ßlêÌ'LŒf—S×~”:97DË¥A9—ÎÛTøR²ø© ro¸³Ãzc(­lœuþÙ ‹Ð–)},ä­Ð Däl}[Ì6Ñ$ûÌÃ*ÄæuÄ'dÝü[3S";ª«yÂ[´./Rú=}è nD‘£ý ŒEó&øA”k5\±&Äæßúác/7Är‘Pzš¥²AT#“9÷Šfšß/×0Ô-9Õù¼Ÿb›þeyt9VÈË.­ŠHT«ž2Z­ƒÀüoöÿûSˆ<²~q!”Oåuz‡0Š>üà [,‚yâ|¶"Óé_1´,‹©tK†¼fÊm€ë‡`;fhÉ5»àì!wÊöŠ†1z¸ZÆvB˪ +^Ô$AËi£‡æõ†6‚•ô½£²Ë,qðp •:jŽœã<{ Ö)HYب±«QŠOž ©ŸDVðŸZ, Ü ¢A4Æç‹›Èø[Ôó£±~ IàSAqmM—¯Ò Öw< ¬‘5Ø@¢b Ÿì˜Cà^ D‚IÌÇH?ŠríçÕÛs„³*ÜX‡HVKÃ(Äáò›û£kšŠ–[ž`Ã!j +KR¶O¹1L±­,kQîXF”M×$_å7+Ä9I‡Ô(<Û°RfÃ䤵'zœ¢»øÇ ¬^qÉ„MÉŸ½ÚÙw¤Y$y(I9ÃîD¨›©Úƒ‚8 +[Ø/ØT®÷;E·‰š¢–I—öÇ׿VL†Ä;Û9ìÌBŸ¬4M_*E*×Ü0Ò9;o!\åèVú^‚UÌÒ]þtl‚Ø—Q“¸{ï±â”ìA¸öÖ\Jz¥mÒr ‡»¿§; áÂ¥ØVà[.>æ ÕBh*´SÜÅ5ÅÛ +è¡ðèá9¬ìNüXtgÐÙúyœM!Ù ìÚßYþìEnõ±£‘ÏBKN&€ÀCPšÎpÝñFhÌ0orúˆŠÈÿ+šlL;,é×êR–?½ô¤%õQ$ ¹ót‚ ÍJbÁ“'ðÑæ¬èªÝX±óyS_Ic* ƒÿŸ9wYÔèt žåÇ9 À!Â͹$ؤ,ó´Faß ›Ù/Ø?1€—á¿Ë¶^~Ž²T “ À[Îo“ø Ÿ{ZÖ‹Ù˜KH•‚X¦ÛßZYA±Ç†ˆöž˜».‹';ÿ2cì8}¡|nÏgür[’±ó 3x6Aã?Hš`ä‘ Û—:ûH)°d>žÊJnÍ ‘40_#wjÆVW»ÞÉ壼uÅý‚„óWË£GÜ + +ZµÐœœÒŠÙ:Ÿœ§–%Ÿ?\žˆ¶&fœl @pRíBDÿ1šcu"›^Ë\DmC,áÓÄüÛˆ+í@wÊë󾑗ª“ƒ@•w‰ 5H?õLMAÉÀJÓÑ59Å“{ús*ï# š…œ£³ßˆ¿Uä‹A¾Æy3b”|ões+À:NªÌÄdD0_—W‰7aÿƒ´ä €L7eû“Äï3Æß<5Cs(èKÝ…Z4¾¶Ò²X Ÿqï`2Z1 ÁtÊL¢}âÁÕÛÏ8¨WÖ"ñoüEÙpWN-ßêâ”N# ÿ²ÀŸKRNÇðr#‡‘ÑÔº¨‘ÇB\“r>ú>Ú_XÈÌÏš8ÉÏ¿ÿ9´YáØL\ñÀ¬ jÖO¯o¾¦Ð]žh²1?Zë¢*#Å~œ”)+O熷\x®gG L vÐí‡ÀË‚ÆOÅâlá&ëÞM¾J¤‘º¼ö™q–ÍS„»=ØïÊÝ·cm†(½q?Ëe°íªßÔ³“Ô5rÑ°;1©÷[¿3;³¿°›' 0š¹õ áü  ýØÚŠ&ð%¯ hÒXª 2½÷òI aÛƒiB+~æBagNß¼kð©=Ð +U]Ðã¡y+vÈ—RuÑNO V‡“I5`{Ì,ŒߥݢVy­˜§Dœƒ'×JÞ÷¸+t”èFÙº†0q$Ó^NÞ(yAÓ²kÛìÕ,ù:ûôm„<ÌœÕghÉáƒÍ;[¹@F¾5 ZK˜àsòNb3 ;82ÛH#fI‰üøÃëFÔ~Þµ•?àÂi{Xµï‰ŒT&dl`V‡KTB*`Í(Ä7ûa_ðîÊ©Ô ¥jÕž*„fvOPgñËèÙi HöC¥«âpWÐÉ7aø’ujáG,)Si PTZX´'ÔO©« ¬#´ˆgÙ0öœ*AÑ"«+‡ñ—¨}bH½¼W¿ 7hÒ:‡‹ˆùw ؈æT®¹¶îÜt9ª¦"³™aØ"Ë —¤”§ÉØ€Ò׃õOŒ +¨v\¨ð@bY­þƒ{HÂÑ°Ù€¼éð¿&Üå‹ óªúÛ2CŒuú¨ŽW6~§Xl«Dˆ…øÅi( +4c Ðr³ñ_ì qµd”Eî=h¶ +$¨áép`E?¸ç5F¾q0qˆ‘¾„!`ÿ‡à!c€a²=9á®Ó!©ZÙC¨óæ‘7Ц»ê¶@–ÏÈ!u8žLñåÿýn5Ê’˜þ¹1!¨ªÅ¿’ŸExù/ø(|_ÇåIÌ£‰—Áàœ$Rˆ#óµPeGVß(ä£U^$Ž¸|¬ƒ÷ÑåZ…©{SžýÀRÔpÞžå{G8ƒïV—!¤Î˜på*pVÒ䥀‰Ø¢u_Ñ;l`‘^­ùîk”|;®"‚V!ü‹Æ€!b-I×ÒÉ`ú6 ÇÓj(2mÊ*ô“Á÷(X¾h”{œßçª_{Y®†UPhg*+/ 78<€² \Q—Š(@5 “ÜukÊŠ¦ê1¨V˜z‹ÀŒV‚:€< ÆÒ!¦>d†,ªUÌÄßÌ®7G¢dT­5U?BÂ2y¨‚þV<|f‡y‘Шø¿Œ3cú4› ¨© ÝÒ2x£¸;>öä·3k(…‹ ‚™Ïb,ˆŠF@q 軞´UM€!U^Žbê1yòDÇ2àoAŠ$8B –þÕÄ"a/7ƒBƒieA—N4¸äóEëÝb*Ï3,\ºêš¨ÞS™³¶ÆÀ +$JHÉ=GsHš'ÑÑâ@7ÆžoC¹®hËFŸRÑ9¡¥œ‡ ‘j¤Z:Šf·IlŒ”k×I ÔÎR.„¹FLÕ*Ôîíaj⬂d¦«`”Tb·\³=lÆ"𛕄 +¤OŠ-¾X¢¨) +•[Åä„mYÙ‘2©Þ¬Ð½‘æ>Öv‰£êSö®E|¸§Šp?LöÒMÆÏ Â8Bò&ÄïËtò…à¤äd&R¾”f^ƒŽ4Û£™{Ð’†bBœ¶ÈC"9 o•©¾Î™À’?[aE ~ðŸýØ'¤ËÝ^ö“ s—C¸ú;~Š´FcÁ¬–(~€ýC'}ùß•÷›ø`]ý¶ +·…·{ã=Ñ{$3çm£ôü(m±>I]!«¬˜í>f˜•‹@;‹wŒŽŠP󇱥« ¯IÇž±(0#µýY'¶8B¦9Eškˆ¹0Dð9ÑB8eÌÀXšýâƒ%3EgHi,a‰ºïslTÞ„•óf/ '½ë¦ìœËó¶Ôãñ¨LC7½öAÅÁz™i¦¾ƒÕNœù¢ª–È9Ús±RéÕœV1—<ÃüQˆ¨ÞÉ(Rxvê¨ê\¢ äâkn#Ѫ¼‘‘/Z£"êÝ6þ{¹¢‰¿IJxCn$!K %ï1ÒÝB‡ŠÙÅðæ³ók§òå›2û‘3ŒóÌפæ‰ø—·óÿ…+KÉÛÐý>–›c +‹òœxG‰kûÐÞ²”ž=AœÚ#ÈįFUËO]ØúÐXËñròX2RýŒXp4*Ó»ÚŠ§Fþ4ÑëtX åe¥ýÕØý ðî'jÓói¥a1uðhÐv@uÄZ€$0'±XßÞÅRkþ²ìðlâ@îeÈOvš* N¤/îPÂÊì¿–e/’’‹@E9ÝlB]–±;üPNåEmÁÀ€,‡+R®Ž Ù7â¸iâÿS÷Ä·\Ðx䘽 IM®˜û;W@¨Mæc„à1>ºS¹Õ0›ÑmŠÄ‹³QƒùQ6 tíXŠ×ýö_À’> –`•G«ÕìÞ(1–·ˆEQ ´)Ÿš ëÉ›1ˆS D˜šàqi©Š§M ûžYÌ™Ñ58®ïöJ´r$Ö²IÆT¸bí“0‚r 17û½‘᥽Zó$¡†LV5NÆ:Ôndþc° ˜¯Š’yð¿/µ;ÒqÕ´B²²šêéGÄúÉçDçßÞ 9üO>æj:mGr·A÷q‚OÙzvç`l²ZZ´n›ˆÁjd^þ~']3ºI·Tº›qSt97瑱Ü=C\œ1»ª·%\k¬2u­k°†f픓ûø ›`·>âÁQ2®·ÂRýÄ&€í, ÔKQ}WIyÀ[Ñ|?º.< +§*X]ÿ·2YTrð!\o4¸qëÙк÷É«Ìâ¹y%’ÀÞrÑ{Š^L¡CõÔ¿°„ÂfÅ98¡œQ¼•?­ÖDÇ\].Õ:¢7!©¼u¥”Ýj™e/—e19!YÏÇó¡$”]WÓ PËêX#’I%Ù¸CG|0›4$ìeWw9ZöÏâ±?’yh-]©Û^MWŠPÊÐë†4"‚`“ÆVÄ]¡v>lí^àžñTÏ*!ÚAQ0 T:6¾GÕ\E´hçv=¬Ö)EÛïÙHbºîE[$1(Ñ2Ô™íã^` »Š(ØûLÂh o†(ÿ|¦‹Â[‰‚†FíÅLÈu‹´vñ|3-''¾Hô"àҰϵŸú|©ä%Û>ô†:¥E¦c÷URf,†€r©;ÙŽ_ôŠ™x\MÖ°×P¦{˜ÂøoMÌÍRJÉe1¼‚¡Ñ[7˜Ì·E@˜ïbp#$Ésô4 wPFøv~„K•®Ï ¡Íwm N€V‰¹pUH´ÅUÀ—¹,šè….ô jà-òµÈ5^*+²OXü€rì-…I¢ögÍhò» ÕÄlÕˆ£îì¾Í—ói{rx ÷¡’>)@˜¥í–úm›œÁQÊŽ)ˆÄ€Õo$ãyÒ5JÁö‹)× Õþïbë +³Ǹ^R)AÏd$8a¶Øö<‚5“ +±Ã Û\½È*ÿ%µz7î5aM¬ý›y;²Þ`ˆr7ud>LåE÷·® $š²´‰åAÀ²üùÀÖu)¼eZðé‰Âž25ß±¨“ocê¡aôéä Ù5AwC§‹zêlõöÇhô ™Ð–ÕËÑæH×ö4!’Ó&…‹.áB¬|F¯=+ (ÆbøÚ fåºêÅÞ¤ºIaÐT2aBt^d™FªjAwפµ…®ÀÁŒ¹RÐ*©Nt³KsÅw=Ïo5@[ÜU|rÀ±_1ph„1à}Ï}A>ßööØåmnV6ì4Ü`ƒmW/>’^@'Û ÿÄø±T[EíŽße£ËFS—+}%3Rdw~j4a§Á°ýYm‘-Ù¡´H +ýGãçÑ'¬{í#æ؃cÜâüÒÿy`u‡…øV\^¼¢+ö ùZîõm×4h§È\tŠ…ÏbP]5ËnõÖ–†J³Á…xã¦æÖg}ÈmØàöƒ³dà÷g¶ûþî@Z²?ɸ²‰¶ÿ1‰Kͺ‘ê]8ª×˜À‘îýî\VÀ7Ý8ÁùQ>ír-b¸¢nþ =àVìçC~:¢Éåp{³8† ˜ó|ý@¾|*øÆý¹Ó°‡‰²x™dj¤k˜‚îD‹Ý±DGžøëÅ3òÃ!Ó*»©GÁð-CôX³ð ­š.²éú©$©«Ìç1Õ_ÀN‚{ FJjÈÛ–,nxàQxüǪ?w·à*&Ñ”””$[zzJ¸ZãžIˆi÷½ié)Št3a€ +(ƒâ™1± A òÓÉŽÈÚ›•‰ë@À‚"z°>!pR¢Á€SÁÂ[pžâ¸0 â<><> +|,öA&ÅE¢ÁaBŽDƒƒ‚Ú +ŸPp²!3sÇ$ ÇÕp`8˜"$ƒÆŠ¡t&0àÈ‘ 8, r&اDƒÂ&`l|&\d|&Tàg$SJ¿ˆ¹y©¡ÉO´‚Òa™R€B%CD ÞÀy©.¹Ô©BçAeLôð&<уɀ˜Oçá@Ì¢%ªx@€%'dÂEœ‰6T ÈmŒCKeCÁ Â0 7< + <:1e’’¥ðTÊ †a±x˜@¡óÀb. d¢‡ 7F± >ì‚FAÈQ4HÆ!Ñà€À…€ GL +Ž +–C£ <%`JB˜jð`€äB ‚HN‡Íˇ€ ‰‡‰˜ +¡C² ,ì‚ ’mà‘Q!Éš•“hpéÇ]` +“ 0,gbFæåÅ&dÆ‚ÌÉ€ ¨ÁáSŽŽ‰‰‰‰,)(0Jhj0P,¬`<à…„Š‡®È€©ÉO‡c‹ &x0@NMÌG&Æ&&¦CL„…€‰ +*|:–ã‚¢’Sã‚#ãrcƒáÅåÓ¡@ãâÒÊ• ˆœ6)ŸNŽLJLʧsƒ!å%E¥Æ…f%%'ƒI‰—™™ ¨à¼È4HQIùTHA0RR lL>)335 /&+& +4*&9lVTTR&Š +Ê„„|xÈ1R:l@<3(#I¢Á?€„K@ ¥æƒaÁ#@¡Y(Xˆàñ —³‘páAâE§ ºäÒs,FÁ^”XrlN‘&ŒXt臕O=4„h¹Š`™ˆ*J¢ÁÁ¦ÇS5 0>ò¤ +È;#ŸNôQ¹%Àøt,(àÈÄHŽ-P@¤0(ƒ,„€³±¹)éœÜ˜ ôÈÅÊÉ'a¨h AÀÆƦ€Í‡Œ„…Zð‘%< ñR#ðD\.Ñà€¡DG^ òÓÑ qÉ(Ì"DÄS3* GÀ¢@;"žêHˆ>,A”ˆ¼ÃÂã!-`0=Db€˜Ž¸ )äã©š!pàÃSàÆ£NI‡$?N[ä§ãÁ@q• +02N`¬É‹ÍŒO'–À”…Kx¢ôTMELaˆ–Slz +p\¼â©âÀ2R7*ùéTˆ|: cÂBÕ<ÈΈ–[¬„x*Ã,p`‚‡¢Q »£À¤%çã‚ 47žp<0»óqãÙxPÓmf"ƒbsâ¥& 0Ù¹0`a8ƒÀÆd#Âd4ømp¬XŒ‡Ëºp32H°xLH`âˆ|:9!?…BÂ(6ÜHÀ| 0’!‚‚tR:ì.àt¨€< ¸àà¸xÀ2R 8JlbŠH„@È …Ì…Œ -ž(‚~:Ì’#!'ç’ð‚ÆI„s±–˜-* °Œs\ü"-O'1ž@"Ê .äL° 4Ÿ‘‹Ða +PXô¢å* ÇÀ2R‰‹§2¤ Õ‚Qâ"ÑàÀ ÇÊÎ9J £p!¡ÃºÄn‚ ‡’ÅàÁ2`â„Ã#£À‰Ö‰‡A + à:„( +/ø¸ã’‚”Ž–‘Â0â)‹aa24ÅÄS7n&Ø]Š‰§nP +PX*1ñTNdÀÄSÉqñŽ‡Iã´Œ”ˆx:ÉqñR âñ”ˆD!2(€œBg#£¢ÂBY`)‹ ‘šWòñT ÉÂO0\$<0nÀÀHEHŽÆGç‚„E¦0N"Áb%Å€ËHM€„ã¦d&GÈ®‰©‚“qƒ%QT‘ÑÉO§SCc1’ŸÎÅK*VZ^Ê“*ªp2tÄ%ˆOÕ<ÈOçsáÆ„Ø<” ØXY ñÔ=-ðp‹G|¼`ðÄÌøpM‹§lÔ\<Œ  Àx°pуD<$H@,è< ðDþˆxøˆùtPx¢L@Àâa§€‘ +0€qÐÔ|ŒƒƒA…ÈK(ˆ$ð˜$°qùEy†ŒKøL\„—’e¡@Й1ÉOÇÂKDI8pÂñ ƒ ?–Hƒ‘óp@‚GÆǃ…ä\À7ž”ñ¢2ƒ`ȱ0`@Œ„ŠT\ÄCÄÙØLàìJ Š ƒ&$FpJl°äÔ8 -*J68.$Î’#A³ G¯”D€s$c%‡G~QbÁÂ0 €Èp`°©ÐùœAà±ÀÀÀ8I A_,¥¢å @„gl"!7!ƒ¨¼ŒP`H}!°àCaâ U8à‚3°PH48$XA›ñØÈD|Dx Àä…¢¥bM¢ãÁ…~Lf.\<`P`:hÜHÀÉȱ101sc£8nl(¢66°”ƒ –’‰ ¬J™P$1 +&Ô‡‹„’hpˆ8@ó jd(”—œT‡ÆJƒJŠ *Ý\ ¨ +$””„Ž-)9"52aBHm€tHM¢À@rÒÙ°)ðétd 0 ƒ A†=,h0sñR¸2 )N>: €t€H¨p‘ ‡©2`à(I~“!âÃJnhx$6nâhb^>L%C†#!¸™`Œ3T°0ÀÀ\ÐY ñ”–$,©NJ²YA‘)@a0…00eq.   \`TdÀTA´ €hÁHˆÃÔ¨ˆÔ¨@@¹€8Lâ4)4)4)4))* ÷H5':'Ó@D¦L‘9,#åÑòR-/µÒò’ÁD$CË MŠ’ÐX8Ñ8 ‰ŸhÜ‘Ð<,ÈÐÈÙ¨éˆË9pšŽ¸œ×  ¹ÆÈp€ä' +Dúc n& + + + ™› f ¹ÜL°ΈûàÜƧ3rÁF.Ü|pl–mÆ\:8/"ŸÎ‡ÍL˜|flfâÃ>$P˜Ø‡„ŠûpQ3aò™™0‘é òI &X¢‚2`dX‚#,"!Ê‚übÃKÉ„3B n$¤ Í0€@'%Ǧb@ ¼édÜØt$ÂÒá ¤<PRfH\`HC  Â€ŽIÆ F8Á4 òÓéx:<<*¤t„cÃ$‡ƒ”)«á D¢ÁAâ691#g`ÓRr› <‘ˆÆ‚ –‰Ž“Q0ÀFŽÇŠ (€dG KÉãÆåÆEän\’‡‡‹JÇ/-qˆC¦FEăB¢Á£†£††&åŠHT<@±1 rššë4NPNPN``'ƒ J Ì„G‘þX°¬Ä‰Æ=,ÈÐ ¡!9‰‡$ 24HhH,ÈÐx8Ñ8“ YÓ9~ ÃŽ˜ xFÄ‘üd˜€Èˆ¸ž˜Ž8 7L¢ÁaäBó™ÐÀÑÀ¹ð‘‘Œ³W’蚈–s¡Bƒ‘òé€Php8 + +T@B#%©` L}HÐÀqñŠ(åCB΄ɇ› Ž‹g˜0‘ˆYɱy( âE€ 9ÑO0>a ã$£DZR\xb$êB +Ò\@ظÀ NÐÙXÀ7 ¨äh€Ë +)+N&B€FB T,0†A„K†¤™‰ (IŘ˜™ŸN®˜XXÁxP,¬`<4 ðヌ‡ ¹xX‰8z(‰ùtÆÃF0¨xÀ!I€ñ&5ÁÆå "$|X #&BñÀq2 +.0ðLXD 6njÃ=‡±â©‘ˆ”K0™<`*yÀH48¤x’L”<`*Ud¤Š ›”‘ªÈ€©ÈBÂâ•>T@>T@îAS4)4))"0j(`.j(`<Ö‰¹ñD8.îss3DfˆÌ<9A¹ˆ¹y)‹˜›—Âñð¨À˜°P:ž²Xiy¹³Xiy© &"Ÿ P $–‘òè ™ )w ìéˆ;PAr73)#â"H®qàdH:ˆÁ@, €877œ7œZ‘i ò錌\¹0ÂBÁXìŒ"¥J‡D)(fÃ1&bA¢c>9,bA䤅ÑpL¹ €h9Ĺ|BZjÌ…æ ¶h·x>E³eۮܯÌJ;Å®dTåN¯lpÈ ÆLjɗ­ÝšâSUB¹ÜÕ×C}¬›}whùâo'´4kþ&âó8]þé,ïWv¼–¼©ŸÕº•œöyö[<¾ÔUæ¼RÕ½ç13KÓÌl±¯[¼Zš o¹•w3uóÅïÄ‹PkŸky³SfrߢÞiÂ[LN§vF4Û«².¾õx3bÜ®Œ ³Ü;Mç;^“bF«{­­dŒ¨ˆA˜šŽwÙ–Ó7{1#(߈Q‹Ÿ÷Ô¼[ôôj—Ä·ŒXûÍ(%³)¢­!3§³Œ 휒±y+£LVSTÛg„¿¡Þb]-)¯²Õ³¢l6d„ðŽŸO4d„°ëïE¶¶|BåS;Æ|ò]*¬ÍèhšzÆö³Íä3ëûð8¿ÌýïoëgK§[ÕW«´Ä{«¸‰¹³îêò’6ç­¥->åæFÎkUìÎýÕ¾®kï’÷mˆf©Ž¨øl;.þJùÇ•NÑ‹Ÿ8‘¯ÃÍÖL;=ìq³î3aâé™3´xx­½ËÌC­­§LC»ãdÏJ-N»¤‰×ÏÅj3Å<[æÞ¤‰Ð¦^ókµ,•U]%ëqoFЕWq9:–eÚõ!ÔȦxIŸÇkÜ3KŒ˜õDÆæÔÕuw‡h›l­™^A™¿VK{-ÿš1]cñž)f¡§<¼m{BÆ6+=ÝßîÙbÕíZZ3Özëî¥~¯é#Hqoz{VxÔýmßS"beí!d§wã?Ym5ña—é•v*DE´ÜßÚ¥=Œ LnDˆk¨¶úÄNˆÈO¼ì,ܲ>µïÙÚnbnÂCT´Tµ¼S»FSÃhÏnhmo~ž]âÍ>ämF6Ägeîê{íeÕ›ø0‚–±ïOÆ<Þ¶>Ö¶æN|„Õô:õa嵎Ðfåñùðw¿õ]úgÝx¹ÎžjŠ·ï·Óš•Ï™zÖQjïñ˜¶°LM31×´¹ªºZïfr_*jmv:b¤§Ýå¯Ñ ±9/)„¨zÛŠ¿Jˆ¸zjV¸©øNyQ=™-­Þizb%_æZ±Þ¦ËSüâY'LŒ ÛNÜ\Æã»÷µ #è¹ +_zµuyÈÅ¿ÜsÚ}ÙUãåMqµ˜Ñ"¨~ªg)9×0‚¬¯ö¥Ù·r#\=B§škœ7‚¶òuý‹3ùO·„¸¥›~C.àÜíMEMm]Å\M…ú{å}9qÙZÕŽÓoo÷~Éîw¹Çmm´c;EÆÍ V®2®» S]¾Mxxë¶ ¾d\Lyy/ÅÔûªVýQÑ6‚Ž†?¼[^kµtÛ·æmáÍV¿¶7TedÎgÊ/b&וbçÚ™ö7‚®ôÒöX­,qí+YmÄZœWœ¶5‚4‹5ñ¸¬Ó*oµ¾¹›B­Øªœh·´ªkj×–:Õ8_›!¿×nüÒÔ¨”ÇV3®Zͤ¨´ˆÇ„¨ÔzÌWY|ìêìx»ô8SFsÚ-—ÍJ#¨¯|¨˜zỷK£¾µÅÊÔ†®j½,ë¦l™É)®aƒƒ‡Bç½Ý%Õ½÷½1ÙÒ2;¡ÖuWo>Ýô¸kÓÙ•"”¦È[|‰™ ÿ¾d¼âć…]˜U™‰‹ˆ7ëÌÂìJ;š&vfÝÚÞVÊDNÎÄÝjyT^M›™ïÄŠÐ/ùJu¯1m£*³a·Ù&ýZéZÝ QM9““.B¹Ç{ÍÏŒ Þ^F+Ãꤋն{­}>ãTõä³L,\þ ¡´ÊÌ—©¥õ!ã5­žA¿ÆFè~¨Ýl‡m—°ÒÂ<ÞNµ§mL­ÌÂ;÷Õ¹9S7¢Ž ÇJA_2 '%P@J ‹C .%PA•dóB€– 2 +Ÿ\,ÈOçhŒ$ÈOç!ÑHRP:h @´ÜÂCÄb&%A~: +., €ZÓ!ò“T‰Ž‘ûéxHòÓI?!!™ðÉD„DA䩦#nãf¦£ |1òNF™úšÙyh÷ãþ;M#Èö:mõòžº:,<5û.–fÚRèò½‡õÕè»ù:#èÒp‹íI9o‹Ï ±uòñÎœ{짵¦ÜD¼Ù;!ûV=;5{µLW+õ*ï*y±^W«A¿gv%ëS^,«^æk%ç]Jžqóu•6ϨÇÙ‰i{“kÓ8Ï–µ;ÝêVYBÜÞåC3T}Wê±½í›0¹~¼•ß¤lÐùʸꬭ.U›bíšéiÂÎÍ_µòäÖTu…zˆË—k¨jVÝ8—#(5i´üÃM´¤eˆ·‰÷öyîÓÎÖ‰ÌÊ ÝîóîÊú–Ó9¡ÜöWRNÜJU>ƾK+Ü3n­nÕ+î&ë„ð*Y¯ó[¬Uj¯Ãkwr³]qÿªÝ­ÔWÔܽ+¶e²ÓN«SȺÓrSߦzÝUl³Ê7&æ\¼ÝÕ1f[BßâÝ'jþ÷,¬np¨`– w¯Þ•êõÍ™ +V| å6óòñ» are68T¸¾®OMäì6ͤ–Õ_¨ùlkµM¨ÍÚƶÿÙéÞîXÍkj׸hÚ e]½·Yy/鵸Y·*ßÖei¢Þ ñt{yWw“Õn®YÖÝj!ä\ͬ…8í¸[3ù:e+”ö=½'e…:{Kqë·UÈ”µY7S®Þrõ¶öž.+šBý}ÛéÕøªW)K¡ë3[&ì„Zm³^U܈լ¶ -Ÿ1œ{,åíó]Õ 7!˺íD~ºU}§›"seb/f!/§;퇛•X󸿸‹¿æÓÐØnù›–Üi5ßwfmeµJ«™kü§å{«~vïÍ­ÃK¨YYg¹{-ƳÖkíÚ²Î*•ïÌ–û¼Õ_Å”šÕŠ‹•‡ÒlmÙºñ×Í}Ó”šV¬™«¸\õß7_Zý´šËXšoür«ž{÷˜WÑ¿» 6#_×Õn§x_ÖäÄÓf5ÔÍ=sé§Ý55‘·Uí‡ÆšT¬ã,ÄbÜÔšÙ¸\Ý¿_ªMè~µÓoB}w‡Ü—©Ç¦šöÚ®Ôš|›X³»ù qÕYY¦ZJMH×®‡‰È——Ìg­)3¡ÿ\È÷âëSeB|œÕ‰Ì˜ÇÖOèR¯¹êÒº¬4Lm‰›š©e +m2vŸ™¥µæ®.?¡³ ! +@$$$$^.´Ð `à ôb3ƒsóbsz¹ÐBó‚Ó‚3##Ó½ åæBƒƒÍËh±qE6/h))ÑKÕôõ–ЭnÞβùöNëª,!äÄ×\½—¶Ï6%&ͳӮ½×.f1žî%ªY'Ä\Ôm¾MŠ­zœ¶*7)¦µf*ÞÒöu¶ÕÄ´iŸGÄËcꮳî\»´vw™êh†Éùß,­K9×RcŸ›u!DNCµêfÞLyh¼…©üÈ–±4?_…÷„~µ§j•Ì2­Ùñ“µesYBØ™‡ÉPÓò\iŸgæFc«>_ïKß|Ê Ÿ©º#ˆ9a+]ÛÞ]}õc·¥?m“Ÿþ\ÎSMuKÝ\¹šÝša©rÊÌ;dÔÌcRKÝ~b7ÞÚë™pñJ±nYv‰ÍVu“âûx+½;·±”=iá£dÍÇ¿ñ½/7[¾æ}SïÑ^¥³Îkþ¥¾r¥Ãë|:VÅ´wZŒö-LL–’Ñqj´G©/ÿè¬ó~÷¶×È°oXZù´WBUÜŸ3á½íUvâµåL»±ÖoSîv=z¾®:Ÿ¬ëT)¹5ã 9S.B—ŒëÑ)cÖ%^¬:LûZÆKg„nÝõhùzw¸êbìVë¾î×ñ“nGЕ1k·jÿÜØ–™~¨‹ËøÄ[ŒÏ­sÂÖzÕ“-BÈ«ö‚Öõ÷è¬gY—4qí¬éòêW˜45×41Þ9iâ­ 5Þ#¿˜úîjªÞ*ùqßAiÚ˜t­¾¼ˆ™ŠéiŽëÇÿÇwqÚ"wÛšoÿZ;FKýÓUý²j›–Ìj™i"š2ÿÛ™_»‰ûÛ}ÈéºXs«ú´“§n§©>!×MÚv¦Ìº¢íÕSS¿øõh*Ô[±îÊ7L~ºîê+DLeßÖÔÛ´½ZÙØtOy´¼êuBlÌw9-Ù±#»ö×TÇt•¶p3«´Ò%"ï5‘•W»«eú‡ +ÉokæoeYWî58T8»Z%k¡mYRN»_Ëä_gª…3×jüÖ*­f[µûßàP¡øÅ™WÕ¬Í-ÜÄÎÝ#›iéu[×°m»óª+ùëά‹»^ûF]>ë¡=WbVõÝÕ½»Û4y»Q¯œjuŒé|y®TÐ¥µû¹;ùW¹ØïÆÝ]4½´TX7~¥nUê¥-:DKNÙ¥ÊUßâÅîäëµ±­º*Y³ÖaUy›ëiº¶¶öÛïíLo“1G¹–×– û‡Ì¿Z]fÖe5Ízáqã1/#׮ŗ2ùVÉ®YŒ»h/Öʳ߷®Ú6ýA¸{ŠW²7²º´¶Õ§¯Ù+OÕ ~îÏz{œ2÷lüÚ+'kF³6]4DìÞÌÛãªkæ¹ö©× =Ãýâ7m­åv—´Œy…‡«È—ÝwÜÜ®3{=mk÷VZ´Vd³þSÞª3+Ë ã{r¾¿›²¡Ý^–YR~ƒC†ã«;éªñ^Ë,åKѺ´»®Ë³×˜1M˜ú«n†¥ÉZ‰-÷µõªZÝ·òÎÈVO×™xUùL]۟ᮢÛq–¥¬kÃz§NŽÒòy¨‹—ܽm„»É{¸­l¬–:S_¦ÊÏkqã[s3éánQ“²âaY³¾«1Ÿ)Y“«:¯Ó5ŸpÚmm¬[º‡g©ÇtÛ3† ëš*Ür]³BÊÇ\•¸8[!µ¾ãzñžXõ×X÷bu¡æ–rêªfæ­ìöÕNÇ‹xó÷5ounߦBwKÛbÓM…ð¹¼·ì?áîª5KÓS¯¶—V·V¡ã9W¡ÔÃZÛÜÔméìÒ¼Îïêj³;¼¾KwûKã=ÝôZ´åUÄVÖE…”·ì˜Qíé¬XwíԾܦ¥yP£µF[a)tˆÊÚ¸úbNUzÞÍ”«göüÝéë®È áþ¿¡¾õÌ©kwï¼cÒ»õ×X-»úL#¨³™­´Uq7ïAÙwj—8JfĤjÉœn­4‚¶³7/·N/µÖZY»3%&´|úµÔ˜f•W¥œ“¯î^ñÊ:/몗/]H{ ³ñuˆu‰Sq_§›9‚Þ\ˆ÷µ…Nñ¸O†lYÕ qò+«­êvë\XÈÚY‰Ê›ô8«¬±Õ’ÝBf«Í•î¸)-+S3uò»³4“~!ÝÞ#rÕíŸò µ²–nêîÛàP!û´ìªË{µ©¾"榭SÞêÖ³ÔµÐ-þªÉŠ#»k*Ü.{«nõµý½Í?½7W_ˆwç>ﶞ_ƒCDVã®Lù´¸ì«©OÌeRùËZY·dºeÆYº¥¹ÌÆ{…^ÏËdKå›ÙZ:+kÖéYßÒ®B˜Z¯U¡ïrY%½'r•"^ïUϽªeוiS!^ÎâMü­äMšŠf©¾ +áOµõRQ9+O›¹õúlWœ©•òwÜ›úT;mßO½±ÝK-yWKó ~¿n#÷ê–žÓ¹ëg÷˜u¨Ч5ÒPÏ¢Ž9E34s0ŠÆÄò ]Çqü€½l0:.˜ÆÄi GACa!d!Æ(¤ ‘ A5=ÎìÁ²{K$ AÜxå^äÖÞ~ø‰¢®ï;?±XÆ Cß°¨6Ü„d"Ì¡žåÄ2yîyò„…w¯X呵Tñn¼šïÀ|Zêý ÄêÈ h>ÂyÁ†dB¡ª}ï…r9qÛSJCl*—·{È–2O Q í|A´£FÉéŸåúJÙúŽsÀ妟¯Z "jjÕM=Ó1¿¥pÐ%j(Ü ë§õžù&ø’_ô€¶š,Ñ¿ÛŠ¾y°¤¸lF#uYˆ"Ž¾…”u0jµÔæGŸÿ½9Ê=Bp ¹gx…‘½5&¥0µãùVåŒwô½G‰•±Œfx©D_}±âüᯊ¾tÍ’AÙƒ"¾|Î9UgU¸ÁÿçòõäP¯Ê©¨JUgÙ¸#€ïmX`VF¤øí(ê{8Ó•Ù½¼Òyqƒ.S´Çùâ‚|^ +]ó‹Øß 4"¡W©ƒ‘‚w3“¥þliùp>XÝUY¦K@••òqøXG2V)¡]¾!&[Ê2&ì8ŒÁ”ƒ|{Ø•€7‘2ï‡9Ï’â8~Q~fÝ”Èà¹Ïã|ß×+ÁÈÈ}ËgøÓJ !‰ >3N%¬-a-š•¦]ûy;:çC ^TCfÅR¹êÈ—@ùT7TEÆ•,ºñä$‰+£Dƒ7!.€òÕKl6½ÅÁZ‚† ì_ŽÆ’ôÉU6 ¡’ox³c3ºw”‘mKBSsŽ R%öÏeØ:)ÑÄÆ"Ðe5ìÁŠ:Ù‹ŽÊ?`ïí?BéÂÊšœÉ”ÝívzcÆ´ OÀÀÀïþ·”9@ +?õE›äIÚ÷e'Ê„þ ã]Ëî±³˜ãñO®XýÌdîHEü¨‚¿ÂŽFˆ¾~o\7Þ·ˆSe¼´3Ãä1ªâl%¡k–‰`t):WÏ«G/“ +:ŠÑá2óDÍJñÖYKÚ!AwyÜ6.Œ ö5P5‚Àׯ½—šªµ¿¥5ód‘¢OL7•=z$™ÙTÃJ\úæÇd`J@¤›þ£œñ9Vã%é¤xš®wP_Ë—{Q6ÄGq³†(e¦Nx¬Sg‚E. Qî Æ­Œh­ëµ>Bz eA”Ó¦–ì1ržH(¥‰°ï•&̆Wbi á`”æTʸX(Æuµ°i¼Úê&5&cxø— + ¾g8c,4 + + ¸Œ X謎ÒBqëŠ-?ìçñ½Ú¿ÈÄs +²EÌï %t–há„œ¨‰Î¶ŒNóÀq¶2€—‡±ìàö™[†¨_ÓIý ã6¬Ä€’¿H0;_Aòƒ)1ÿL¯Qz( ,bd«6j‰Ða :ÆM>ÇçHø·¦,ÿ<Ïêeqý«((·D)¯ãUè0(GW¿_ìG‚üÆ~~}Ãî8· ®«VÉùìë»Å)ó÷*,«0é]ÜĦ£3ä>Ÿõ÷$ò³ÓnÑ<4Ó Í?úY·¦O ­3Ÿ®C’jp–i‡çëe0%Q°áe|~îDuoPqÐÖPÐN#c³6n¶­C•Ÿ…Úº°^”Ÿ•m¿öBS¦ƒ‘™A& Ú ¶6öµ†Ñ}ìûÅfZejz;tV™€YõªRâ[³mÈ5nR%hä}\5ˆXÈ…¾/®&î˜H×€R&¬Þ)…ï:p1uÍâÞ ÁÙ¤œ¶”÷l“èH?nE!¶˜µêÙÇ+ ×ìÛeª¦ ]Sžîä·Gd€$0n1q´ET컳š@7èÐjS¢O±GxÈa6€oðÆ>3–«Öî-õHüqùu†ð°vZMEÌFó,w³íþ8¨ÃAê"b‡.×ÿŠŒ3#çð4¾FÆ%9R¯ +àîtoF²Ò U³áv£L–¸WMùv}y0Vœà¹©,Ÿ©¢…o Ž‘ñ8-%Œ2›ž°;ÜÙŠŽÔÇ[«âX¥ós Õü©Œ©Ÿ×Þõ\HÕÅê­ÏE‰ØÃØ¥±ÂÂ…÷ššUðáCå©‚~²:“¦Z KAÌdÃ5MË“ž[µ“OYìÛ#ÒxS”ñu86­@E‹Yªž¼æž'ØÆÇV§ýk¬É¡ÿý™ª—Uøg*Ãgù¯òX¦,òS–—€Ã””/L„qa~ ˆÎÖ@¸mHˆÄOG†w¥ß²™Q~X59NIbÉV.(»E šé/×5^Ãïa3bÏ\ðÝÌk12ì=¯aË éšQ©¤œ·ÒŠm1¨Õ§ý#d†‡6Ê ¿ŸBJËö¿"?¼ai]\ûç²(p"b šI_»Æ %uZ2ãìI1 ?nø Ù̹0Ô×Ä.'‚% ©xK|áÛ°ìÕ’Á¹©žñl•*«Ÿ±N”|s<ˬø¦þÄꟸJO¦5±ûÒhvnÔ´`¡ +“ êÓ ¢’·Ô\O™12¶ÌL!þ5¡ùH[ §H™ mÒ3YvgËβå·Îët)z¶óYÐM7Pš‘4‘\R!‡´åòÕ‚0¸Ô”Þ¾xŸ!?zQø0HGêO.îEè£Òvuê>9.'Ç–ÒJE€Ê‘ÎÕ´ „`+Yð$UÐÒdºFSFKãs›&v˸ ªŽH¡@ìÐVTl$&„Y‘óøä¦=æ}º4?;ü(­nä¶hÂàš9—8q/,¤\üH¦VåEêGꃰÉ +6ª+EZ,㚎¾êè +n߆ms{Ùú:(¯†F $¬$–ãmÛŽ`ÐP?¾gKeó¤±wÔ6a9±%³òb-±Œ! ðºgóÐ8?nK=EêKâZË›U®l—†c¹.DKiþUØøÆîrpˆÿa[b–¤CñÚ3¥*¡"p p‚ÑÁBµœ3¶ø%ö¾)Ù#»£âDÖÞ­Bƒf$¹è#³¼{y¶¥µ…H×°»ï¶étd®ŠµÈ™¹mħƒ(¤õÑ(Ë"¬¯žì×hÉè30‡áóD“$eæ95+op~o é ³´à…ÛBAè¾,toÓÖGƒšËfÖ„•ÿ‰²é%h˜ ¼È(%·N’0LÓAÛ #¿®Y…ö$c#V6k„«È·m|*VX±Õ ü¹('ßt»G7qÐ'b-ÍGâˆp +RIg¡•>`àE®,”P˜ú 0ƒ°‘ÅÀRKÀ­´ýeFß‹/@™Ø74F¤ +è‚=QØÁ˜œ-÷~TÊÒÈÖrz:SÔi‡ 8kêM!ƒ÷t"1Û׿! $ÉxiQTyÂ#"ÑŠ°äE¥‡5GɶöÍ{3ØÒBŽ#§…z9·¶ÂYY}PŽ#‚µ½ 1 Ó¢G1i,Eäe¡Ä¤Ý£u¾a1úÕ5@Ðœ"ñä%\#ØÖ‡¹!J:]m´°ŽH»úËp½+áNȇuGz+®)iƒLë¼O¨L’ŠI*_G-£@!în¼™ïcEÐ0=ö—j´,XX¶‚pB˜bÓp&,WW€xÆÚ÷;¾Äÿþ°^‘+уݘ"æóU»-£FX ,Û5D2ºù¬À8ôÂN†ñ,I~hZ‚ëJ,7)–Öz$8P“A’µ h`":ö_’×A‰— >3RreVPË…´´‹eÿÂ_‚Ä°fý0oº…’†¯ú°‡¿ˆ²á<u…,zû¸‹"ÒDEì¢Oß\U'7ºØ{‚o—¢zÐãTª·gÌÛ°b‚tK+{¢ ¸Rá+¥•Øýô–U)ºh¬‘û“wÓV[Lµ‹ÇÕ£š­A‡|Ý6ý \~d¯Ò×­ ,e9ÑòN!Bm:ó`na´ÐÄü°P«OÀHý™gÿJkÀw»2¸«µ®(R„ÔÝ`‚ÜÊ.¼Œà>|õ䘇P¾v(‰‰{úØ·4)­¼µ\Áî¸U +#;#Ip9Èí–És?…×$ÓUnµ#Ê™ô…ô8©dxdýµ@ƒþÕïôË4ë4§Oœ ‰SÞ~:féôdjÞ¡¼|O5áa_†M•iNGãL)º5×%’L+”q…rÏ\ 7ü’„`5T§þ@·î +e ½©Q| @è¾#î§LØâÇѵ•ƒäSZ ñ#:œÃ-l½a«·Â¢2Fä(õ4Ðiê¼æûÑ) ,lâìs¤°Mz\XÕ•h‘‘}7G&”«¦µ -dÖë: 1W2C ~lùX‘àÈLí$€±¥ ë5kWâºoÊWâ»qCÕÓb8Nó­Ð)ÚDŒ² €#º6{Ä-¥2Ø8%ö…™Ë²Ã$dJNr+l¬š@/¼[ê\w-@þâeÖnݽíí–¢‘rPؼíïs¦A[äÇ¡«cM]±S³è²¶Úd¢‡=íf ¥ïÎ  v‡ïÁ‚tp®m™,º¿@±Yz¶4êkÇêám%Ú9¼(M ++`7ÂôtLð¨;ÌÙ­¾ÑŽ”fÞŒ%"h'kVXá beVÌ(>Â’I}}‚8/qNQŽƒàî£LÔWà f©â—6Á½!ï‹d%IÕeö°_шlhÈž‘Éoœüµ³w+Èò΋ v?<­í‘&HéÑ^4{0-¤On +íkå,æ‚°Iš›bšŒ%Æ_G—72eU¼Æ€ MpbÙÓ’Û$N'`Ú2ZTõí×l+ s„LŽ`Z ÕB¬)ˆYbIø”P–@©#AØ^Ì#j¼Ç‹«°pŽÙ–Lír-¥Îªw 3Sº'[BŸ¢)ƒ2ŒLâilˆ@%Õ,ƒk÷FŠBå.`1ÂzP¾AVåòÜc>ý×ÕL´-ÈF’3šóO÷žÈMÿ z9ôz¿\,즣^-(èÌ8GÄ |ZÁÔ\ +BÃ'EDþ@ÔÃsj]Tôèí»°ñá`HEÌî†#¤lI•Ü:ô1,Àƒ7{³9m+jeŽ ]TEtQ„Ú6ÝolŠŸKûÍúƒE¢Æ³übÆ¥é-èrõI©oá5¢ê)@íçÏëBb;“ßÿ¤ôB àX£»Ž+\ø@wŽQ‡ÀgÅfE4ç'´¼ª„Èé ¾3 ¾F¹êÁ +Ñó§½ýà—ÕTµ‡‡Žóõá#8JÇkuþÙX ª•âÕâ’zùà"!ÊÒÎ<ÏEÕ붩»ÃsØ#P'Ôq\ÍøXpvæû– SÜø#’L?Eõ5µC) ‚Z:Tm Ï‚ã¥Ô¦ ²Œ]×±O&‘€žÜ”ËÈ7ð.†ª·a¦×·%‹ÄŒrlÔA3¡œØ­ãL¶q•9šŸMÆ +ÂïqË +ð¦|^z $EAVåøUtd‡š„W´øsõ·šaEoƒˆºGr‹ÛÜ\ Ü`-ñ‰´Æ\¥hÐJpÓÏñÄ…0´Ž;f.Y¾Šg ÑÖ½Ô–n÷°/Ò¨)9J>Z{l:Ù%ÍuYªÑK"~µ¨®½1#|κl³b¹µ,€[uãá:)É8Æ”Iݲ9^ªjn ã¤Tw@c©J·ø{7UÊ©€ÓÜ…”¡”¤4ìÁØ[± ZJ“LPnõ‘À +&šÌ“ùÖûÇsá. Ä(šbñèZh§p +}B²êž[(n—“‘I3 —ïžvN_N¦}z¹¶øä;°€GÔkZ³.ßb‡ÅJè7¨YèÙÖ+hÆÄ$v”]Ç>nÜpvtgŽš)PaQñ*B\[g²X´÷ÕJ}jë<\–Ñ…2†rUŒ€ÓãlmŸQGÉæ›Ø¤¥™—´ª}t…b%Gëó!@:!·@ A«Æ‘¡æ²ñõ—ºøqÌú»Ìɽ@ÜÅl 1ÀÙÂa +i›r—"w) »,ÈÕêL €Óðt¯íüTo±'eå.:KÀÇb&=Ñá£æô:˨#³R(·¦l{’8ËgÓs㦤/1·v° +‰NQò}RÊ~•âÖ+Þ »Û” K×#§Šf+üìªA!pñÊ"É©ç 9¸b(UxNí<}=azB0 +ùJÕ*Ò&%}QÑN;S¥æ þ%¹ œnIp…•rBóÒ»Ô’ÖÁH°{å5xmÅ×'âS½k™ß‘æùÖ™œ¦"Á6€ '€Û™,VÙ©ØoB¹¶dcüÌËŽ(5FNçˆ$ŒV‡IÑHh)Ñá®pjzN‚Ð`¶ÚÜ“™JÕÝÞL;Ø É¦Uªz‹¼åvFì©›¨¹„m=·†<¨ÚÍu¿!goµ_` 6:ÖA¡HÀÞº«†+µØQA›!xhxkNÿµá¦’¬ŒÑÚóì C +ˆÆeLÐõ„h°]KÐ~é¿GP²º›ýÖ}«mòÊl4Ú™âa8õuRˆÝntm—ºóB>ÛÛo ¨4&û©ÏÄDÌ«,4Æù¡+ØfºÃ+Íq ÄÓKú­á΂ÖæBN¡Ë…À^ fÅ»Ñ^M¦ê´×» ¸<ö° V‰:Ó3€_å¨î0 £ 8å—˜ŒÓ«ô ¼ìrÃz(JTËjèå%?¯¼H«…G …Ò¡Q×C?ã ÝVPXÿá6{¯‹Tô’ól’Ðãæ=T†ëÉÅÔ¦ÃÁÄu&×P\g§µËðAå㢒Ò¡kJu‹ˆZš‘T4ÆU—ëX%2ä¤ÛmS‹™úÎ6×÷n%°UÆs &k߸­Ÿ*U ØrpÔO¯FùzjÀg³Lº”/?Ùí‰ +ä G%,Mí !†Ë˜£ÇJ€þ7 5?ÐãÝ…a9¹ ÀÂâ ™¬÷@»™½r°Å¶O@œm1œŽ,zdB: ÝêVf1rœ×&3:$uI1çbëa#¯O ”üƒÅ@°ïap‚bLÌ•é<™UþC|—b\!XK¿ eóp£éj•R= W1Î#iTÝ[ò‹Y1Ú€ +fcŠaÔ†(b̶n ;®[m…ߥŠõèUw²Vh›z׳v†ÀãAUr‹ÙÛû¯oàâxXŒ€X |½gõ-Æ®‚*Œã{(2yéø\1JEN½C‚‰¬ƒüŸTKSe‰•Îú˜1PaU’1Œ¢¡GûŸ1ú‚IERU¯åŒriòµÆ‚†ÑÛ3;Ÿý¦„˜1’øø> > }ÎmÆØTý¢k T–‘é:ÊRÆàŽÞƒ—m†L¢B ‰Å@¾i/¼”d1T_ƒâ\4;î0D£'Ò¬%ƒàÌÎbD“3BÕEöÔÅb¨çÛùþJÕ¹/Ș¡Fpy;œÅÀ x1Há¶Å€—ý øM‹Á‚UÕºÕ¼E?q€GçbÐæ÷ƒ”Kƒ‰0ÆOÖ’u|Ì•–ŒS¯,ŒAÉ ÚÔ{2¨0Æy +‚—U¬üŒÁQÂlPHÀ²—C©¯9ðÍ_16a+ã“óaŒ–ˆâði ž`ÅÄ–~±«™žW­ÃTÆèɦp'užšzí– P¿H¡fLÏ×Zcìi&“¸äÞpúe2ƒÆ(pT†P©Î}‹P-c Ú +¡Þ]Ïî`uÇ‚# +ª{Zˆ1ÀB ‹c+ZÁ?cðOúÅ%ænHŒñM&E0†÷V–Ê2Ÿ{†1Žd¢xÎJx`Œäìv©zÝ„F#{)Å ©ØÕspÆKÁQ?[¯DèAŽ1–Æ£#ÆÀ£ú r¶”lÆð0ïõ§¾çŠ ÃË¡hGÇa‹¦i•1N6·¡/êWõðCŠ1JXpЉˆVý ££ÚÆðõ[Ò¦\c€1ª"(’êb&@:MtWÕ‡Oáô\ŒÔî›{ºn¸r;`œãi¹Òù¹dó $c”BO‰ŒÎ´0ÊdX¦ZC©d¯ŒŒjŒ—k…ƒp—#s›ú‰¡5…uòÙÆ°ƒ£‚æmŒKrs³ O.Ç(éß*Ç(ùƒ–9uù7Æì“ ¤3£î?v…8›ÏrŸÁ1ŒVBöy³m[ÎðæÈFú‡$:SîÒTä[rå—ßC#UšŠPY÷:Æsƒ's¸uÌwèl•´åД-kÕS7ƒÓÈ*‚HóŽxŒøÏ`SûI?†¿•v\ÔÈðÉÝ?oSyì¡ÂÌtêvømä·På>cþ> tý0¯Cé Ëšf½ð¢Æˆ¤¥®ÿ;i {C©›§c-4ÆCã×ÊâQ”1ˆ³™÷á“fØg±#c€Õoú=,+@Æ`©0Ewù.eŒ·ªœS²{ÅeŒ¬W®³ä<<ó£ž0·þI…ÆàjSÉÕ§ö”‰;ObQðë³mÄ,¢17rê°F˜Æ ¢&¹!Ž»vÊÁca^_&ǨþËmå’¦œŒÁöá§ÄÏîd #e @jŠ #xÆ ²1ðEdÌZ80F¢¹ºaÛ13õ`§©.ƦïpÂE#$DgûC¯ïÅÀ2Ê‚-‚‡UÆ£¤À-GH)R½îÅ€.rÄ)L1Äãf…š+†(´€Á+a0~îýl[%ì#<×’£¶§´í~Å(SF`{2zR1d;ަǑ•§b„ÉÌ(5#õYň #<° W1¢©ç…K{&qÄ×ÞV éHô•R’ãLZ[Qí+†ªÅTNžÇªbx’‹È½ï¬­Ö HŽïÄxÀý îd©vMŒ“µflö81J_£Zò9A×Ä8šF°bÀŽõÄ€]¸›¯&ò¼Fÿ&žI直71šSûH8qýYS±ª21¶‹Ü‰e01$/9c–þŽB–2F—瘸ã%Ø$†)PGA <*¡Mb¼Å̉ÞJ.@^“5Êã}§áyÇár]®Sù sg±¨›+^<ÕÚÕÃÑú¹zǾ FÁƒŽßõ‚ÀSŠ4ù%# öµ‹ZÚKˆv²ÐÀ 1D‰4u†_ÕBV‡¹€bâÚk Šðx…ˆ¾ +Æ…Ï2ÅÂ’ð\.0²ÏdŠ +âÅ…¶Í.‚(]„ˆ  îøÐa_l*ý—%‚&á ¾(b7ên–¾Pëoþ„üB0•D/ê4ÅßÍÜ’Çy1ÒNi¢SÍ‹VÁ·Ê8}¿˜éù9Çä‹©›`À?a¡Á0vI6 Ÿ³¸<Q8 +×l²ÞÅ| áÁPŒ¶œã?š"1B„pmj4Âq„…SbnýÖ»¶`vžœú&Çêi~BATÄŠýüÁ‹¼tÜ-sÞÐí¢[É`=8Z¯¿îœ~âõÅÚSÀÑ›€ žV©f:zq ­,|-%éˆ`ÀYza*"_Is(äœ,•\„{™Xf)Oñ‘G¸ +v[PðÛÅ{<õPô'9ð—·ÚT[wÕ mJùÙe läY=S}ð4|}¦ù›ìåÓøÅTƒÙ’K[#7®cŸåyj³¡®nçªS[É!@åÖYɱdqýäi#¾·Ø2²©‡ûÔÔ§Õá7-˜ Žâ£·Aå|chjãkKR*Ë÷+—qÃÞHë^›ëzD»Ð^’Éç{™ˆ°õP@ã uá;õÚº’2­|¸¶‚|ZÓnhéÚCðS¹Q¨“†¿»ý©2HC‘6†¶+E^b¨Vš7U(°êåD¬î¢¾%¤DèÇQz+KÆëlÔåÇGÀ’Õé b‹ºúô6Àqðå`™*ƒ–3LÛ¨ÞGøÑC ý\šƒ0èö|×`u’î-1AšV÷W•ox$èáž¾±ZíÛd[X§¾[¡K´jÜ°]4*i6áB ðùRM =yÖø¥=ò¬Qá_ý€#¢¨¢¼«N:FŽ¼ @ƒèì©лZ`'Á|¬¨%¾%¥xU€7Dy .´ê£‘L=1’î5,D›’ßHDÒšZ´7_ÑÞG8‰õ&ÍŽTA+D$}„"†ôÞ^:‚ ¨È”hðŒ²;ËÇ ×Vƒæ¤?5gq™ãÇ“Ê@hþ>ÛS y<0ÑÒ®ó˜„tB·_>In¼M-µüo,è>ƒa+%ùùI;¥ 8Ï>¨¯I!b\g7¥8 û=ZNKźW•…Î x«– øxÊoɉ>Ç’3Aé?/9?ßQÜ`Ð…> ÏJ]L!LŽ¾»M˜óHÛ×?6ì›X±‰#‡,T5hp.ÖFJœã$L~‚AꩱT§Ûƒ€ lPƒEoÎCA‚Š®)€?œh‘Кž.©Ò“ŒoÁ&'ZvÈNõÍ…(& hOøŒ”4±úPd&ãQG„z€˜3+…mˆÂ_ o–«¾€AËÒ÷Ô§ýæ/$Ü€[]ÿáÝŽ[7¯àÓiÿ¦Pi¢”®¾Š§WœTè[ 9ÅVfù÷¤ÖÁ+Ø)¬ü¾ü6Šáøߤ3ØW[ô:Z@ µ’ÈŽëÚ%·³ ÎLç-ëÌbŸÂ´nA¤Îb/K”µÎX5¼NpÑwÞLÔ?º r0Ü5¥dŠÔžvüBÙAn,3bBÀT? )ü„­ ½‘Þpᬉé¾D惼 ‚3B¡ÅHFë….€j¶ãÍ6)[Ÿ•üAE‡R*†Ä$ñ‘—`vå$gh¡%C úu¾c4m@†.vb §õ&p@Qiijãy"ãi4d¡ÑvÑOa¨y›…ûJ‘‡*¢)é‘ðõ‚è"sr¤©rÓ8i¦ 7®Ì/¹fµÐ¸AË$$9ôâí)39 3ÞÈ9Øë8«ìÂ¾Ñ Ê‡sÜq¬®–J˜¶ò~¥NÞ® Õ¤1W3[³ acÀ¹Ã’¿2_mÎf¯@q§Ô£÷ÓƹÛôDãqcÑ0Ü@ÒWù¦ÙxÛ%’ þÍq®Ð^&TÅ«[¼Uƒ1Ë\¦x@Ô!OK<¶àDuhŠÖ­é ä9–l%ãz +¥f×ÉëÄó$8 ¬‰u %Ó[õ@žYÆ‘3r}2|Òa4­ñ±aÔÄgˆé«7¤<¸f©ËÞTbzZ6+c¼ÿ¡GÝ’äq†¬JµèYÄÀùæS4P)&ÇÀ‹¸5ïc\¿;AìUöyu]B‹cЂ¡á¼=7ôó7ÛXŠ5ú¿¬Ó‚;¢„'‘a½Jù³ª¸˜|÷Ü#1¶Ò$$¥´ÑJJ‹‰—5먰èë•9>­vI…”öiºS’ ¤œcD–pÔ1Ž^‡2]Ý„Íþúr{Ý‘cuߟ\™ÆÌÕ-k=UÏ9[Ój@Ϲ¿©õl/CeýÛבw‚>»ì‚­?VEéÕ›ÝÒJÑ,ÿåšXï¿Ó¼ñU†N±ñÔ/­”eEÝ{–"Acšð<Ì8ÙcÚ\@ÊÓ•ÝrÖöäÍJ5(ñ÷ĤŒ«©M;™"š+²À¬ŸQMkÚBSy]Gèf$xÁéhú'N Diñ)w¾ºÎ ü“p¹kÑyñ  ¿ŸéŠ.—Lâ”F¶þ÷•Çp­µËµ%û| ã}W渓;ðôU*CBÍÔùlj°'e|{jù%›‡¤øªä²'œƒ;û7ñµ«tcç^'žŒ€P/ŒâÜÃq¯^Áw¡¸4<ï¸Pc«NÇá›ÀÛœÐý8t„¹TD {š)àÉr€™ —}A±v>ÁxŽyÙÊêÔ¦>%‹EoO¦®áØùÞ+ÕMÉ^(‘¸j´áØúÏéáSÛO7*g¦G›œùªÕt±‘áv¥ÌEì +Ú|§ìV_ïκN +Ï\ l$@úظ¢|NMb´MG¿«•µ§¶æ_‡¡‰ŸÐãÖìѯ?­—ËòË]¡\&Ó?$^YiULIr¸j_N#€ì0Õ«Fq¬§4¾jçIqÞA·HTÇ“ÒŒ…ºhˆzU¢ó¯ÕIÐڧöëÃtj>tÀ +ÐþOhy:Ÿ]þl¢ú(D£æ™P£Qóëb·†ÔŽ4¹„/ÿÞYtè°¿)áýß;ñêÆ<5þeIàƒe%÷ë+[õ®çÊEÏ'²^õ£¹RáZWŸtõâ*ê&²êhiâà€º‡«#>šN½ê47sˆpƒãUðŒÎ †È}K‘lÏ[«n™ÊôlVâfy?£ó¦ÍÔXõC;ñ +Å­ÜWâæDÆ_ró&¬z(–l÷Q½ ßvÕñîýxXW†e‹¶êM`³°¬«NëXyìpA:aIV«Þº– å3®.s >,kÕÏ—®.·Ì\½bG›¬út®NkÃ~°êïèç Æ_•¤««§W=´iã¹:®66Ù/É®"«¾ÞÕo§Œ¹ê‹êàÝ£¹:Þ„à«þ³‚¸ºk©éÚU—C Õˆ)© +lU‡ÎÕQ#½Ÿ¬zËÕ«ðIV»ê}æênM.YdÕ­sõÒ'ô“tuuêªÃÎå¨a´ÿ(Zõ¬®ŽŽ.@òH?Wÿít]u’>®>;Ñ}̪çˆ>Wï™ìªS'4Ìgß‹@¢”¿/• ¸?Ê*&4g®þ¼è¡ Y dËÕ{­É¬º­âqõCI‰ ®úÂü=‰ÈiÛ ¤K•¸..é‡ñ늸â÷>Ì×Y·ÂN!|mƒëèùWa c~N‚ìD»‘éo^É÷IJØÆ}=·ÊQª52#ðóáô—A|ãDHÇ'×›Oh {Gy:úS1ˆ·,¯ÿû`øú•&Dbôu8²?°À‡E¬‡…(?p­n«ÕÜ»V¤ÈVKçZ¸ÖèW ¼Àµ.éï&®%ýjA¨®…ÄôöÅøE} øÔ¢À‚݃íaÞ3„<Àœ`·~EéäÿùŸA†d1º^àŠßv ÷êÃŒ©9Úuqh¥)®› æ¼Uì“Uu˯t‘GÔ+Û6"êU6ÔðÒ˜-„"„”¡­)ÀGý¥ZŽRÐ4¾æ·º™sÌn_fæF&8}rÌþük8¾´Æ l¥âZ¼çÁßæþuŸY‰‰‡JѶ´­!°P«“ƒŒ_•i±Ãn©f0 þ†6SÞ›ï6ój"Ð5lYðÆ•ÀPh±<FÁµT7:”Oó4kî¥nóS9Žç™E÷   QFÍ÷HÏT¹DEQgžŒ]VÔ~)æ‡;SmÞ€åêvô(*ŒSTñVÎJ‘+=ÁÁIR—&ý1ÇÅf>òOrê…0±å‚ñwfmd‹|t›€Ø(À“ÐB:ë þÒÚü@%rÖºS*èpOFAþq¬.¨SÖ¦¦QÄTѼ8í -~Ó\ZOpüÛ.„§^h´áÐHt“ akŒüã-„ZâãÑ9¤ðQ€B1h÷ªä‰/LîÙ'Áqô ‚» ÃC™ÌF0•á¨Ígo‹ú¼ê¤{Âå]€ŽÃ£¥þaÁ‹ýÉ!b»{G¶zšš]{\àáPyžÛŒÑ ™çÈ]ÌîaÏŸ=ÄGÖ8íl 8 ÷Í3uDáõŽ7ÅôZZ†82”Ü`@$ú(Çý;ŽÄ"Ò‰ŸÄˆ +©¡6}8‡(}Gð¤*°èä™ùªT6CãO²à”gë†ØbÚ6i«{é¾#¨:‹^Ò†©üOëŒ{Ž”e ¬F­Œ™™t×L Ue´©FgÊçžEÚ œN6¬jv¬f”s½½ +ß3~‚à†f9µ©JÅ:Ü£ÚQ£a{úK„Ö W¢‰Oµ7?¦9 ëw#Še«ÄT „<]RÝoöƨ¿üè®´|û9{ü>ñhï”=†6àÿ ΨA,š£?;χ!yÔÅûöÕpÑ‚ûÎžóg€nSS¾;?\Žbqyg z’Z‰šž¹O1û¬cƒd¢O´­"”íAmï‰=Zí}0ÀWåvš=‘nj¥ ÇaÜ £¶rn–ö¥Ð®º3UHïcÅ.m‚ú0Ž±?@Žó?N´NºAjý ?²Óì[l\Óg—ÐJÜ,7¨\ñgè¼ý%>&DÛM†.l5 †€÷ýèE$Hçìß‚müI¡Ò)œ"Š:½ÍßÄ]žgÛɉ¦ŒÎ9õ‡âgÚ†å Í÷y¯2 ¹y;¬!°9pºF~úJHOÓÐÔŸãùÃ>õ­þbÆš›ã/ÄÑñ™¸“ìx\¬j°’âDl +jÐ.]‰Z@•xjüc>ÿæЇ>áuõ¶Ï £~ÿ±6U*Ša·Ld‘±¯ñ¾^ì +€úãhX&hësÈtÈ2K*6 G§YÀ –´™ d lÈ… +ù ÇyDS8KS·—ÔBo„¨ƒ–d8 +w£O"°¤)wÓK=ÅK +©¨€õœ¦RB »y"Ê_R¼¶,¢uNÒØsqÁå ‘µÁ§a'c ú’‹4E¯Ä«–h78Yß&=XîÇØŸ%c›ÍXÈ+R‰'Ú¾wÊøFãèÚáOCªO;e¡LE…æ=0h²´ƒø3¼2_ÔV%°ôK&ÕŽ¾JÜSïAÅ%Ë'[y׬ ÝAü”Ë%42pÓdÎh1‡a‹’܈»ˆÕO-þñ>`ñÇÅ_@r´$KDN I¦<šš„¸úÌ·ôËnyñÃI’;²“‚5Žßr!­ë‘ÐòñšÐ%xúE¶ó¡Çß¾œt x£IÎ3˜Ï­2&uÀŸb‹æ4©Uá¬'J¶«‡í/[¿Ç/é€>ŠÝB*3ój¹‘¥o ˆéBSC줴ýñ5arÔÿrÂîu¿ÓBF{&„Ö…`£d¡I’š¨#5Âgó&É ÿ[Ë'îYaSií+ oÖ¿¬HŠlÏFùiÓ# ¢µâ¢½Sš^ù†|9å@û +¼.=¿"N³©ñË/JñpÝ,ïI˜¥æY‚¿á(&Q䘯×:µ²{HJä6"yÿ‚,ÔÓrë‹ ž2hÒ¨ŒÚ÷oËÿš4£H•P­žƵ\ƒøÒxÿ}€A»½XÆy à2¾È×w)kà"Ù£B^ûµi\¾ãÎ÷·Õ£©9¿¤*Óf>þ÷>*l¥ó '¥pðû‰¨yjtèã4“ '’“ 3¼B‚®Ù0Ó&Ê•&9ìD9XX±–ÑiùTÇDÈDÔ•®˜A÷ðþ>B\ +ÓâXÞ„Lz1D9U¸„áZ8^bVSþd$wÕ€ã#3ÍA¬+âßPŒˆCLùd;b6¼Ü!.žÖÂðÞ„˜J¢)ÕjÀ (”Ô‰šj;¢‰Bx¡5¥®®´r"Äyž5Éxzò“û3.ô/`¡§Ã9ÄÓŽ=¶ßê‰SJñ‹d|´…W]s&8Ä\(¿ñ‰˜[ÓŠ;Ä%É'Zƒ++VsÜT„‹ûˆUW‡¸”]À‚sp™À‡¾± TþaœYÊ!)þO¬fÇ!N”É ýŸ@«£ Ä^û®ÉHTPqo&7qO¾üii¤¼†«©ß†3b¸üU! ‚"&u¥¢ÞuªfîtÉLÅÞÓÈÝÎXH²—•é{¦TÙý«,o–¯Æ©>¾*—t˜ìÐêÏoäÊŽµëýìPº'—ß”ªô¿‰ˆ´/ÚÅHJX-tÆPÐ.Kì°í†(&+=ðZÜ¥Õ ›ZA§EìûéaþÄÓ$t½¤óWS}ÊCØÐàŒ#'¬®ÁQhÖÕnÈ|«¡L…ÁçÚ7ojð'hB4–Àá>g0ÜËX + Ç]ÝlÏk’ºGg5˜d¡<]#g}’UÝ¡5f´ÝÎ`( ªx"ÊüÉÛ™Üñ¶“y ¯ûžtÏ ;Y¢š2ÛAä!ˆ!½ìJCKoŸªä,I©|eHÊÒ™Øw»e3NkŸç÷³Ìrád ¥!³e‘û‹tJ®Ù^› y(¿š0 ™6ûHV…X Ýüº†î¨Xˆÿ eáHæ¥]˜i\á/?ÒUþF’¼%s‚É›%ÙG@ÝQd@#dœKúÓÉÙÓ“… {J~M©ã1™©Ò”šë”‹%éc[LÎ]ZŸï:áÉ1¿ ý !‹Öf;y>>WSE”(*HݱaR ³ŽÆ¡.,ó@UÖ†%ÙKíšð’°±¡ƒ¡öV&H¨ÕéO´m5&º?@IÉ!NÙXƒ溽Æ e&.¨ÒéØgg© ÇÉÂñˆ+ØíœkÍ õ¥£v‚#†ØÓaݼ€ŸùëoÀtL&•Ýלó Q¦¡u•LƒKŒè¯€-Ã9šÑ»Pó—r,Ô8’À.‘ FƒHz¾•ü¼!JÒ±ÁþšGà ~ŠT™Ø`IèÎÈQ ì V´/ª¬þr •l÷zu6‡ Ç'¹¤Zà7EDѦè¨78>ÏrÌ ®˜fµ6Îaƒ¹Fà:VBòæöŠ \µ{ÿÍipZöxž þxË®Ÿhµ=x6ØÒûµ·Å+\þmÍ#opguQV•É[ÐÂÉ’ó2ɶE +Go ¼H·×]´ðNÕt. $ðvägë+ ]~{Eµö·™Ö°QAPÀ6©é}ÑX@™Ò‰ŸCD?Í$NÐ'ì‹r&Þ3vOR( ²¨‘$7g,—ésEkL'é w™1~Ô;~ž“™ñe€Ùn_r¬yÆ™}«Í¢*ø™ ì*>W0j&<蹜ÛŒa"ʨ?ß;§ÞÄÛüø—:ƒ<Ô2Cøû>}€AJ‹º*Ù›ös•lv™˜ I/ÚeR/¿ÂÉ;±©œ9;öÒ£Þ‰µâAaì+w©{)KzƒCM=/)š¯eô¯ðwÍ©DF Èöö˜× xÁŸ1¸Þ:³ú‡÷<×Ô@hfsÿÅæÞR]®4¥F+v”õù<᪒ƛ"©|Ýõøü•ý½›ƒŽ :XLxMAo€rS-I©ôíY]M:‹k-¹Ó,â[ȘL)úûJVPì&Ç.sÈãz°-;Èù¢Â3Àj +àZÎúߊ(MïmÊqvŒ³<,L–Ôù+ë±æVeÈ—Xõž `ˆò"™6+újåOc¤=|îD€¿ Ó;Y0ð“ß.‹ª6¢}RÖÞ©ò:ƒÜ.c›Y.ù—\CmŠ{¹ «5ãE€[,à–Žš5¯´ó½yû›žÀØbîÓ‡ =uøЖ7y®8W‘p¢¨%’~ûh2Î9„UA HB*ÆM®O&تoõ$1gÄ\ bñÆí L°!¯rëH1 +ºùâiÿóLÞ.ÉèkõÛÜ5ÜŒ}yÛ ,Ы#F \¢¶ŽÃ,J2µú€ZÅw©ðO+òûäΠئ9Ã);¾ +vy|²Kž)óv6*I£IîYŠâ^ +ÀÐuœ£ (¼·ÏØV¬¹š%.hè»\‰&ã¥bB÷^Ì©&42&Ü߸ί§—ÖÌ|kzDáâQæ °0Š~‹oPƒA¥#cU¢Ì*nS÷«Íç$ôô{™zkèÁô[UÓay~4ýªU€R&DÚ$Uˆ”î +VÕÎQ¦e“q<§oW»økå2ØÆ5|*lÑVc°VÝŽsè6€r.Ϻÿ|;6®¸òà³h4 ãÀÃÇ:pËÏMŒ9¦ÒìwîÌëOáCo:nÀBÁ%üb=½:.’gŸÁ%Ò²=yes>_…o­è¯ããø)Q¤Í+“¦jEý¾—¼‘åæÈÅæþ¢ +å½ÜºˆŒm‰^Œ5øoeÆ£½½·•õ‹”¿b di×þ3ÎF¾H ÚZU4DL8#¾.u[¤V|—ôPo,ó»¿lƒÙ°KßÌ„<ìÄ¢HUÚÝzŸ2ez=´è£îÚ€IÈ>Kf•·Ð†íQ†98ãÓ¼-6¡©w!©Ÿ©—Å P×(sËŽ.øìνšª·@¤)òn(q" û ¯Žp“³ÚãR4ùîTF~!Ðy †ùˆÝQGW…é{Ò÷Å—}…™Ãôòõ£ékî?È"jÔàf+ÝÚØv;ÐnóÒw}ˆ€… }¡´B’8i‹Þü¤4·£D£L©*Ï÷A8^Ñ5)}©D}–!$/§´aúJ_+)žÒÃÜa}¾–,Xmé+(Ã{T|Z“MØR)û•Ð@¦[q¾ˆ6¢j”—ÔàÕð¯vÖYë‰Â†O{ç2åÖ†ã™p¦ tYwÞÌm¦à•Ž܃’o96¥Zh®Ü³ë.‚ú#Ü÷N~fÎ¥b{S7ùë£ré@y[z×¥Èä–vOr>úÐrz^˜¼¹×—Ž¿Ìä`$={I#|·Ô-=2âµr¹JlÙGæg`¼2¡¸D,]év·3CN{G³›]žâÙÖž¢,RžJààw=á^’ÉJŽÎ™§cY¥šÛ¬-Q®„MÇ7¤ßõ#Ά༭wüŒË=B¤ 8¥1÷K¹Ob× D°Î2T õ} ØR’Õ¬×Lä눈¢ä™ꢭJ DkkcɦEÕB½¾°ê×´«™-‘Ǩs%Ìhn”[_ˆíþ±ukN¢BRÄaFεäM`%‡‰ )ÿlrÂÔdnôL蟉éA« S‹Ç©j‰§70ñp·I3±ãvµÉ.y¯#5¼Ž+©²£ˆH›dCÕrÑ­ÀlÆÔ¯ékD+UE}sa +¯¯+Ói«ûå BQ#Û.zŠ¿©gtQsrÉàqêK®kÎõõ5r¥ú9s—îõœÕÛp,K}%-×wÜÏŸÑ+¼¾¢Lµ¦G}Aê+i»¾ëO‘ÀÓÁüÏõëÅܦ‹Ûv{\_æ¯îá¿Ðó5ü&ÿ2 Ξо—À˜bà²Y¥ƒ‚/Þñ‰d·h\¯ +> +Ðk2#6áDbN ˜I g |,[Á<‹ruîð)÷]ñ≲8ÁÑÄ‹r­Ê>¦ýYîs“K»Í"8¾ÓíǦÚ7d«ÇÃ&<^©†'¾X³K…ò¹l†¶ÍNŸ]²¯™?uÏDµt”wœ£´Út_eøÍø.9 ÀÏkœF*Aú„Ú¤ë2èF–µY#iig5–!†¢£Ï°– 鄵l‹2*3b­ø­gÖzŠö°ÿTüªG‡ð‘jVŸ©PÍUpNî%I‚.ÏIÄœo8œà&u‚ÄÊ:z[ð—gO3Þ¤æ³ÝOÉH牮ˆR+ŸUo$“ÍgØF0røº]âI +á<ýÔ]g׫Éã[qŽÈ˜¾íVç‰ÛRI²Xb­.$±Ð‘“ ¥”[¢ÖÆè~üÓð<ÌÕ&Ò¸…QãÓ`B,Úp!;LµÏ|*ëVá*?á6çC,bªP +endstream endobj 22 0 obj [21 0 R] endobj 34 0 obj <> endobj xref +0 35 +0000000004 65535 f +0000000016 00000 n +0000000147 00000 n +0000037843 00000 n +0000000000 00000 f +0000037894 00000 n +0000000000 00000 f +0000047389 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000047462 00000 n +0000047636 00000 n +0000048949 00000 n +0000114538 00000 n +0000000000 00000 f +0000046180 00000 n +0000161718 00000 n +0000038289 00000 n +0000046480 00000 n +0000046367 00000 n +0000044698 00000 n +0000045618 00000 n +0000045666 00000 n +0000046251 00000 n +0000046282 00000 n +0000046648 00000 n +0000046683 00000 n +0000047271 00000 n +0000161743 00000 n +trailer +<<70F4B56887079847B5D70147067C5F70>]>> +startxref +161927 +%%EOF diff --git a/hw7/schema.svg b/hw7/schema.svg new file mode 100644 index 0000000..c9b37fa --- /dev/null +++ b/hw7/schema.svg @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw7/shader.frag b/hw7/shader.frag new file mode 100644 index 0000000..993d1be --- /dev/null +++ b/hw7/shader.frag @@ -0,0 +1,39 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +varying vec3 norm; +varying float id; +varying vec3 glpos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + + if(id < 1.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = 1.; df=.2; amb = .7, ex = 25.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) {color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + if(id < 0.){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alpha); +} diff --git a/hw7/shader.vert b/hw7/shader.vert new file mode 100644 index 0000000..e5d222d --- /dev/null +++ b/hw7/shader.vert @@ -0,0 +1,16 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/hw8/.vscode/launch.json b/hw8/.vscode/launch.json new file mode 100644 index 0000000..a8c9784 --- /dev/null +++ b/hw8/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch index.html", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + }, + { + "name": "windows", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "C:/Users/sunyi/AppData/Local/Google/Chrome SxS/Application/chrome.exe", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + } + ] +} \ No newline at end of file diff --git a/hw8/.vscode/settings.json b/hw8/.vscode/settings.json new file mode 100644 index 0000000..8ad19d5 --- /dev/null +++ b/hw8/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/usr/local/bin/python" +} \ No newline at end of file diff --git a/hw8/1.jpg b/hw8/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw8/1.jpg differ diff --git a/hw8/2.jpg b/hw8/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw8/2.jpg differ diff --git a/hw8/3.jpg b/hw8/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw8/3.jpg differ diff --git a/hw8/4.jpg b/hw8/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw8/4.jpg differ diff --git a/hw8/5.jpg b/hw8/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/hw8/5.jpg differ diff --git a/hw8/RTX.frag b/hw8/RTX.frag new file mode 100644 index 0000000..1343d7b --- /dev/null +++ b/hw8/RTX.frag @@ -0,0 +1,231 @@ + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying float id; +varying vec3 norm; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=15; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + bool selected = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin){ + vec3 tex_sph = (S-Sph[i].xyz); + color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*color; + if(final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if( final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor.xyz),1.); +} diff --git a/hw8/RTX.vert b/hw8/RTX.vert new file mode 100644 index 0000000..8ae6412 --- /dev/null +++ b/hw8/RTX.vert @@ -0,0 +1,24 @@ +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying vec3 trPos; +uniform mat3 transformation; +uniform mat4 uMatrix; +varying float id; +varying vec3 norm; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos; + id = oid; + norm = normal; + trPos = transformation *vec3(aPos.x, -aPos.y, -1); +} \ No newline at end of file diff --git a/hw8/RTXoff.svg b/hw8/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw8/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw8/RTXon.svg b/hw8/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw8/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw8/demo.webm b/hw8/demo.webm new file mode 100644 index 0000000..fe29e75 Binary files /dev/null and b/hw8/demo.webm differ diff --git a/hw8/fs/1.jpg b/hw8/fs/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw8/fs/1.jpg differ diff --git a/hw8/fs/2.jpg b/hw8/fs/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw8/fs/2.jpg differ diff --git a/hw8/fs/3.jpg b/hw8/fs/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw8/fs/3.jpg differ diff --git a/hw8/fs/4.jpg b/hw8/fs/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw8/fs/4.jpg differ diff --git a/hw8/fs/5.jpg b/hw8/fs/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/hw8/fs/5.jpg differ diff --git a/hw8/fs/RTX.frag b/hw8/fs/RTX.frag new file mode 100644 index 0000000..1343d7b --- /dev/null +++ b/hw8/fs/RTX.frag @@ -0,0 +1,231 @@ + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying float id; +varying vec3 norm; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=15; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + bool selected = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin){ + vec3 tex_sph = (S-Sph[i].xyz); + color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*color; + if(final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if( final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor.xyz),1.); +} diff --git a/hw8/fs/RTX.vert b/hw8/fs/RTX.vert new file mode 100644 index 0000000..8ae6412 --- /dev/null +++ b/hw8/fs/RTX.vert @@ -0,0 +1,24 @@ +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying vec3 trPos; +uniform mat3 transformation; +uniform mat4 uMatrix; +varying float id; +varying vec3 norm; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos; + id = oid; + norm = normal; + trPos = transformation *vec3(aPos.x, -aPos.y, -1); +} \ No newline at end of file diff --git a/hw8/fs/RTXoff.svg b/hw8/fs/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw8/fs/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw8/fs/RTXon.svg b/hw8/fs/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw8/fs/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw8/fs/hw8.html b/hw8/fs/hw8.html new file mode 100644 index 0000000..83f1a06 --- /dev/null +++ b/hw8/fs/hw8.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + An RPG Game + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+ +
+ + What's new: +
    +
  • You can press three sphere-like buttons on the right of the TV to + switch between channels.
  • +
  • Bonus Point: special control 1: touch screen. When Ray Tracing Channel is selected you can 'touch' the + screen to select the spheres on the screen and drag to move it. +
  • +
  • Bonus Point: sliders and toggle button. You can walk to the witch to show 2 additional controls. One to + button show the stars. The slider will change the opacity of the body. +
  • +
  • Walk to the torus to 'eat' it.
  • +
  • Bonus Point: special control 2: clickable ground. You can walk by pressing the arrow keys on your keyboard, or click on + the ground where you want to go. +
  • +
  • Demo Video
  • +
  • Here you can browse the source of this assignment.
  • +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + diff --git a/hw8/fs/lib8.ext.js b/hw8/fs/lib8.ext.js new file mode 100644 index 0000000..61a501c --- /dev/null +++ b/hw8/fs/lib8.ext.js @@ -0,0 +1,1283 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = true, + presentation = false, + sRotation = matrix_identity(); +var facing = 1, + running = 0; +var figure_rot = pi; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +let show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +let channel = 2, channel_disp = -1; +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function (_) { + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + delta_l = [0, 0]; + facing = 1; + rtx.src = './RTXoff.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 200) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + + if (!(mouselastX == undefined || mouselastY == undefined) && moving && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy / 60)); + sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx / 60)); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +rtx.style.cursor = "pointer"; +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + figure_rot = (pi*(facing/2)); + else figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + running = 20; + dir_sdl = 0; + keyhold = true; + } + else + running = running > 5? running:5; + rebuild = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => { + delete objects; + objects = []; +}; +let delta_height = 0, + delta_l = [0, 0]; +var direction_l = [0, 0], dir_sdl = 0; +class State { + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + } + initialize() { + this.leg = true; + this.progress = 0; + } + next() { + //return this.presentation(); + if (running <= 0) + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: 1.8522 / steps + }; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state) => { + if (running === 0) + rebuild = false; + let { + lh, + lf, + rh, + rf, + dh, + dl + } = state.next(); + if(dir_sdl > 0){ + delta_l = plus(delta_l, const_multiply(dl, direction_l)); + dir_sdl -= dl; + running = 1; + }else + delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + let sqlen = (a, b) => { return a*a + b*b; } + if(sqlen(delta_l[0] - 8, delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(delta_l[0] - 6, delta_l[1] - 6) < 5){ + donut_eaten = true; + pushStatus('Yum'); + } + delta_height = dh; + clearObject(); + M.save(); + + M.translate(0, 1, 0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.53 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.restore(); + if (running < 0) + rebuild = false; +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 20; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +let state = new State(); +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + // M.save(); + // M.rotateZ((uTime)); + // setM(M.value()); + // drawMesh(new Float32Array(star)); + // M.restore(); + + + // M.save(); + // M.scale(0.1); + // M.translate(6,0,1); + // M.rotateX(1); + // M.rotateY(uTime/4); + // setM(M.value()); + // drawMesh(torusMash,gl.LINES); + // M.restore(); + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore() +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if(channel_disp == 4 && i == 0){ + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + setM(m); + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + if(!shader1_inited){ + initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + drawMesh(obj[0]); + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; + } + else + { + setM(m); + //for(let i = 0; i < ns + 2; ++ i){ + // textures[i] = i; + // gl.activeTexture(gl.TEXTURE0 + i); + // gl.bindTexture(gl.TEXTURE_2D, texture[i]); + //} + //gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let tv_ctrl = new Button(()=>{channel = 2;}); +let tv_ctrl1 = new Button(()=>{channel = 3;}); +let tv_ctrl2 = new Button(()=>{channel = 4;}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Turn TV OFF.', 8); + setup_control(tv_ctrl1, 'Play a Video.', 9); + setup_control(tv_ctrl2, 'Show Ray Tracing.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +function animate(gl) { + buildsplines &&= build_splines() || setTVCtrl(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + gl.enable(gl.DEPTH_TEST); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(delta_l[0], -delta_height, delta_l[1]); + + M.rotateY(figure_rot); + overall_trans = M.value(); + M.restore(); + + + + M.save(); + + M.translate(0,-3.155,0); + M.rotateX(pi/2); + M.scale(10); + + ground_m = matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())); + setM(ground_m); + drawMesh(ground); + M.restore(); + + + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + M.save(); + M.translate(8, -.5, -6); + M.scale(3); + M.applyl(overall_ground); + M.rotateX(pi/2) + draw_magician(gl); + M.restore(); + draw_tv(gl); + // M.save(); + // M.translate(-.7, .7, 0); + // M.scale(.3); + // draw_deco(gl); + // M.restore(); + // setM(M.value()); + // test = new Float32Array([10, -1,-1,ttt,0,0,0,10,1,1,ttt,0,0,0]); + // drawMesh(test, gl.LINES); + if(channel != channel_disp){ + channel_disp = channel; + if(channel != 4) + { + for(let i = 0; i < tv[0][0].length; i+=7) + tv[0][0][i] = -channel; + rtx.src = './RTXoff.svg'; + } + else + rtx.src = './RTXon.svg'; + } + M.save(); + M.translate(5, 4, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl.draw(); + M.restore(); + M.save(); + M.translate(5, 2., -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl1.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl1.draw(); + M.restore(); + M.save(); + M.translate(5, 0, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl2.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl2.draw(); + M.restore(); + if(!slider) make_slider(); + if(near_magician) + draw_slider(); + if(show_magic) + drawstars(); + if(rebuild) + build_objects(state); + + M.save(); + if(donut_eaten){ + M.translate(0, -.4, 0); + M.rotateY(uTime / 5); + M.scale(0.23); + setM(matrix_multiply(sRotation,matrix_multiply(overall_trans, M.value()))); + } else { + M.translate(6, 2., 6); + M.rotateY(uTime / 10); + M.scale(0.4); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground, M.value()))); + } + drawMesh(torusMash); + M.restore(); + + for(const [obj, mat] of objects){ + let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)); + setM(m); + drawMesh(obj); + } + + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + +} + +requestAnimationFrame(fpscounter); + +pjsk.play(); +pjsk.addEventListener('timeupdate', ()=>{updateVideoTextures = true;}) diff --git a/hw8/fs/lib8.header.js b/hw8/fs/lib8.header.js new file mode 100644 index 0000000..c97aa92 --- /dev/null +++ b/hw8/fs/lib8.header.js @@ -0,0 +1,786 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); +let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt; +rtx_VFetch.open('GET', './RTX.vert'); +rtx_VFetch.onloadend = function() { + rtxvshader_txt = rtx_VFetch.responseText; +}; +rtx_VFetch.send(); +let rtx_FFetch = new XMLHttpRequest(); +rtx_FFetch.open('GET', './RTX.frag'); +rtx_FFetch.onloadend = function () { + let rtxfs_text = rtx_FFetch.responseText; + + let interval = setInterval(()=>{ + if(buildShaders && rtxvshader_txt && canvas1.gl) + { + gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text); + clearInterval(interval); + } + }, 1); +} +rtx_FFetch.send() +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + //console.log(P); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else { + if(clicked || running<=0) + { + direction_l = [P[0] - delta_l[0]/10, P[1] - delta_l[1]/10]; + if(P[1] < 0.00000000000001) + P[1] = 0.0000000000000001; + figure_rot = atan(direction_l[0]/direction_l[1]); + if(direction_l[1] < 0){ + figure_rot = pi + figure_rot; + } + } + //if(figure_rot < 0) figure_rot += pi; + //updateStatus([figure_rot, direction_l[0]/direction_l[1]]); + if(clicked) + { + dir_sdl = vec_len(direction_l)*10; + direction_l = normalize(direction_l); + rebuild = true; + running = 1; + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); diff --git a/hw8/fs/lib8.js b/hw8/fs/lib8.js new file mode 100644 index 0000000..b40f40f --- /dev/null +++ b/hw8/fs/lib8.js @@ -0,0 +1,292 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }' +].join('\n'); +var ns = 5, + cns = 5; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = 'The TV is a touch screen!'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + for (let i = 0; i < ns; ++i) { + loadTexture( gl, './' + (i + 1) + '.jpg', i); //Texture loading. + textures[i] = i; + } + textures[ns] = ns; + initVideoTexture(gl, ns + 0); + textures[ns + 1] = ns + 1; + loadTexture(gl, './RTXon.svg', ns + 1); + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); +} +function initVideoTexture(gl, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0., .0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} diff --git a/hw8/fs/paths.txt b/hw8/fs/paths.txt new file mode 100644 index 0000000..b331549 --- /dev/null +++ b/hw8/fs/paths.txt @@ -0,0 +1,104 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N \ No newline at end of file diff --git a/hw8/fs/pjsk.mp4 b/hw8/fs/pjsk.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/hw8/fs/pjsk.mp4 differ diff --git a/hw8/fs/shader.frag b/hw8/fs/shader.frag new file mode 100644 index 0000000..e073d1d --- /dev/null +++ b/hw8/fs/shader.frag @@ -0,0 +1,59 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +uniform sampler2D uSampler[ns + 1]; //ns + vs +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + float alp = 2.*abs(.49999 - fract(id+.00001)); + if(id < -2.5){gl_FragColor = vec4(texture2D(uSampler[ns + 0], (1.+texPos.xy)/2.).xyz, 1.); return;} //pjsk + else if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id <-.5) {color = vec3(0.,1.,0.2034);} + else if(id < 1.5) {color = vec3(1.0000, 0.3570, 0.3570);sp = 1.; df=.2; amb = .7, ex = 20.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) { + color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color; + if(texPos.y > .3) + color = vec3(1.,1.,1.); + } + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;} + else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;} + else if (id < 18.5) { + color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.; + vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(.8 * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alp); +} diff --git a/hw8/fs/shader.vert b/hw8/fs/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/hw8/fs/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/hw8/index.html b/hw8/index.html new file mode 100644 index 0000000..83f1a06 --- /dev/null +++ b/hw8/index.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + An RPG Game + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+ +
+ + What's new: +
    +
  • You can press three sphere-like buttons on the right of the TV to + switch between channels.
  • +
  • Bonus Point: special control 1: touch screen. When Ray Tracing Channel is selected you can 'touch' the + screen to select the spheres on the screen and drag to move it. +
  • +
  • Bonus Point: sliders and toggle button. You can walk to the witch to show 2 additional controls. One to + button show the stars. The slider will change the opacity of the body. +
  • +
  • Walk to the torus to 'eat' it.
  • +
  • Bonus Point: special control 2: clickable ground. You can walk by pressing the arrow keys on your keyboard, or click on + the ground where you want to go. +
  • +
  • Demo Video
  • +
  • Here you can browse the source of this assignment.
  • +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + diff --git a/hw8/lib8.ext.js b/hw8/lib8.ext.js new file mode 100644 index 0000000..61a501c --- /dev/null +++ b/hw8/lib8.ext.js @@ -0,0 +1,1283 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = true, + presentation = false, + sRotation = matrix_identity(); +var facing = 1, + running = 0; +var figure_rot = pi; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +let show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +let channel = 2, channel_disp = -1; +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function (_) { + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + delta_l = [0, 0]; + facing = 1; + rtx.src = './RTXoff.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 200) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + + if (!(mouselastX == undefined || mouselastY == undefined) && moving && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy / 60)); + sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx / 60)); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +rtx.style.cursor = "pointer"; +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + figure_rot = (pi*(facing/2)); + else figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + running = 20; + dir_sdl = 0; + keyhold = true; + } + else + running = running > 5? running:5; + rebuild = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => { + delete objects; + objects = []; +}; +let delta_height = 0, + delta_l = [0, 0]; +var direction_l = [0, 0], dir_sdl = 0; +class State { + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + } + initialize() { + this.leg = true; + this.progress = 0; + } + next() { + //return this.presentation(); + if (running <= 0) + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: 1.8522 / steps + }; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state) => { + if (running === 0) + rebuild = false; + let { + lh, + lf, + rh, + rf, + dh, + dl + } = state.next(); + if(dir_sdl > 0){ + delta_l = plus(delta_l, const_multiply(dl, direction_l)); + dir_sdl -= dl; + running = 1; + }else + delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + let sqlen = (a, b) => { return a*a + b*b; } + if(sqlen(delta_l[0] - 8, delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(delta_l[0] - 6, delta_l[1] - 6) < 5){ + donut_eaten = true; + pushStatus('Yum'); + } + delta_height = dh; + clearObject(); + M.save(); + + M.translate(0, 1, 0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.53 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.restore(); + if (running < 0) + rebuild = false; +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 20; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +let state = new State(); +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + // M.save(); + // M.rotateZ((uTime)); + // setM(M.value()); + // drawMesh(new Float32Array(star)); + // M.restore(); + + + // M.save(); + // M.scale(0.1); + // M.translate(6,0,1); + // M.rotateX(1); + // M.rotateY(uTime/4); + // setM(M.value()); + // drawMesh(torusMash,gl.LINES); + // M.restore(); + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore() +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if(channel_disp == 4 && i == 0){ + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + setM(m); + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + if(!shader1_inited){ + initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + drawMesh(obj[0]); + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; + } + else + { + setM(m); + //for(let i = 0; i < ns + 2; ++ i){ + // textures[i] = i; + // gl.activeTexture(gl.TEXTURE0 + i); + // gl.bindTexture(gl.TEXTURE_2D, texture[i]); + //} + //gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let tv_ctrl = new Button(()=>{channel = 2;}); +let tv_ctrl1 = new Button(()=>{channel = 3;}); +let tv_ctrl2 = new Button(()=>{channel = 4;}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Turn TV OFF.', 8); + setup_control(tv_ctrl1, 'Play a Video.', 9); + setup_control(tv_ctrl2, 'Show Ray Tracing.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +function animate(gl) { + buildsplines &&= build_splines() || setTVCtrl(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + gl.enable(gl.DEPTH_TEST); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(delta_l[0], -delta_height, delta_l[1]); + + M.rotateY(figure_rot); + overall_trans = M.value(); + M.restore(); + + + + M.save(); + + M.translate(0,-3.155,0); + M.rotateX(pi/2); + M.scale(10); + + ground_m = matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())); + setM(ground_m); + drawMesh(ground); + M.restore(); + + + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + M.save(); + M.translate(8, -.5, -6); + M.scale(3); + M.applyl(overall_ground); + M.rotateX(pi/2) + draw_magician(gl); + M.restore(); + draw_tv(gl); + // M.save(); + // M.translate(-.7, .7, 0); + // M.scale(.3); + // draw_deco(gl); + // M.restore(); + // setM(M.value()); + // test = new Float32Array([10, -1,-1,ttt,0,0,0,10,1,1,ttt,0,0,0]); + // drawMesh(test, gl.LINES); + if(channel != channel_disp){ + channel_disp = channel; + if(channel != 4) + { + for(let i = 0; i < tv[0][0].length; i+=7) + tv[0][0][i] = -channel; + rtx.src = './RTXoff.svg'; + } + else + rtx.src = './RTXon.svg'; + } + M.save(); + M.translate(5, 4, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl.draw(); + M.restore(); + M.save(); + M.translate(5, 2., -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl1.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl1.draw(); + M.restore(); + M.save(); + M.translate(5, 0, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl2.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl2.draw(); + M.restore(); + if(!slider) make_slider(); + if(near_magician) + draw_slider(); + if(show_magic) + drawstars(); + if(rebuild) + build_objects(state); + + M.save(); + if(donut_eaten){ + M.translate(0, -.4, 0); + M.rotateY(uTime / 5); + M.scale(0.23); + setM(matrix_multiply(sRotation,matrix_multiply(overall_trans, M.value()))); + } else { + M.translate(6, 2., 6); + M.rotateY(uTime / 10); + M.scale(0.4); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground, M.value()))); + } + drawMesh(torusMash); + M.restore(); + + for(const [obj, mat] of objects){ + let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)); + setM(m); + drawMesh(obj); + } + + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + +} + +requestAnimationFrame(fpscounter); + +pjsk.play(); +pjsk.addEventListener('timeupdate', ()=>{updateVideoTextures = true;}) diff --git a/hw8/lib8.header.js b/hw8/lib8.header.js new file mode 100644 index 0000000..c97aa92 --- /dev/null +++ b/hw8/lib8.header.js @@ -0,0 +1,786 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); +let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt; +rtx_VFetch.open('GET', './RTX.vert'); +rtx_VFetch.onloadend = function() { + rtxvshader_txt = rtx_VFetch.responseText; +}; +rtx_VFetch.send(); +let rtx_FFetch = new XMLHttpRequest(); +rtx_FFetch.open('GET', './RTX.frag'); +rtx_FFetch.onloadend = function () { + let rtxfs_text = rtx_FFetch.responseText; + + let interval = setInterval(()=>{ + if(buildShaders && rtxvshader_txt && canvas1.gl) + { + gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text); + clearInterval(interval); + } + }, 1); +} +rtx_FFetch.send() +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + //console.log(P); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else { + if(clicked || running<=0) + { + direction_l = [P[0] - delta_l[0]/10, P[1] - delta_l[1]/10]; + if(P[1] < 0.00000000000001) + P[1] = 0.0000000000000001; + figure_rot = atan(direction_l[0]/direction_l[1]); + if(direction_l[1] < 0){ + figure_rot = pi + figure_rot; + } + } + //if(figure_rot < 0) figure_rot += pi; + //updateStatus([figure_rot, direction_l[0]/direction_l[1]]); + if(clicked) + { + dir_sdl = vec_len(direction_l)*10; + direction_l = normalize(direction_l); + rebuild = true; + running = 1; + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); diff --git a/hw8/lib8.js b/hw8/lib8.js new file mode 100644 index 0000000..b40f40f --- /dev/null +++ b/hw8/lib8.js @@ -0,0 +1,292 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }' +].join('\n'); +var ns = 5, + cns = 5; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = 'The TV is a touch screen!'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + for (let i = 0; i < ns; ++i) { + loadTexture( gl, './' + (i + 1) + '.jpg', i); //Texture loading. + textures[i] = i; + } + textures[ns] = ns; + initVideoTexture(gl, ns + 0); + textures[ns + 1] = ns + 1; + loadTexture(gl, './RTXon.svg', ns + 1); + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); +} +function initVideoTexture(gl, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0., .0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} diff --git a/hw8/paths.txt b/hw8/paths.txt new file mode 100644 index 0000000..b331549 --- /dev/null +++ b/hw8/paths.txt @@ -0,0 +1,104 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N \ No newline at end of file diff --git a/hw8/pjsk.mp4 b/hw8/pjsk.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/hw8/pjsk.mp4 differ diff --git a/hw8/shader.frag b/hw8/shader.frag new file mode 100644 index 0000000..e073d1d --- /dev/null +++ b/hw8/shader.frag @@ -0,0 +1,59 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +uniform sampler2D uSampler[ns + 1]; //ns + vs +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + float alp = 2.*abs(.49999 - fract(id+.00001)); + if(id < -2.5){gl_FragColor = vec4(texture2D(uSampler[ns + 0], (1.+texPos.xy)/2.).xyz, 1.); return;} //pjsk + else if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id <-.5) {color = vec3(0.,1.,0.2034);} + else if(id < 1.5) {color = vec3(1.0000, 0.3570, 0.3570);sp = 1.; df=.2; amb = .7, ex = 20.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) { + color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color; + if(texPos.y > .3) + color = vec3(1.,1.,1.); + } + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;} + else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;} + else if (id < 18.5) { + color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.; + vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(.8 * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alp); +} diff --git a/hw8/shader.vert b/hw8/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/hw8/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/hw9/.DS_Store b/hw9/.DS_Store new file mode 100644 index 0000000..d2c8883 Binary files /dev/null and b/hw9/.DS_Store differ diff --git a/hw9/.vscode/launch.json b/hw9/.vscode/launch.json new file mode 100644 index 0000000..a8c9784 --- /dev/null +++ b/hw9/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch index.html", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + }, + { + "name": "windows", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "C:/Users/sunyi/AppData/Local/Google/Chrome SxS/Application/chrome.exe", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + } + ] +} \ No newline at end of file diff --git a/hw9/.vscode/settings.json b/hw9/.vscode/settings.json new file mode 100644 index 0000000..7a05c85 --- /dev/null +++ b/hw9/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.pythonPath": "/usr/local/bin/python", + "svg.preview.background": "black" +} \ No newline at end of file diff --git a/hw9/1.jpg b/hw9/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/hw9/1.jpg differ diff --git a/hw9/2.jpg b/hw9/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/hw9/2.jpg differ diff --git a/hw9/3.jpg b/hw9/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/hw9/3.jpg differ diff --git a/hw9/4.jpg b/hw9/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/hw9/4.jpg differ diff --git a/hw9/5.jpg b/hw9/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/hw9/5.jpg differ diff --git a/hw9/RTX.frag b/hw9/RTX.frag new file mode 100644 index 0000000..f4ae5fb --- /dev/null +++ b/hw9/RTX.frag @@ -0,0 +1,237 @@ +#ifndef _NDEBUG + precision highp float; + const int ns = 5; + const int cns = 5; + float noise(vec3 v){return 1.;} +#endif + +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns+3]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying float id; +varying vec3 norm; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=15; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + bool selected = false; + float currentK = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + float t = tMin; + vec3 S=V+t*W; + for(int i = 0; i < cns; ++ i) + if(i == iMin){ + vec3 tex_sph = (S-Sph[i].xyz); + color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*color; + if(final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if( final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor.xyz),1.); +} diff --git a/hw9/RTX.vert b/hw9/RTX.vert new file mode 100644 index 0000000..8ae6412 --- /dev/null +++ b/hw9/RTX.vert @@ -0,0 +1,24 @@ +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying vec3 trPos; +uniform mat3 transformation; +uniform mat4 uMatrix; +varying float id; +varying vec3 norm; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos; + id = oid; + norm = normal; + trPos = transformation *vec3(aPos.x, -aPos.y, -1); +} \ No newline at end of file diff --git a/hw9/RTXoff.svg b/hw9/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/hw9/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw9/RTXon.svg b/hw9/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/hw9/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw9/animation.js b/hw9/animation.js new file mode 100644 index 0000000..2c4d2a4 --- /dev/null +++ b/hw9/animation.js @@ -0,0 +1,301 @@ +let lerp = (a,b,t) => a + t * (b - a); // LINEAR INTERPOLATION + +let N = 0; // GREATER N INCREASES "NERVOUSNESS" OF BIG OBJECT + +// TIMING TABLE FOR THE ANIMATION SEQUENCE + +let timing = [ + [0, lerp(1,.5,N)], + [1.8, lerp(2.3,2.1,N)], + [lerp(2,2.5,N), 2.8], + [2.0, 3.2], + [3.4, 3.9], + [4.3, 5.0], +]; + +// EASE CURVE TO ACCELERATE FROM REST AND THEN DECELERATE TO REST + +let sCurve = t => (3 - 2 * t) * t * t; +let jCurve = t => t*t*t*t; +let jCurve3 = t => t*t*t; +// EVALUATE THE TIMING OF ONE ANIMATION PARAMETER FOR THIS FRAME + +let evalTiming = n => { + let t0 = timing[n][0]; + let t1 = timing[n][1]; + + if (animationTime < t0) + return 0; + if (animationTime > t1) + return 1; + return sCurve((animationTime - t0) / (t1 - t0)); +} + + +let bounce = t => Math.sin(Math.PI * t); +let wiggle = t => Math.sin(6 * Math.PI * t); + +let trees = [] + +function add_tree(pos) { + trees.append(new Float32Array(pos)) + createMesh(32,32,uvToCone,-1, 20); +} + +class State { + constructor(idx = 0, step = 1) { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + this.running = 0; + this.direction_l = [0, 0]; + this.dir_sdl = 0; + this.delta_l = [0, 0]; + this.wiggle_t = 0; + this.punch_t = 1; + this.idx = idx; + if(rebuild[idx] === undefined) + rebuild.push(true); + states[this.idx] = this; + this.fig_rot_t = 0; + this.figure_rot = pi; + this.old_figure_rot = pi; + this.curr_figure_rot = pi; + this.target_figure_rot = pi; + this.delta_height = 0; + this.delta = const_multiply(step, [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]); + this.stepSize = step; + this.turnStep = .1; + this.stepLength = this.stepSize*(1.8522); + this.life = 3; + this.damage = .05; + this.dead = false; + this.death_t = 0; + this.hitID = 19; + } + reset(){ + this.running = 0; + this.leg = true; + this.progress = 0; + this.delta_height = 0; + this.dir_sdl = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + this.dead = false; + this.wiggle_t = 0; + this.death_t = 0; + } + initialize() { + this.figure_rot = pi; + this.old_figure_rot = pi; + this.curr_figure_rot = pi; + this.target_figure_rot = pi; + this.reset(); + this.wiggle_t = 0; + this.punch_t = 1; + this.fig_rot_t = 0; + this.direction_l = [0, 0]; + this.delta_l = [0, 0]; + this.life = 3; + this.delta_l = [0,0]; + this.direction_l = [0, 0]; + this.dir_sdl = 0; + } + set_stepSize(step){ + this.delta = const_multiply(step, [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]); + this.stepSize = step; + this.stepLength = this.stepSize*(1.8522); + } + hitTest(punchProg){ + states.forEach((st, i)=>{ + if(i != this.idx){ + //1.15=.5*.23*10; .92 = .4*.23*10 .23=figure_scale, 10=overall_scale + const armlength = 1.15*cos(punchProg*pi/4)+.92*cos(-.53*pi*punchProg) + let dir = normalize(this.direction_l); + let punchpos = plus(plus(this.delta_l, + const_multiply(.3, [dir[1],-dir[0]])), + const_multiply(armlength, dir)); + if(vec_len(minus(punchpos, st.delta_l)) < .65){ + st.hit(this); + } + } + }); + } + start_punch(){ + if(this.punch_t >= 1){ + this.punch_t = 0; + rebuild[this.idx] = true; + } + } + punch(){ + if(this.punch_t < 1) + { + rebuild[this.idx] = true; + this.punch_t+=.05; + if(this.punch_t <= .05) + return 1.2; + else + { + let punchProg; + if(this.punch_t <= .4) + punchProg = 1.2*(1-jCurve((this.punch_t- .05)/.35)); + else + punchProg = 1-jCurve(2-2*(this.punch_t+.1)/1.1); + if(this.hitTest(punchProg)) + this.punch_t = 1; + return punchProg; + } + } + return 1; + } + restoreID(st){ + objects[st.idx].forEach( + (obj, i)=>{ + changeID(st.orig_objid[i], obj[0]); + } + ) + } + defaultHit(){ + if(objects[this.idx][0][0][0] != this.hitID){ + this.orig_objid = []; + objects[this.idx].forEach( + (obj, i) => { + this.orig_objid[i] = (obj[0])[0]; + } + ) + objects[this.idx].forEach( + (obj, i) => { + changeID(this.hitID, obj[0]); + } + ) + setTimeout(this.restoreID, 100, this); + } + if(this.dead&&this.wiggle_t<=0){ + rebuild[this.idx] = true; + this.wiggle_t = 2.5; + } + else if(this.life > 0) + { + this.life -= this.damage; + } + else { + this.death(); + } + } + defaultDeath(){ + this.dead = true; + this.death_t = 1; + rebuild[this.idx] = true; + } + hit(hitter){this.defaultHit(hitter);} + death() {if(!this.dead){this.defaultDeath();}} + animated_turn(){ + if(this.target_figure_rot != this.figure_rot) + { + this.fig_rot_t = 1; + this.figure_rot %= 2*pi; + if(this.figure_rot < 0) this.figure_rot += 2*pi; + this.curr_figure_rot %= 2*pi; + if(this.curr_figure_rot < 0) this.curr_figure_rot += 2*pi; + if((this.curr_figure_rot - this.figure_rot) > pi) + this.curr_figure_rot -= 2*pi; + else if (this.curr_figure_rot - this.figure_rot < -pi) + this.figure_rot -= 2*pi; + this.old_figure_rot = this.curr_figure_rot; + this.target_figure_rot = this.figure_rot; + } + if(this.fig_rot_t > 0) + { + this.fig_rot_t-=this.turnStep; + this.curr_figure_rot = lerp( + this.old_figure_rot, this.figure_rot, sCurve(1-this.fig_rot_t)); + } + } + turn(pt, animated = true){ + this.direction_l = [pt[0] - this.delta_l[0]/10, pt[1] - this.delta_l[1]/10]; + if(this.direction_l[1] == 0) + this.direction_l[1] = 0.0000000000000001; + this.figure_rot = atan(this.direction_l[0]/this.direction_l[1]); + if(this.direction_l[1] < 0) + this.figure_rot = pi + this.figure_rot; + if(!animated) + { + this.curr_figure_rot = this.target_figure_rot = this.figure_rot; + this.fig_rot_t = 0; + } + } + walk(){ + this.dir_sdl = vec_len(this.direction_l)*10; + this.direction_l = normalize(this.direction_l); + rebuild[this.idx] = true; + this.running = 1; + } + next() { + //return this.presentation(); + if (this.running <= 0) + { + this.reset(); + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + } + this.running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = deepcopy(this.delta); + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: this.stepLength / steps + }; + } + presentation(){ + return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0, dl: 0}; + } + }; + +function update_tree(idx){ + //scale, add leaves, add branch + +} \ No newline at end of file diff --git a/hw9/index.html b/hw9/index.html new file mode 100644 index 0000000..3979651 --- /dev/null +++ b/hw9/index.html @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + A Boxing Game + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+ +
+ + What's new: +
    +
  • You can press three sphere-like buttons on the right of the TV to + choose difficulty levels.
  • +
  • Press the second button beside the TV to restart the game. +
  • +
  • Eating the torus will grant you additional life. +
  • +
  • Your bar is on the top left.
  • +
  • Press F to fight.
  • +
  • You can walk by pressing the arrow keys on your keyboard, or click on + the ground where you want to go. +
  • +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + \ No newline at end of file diff --git a/hw9/lib9.ext.js b/hw9/lib9.ext.js new file mode 100644 index 0000000..79dd424 --- /dev/null +++ b/hw9/lib9.ext.js @@ -0,0 +1,1315 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +let Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = [true, true], + presentation = false, + sRotation = matrix_identity(); +var state = new State(0), state1 = new State(1); +state1.death = (hitter)=>{if(!state1.dead){state1.defaultDeath();channel=5;show_magic = true;}}; +state.death = (hitter)=>{if(!state.dead){state.defaultDeath();channel=6;}}; +state1.hitID = 9; +var facing = 1; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +var show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +var Boxing = true; +var gamestart = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +var channel = 3, channel_disp = -1; +let difficulty_levels = { + hard: { + flex : .1, + freq : 25, + speed: .8, + th : 2., + startTimer : 15, + punchSpeed: 10, + original : 0 + }, + medium: { + flex : .07, + freq : 200, + speed: .4, + th : 1.8, + startTimer : 5, + punchSpeed: 5, + original : 1 + }, + easy: { + flex : .03, + freq : 400, + speed: .2, + th : 1.5, + startTimer : 4, + punchSpeed: 8, + original: 2 + } +}, dl_orig = [difficulty_levels.hard, difficulty_levels.medium, difficulty_levels.easy]; +var difficulty = deepcopy(difficulty_levels.medium); +state1.set_stepSize(difficulty.speed); +state1.turnStep = difficulty.flex; +state1.delta_l = [4, -5]; +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +} +reset.onclick = function (_) { + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + gamestart = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + state.delta_l = [0, 0]; + facing = 1; + rtx.src = './RTXoff.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 200) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + + if (!(mouselastX == undefined || mouselastY == undefined) && moving && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy / 60)); + sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx / 60)); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +rtx.style.cursor = "pointer"; +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + state.figure_rot = (pi*(facing/2)); + else state.figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + state.running = 20; + state.dir_sdl = 0; + keyhold = true; + } + else + state.running = state.running > 5? state.running:5; + rebuild[0] = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } + if(e.code == 'KeyF'&&Boxing){ + state.start_punch(); + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat, i) => { + objects[i].push([obj, mat]); +}; +let clearObject = (i) => { + delete objects[i]; + objects[i] = []; +}; + + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state, i) => { + if(state.head === undefined) + { + state.head = head.slice(); + state.cylinderMesh = cylinderMesh.slice(); + state.cylinderMesh2 = cylinderMesh2.slice(); + state.cylinderMesh3 = cylinderMesh3.slice(); + if(state.idx == 0) + { + changeID(12, state.head); + changeID(6, state.cylinderMesh); + changeID(8, state.cylinderMesh2); + changeID(10, state.cylinderMesh3); + } + } + let lh,lf,rh,rf,dh,dl, head_rot = 0; + if(!state.dead){ + if (state.running === 0) + rebuild[i] = false; + res = state.next(); + lh=res.lh;lf=res.lf;rh=res.rh;rf=res.rf;dh=res.dh;dl=res.dl; + if(state.dir_sdl > 0){ + state.delta_l = plus(state.delta_l, const_multiply(dl, state.direction_l)); + state.dir_sdl -= dl; + state.running = 1; + }else + state.delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + if(state.idx === 0) + if(sqlen(state.delta_l[0] - 8, state.delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(state.delta_l[0] - 6, state.delta_l[1] - 6) < 5){ + donut_eaten = true; + state.life += 1; + pushStatus('Yum'); + } + state.delta_height = dh; + } else { + rh = lh = pi/2; + lf = rf = 0; + if(state.wiggle_t > 0){ + state.running = 1; + state.wiggle_t -= .025; + if(state.wiggle_t > 2) + head_rot = wiggle(state.wiggle_t-2); + } + } + clearObject(i); + const punch = state.dead?1:state.punch(); + let overall_rot = 0, overall_death_trans = [0,0]; + if(state.dead && state.death_t > 0){ + state.death_t -= .025; + const t = jCurve(1-state.death_t); + overall_rot = -t*pi/2; + overall_death_trans[0] = 2.3*sin(overall_rot); + overall_death_trans[1] = -2.3*(1-cos(overall_rot)); + rebuild[state.idx] = true; + }else if (state.dead){ + overall_rot = -pi/2; + overall_death_trans[0] = -2.3; + overall_death_trans[1] = -2.3; + } + M.save(); + M.translate(0, overall_death_trans[0], overall_death_trans[1]); + M.rotateX(overall_rot); + M.save(); + M.translate(0, 1, 0); + M.rotateY(head_rot); + addObject(state.head, M.value(),i); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX((pi / 4)*punch); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX((-0.53 * pi)*punch); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh2, M.value(),i); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(state.cylinderMesh3, M.value(),i); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(state.cylinderMesh, M.value(),i); + M.restore(); + + M.restore(); + if (state.running < 0) + rebuild[i] = false +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 10; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + for(let i = 0; i < 7; ++ i){ + sakura[i] = new Float32Array(thicken_contour(.8, sakura[i])); + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + if(state.life >=.5) + { + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + setM(M.value()); + for(let l = 0; l < 5; ++l) + drawMesh(sakura[l],gl.TRIANGLE_STRIP); + M.restore(); + } + if(state.life >= 1.5 ) + { + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + setM(M.value()); + for(let l = 0; l < 5; ++l) + drawMesh(sakura[l],gl.TRIANGLE_STRIP); + M.restore(); + } + if (state.life > 2.5) + { + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 0; l < 5; ++l) + drawMesh(new Float32Array(sakura[l]),gl.TRIANGLE_STRIP); + M.restore(); + } +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if(channel_disp == 4 && i == 0){ + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + setM(m); + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3, + .1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .3,1.,1.3, + .1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5, + .1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1., + .0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + if(!shader1_inited){ + initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + drawMesh(obj[0]); + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; + } + else + { + setM(m); + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let initialize=(lv) => { + difficulty = deepcopy(lv); + state1.set_stepSize(difficulty.speed); + state1.turnStep = difficulty.flex; + state1.delta_l = [4, -5]; + channel = 3;state.initialize();state1.initialize();reset.onclick();state1.delta_l = [4,-5]; +}; +let tv_ctrl = new Button(()=>{initialize(difficulty_levels.hard);}); +let tv_ctrl1 = new Button(()=>{initialize(difficulty_levels.medium);}); +let tv_ctrl2 = new Button(()=>{initialize(difficulty_levels.easy);}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Difficulty Level: Hard.', 8); + setup_control(tv_ctrl1, 'Difficulty Level: Medium.', 9); + setup_control(tv_ctrl2, 'Difficulty Level: Easy.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +function animate(gl) { + buildsplines &&= build_splines() || setTVCtrl(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + gl.enable(gl.DEPTH_TEST); + + states.forEach((s)=>s.animated_turn()); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(state.delta_l[0], -state.delta_height, state.delta_l[1]); + M.rotateY(state.curr_figure_rot); + overall_trans = M.value(); + M.restore(); + + + M.save(); + + M.translate(0,-3.155,0); + M.rotateX(pi/2); + M.scale(10); + + ground_m = matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())); + setM(ground_m); + drawMesh(ground); + M.restore(); + + + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + M.save(); + M.translate(8, -.5, -6); + M.scale(3); + M.applyl(overall_ground); + M.rotateX(pi/2) + draw_magician(gl); + M.restore(); + draw_tv(gl); + M.save(); + M.translate(-.7, .7, 0); + M.scale(.3); + draw_deco(gl); + M.restore(); + //setM(M.value()); + // test = new Float32Array([10, -1,-1,ttt,0,0,0,10,1,1,ttt,0,0,0]); + // drawMesh(test, gl.LINES); + if(channel != channel_disp){ + channel_disp = channel; + if(channel != 4) + { + for(let i = 0; i < tv[0][0].length; i+=7) + tv[0][0][i] = -channel; + rtx.src = './RTXoff.svg'; + } + else + rtx.src = './RTXon.svg'; + } + M.save(); + M.translate(5, 4, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl.draw(); + M.restore(); + M.save(); + M.translate(5, 2., -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl1.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl1.draw(); + M.restore(); + M.save(); + M.translate(5, 0, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl2.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl2.draw(); + M.restore(); + if(!slider) make_slider(); + if(near_magician) + draw_slider(); + if(show_magic) + drawstars(); + if(rebuild[0]) + build_objects(state, 0); + if(rebuild[1]) + build_objects(state1, 1); + M.save(); + if(donut_eaten){ + M.translate(0, -.4, 0); + M.rotateY(uTime / 5); + M.scale(0.23); + setM(matrix_multiply(sRotation,matrix_multiply(overall_trans, M.value()))); + } else { + M.translate(6, 2., 6); + M.rotateY(uTime / 10); + M.scale(0.4); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground, M.value()))); + } + drawMesh(torusMash); + M.restore(); + objects.forEach( (objs, i) => { + trans = i == 0? overall_trans:overall_ground; + for(const [obj, mat] of objs){ + M.save(); + //if( i != 0 ){ + M.translate(states[i].delta_l[0], -states[i].delta_height, states[i].delta_l[1]); + M.rotateY(states[i].curr_figure_rot); + //} + let m = matrix_multiply(sRotation,matrix_multiply(overall_ground, matrix_multiply(M.value(), mat))); + setM(m); + M.restore(); + drawMesh(obj); + } + } + ); + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + +} + +requestAnimationFrame(fpscounter); +let ai_callback = ()=>{ + if(gamestart) + { + if(!state1.dead && difficulty.startTimer <= 0) + { + let dir = minus(state.delta_l,state1.delta_l); + let len = vec_len(dir); + state1.turn(const_multiply(.1,state.delta_l), true); + if(len < 1.6){ + if(state1.cd == undefined) state1.cd = difficulty.punchSpeed; + if(state1.cd <= 0) + { + state1.cd = difficulty.punchSpeed; + state1.start_punch(); + state1.running = 0; + } + else state1.cd -= 1; + } + if(len>1.3){ + state1.walk(); + } + } else difficulty.startTimer --; + } + else if (state.delta_l[0] !=0 || state.delta_l[1] != 0) + gamestart = true; + setTimeout(ai_callback, difficulty.freq); +} +ai_callback(); + +pjsk.play(); +pjsk.addEventListener('timeupdate', ()=>{updateVideoTextures = true;}) \ No newline at end of file diff --git a/hw9/lib9.header.js b/hw9/lib9.header.js new file mode 100644 index 0000000..29386ba --- /dev/null +++ b/hw9/lib9.header.js @@ -0,0 +1,826 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +var states = []; +function deepcopy(obj) { + return JSON.parse(JSON.stringify(obj)); +} +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); +let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt; +rtx_VFetch.open('GET', './RTX.vert'); +rtx_VFetch.onloadend = function() { + rtxvshader_txt = rtx_VFetch.responseText; +}; +rtx_VFetch.send(); +let rtx_FFetch = new XMLHttpRequest(); +rtx_FFetch.open('GET', './RTX.frag'); +rtx_FFetch.onloadend = function () { + let rtxfs_text = rtx_FFetch.responseText; + + let interval = setInterval(()=>{ + if(buildShaders && rtxvshader_txt && canvas1.gl) + { + gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text); + clearInterval(interval); + } + }, 1); +} +rtx_FFetch.send() +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + +let uvToCone = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta)*(1-v); + let y = Math.sin(theta)*(1-v); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + console.log(P); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else if (!state.dead){ + if(clicked || state.running<=0) + { + state.turn(P, clicked); + } + //if(state.figure_rot < 0) state.figure_rot += pi; + //updateStatus([state.figure_rot, state.direction_l[0]/state.direction_l[1]]); + if(clicked) + { + state.walk(); + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); + +let transform_mesh = (m,arr) => { + for(let i = 0; i < arr.length; i+=7){ + const mod = dot_xyz( + matrix_multiply(m, [arr[i+1], arr[i+2], arr[i+3], 1])); + const mod_nor = normalize(dot_xyz( + matrix_multiply(m, [arr[i+4], arr[i+5], arr[i+6], 0]))); + arr[i+1] = mod[0]; + arr[i+2] = mod[1]; + arr[i+3] = mod[2]; + arr[i+4] = mod_nor[0]; + arr[i+5] = mod_nor[1]; + arr[i+6] = mod_nor[2]; + } +} + +let thicken_contour = (thickness, contour) =>{ + let res = []; + let inner_contour = contour.slice(); + transform_mesh(matrix_scale(thickness), inner_contour); + for(let i = 0; i < contour.length - 7; i+=7){ + for (let j = 0; j < 7; ++ j) + res.push(contour[i + j]); + res.push(contour[0]); + for (let j = 1; j < 7; ++ j) + res.push((inner_contour[i + j]+inner_contour[i+7+j])/2); + } + for (let j = 0; j < 7; ++ j) + res.push(contour[contour.length - 7 + j]); + res.push(contour[0]); + for (let j = 1; j < 7; ++ j) + res.push((inner_contour[contour.length - 7 + j]+inner_contour[j])/2); + for (let j = 0; j < 7; ++ j) + res.push(contour[j]); + return res; +} + +let sqlen = (a, b) => { return a*a + b*b; } diff --git a/hw9/lib9.js b/hw9/lib9.js new file mode 100644 index 0000000..5e44af4 --- /dev/null +++ b/hw9/lib9.js @@ -0,0 +1,298 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', '#define _NDEBUG\n','float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }' +].join('\n'); +var ns = 5, + cns = 5; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = 'Move your character to start!'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + for (let i = 0; i < ns; ++i) { + loadTexture( gl, './' + (i + 1) + '.jpg', i); //Texture loading. + textures[i] = i; + } + textures[ns] = ns; + initVideoTexture(gl, ns + 0); + textures[ns + 1] = ns + 1; + loadTexture(gl, './RTXon.svg', ns + 1); + textures[ns+2] = ns+2; + loadTexture(gl, './wwe.svg', ns + 2); + textures[ns+3] = ns+3; + loadTexture(gl, './win.svg', ns + 3); + textures[ns+4] = ns+4; + loadTexture(gl, './lose.svg', ns + 4); + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); +} +function initVideoTexture(gl, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0., .0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} \ No newline at end of file diff --git a/hw9/lose.svg b/hw9/lose.svg new file mode 100644 index 0000000..30d5110 --- /dev/null +++ b/hw9/lose.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw9/paths.txt b/hw9/paths.txt new file mode 100644 index 0000000..b331549 --- /dev/null +++ b/hw9/paths.txt @@ -0,0 +1,104 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N \ No newline at end of file diff --git a/hw9/pjsk.mp4 b/hw9/pjsk.mp4 new file mode 100644 index 0000000..ae68718 Binary files /dev/null and b/hw9/pjsk.mp4 differ diff --git a/hw9/pjsk_.mp4 b/hw9/pjsk_.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/hw9/pjsk_.mp4 differ diff --git a/hw9/shader.frag b/hw9/shader.frag new file mode 100644 index 0000000..9142811 --- /dev/null +++ b/hw9/shader.frag @@ -0,0 +1,68 @@ + +#ifndef _NDEBUG + precision highp float; + const int ns = 5; + const int cns = 5; + float noise(vec3 v){return 1.;} +#endif +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +uniform sampler2D uSampler[ns + 5]; //ns + vs +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + float alp = 2.*abs(.49999 - fract(id+.00001)); + if(id < -5.5){gl_FragColor = vec4(texture2D(uSampler[ns + 4], (1.+texPos.xy*2.5)/2.).xyz, 1.); return;} //pjsk + else if(id < -4.5){gl_FragColor = vec4(texture2D(uSampler[ns + 3], (1.+texPos.xy*2.5)/2.).xyz, 1.); return;} //pjsk + else if(id < -2.5){gl_FragColor = vec4(texture2D(uSampler[ns + 2], (1.+texPos.xy*2.5)/2.).xyz, 1.); return;} //pjsk + else if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id <-.5) {color = vec3(0.,1.,0.2034);} + else if(id < 1.5) {color = vec3(1.0000, 0.3570, 0.3570);sp = 1.; df=.2; amb = .7, ex = 20.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) { + color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color; + if(texPos.y > .3) + color = vec3(1.,1.,1.); + } + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;} + else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;} + else if (id < 18.5) { + color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.; + vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(.8 * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + else if (id < 19.5) {color = vec3(.4,.4,.4);sp = .0; df=.0; amb = 1., ex = 1.,alp=1.;} + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alp); +} diff --git a/hw9/shader.vert b/hw9/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/hw9/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/hw9/win.svg b/hw9/win.svg new file mode 100644 index 0000000..625ad9b --- /dev/null +++ b/hw9/win.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw9/wwe.svg b/hw9/wwe.svg new file mode 100644 index 0000000..a116eae --- /dev/null +++ b/hw9/wwe.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw9/wwe_logo.min.svg b/hw9/wwe_logo.min.svg new file mode 100644 index 0000000..c6b8738 --- /dev/null +++ b/hw9/wwe_logo.min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..f69c586 --- /dev/null +++ b/index.html @@ -0,0 +1,25 @@ + + + + + Bill Sun - Graphics + + + + + +

Computer Graphics Homework Directory

+ diff --git a/proj/.DS_Store b/proj/.DS_Store new file mode 100644 index 0000000..d2c8883 Binary files /dev/null and b/proj/.DS_Store differ diff --git a/proj/.vscode/launch.json b/proj/.vscode/launch.json new file mode 100644 index 0000000..a8c9784 --- /dev/null +++ b/proj/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch index.html", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + }, + { + "name": "windows", + "type": "chrome", + "request": "launch", + "file": "${workspaceFolder}/index.html", + "runtimeExecutable": "C:/Users/sunyi/AppData/Local/Google/Chrome SxS/Application/chrome.exe", + "runtimeArgs": ["--args", "--allow-file-access-from-files"] + } + ] +} \ No newline at end of file diff --git a/proj/.vscode/settings.json b/proj/.vscode/settings.json new file mode 100644 index 0000000..8ad19d5 --- /dev/null +++ b/proj/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/usr/local/bin/python" +} \ No newline at end of file diff --git a/proj/1.jpg b/proj/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/proj/1.jpg differ diff --git a/proj/2.jpg b/proj/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/proj/2.jpg differ diff --git a/proj/3.jpg b/proj/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/proj/3.jpg differ diff --git a/proj/4.jpg b/proj/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/proj/4.jpg differ diff --git a/proj/5.jpg b/proj/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/proj/5.jpg differ diff --git a/proj/6.jpg b/proj/6.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/proj/6.jpg differ diff --git a/proj/RTX.frag b/proj/RTX.frag new file mode 100644 index 0000000..132bdff --- /dev/null +++ b/proj/RTX.frag @@ -0,0 +1,352 @@ + +#ifndef _NDEBUG + precision highp float; + const int ns = 5; + const int cns = 5; + float noise(vec3 v){return 1.;} +#endif +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform vec3 SphCanvas[ns]; +uniform vec3 Glow[ns]; +uniform sampler2D uSampler[ns+3]; +uniform vec3 V0; +uniform bool glassy; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying float id; +varying vec3 norm; +varying vec3 texPos; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK, decay; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +#define clamp1(x) ((x)<0.?0.:((x) > 1. ? 1.:(x))) +vec3 vec_clamp01(vec3 x) { + return vec3(clamp1(x.x), clamp1(x.y), clamp1(x.z)); +} +void main(){ +// gl_FragColor=vec4(_glassy,0,0,1); return; + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + float glassyw = 1.; +// bool glassy = _glassy > .1; + if(glassy) + { + vec2 noiseTex = (texPos.xy + 1.)/2.; + noiseTex = vec2(clamp1(noiseTex.x ), clamp1(noiseTex.y )); + vec4 Wnoise = texture2D(uSampler[ns+2], noiseTex); + W += (Wnoise.xyz-.5)*.4; + glassyw = Wnoise.w; + } + bool selected = false; + float currentK = 1.; + float curr_decay = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + curr_decay = currR.decay; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + vec3 S=V+tMin*W; + if(curr_decay > .01) + curr_decay = curr_decay/(1.+tMin*tMin); + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + if(j == 0) + { + float intensity = sqrt(dot(Glow[i], Glow[i])); + if(intensity > 1.7) + { + gl_FragColor = vec4(1.74*normalize(Glow[i]), 1.); + if(selected) + gl_FragColor = vec4(normalize(Glow[i])+vec3(.5,0.,0.), 1.); + return; + } + } + + vec3 texture_color; + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + for(int k = 0; k < cns; ++k){ + if(Glow[k].x > .01){ + vec3 lDir = Sph[k].xyz - S; + float dist = length(lDir); + lDir /= (dist); + dist -= Sph[k].w; + color += (Glow[k]/(1.+dist*dist))*Diffuse[i]* + max(0.,dot(N,lDir)); + } + } + if(final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + if(curr_decay > .01 && Glow[i].x > .01) + { + vec3 glow_color = currentK*curr_decay*Glow[i]; + float l = length(glow_color); + if(l >=1.7) + glow_color/=l/1.7; + scolor += glow_color; + } + + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks, curr_decay); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks, curr_decay); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + if(curr_decay > .01 && Glow[i].x > .01) + { + vec3 glow_color = currentK*curr_decay*Glow[i]; + float l = length(glow_color); + if(l >=1.7) + glow_color/=l/1.7; + scolor += glow_color; + } + + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + if(curr_decay > .01) + curr_decay = curr_decay/(1.+t*t); + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[ns], vec2((sx+1.5)/3., (sz+3.)/6.)).xyz; + if(final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + for(int k = 0; k < cns; ++k){ + if(Glow[k].x > .01){ + vec3 lDir = Sph[k].xyz - S; + float dist = length(lDir); + lDir /= (dist); + dist -= Sph[k].w; + color += (Glow[k]/(1.+dist*dist))* //specular for ground. + pow(max(0., dot(vec3(-lDir.x, lDir.y,-lDir.z),-W)),groundSpecular.w); + } + } + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15, curr_decay); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15, curr_decay); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + { + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + for(int k = 0; k < cns; ++k){ + if(Glow[k].x > .01){ + vec3 lDir = Sph[k].xyz - V; + float dist = length(lDir); + lDir /= (dist); + dist -= Sph[k].w; + color += (Glow[k]/(1.+dist*dist))* //specular for ground. + pow(max(0., dot(vec3(-lDir.x, lDir.y,-lDir.z),W)),groundSpecular.w); + } + } + } + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + + + if(glassy) + scolor *= glassyw*glassyw; + for(int i = 0; i < ns; ++i){ + if(i == sel) + continue; + vec2 sx = SphCanvas[i].xy - texPos.xy; + float theta = atan(sx.y/sx.x); + if(sx.x > 0. && theta < 0.) + theta += _2pi; + else if(sx.x < 0.) + theta += pi; + float intensity = sqrt(dot(Glow[i], Glow[i])); + vec3 realD = Sph[i].xyz - V0; + float realDist = sqrt(dot(realD, realD))-Sph[i].w; + intensity/=(1.+realDist*realDist); + float dist = dot(sx, sx) - SphCanvas[i].z*SphCanvas[i].z; + intensity *= .5; + if(dist < intensity && dist > 0.) + { + vec3 flare = texture2D(uSampler[ns+1], vec2(0.1+0.9*(dist/intensity), theta/float(2*i+7))).xyz; + scolor += flare*flare*Glow[i]/(1.+realDist*realDist); + } + } + scolor = vec_clamp01(scolor); + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/proj/RTX.vert b/proj/RTX.vert new file mode 100644 index 0000000..e00d0a3 --- /dev/null +++ b/proj/RTX.vert @@ -0,0 +1,26 @@ +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying vec3 trPos; +uniform mat3 transformation; +uniform mat4 uMatrix; +varying float id; +varying vec3 norm; +varying vec3 texPos; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos; + id = oid; + norm = normal; + trPos = transformation *vec3(aPos.x, -aPos.y, -1); + texPos = vec3(aPos.x, -aPos.y, -1.); +} \ No newline at end of file diff --git a/proj/RTXoff.svg b/proj/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/proj/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proj/RTXon.svg b/proj/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/proj/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proj/fs/1.jpg b/proj/fs/1.jpg new file mode 100644 index 0000000..7dcab8a Binary files /dev/null and b/proj/fs/1.jpg differ diff --git a/proj/fs/2.jpg b/proj/fs/2.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/proj/fs/2.jpg differ diff --git a/proj/fs/3.jpg b/proj/fs/3.jpg new file mode 100644 index 0000000..0b0545c Binary files /dev/null and b/proj/fs/3.jpg differ diff --git a/proj/fs/4.jpg b/proj/fs/4.jpg new file mode 100644 index 0000000..9bb2c0b Binary files /dev/null and b/proj/fs/4.jpg differ diff --git a/proj/fs/5.jpg b/proj/fs/5.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/proj/fs/5.jpg differ diff --git a/proj/fs/6.jpg b/proj/fs/6.jpg new file mode 100644 index 0000000..c6ba47e Binary files /dev/null and b/proj/fs/6.jpg differ diff --git a/proj/fs/RTX.frag b/proj/fs/RTX.frag new file mode 100644 index 0000000..132bdff --- /dev/null +++ b/proj/fs/RTX.frag @@ -0,0 +1,352 @@ + +#ifndef _NDEBUG + precision highp float; + const int ns = 5; + const int cns = 5; + float noise(vec3 v){return 1.;} +#endif +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform vec3 SphCanvas[ns]; +uniform vec3 Glow[ns]; +uniform sampler2D uSampler[ns+3]; +uniform vec3 V0; +uniform bool glassy; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying float id; +varying vec3 norm; +varying vec3 texPos; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays. +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK, decay; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +#define clamp1(x) ((x)<0.?0.:((x) > 1. ? 1.:(x))) +vec3 vec_clamp01(vec3 x) { + return vec3(clamp1(x.x), clamp1(x.y), clamp1(x.z)); +} +void main(){ +// gl_FragColor=vec4(_glassy,0,0,1); return; + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + float glassyw = 1.; +// bool glassy = _glassy > .1; + if(glassy) + { + vec2 noiseTex = (texPos.xy + 1.)/2.; + noiseTex = vec2(clamp1(noiseTex.x ), clamp1(noiseTex.y )); + vec4 Wnoise = texture2D(uSampler[ns+2], noiseTex); + W += (Wnoise.xyz-.5)*.4; + glassyw = Wnoise.w; + } + bool selected = false; + float currentK = 1.; + float curr_decay = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + curr_decay = currR.decay; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + vec3 S=V+tMin*W; + if(curr_decay > .01) + curr_decay = curr_decay/(1.+tMin*tMin); + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + if(j == 0) + { + float intensity = sqrt(dot(Glow[i], Glow[i])); + if(intensity > 1.7) + { + gl_FragColor = vec4(1.74*normalize(Glow[i]), 1.); + if(selected) + gl_FragColor = vec4(normalize(Glow[i])+vec3(.5,0.,0.), 1.); + return; + } + } + + vec3 texture_color; + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + for(int k = 0; k < cns; ++k){ + if(Glow[k].x > .01){ + vec3 lDir = Sph[k].xyz - S; + float dist = length(lDir); + lDir /= (dist); + dist -= Sph[k].w; + color += (Glow[k]/(1.+dist*dist))*Diffuse[i]* + max(0.,dot(N,lDir)); + } + } + if(final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + if(curr_decay > .01 && Glow[i].x > .01) + { + vec3 glow_color = currentK*curr_decay*Glow[i]; + float l = length(glow_color); + if(l >=1.7) + glow_color/=l/1.7; + scolor += glow_color; + } + + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks, curr_decay); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks, curr_decay); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + if(curr_decay > .01 && Glow[i].x > .01) + { + vec3 glow_color = currentK*curr_decay*Glow[i]; + float l = length(glow_color); + if(l >=1.7) + glow_color/=l/1.7; + scolor += glow_color; + } + + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + if(curr_decay > .01) + curr_decay = curr_decay/(1.+t*t); + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[ns], vec2((sx+1.5)/3., (sz+3.)/6.)).xyz; + if(final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + for(int k = 0; k < cns; ++k){ + if(Glow[k].x > .01){ + vec3 lDir = Sph[k].xyz - S; + float dist = length(lDir); + lDir /= (dist); + dist -= Sph[k].w; + color += (Glow[k]/(1.+dist*dist))* //specular for ground. + pow(max(0., dot(vec3(-lDir.x, lDir.y,-lDir.z),-W)),groundSpecular.w); + } + } + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15, curr_decay); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15, curr_decay); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + { + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + for(int k = 0; k < cns; ++k){ + if(Glow[k].x > .01){ + vec3 lDir = Sph[k].xyz - V; + float dist = length(lDir); + lDir /= (dist); + dist -= Sph[k].w; + color += (Glow[k]/(1.+dist*dist))* //specular for ground. + pow(max(0., dot(vec3(-lDir.x, lDir.y,-lDir.z),W)),groundSpecular.w); + } + } + } + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + + + if(glassy) + scolor *= glassyw*glassyw; + for(int i = 0; i < ns; ++i){ + if(i == sel) + continue; + vec2 sx = SphCanvas[i].xy - texPos.xy; + float theta = atan(sx.y/sx.x); + if(sx.x > 0. && theta < 0.) + theta += _2pi; + else if(sx.x < 0.) + theta += pi; + float intensity = sqrt(dot(Glow[i], Glow[i])); + vec3 realD = Sph[i].xyz - V0; + float realDist = sqrt(dot(realD, realD))-Sph[i].w; + intensity/=(1.+realDist*realDist); + float dist = dot(sx, sx) - SphCanvas[i].z*SphCanvas[i].z; + intensity *= .5; + if(dist < intensity && dist > 0.) + { + vec3 flare = texture2D(uSampler[ns+1], vec2(0.1+0.9*(dist/intensity), theta/float(2*i+7))).xyz; + scolor += flare*flare*Glow[i]/(1.+realDist*realDist); + } + } + scolor = vec_clamp01(scolor); + if(selected) + scolor.x += 0.5; + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/proj/fs/RTX.vert b/proj/fs/RTX.vert new file mode 100644 index 0000000..e00d0a3 --- /dev/null +++ b/proj/fs/RTX.vert @@ -0,0 +1,26 @@ +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying vec3 trPos; +uniform mat3 transformation; +uniform mat4 uMatrix; +varying float id; +varying vec3 norm; +varying vec3 texPos; +//I used mat3 instead of the augmented mat4 matrix because we +//are not doing complex projections yet. I implemented simple +//perspective projection back in hw2 by changing focal length +//and adjusting the size of the projected surface accrodingly. +//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface +// = (deltaFl + fl + 1)/(fl + 1) * old surface +//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos; +//Because we will multiply the resulting vPos by the transformation matrix +//anyway, I smashed the ratio into the matrix into avoid doing this in shaders. +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + gl_Position = pos; + id = oid; + norm = normal; + trPos = transformation *vec3(aPos.x, -aPos.y, -1); + texPos = vec3(aPos.x, -aPos.y, -1.); +} \ No newline at end of file diff --git a/proj/fs/RTXoff.svg b/proj/fs/RTXoff.svg new file mode 100644 index 0000000..80488f7 --- /dev/null +++ b/proj/fs/RTXoff.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proj/fs/RTXon.svg b/proj/fs/RTXon.svg new file mode 100644 index 0000000..d3124d7 --- /dev/null +++ b/proj/fs/RTXon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proj/fs/implicitSurface.js b/proj/fs/implicitSurface.js new file mode 100644 index 0000000..5615dc6 --- /dev/null +++ b/proj/fs/implicitSurface.js @@ -0,0 +1,201 @@ + +function implicitSurfaceTriangleMesh(implicitFunction, n, args, id = 8) { + + // HERE IS WHERE MOST OF THE WORK HAPPENS + + let marchingTetrahedra = function(V, ni, nj) { + + // CONVENIENCE FUNCTIONS TO COMPUTE (i,j,k) FROM VOLUME INDEX n + + function n2i(n) { return n % ni; } + function n2j(n) { return (n / dj >>> 0) % nj; } + function n2k(n) { return n / dk >>> 0 ; } + + // ADD A VERTEX, AND RETURN A UNIQUE ID FOR THAT VERTEX + + function E(a, b) { + if (a > b) { let tmp = a; a = b; b = tmp; } + let ai = n2i(a), aj = n2j(a), ak = n2k(a), + bi = n2i(b), bj = n2j(b), bk = n2k(b); + let m = (n << 6) + (ai & bi ? 1 << 6 : ai | bi << 3) + + (aj & bj ? dj << 6 : aj << 1 | bj << 4) + + (ak & bk ? dk << 6 : ak << 2 | bk << 5); + + // ADD TO VERTEX ARRAY ONLY THE FIRST TIME THE VERTEX IS ENCOUNTERED + + if (vertexID[m] === undefined) { + vertexID[m] = P.length / 3; + let t = -V[n+a] / (V[n+b] - V[n+a]), + c = function(i,a,b) { return (i + (1-t)*a + t*b) / ni * 2 - 1; }; + P.push( c(i,ai,bi), c(j,aj,bj), c(k,ak,bk) ); + } + + return vertexID[m]; + } + + // CASE WHERE WE ADD ONE TRIANGLE IN A TETRAHEDRON + + function tri(a, b, c, d) { + T.push(E(a,b), E(a,c), E(a,d)); + } + + // CASE WHERE WE ADD TWO TRIANGLES IN A TETRAHEDRON + + function quad(a, b, c, d) { + let ac = E(a,c), bc = E(b,c), ad = E(a,d), bd = E(b,d); + T.push(bc, ac, ad); + T.push(ad, bd, bc); + } + + // DECLARE VARIABLES + + let nk = V.length / (ni * nj), di = 1, dj = ni, dk = ni * nj; + let dij = di + dj, dik = di + dk, djk = dj + dk, dijk = di + dj + dk; + let P = [], T = [], vertexID = [], i, j, k, m = 0, n, S = [0,di,dij,dijk]; + let lo = new Array(nj * nk), + hi = new Array(nj * nk); + + // THE SIX POSSIBLE INTERMEDIATE PATHS THROUGH A TETRAHEDRON + + let S1 = [di , dj , dk , di , dj , dk ]; + let S2 = [dij, djk, dik, dik, dij, djk]; + + // THERE ARE 16 CASES TO CONSIDER + + let cases = [ [0 ], [1, 0,1,2,3], [1, 1,2,0,3], [2, 0,1,2,3], + [1, 2,3,0,1], [2, 0,2,3,1], [2, 1,2,0,3], [1, 3,1,2,0], + [1, 3,0,2,1], [2, 0,3,1,2], [2, 1,3,2,0], [1, 2,1,0,3], + [2, 2,3,0,1], [1, 1,3,0,2], [1, 0,3,2,1], [0 ], ]; + + // FOR EACH (Y,Z), DON'T DO ANY WORK OUTSIDE OF X RANGE WHERE SURFACE MIGHT BE + + for (k = 0 ; k < nk ; k++) + for (j = 0 ; j < nj ; j++, m++) { + let n0 = m * ni, n1 = n0 + ni - 1; + for (n = n0 ; n <= n1 && V[n] > 0 ; n++) ; + lo[m] = Math.max(0, n-1 - n0); + for (n = n1 ; n >= n0 && V[n] > 0 ; --n) ; + hi[m] = Math.min(ni-1, n+1 - n0); + } + + // FOR ALL Y AND Z IN THE VOLUME + + for (k = 0 ; k < nk - 1 ; k++) { + let i0, i1, m = k * nj, n1, s0, s1; + for (j = 0 ; j < nj - 1 ; j++, m++) { + i0 = Math.min(lo[m], lo[m+1], lo[m+ni], lo[m+1+ni]); + i1 = Math.max(hi[m], hi[m+1], hi[m+ni], hi[m+1+ni]); + + // GO THROUGH RANGE OF X WHERE THE SURFACE MIGHT BE (IE: WITH ANY POSITIVE VALUES) + + if (i0 <= i1) { + n = m * ni + i0; + n1 = m * ni + i1; + s0 = (V[n]>0) + (V[n+dj]>0) + (V[n+dk]>0) + (V[n+djk]>0); + for (i = i0 ; n <= n1 ; i++, n++, s0 = s1) { + + // FOR EACH CUBE + + s1 = (V[n+di]>0) + (V[n+dij]>0) + (V[n+dik]>0) + (V[n+dijk]>0); + if (s0 + s1 & 7) { + let C14 = (V[n] > 0) | (V[n+dijk] > 0) << 3; + + // CYCLE THROUGH THE SIX TETRAHEDRA THAT TILE THE CUBE + + for (let p = 0 ; p < 6 ; p++) { + let C = cases [ C14 | (V[n+S1[p]] > 0) << 1 | (V[n+S2[p]] > 0) << 2 ]; + + // FOR EACH TETRAHEDRON, OUTPUT EITHER ZERO, ONE OR TWO TRIANGLES + + if (C[0]) { // C[0] == number of triangles to be created. + S[1] = S1[p]; // assign 2nd and 3rd corners of simplex. + S[2] = S2[p]; + (C[0]==1 ? tri : quad)(S[C[1]], S[C[2]], S[C[3]], S[C[4]]); + } + } + } + } + } + } + } + + // MAKE SURE ALL TRIANGLE VERTICES ARE LISTED IN COUNTERCLOCKWISE ORDER + + for (let m = 0 ; m < T.length ; m += 3) { + let a = 3 * T[m], b = 3 * T[m+1], c = 3 * T[m+2], + n = Math.floor(ni*(P[a ]+1)/2) + + Math.floor(ni*(P[a+1]+1)/2) * dj + + Math.floor(ni*(P[a+2]+1)/2) * dk, + u = cross([P[b] - P[a], P[b+1] - P[a+1], P[b+2] - P[a+2]], + [P[c] - P[b], P[c+1] - P[b+1], P[c+2] - P[b+2]]), + v = [ V[n+1] - V[n], V[n+dj] - V[n], V[n+dk] - V[n] ]; + if (dot(u, v) < 0) { let tmp = T[m]; T[m] = T[m + 2]; T[m + 2] = tmp; } + } + + // RETURN POINTS AND TRIANGLES + + return [P, T]; + } + + // SAMPLE THE VOLUME + + let F = i => (i - n/2) / (n/2); + let volume = []; + + for (let k = 0 ; k < n ; k++) + for (let j = 0 ; j < n ; j++) + for (let i = 0 ; i < n ; i++) + volume.push(implicitFunction(F(i), F(j), F(k), args)); + + // FIND ALL VERTICES AND TRIANGLES IN THE VOLUME + + let VT = marchingTetrahedra(volume, n, n); + let V = VT[0]; + let T = VT[1]; + + // COMPUTE SURFACE NORMALS + + let N = new Array(V.length); + for (let i = 0 ; i < V.length ; i += 3) { + let x = V[i], y = V[i+1], z = V[i+2], e = .001, + f0 = implicitFunction(x ,y ,z , args), + fx = implicitFunction(x+e,y ,z , args), + fy = implicitFunction(x ,y+e,z , args), + fz = implicitFunction(x ,y ,z+e, args), + normal = normalize([f0-fx,f0-fy,f0-fz]); + for (let j = 0 ; j < 3 ; j++) + N[i+j] = normal[j]; + } + + // CONSTRUCT AND RETURN THE TRIANGLES MESH + + let mesh = []; + for (let i = 0; i < T.length; i += 3) { + let a = 3 * T[i ], + b = 3 * T[i + 1], + c = 3 * T[i + 2]; + mesh.push( id, V[a],V[a+1],V[a+2] , N[a],N[a+1],N[a+2] , + id, V[b],V[b+1],V[b+2] , N[b],N[b+1],N[b+2] , + id, V[c],V[c+1],V[c+2] , N[c],N[c+1],N[c+2] ); + } + return new Float32Array(mesh); +} + +let blob = (center, radius, x, y, z) => { + x -= center[0]; + y -= center[1]; + z -= center[2]; + return Math.max(0, 1 - .16 * (x*x + y*y + z*z) / (radius * radius)); +} + +var implicitFunction = (x,y,z,args) => { + let ret = -.5; + let x4 = _x => _x*_x*_x*_x; + args.forEach((paras, _)=>{ + const center = paras[0], + radius = paras[1]; + ret += x4(blob(center, radius, x, y, z)); + }); + return ret; +} + diff --git a/proj/fs/libX.ext.js b/proj/fs/libX.ext.js new file mode 100644 index 0000000..cdda61d --- /dev/null +++ b/proj/fs/libX.ext.js @@ -0,0 +1,1355 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +var Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = true, + presentation = false, + sRotation = matrix_identity(); +var facing = 1, + running = 0; +var glassy = false, glassy_changed = false; +var figure_rot = pi; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +let show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +let channel = 5, channel_disp = -1; +var invTr = matrix_identity(); +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +}; +glsy.onclick = function(_){ + glassy_changed = true; + glassy = !glassy; +}; +reset.onclick = function (_) { + glassy = !glassy; + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + delta_l = [0, 0]; + facing = 1; + rtx.src = './RTXon.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 100) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = _=>{channel = channel == 5? 0:5 + if (channel == 5) rtx.src='./RTXon.svg'; + else rtx.src = 'RTXoff.svg'; +}//pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + if(channel_disp == 5){ + let i = hitTest([2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1-2 * e.offsetY / parseInt(canvas1.style.height), -1]); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + + if (!(mouselastX == undefined || mouselastY == undefined) && moving && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(matrix_rotateX(-dy / 60), sRotation); + sRotation = matrix_multiply(matrix_rotateY(-dx / 60), sRotation); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if(channel_disp != 5) + { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +rtx.style.cursor = "pointer"; +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + figure_rot = (pi*(facing/2)); + else figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + running = 20; + dir_sdl = 0; + keyhold = true; + } + else + running = running > 5? running:5; + rebuild = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => { + delete objects; + objects = []; +}; +let delta_height = 0, + delta_l = [0, 0]; +var direction_l = [0, 0], dir_sdl = 0; +class State { + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + } + initialize() { + this.leg = true; + this.progress = 0; + } + next() { + //return this.presentation(); + if (running <= 0) + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: 1.8522 / steps + }; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state) => { + if (running === 0) + rebuild = false; + let { + lh, + lf, + rh, + rf, + dh, + dl + } = state.next(); + if(dir_sdl > 0){ + delta_l = plus(delta_l, const_multiply(dl, direction_l)); + dir_sdl -= dl; + running = 1; + }else + delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + let sqlen = (a, b) => { return a*a + b*b; } + if(sqlen(delta_l[0] - 8, delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(delta_l[0] - 6, delta_l[1] - 6) < 5){ + donut_eaten = true; + pushStatus('Yum'); + } + delta_height = dh; + clearObject(); + M.save(); + + M.translate(0, 1, 0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.53 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.restore(); + if (running < 0) + rebuild = false; +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 20; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +let state = new State(); +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + Sph[5] = [-0.6,0.3,.5,1,.2]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + // M.save(); + // M.rotateZ((uTime)); + // setM(M.value()); + // drawMesh(new Float32Array(star)); + // M.restore(); + + + // M.save(); + // M.scale(0.1); + // M.translate(6,0,1); + // M.rotateX(1); + // M.rotateY(uTime/4); + // setM(M.value()); + // drawMesh(torusMash,gl.LINES); + // M.restore(); + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore() +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let rtx_draw = (gl, mesh, m) => { + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + if(m !== undefined) + setM(m); + else + { + m = matrix_identity(); + m[5] = -1; + setUniform('Matrix4fv', 'uMatrix', false,m); + } + + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0,0,0, 0.,.0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., 0,0,0, .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0,0,0, 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 5,5,0, 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0,0,0, 0., .85, 1.5, + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 6,6,6, 0., .85, 1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('3fv', 'Glow[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + Sph[5] = [-0.6*cos(uTime),-0.3*sin(uTime),-.5,1,.2]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(invTr, matrix_multiply(SphTr[i], Sph[i])); + const ratio = (fl+1)/(fl-trsph[2]); + setUniform('3fv', 'SphCanvas['+i+']', const_multiply(ratio, [trsph[0], trsph[1], Sph[i][4]])); + } + + if(!shader1_inited){ + initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + + drawMesh(mesh); + if(glassy_changed){ + setUniform('1i', 'glassy', glassy); + glassy_changed = false; + } + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; +} +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4 ){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if((channel_disp == 4 && i == 0)||channel_disp == 5){ + rtx_draw(gl, obj[0], m); + } + else + { + setM(m); + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let tv_ctrl = new Button(()=>{channel = 2;}); +let tv_ctrl1 = new Button(()=>{channel = 3;}); +let tv_ctrl2 = new Button(()=>{channel = 4;}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Turn TV OFF.', 8); + setup_control(tv_ctrl1, 'Play a Video.', 9); + setup_control(tv_ctrl2, 'Show Ray Tracing.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +let canvas_mesh = new Float32Array([0,-1, 1, 0,0,0,0, 0,1, 1, 0,0,0,0, 0,-1, -1, 0,0,0,0, 0,1, -1, 0,0,0,0]); + +let check_disp =_=>{ + if(channel != channel_disp){ + channel_disp = channel; + if(channel != 4) + { + for(let i = 0; i < tv[0][0].length; i+=7) + tv[0][0][i] = -channel; + rtx.src = './RTXoff.svg'; + } + else + rtx.src = './RTXon.svg'; + if(channel == 5) + movescene = false; + } +} +function animate(gl) { + buildsplines &&= build_splines() || setTVCtrl(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + + if(channel_disp != 5) + { + gl.enable(gl.DEPTH_TEST); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(delta_l[0], -delta_height, delta_l[1]); + + M.rotateY(figure_rot); + overall_trans = M.value(); + M.restore(); + + + M.save(); + + M.translate(0,-3.155,0); + M.rotateX(pi/2); + M.scale(10); + + ground_m = matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())); + setM(ground_m); + drawMesh(ground); + M.restore(); + + + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + M.save(); + M.translate(8, -.5, -6); + M.scale(3); + M.applyl(overall_ground); + M.rotateX(pi/2) + draw_magician(gl); + M.restore(); + draw_tv(gl); + // M.save(); + // M.translate(-.7, .7, 0); + // M.scale(.3); + // draw_deco(gl); + // M.restore(); + // setM(M.value()); + // test = new Float32Array([10, -1,-1,ttt,0,0,0,10,1,1,ttt,0,0,0]); + // drawMesh(test, gl.LINES); + + M.save(); + M.translate(5, 4, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl.draw(); + M.restore(); + M.save(); + M.translate(5, 2., -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl1.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl1.draw(); + M.restore(); + M.save(); + M.translate(5, 0, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl2.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl2.draw(); + M.restore(); + if(!slider) make_slider(); + if(near_magician) + draw_slider(); + if(show_magic) + drawstars(); + if(rebuild) + build_objects(state); + + M.save(); + if(donut_eaten){ + M.translate(0, -.4, 0); + M.rotateY(uTime / 5); + M.scale(0.23); + setM(matrix_multiply(sRotation,matrix_multiply(overall_trans, M.value()))); + } else { + M.translate(6, 2., 6); + M.rotateY(uTime / 10); + M.scale(0.4); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground, M.value()))); + } + drawMesh(torusMash); + M.restore(); + + for(const [obj, mat] of objects){ + let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)); + setM(m); + drawMesh(obj); + } + } + else + { + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + rtx_draw(gl, squareMesh()); + } + check_disp(); + + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + +} + +requestAnimationFrame(fpscounter); + +pjsk.play(); +pjsk.addEventListener('timeupdate', ()=>{updateVideoTextures = true;}) \ No newline at end of file diff --git a/proj/fs/libX.header.js b/proj/fs/libX.header.js new file mode 100644 index 0000000..fae07a8 --- /dev/null +++ b/proj/fs/libX.header.js @@ -0,0 +1,793 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); +let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt; +rtx_VFetch.open('GET', './RTX.vert'); +rtx_VFetch.onloadend = function() { + rtxvshader_txt = rtx_VFetch.responseText; +}; +rtx_VFetch.send(); +let rtx_FFetch = new XMLHttpRequest(); +rtx_FFetch.open('GET', './RTX.frag'); +rtx_FFetch.onloadend = function () { + let rtxfs_text = rtx_FFetch.responseText; + + let interval = setInterval(()=>{ + if(buildShaders && rtxvshader_txt && canvas1.gl) + { + gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text); + clearInterval(interval); + } + }, 1); +} +rtx_FFetch.send() +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + invTr = matrix_inverse(m); + + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + if(channel_disp == 5) + return; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + console.log(P); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else { + if(clicked || running<=0) + { + direction_l = [P[0] - delta_l[0]/10, P[1] - delta_l[1]/10]; + if(P[1] < 0.00000000000001) + P[1] = 0.0000000000000001; + figure_rot = atan(direction_l[0]/direction_l[1]); + if(direction_l[1] < 0){ + figure_rot = pi + figure_rot; + } + } + //if(figure_rot < 0) figure_rot += pi; + //updateStatus([figure_rot, direction_l[0]/direction_l[1]]); + if(clicked) + { + dir_sdl = vec_len(direction_l)*10; + direction_l = normalize(direction_l); + rebuild = true; + running = 1; + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + + + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); \ No newline at end of file diff --git a/proj/fs/libX.js b/proj/fs/libX.js new file mode 100644 index 0000000..2c9b115 --- /dev/null +++ b/proj/fs/libX.js @@ -0,0 +1,327 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }','\n#define _NDEBUG\n' +].join('\n'); +var ns = 6, + cns = 6; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = 'The TV is a touch screen!'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + for (let i = 0; i < ns; ++i) { + loadTexture( gl, './' + (i + 1) + '.jpg', i); //Texture loading. + textures[i] = i; + } + textures[ns] = ns; + initVideoTexture(gl, ns + 0); + textures[ns + 1] = ns + 1; + loadTexture(gl, './starburst.png', ns + 1); + createNoiseTexture(); + textures[ns+2] = ns+2; + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); +} +function initVideoTexture(gl, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function createNoiseTexture(){ + const level = 0; + const internalFormat = gl.RGBA; + const width = 100; + const height = 100; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + texture[ns+2] = gl.createTexture(); + let random_list = []; + let get_random = ()=>Math.pow(Math.random(), .1); + let get_random2 = ()=>{ + let r = Math.random(); + if (r < .5) r = .5*Math.pow(2*r,.1); + else r = .5 + .5*Math.pow(r,10); + return r;}; + for(let i = 0; i < 40000; ++i){ + if(i%4 == 3) + random_list.push(255*get_random()); + else + random_list.push(255*get_random2()); + } + const pixel = new Uint8Array(random_list); + gl.activeTexture(gl.TEXTURE0+ns+2); + gl.bindTexture(gl.TEXTURE_2D, texture[ns+2]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 5,0,0, 0.,.0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .1,8,0, .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., .1,0,9, 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 2,2,0, 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 4,4,4, 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('3fv', 'Glow[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} \ No newline at end of file diff --git a/proj/fs/main.html b/proj/fs/main.html new file mode 100644 index 0000000..c01b3e1 --- /dev/null +++ b/proj/fs/main.html @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + Final Project + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+ +
+ + What's new: +
    +

    + In the final project I implemented Flares and glowing object for ray tracing. The technical + details can be found in this paper from + SIGGRAPH 2011.
    + I also implemented a glassy filter by randomly bending the rays shot from the screen. The idea was from my + failed attempt to create flares which I thought was caused by diffusion of lights caused by fogs on my glasses. + But this actually resembles the images we saw through a bumpy glass. + Because the light rays was randomly bent by refractions through it.
    + Also, as usual, I integrated my homework compilation, since I put extra work into every single homeworks and I'm proud to show + them in my final project.
    + You may browse the source here.
    +

    +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + diff --git a/proj/fs/paths.txt b/proj/fs/paths.txt new file mode 100644 index 0000000..b331549 --- /dev/null +++ b/proj/fs/paths.txt @@ -0,0 +1,104 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N \ No newline at end of file diff --git a/proj/fs/pjsk.mp4 b/proj/fs/pjsk.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/proj/fs/pjsk.mp4 differ diff --git a/proj/fs/porc.py b/proj/fs/porc.py new file mode 100644 index 0000000..d64670b --- /dev/null +++ b/proj/fs/porc.py @@ -0,0 +1,25 @@ +import cv2 +import numpy as np +import math +mat = cv2.imread('starburst.png') +mat2 = np.zeros((900, 1273, 3)) +print(mat.shape) +rad = 0 +for i in range(0, 900): + dt = 0 + s = math.sin(rad) + c = math.cos(rad) + for j in range(0, 1273): + # if(dt*c > 913 or dt*c < -913 or dt * s > 900 or dt*s < -900): + # break + x = 900+int(dt*s) + x = x if x < 1800 else 1799 + y = 913+int(dt*c) + y = y if y < 1826 else 1799 + + mat2[i][j] = mat[900+int(dt*s)][913+int(dt*c)] + dt += 1 + rad += .007 +cv2.imwrite('starburst3.png',mat2) +cv2.imshow('a', mat2) +cv2.waitKey(0) \ No newline at end of file diff --git a/proj/fs/scene.mp4 b/proj/fs/scene.mp4 new file mode 100644 index 0000000..ae68718 Binary files /dev/null and b/proj/fs/scene.mp4 differ diff --git a/proj/fs/shader.frag b/proj/fs/shader.frag new file mode 100644 index 0000000..e073d1d --- /dev/null +++ b/proj/fs/shader.frag @@ -0,0 +1,59 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +uniform sampler2D uSampler[ns + 1]; //ns + vs +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + float alp = 2.*abs(.49999 - fract(id+.00001)); + if(id < -2.5){gl_FragColor = vec4(texture2D(uSampler[ns + 0], (1.+texPos.xy)/2.).xyz, 1.); return;} //pjsk + else if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id <-.5) {color = vec3(0.,1.,0.2034);} + else if(id < 1.5) {color = vec3(1.0000, 0.3570, 0.3570);sp = 1.; df=.2; amb = .7, ex = 20.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) { + color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color; + if(texPos.y > .3) + color = vec3(1.,1.,1.); + } + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;} + else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;} + else if (id < 18.5) { + color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.; + vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(.8 * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alp); +} diff --git a/proj/fs/shader.vert b/proj/fs/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/proj/fs/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/proj/fs/starburst.png b/proj/fs/starburst.png new file mode 100644 index 0000000..8b656fb Binary files /dev/null and b/proj/fs/starburst.png differ diff --git a/proj/implicitSurface.js b/proj/implicitSurface.js new file mode 100644 index 0000000..5615dc6 --- /dev/null +++ b/proj/implicitSurface.js @@ -0,0 +1,201 @@ + +function implicitSurfaceTriangleMesh(implicitFunction, n, args, id = 8) { + + // HERE IS WHERE MOST OF THE WORK HAPPENS + + let marchingTetrahedra = function(V, ni, nj) { + + // CONVENIENCE FUNCTIONS TO COMPUTE (i,j,k) FROM VOLUME INDEX n + + function n2i(n) { return n % ni; } + function n2j(n) { return (n / dj >>> 0) % nj; } + function n2k(n) { return n / dk >>> 0 ; } + + // ADD A VERTEX, AND RETURN A UNIQUE ID FOR THAT VERTEX + + function E(a, b) { + if (a > b) { let tmp = a; a = b; b = tmp; } + let ai = n2i(a), aj = n2j(a), ak = n2k(a), + bi = n2i(b), bj = n2j(b), bk = n2k(b); + let m = (n << 6) + (ai & bi ? 1 << 6 : ai | bi << 3) + + (aj & bj ? dj << 6 : aj << 1 | bj << 4) + + (ak & bk ? dk << 6 : ak << 2 | bk << 5); + + // ADD TO VERTEX ARRAY ONLY THE FIRST TIME THE VERTEX IS ENCOUNTERED + + if (vertexID[m] === undefined) { + vertexID[m] = P.length / 3; + let t = -V[n+a] / (V[n+b] - V[n+a]), + c = function(i,a,b) { return (i + (1-t)*a + t*b) / ni * 2 - 1; }; + P.push( c(i,ai,bi), c(j,aj,bj), c(k,ak,bk) ); + } + + return vertexID[m]; + } + + // CASE WHERE WE ADD ONE TRIANGLE IN A TETRAHEDRON + + function tri(a, b, c, d) { + T.push(E(a,b), E(a,c), E(a,d)); + } + + // CASE WHERE WE ADD TWO TRIANGLES IN A TETRAHEDRON + + function quad(a, b, c, d) { + let ac = E(a,c), bc = E(b,c), ad = E(a,d), bd = E(b,d); + T.push(bc, ac, ad); + T.push(ad, bd, bc); + } + + // DECLARE VARIABLES + + let nk = V.length / (ni * nj), di = 1, dj = ni, dk = ni * nj; + let dij = di + dj, dik = di + dk, djk = dj + dk, dijk = di + dj + dk; + let P = [], T = [], vertexID = [], i, j, k, m = 0, n, S = [0,di,dij,dijk]; + let lo = new Array(nj * nk), + hi = new Array(nj * nk); + + // THE SIX POSSIBLE INTERMEDIATE PATHS THROUGH A TETRAHEDRON + + let S1 = [di , dj , dk , di , dj , dk ]; + let S2 = [dij, djk, dik, dik, dij, djk]; + + // THERE ARE 16 CASES TO CONSIDER + + let cases = [ [0 ], [1, 0,1,2,3], [1, 1,2,0,3], [2, 0,1,2,3], + [1, 2,3,0,1], [2, 0,2,3,1], [2, 1,2,0,3], [1, 3,1,2,0], + [1, 3,0,2,1], [2, 0,3,1,2], [2, 1,3,2,0], [1, 2,1,0,3], + [2, 2,3,0,1], [1, 1,3,0,2], [1, 0,3,2,1], [0 ], ]; + + // FOR EACH (Y,Z), DON'T DO ANY WORK OUTSIDE OF X RANGE WHERE SURFACE MIGHT BE + + for (k = 0 ; k < nk ; k++) + for (j = 0 ; j < nj ; j++, m++) { + let n0 = m * ni, n1 = n0 + ni - 1; + for (n = n0 ; n <= n1 && V[n] > 0 ; n++) ; + lo[m] = Math.max(0, n-1 - n0); + for (n = n1 ; n >= n0 && V[n] > 0 ; --n) ; + hi[m] = Math.min(ni-1, n+1 - n0); + } + + // FOR ALL Y AND Z IN THE VOLUME + + for (k = 0 ; k < nk - 1 ; k++) { + let i0, i1, m = k * nj, n1, s0, s1; + for (j = 0 ; j < nj - 1 ; j++, m++) { + i0 = Math.min(lo[m], lo[m+1], lo[m+ni], lo[m+1+ni]); + i1 = Math.max(hi[m], hi[m+1], hi[m+ni], hi[m+1+ni]); + + // GO THROUGH RANGE OF X WHERE THE SURFACE MIGHT BE (IE: WITH ANY POSITIVE VALUES) + + if (i0 <= i1) { + n = m * ni + i0; + n1 = m * ni + i1; + s0 = (V[n]>0) + (V[n+dj]>0) + (V[n+dk]>0) + (V[n+djk]>0); + for (i = i0 ; n <= n1 ; i++, n++, s0 = s1) { + + // FOR EACH CUBE + + s1 = (V[n+di]>0) + (V[n+dij]>0) + (V[n+dik]>0) + (V[n+dijk]>0); + if (s0 + s1 & 7) { + let C14 = (V[n] > 0) | (V[n+dijk] > 0) << 3; + + // CYCLE THROUGH THE SIX TETRAHEDRA THAT TILE THE CUBE + + for (let p = 0 ; p < 6 ; p++) { + let C = cases [ C14 | (V[n+S1[p]] > 0) << 1 | (V[n+S2[p]] > 0) << 2 ]; + + // FOR EACH TETRAHEDRON, OUTPUT EITHER ZERO, ONE OR TWO TRIANGLES + + if (C[0]) { // C[0] == number of triangles to be created. + S[1] = S1[p]; // assign 2nd and 3rd corners of simplex. + S[2] = S2[p]; + (C[0]==1 ? tri : quad)(S[C[1]], S[C[2]], S[C[3]], S[C[4]]); + } + } + } + } + } + } + } + + // MAKE SURE ALL TRIANGLE VERTICES ARE LISTED IN COUNTERCLOCKWISE ORDER + + for (let m = 0 ; m < T.length ; m += 3) { + let a = 3 * T[m], b = 3 * T[m+1], c = 3 * T[m+2], + n = Math.floor(ni*(P[a ]+1)/2) + + Math.floor(ni*(P[a+1]+1)/2) * dj + + Math.floor(ni*(P[a+2]+1)/2) * dk, + u = cross([P[b] - P[a], P[b+1] - P[a+1], P[b+2] - P[a+2]], + [P[c] - P[b], P[c+1] - P[b+1], P[c+2] - P[b+2]]), + v = [ V[n+1] - V[n], V[n+dj] - V[n], V[n+dk] - V[n] ]; + if (dot(u, v) < 0) { let tmp = T[m]; T[m] = T[m + 2]; T[m + 2] = tmp; } + } + + // RETURN POINTS AND TRIANGLES + + return [P, T]; + } + + // SAMPLE THE VOLUME + + let F = i => (i - n/2) / (n/2); + let volume = []; + + for (let k = 0 ; k < n ; k++) + for (let j = 0 ; j < n ; j++) + for (let i = 0 ; i < n ; i++) + volume.push(implicitFunction(F(i), F(j), F(k), args)); + + // FIND ALL VERTICES AND TRIANGLES IN THE VOLUME + + let VT = marchingTetrahedra(volume, n, n); + let V = VT[0]; + let T = VT[1]; + + // COMPUTE SURFACE NORMALS + + let N = new Array(V.length); + for (let i = 0 ; i < V.length ; i += 3) { + let x = V[i], y = V[i+1], z = V[i+2], e = .001, + f0 = implicitFunction(x ,y ,z , args), + fx = implicitFunction(x+e,y ,z , args), + fy = implicitFunction(x ,y+e,z , args), + fz = implicitFunction(x ,y ,z+e, args), + normal = normalize([f0-fx,f0-fy,f0-fz]); + for (let j = 0 ; j < 3 ; j++) + N[i+j] = normal[j]; + } + + // CONSTRUCT AND RETURN THE TRIANGLES MESH + + let mesh = []; + for (let i = 0; i < T.length; i += 3) { + let a = 3 * T[i ], + b = 3 * T[i + 1], + c = 3 * T[i + 2]; + mesh.push( id, V[a],V[a+1],V[a+2] , N[a],N[a+1],N[a+2] , + id, V[b],V[b+1],V[b+2] , N[b],N[b+1],N[b+2] , + id, V[c],V[c+1],V[c+2] , N[c],N[c+1],N[c+2] ); + } + return new Float32Array(mesh); +} + +let blob = (center, radius, x, y, z) => { + x -= center[0]; + y -= center[1]; + z -= center[2]; + return Math.max(0, 1 - .16 * (x*x + y*y + z*z) / (radius * radius)); +} + +var implicitFunction = (x,y,z,args) => { + let ret = -.5; + let x4 = _x => _x*_x*_x*_x; + args.forEach((paras, _)=>{ + const center = paras[0], + radius = paras[1]; + ret += x4(blob(center, radius, x, y, z)); + }); + return ret; +} + diff --git a/proj/index.html b/proj/index.html new file mode 100644 index 0000000..c01b3e1 --- /dev/null +++ b/proj/index.html @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + Final Project + +Turn Ray Tracing On/OFF +
+ + + +
+ + +
+ +
+ + What's new: +
    +

    + In the final project I implemented Flares and glowing object for ray tracing. The technical + details can be found in this paper from + SIGGRAPH 2011.
    + I also implemented a glassy filter by randomly bending the rays shot from the screen. The idea was from my + failed attempt to create flares which I thought was caused by diffusion of lights caused by fogs on my glasses. + But this actually resembles the images we saw through a bumpy glass. + Because the light rays was randomly bent by refractions through it.
    + Also, as usual, I integrated my homework compilation, since I put extra work into every single homeworks and I'm proud to show + them in my final project.
    + You may browse the source here.
    +

    +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + diff --git a/proj/index.html.1 b/proj/index.html.1 new file mode 100644 index 0000000..7946b0c --- /dev/null +++ b/proj/index.html.1 @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + Pikachu + +
+ + + +
+ + +
+ +
+ + What's new: +
    + +
+

+

+
+ +
+ + +
+ +
+ + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + diff --git a/proj/lib7.ext.js b/proj/lib7.ext.js new file mode 100644 index 0000000..d6ae54b --- /dev/null +++ b/proj/lib7.ext.js @@ -0,0 +1,1352 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +var Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = true, + presentation = false, + sRotation = matrix_identity(); +var facing = 1, + running = 0; +var glassy = false, glassy_changed = false; +var figure_rot = pi; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +let show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +let channel = 5, channel_disp = -1; +var invTr = matrix_identity(); +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +}; +glsy.onclick = function(_){ + glassy_changed = true; + glassy = !glassy; +}; +reset.onclick = function (_) { + glassy = !glassy; + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + delta_l = [0, 0]; + facing = 1; + rtx.src = './RTXoff.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 100) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = _=>{channel = channel == 5? 0:5;}//pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + if(channel_disp == 5){ + let i = hitTest([2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1-2 * e.offsetY / parseInt(canvas1.style.height), -1]); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + + if (!(mouselastX == undefined || mouselastY == undefined) && moving && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(matrix_rotateX(-dy / 60), sRotation); + sRotation = matrix_multiply(matrix_rotateY(-dx / 60), sRotation); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if(channel_disp != 5) + { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +rtx.style.cursor = "pointer"; +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + figure_rot = (pi*(facing/2)); + else figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + running = 20; + dir_sdl = 0; + keyhold = true; + } + else + running = running > 5? running:5; + rebuild = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => { + delete objects; + objects = []; +}; +let delta_height = 0, + delta_l = [0, 0]; +var direction_l = [0, 0], dir_sdl = 0; +class State { + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + } + initialize() { + this.leg = true; + this.progress = 0; + } + next() { + //return this.presentation(); + if (running <= 0) + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: 1.8522 / steps + }; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state) => { + if (running === 0) + rebuild = false; + let { + lh, + lf, + rh, + rf, + dh, + dl + } = state.next(); + if(dir_sdl > 0){ + delta_l = plus(delta_l, const_multiply(dl, direction_l)); + dir_sdl -= dl; + running = 1; + }else + delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + let sqlen = (a, b) => { return a*a + b*b; } + if(sqlen(delta_l[0] - 8, delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(delta_l[0] - 6, delta_l[1] - 6) < 5){ + donut_eaten = true; + pushStatus('Yum'); + } + delta_height = dh; + clearObject(); + M.save(); + + M.translate(0, 1, 0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.53 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.restore(); + if (running < 0) + rebuild = false; +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 20; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +let state = new State(); +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + Sph[5] = [-0.6,0.3,.5,1,.2]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + // M.save(); + // M.rotateZ((uTime)); + // setM(M.value()); + // drawMesh(new Float32Array(star)); + // M.restore(); + + + // M.save(); + // M.scale(0.1); + // M.translate(6,0,1); + // M.rotateX(1); + // M.rotateY(uTime/4); + // setM(M.value()); + // drawMesh(torusMash,gl.LINES); + // M.restore(); + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore() +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let rtx_draw = (gl, mesh, m) => { + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + if(m !== undefined) + setM(m); + else + { + m = matrix_identity(); + m[5] = -1; + setUniform('Matrix4fv', 'uMatrix', false,m); + } + + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0,0,0, 0.,.0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., 0,0,0, .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0,0,0, 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 5,5,0, 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0,0,0, 0., .85, 1.5, + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 6,6,6, 0., .85, 1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('3fv', 'Glow[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + Sph[5] = [-0.6,-0.3,-.5,1,.2]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(invTr, matrix_multiply(SphTr[i], Sph[i])); + const ratio = (fl+1)/(fl-trsph[2]); + setUniform('3fv', 'SphCanvas['+i+']', const_multiply(ratio, [trsph[0], trsph[1], Sph[i][4]])); + } + + if(!shader1_inited){ + initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + + drawMesh(mesh); + if(glassy_changed){ + setUniform('1i', 'glassy', glassy); + glassy_changed = false; + } + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; +} +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4 ){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if((channel_disp == 4 && i == 0)||channel_disp == 5){ + rtx_draw(gl, obj[0], m); + } + else + { + setM(m); + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let tv_ctrl = new Button(()=>{channel = 2;}); +let tv_ctrl1 = new Button(()=>{channel = 3;}); +let tv_ctrl2 = new Button(()=>{channel = 4;}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Turn TV OFF.', 8); + setup_control(tv_ctrl1, 'Play a Video.', 9); + setup_control(tv_ctrl2, 'Show Ray Tracing.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +let canvas_mesh = new Float32Array([0,-1, 1, 0,0,0,0, 0,1, 1, 0,0,0,0, 0,-1, -1, 0,0,0,0, 0,1, -1, 0,0,0,0]); + +let check_disp =_=>{ + if(channel != channel_disp){ + channel_disp = channel; + if(channel != 4) + { + for(let i = 0; i < tv[0][0].length; i+=7) + tv[0][0][i] = -channel; + rtx.src = './RTXoff.svg'; + } + else + rtx.src = './RTXon.svg'; + if(channel == 5) + movescene = false; + } +} +function animate(gl) { + buildsplines &&= build_splines() || setTVCtrl(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + + if(channel_disp != 5) + { + gl.enable(gl.DEPTH_TEST); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(delta_l[0], -delta_height, delta_l[1]); + + M.rotateY(figure_rot); + overall_trans = M.value(); + M.restore(); + + + M.save(); + + M.translate(0,-3.155,0); + M.rotateX(pi/2); + M.scale(10); + + ground_m = matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())); + setM(ground_m); + drawMesh(ground); + M.restore(); + + + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + M.save(); + M.translate(8, -.5, -6); + M.scale(3); + M.applyl(overall_ground); + M.rotateX(pi/2) + draw_magician(gl); + M.restore(); + draw_tv(gl); + // M.save(); + // M.translate(-.7, .7, 0); + // M.scale(.3); + // draw_deco(gl); + // M.restore(); + // setM(M.value()); + // test = new Float32Array([10, -1,-1,ttt,0,0,0,10,1,1,ttt,0,0,0]); + // drawMesh(test, gl.LINES); + + M.save(); + M.translate(5, 4, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl.draw(); + M.restore(); + M.save(); + M.translate(5, 2., -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl1.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl1.draw(); + M.restore(); + M.save(); + M.translate(5, 0, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl2.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl2.draw(); + M.restore(); + if(!slider) make_slider(); + if(near_magician) + draw_slider(); + if(show_magic) + drawstars(); + if(rebuild) + build_objects(state); + + M.save(); + if(donut_eaten){ + M.translate(0, -.4, 0); + M.rotateY(uTime / 5); + M.scale(0.23); + setM(matrix_multiply(sRotation,matrix_multiply(overall_trans, M.value()))); + } else { + M.translate(6, 2., 6); + M.rotateY(uTime / 10); + M.scale(0.4); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground, M.value()))); + } + drawMesh(torusMash); + M.restore(); + + for(const [obj, mat] of objects){ + let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)); + setM(m); + drawMesh(obj); + } + } + else + { + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + rtx_draw(gl, squareMesh()); + } + check_disp(); + + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + +} + +requestAnimationFrame(fpscounter); + +pjsk.play(); +pjsk.addEventListener('timeupdate', ()=>{updateVideoTextures = true;}) \ No newline at end of file diff --git a/proj/lib7.header.js b/proj/lib7.header.js new file mode 100644 index 0000000..23a3b78 --- /dev/null +++ b/proj/lib7.header.js @@ -0,0 +1,790 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); +let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt; +rtx_VFetch.open('GET', './RTX.vert'); +rtx_VFetch.onloadend = function() { + rtxvshader_txt = rtx_VFetch.responseText; +}; +rtx_VFetch.send(); +let rtx_FFetch = new XMLHttpRequest(); +rtx_FFetch.open('GET', './RTX.frag'); +rtx_FFetch.onloadend = function () { + let rtxfs_text = rtx_FFetch.responseText; + + let interval = setInterval(()=>{ + if(buildShaders && rtxvshader_txt && canvas1.gl) + { + gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text); + clearInterval(interval); + } + }, 1); +} +rtx_FFetch.send() +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + invTr = matrix_inverse(m); + + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + if(channel_disp == 5) + return; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + console.log(P); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else { + if(clicked || running<=0) + { + direction_l = [P[0] - delta_l[0]/10, P[1] - delta_l[1]/10]; + if(P[1] < 0.00000000000001) + P[1] = 0.0000000000000001; + figure_rot = atan(direction_l[0]/direction_l[1]); + if(direction_l[1] < 0){ + figure_rot = pi + figure_rot; + } + } + //if(figure_rot < 0) figure_rot += pi; + //updateStatus([figure_rot, direction_l[0]/direction_l[1]]); + if(clicked) + { + dir_sdl = vec_len(direction_l)*10; + direction_l = normalize(direction_l); + rebuild = true; + running = 1; + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); \ No newline at end of file diff --git a/proj/lib7.js b/proj/lib7.js new file mode 100644 index 0000000..2c9b115 --- /dev/null +++ b/proj/lib7.js @@ -0,0 +1,327 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }','\n#define _NDEBUG\n' +].join('\n'); +var ns = 6, + cns = 6; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = 'The TV is a touch screen!'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + for (let i = 0; i < ns; ++i) { + loadTexture( gl, './' + (i + 1) + '.jpg', i); //Texture loading. + textures[i] = i; + } + textures[ns] = ns; + initVideoTexture(gl, ns + 0); + textures[ns + 1] = ns + 1; + loadTexture(gl, './starburst.png', ns + 1); + createNoiseTexture(); + textures[ns+2] = ns+2; + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); +} +function initVideoTexture(gl, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function createNoiseTexture(){ + const level = 0; + const internalFormat = gl.RGBA; + const width = 100; + const height = 100; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + texture[ns+2] = gl.createTexture(); + let random_list = []; + let get_random = ()=>Math.pow(Math.random(), .1); + let get_random2 = ()=>{ + let r = Math.random(); + if (r < .5) r = .5*Math.pow(2*r,.1); + else r = .5 + .5*Math.pow(r,10); + return r;}; + for(let i = 0; i < 40000; ++i){ + if(i%4 == 3) + random_list.push(255*get_random()); + else + random_list.push(255*get_random2()); + } + const pixel = new Uint8Array(random_list); + gl.activeTexture(gl.TEXTURE0+ns+2); + gl.bindTexture(gl.TEXTURE_2D, texture[ns+2]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 5,0,0, 0.,.0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .1,8,0, .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., .1,0,9, 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 2,2,0, 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 4,4,4, 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('3fv', 'Glow[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} \ No newline at end of file diff --git a/proj/libX.ext.js b/proj/libX.ext.js new file mode 100644 index 0000000..cdda61d --- /dev/null +++ b/proj/libX.ext.js @@ -0,0 +1,1355 @@ +let ctrl = false, + alt = false, + shift = false, + fpson = true, + moving = false, + over = false, + keyhold = false; +let lastClick = undefined; +let animating = true; +let flags = 0x0; +var uTime = 0, + startTime = Date.now(); +let lastTime = Date.now(), + rotTime = 0; +var lastFrameTime = 0; +let oldDocument; +let fullscreen = false, + btntoggled = false; +let movescene = true; +let oldparents = {}; +var tr, div; +let canvas_originalsize; +var Sph = []; +let SphTr = []; +let SphDletaR = []; +let selected = false, + selection = -1, + dragging = false; +let overall_trans = matrix_identity(), + overall_ground, ground_m = matrix_identity(); +var rebuild = true, + presentation = false, + sRotation = matrix_identity(); +var facing = 1, + running = 0; +var glassy = false, glassy_changed = false; +var figure_rot = pi; +let curr_mouse_pos = [-10, -10, -10]; +var updateVideoTextures = false; +let show_magic = false; +var slider_dl = 0; +var donut_eaten = false; +let int_fxydL = 0, trace = [];//[x, y, tijp[o'm]] +//schema.height = screen.height * .9; +let channel = 5, channel_disp = -1; +var invTr = matrix_identity(); +for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; +} + +function ev_supersample(e) { + let multiplier = insamp.value; + let w = parseInt(canvas1.style.width) * multiplier; + let h = parseInt(canvas1.style.height) * multiplier; + canvas1.height = h; + canvas1.width = w; + gl.viewport(0, 0, w, h); + //gl.clearRect(0, 0, w, h); +} + +function toggleFullscreen(element) { + if (fullscreen) { + if (document.exitFullscreen) + document.exitFullscreen(); + else if (document.webkitExitFullscreen) + document.webkitExitFullscreen(); + else if (document.mozCancelFullScreen) + document.mozCancelFullScreen(); + else if (document.msExitFullscreen) + document.msExitFullscreen(); + fullscreen = false; + bnfs.innerText = "Fullscreen"; + } else { + if (element.requestFullscreen) + element.requestFullscreen(); + else if (element.webkitRequestFullscreen) + element.webkitRequestFullscreen(); + else if (element.msRequestFullscreen) + element.msRequestFullscreen(); + fullscreen = true; + bnfs.innerText = "Exit Fullscreen"; + } +} +bnfs.onclick = function (e) { + if (e === "no") + ; + else + btntoggled = true; + if (fullscreen) { + oldparents[controls].appendChild(controls); + oldparents[canvas1].appendChild(canvas1); + canvas1.style.width = canvas_originalsize[0]; + canvas1.style.height = canvas_originalsize[1]; + howitworks.hidden = false; + } else { + div = document.createElement("div"); + tr = document.createElement("table").insertRow(); + tr.style.backgroundColor = "white"; + let size = Math.min(screen.availHeight, screen.availWidth); + canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height]; + canvas1.style.height = canvas1.style.width = size; + howitworks.hidden = true; + oldparents[controls] = controls.parentNode; + oldparents[canvas1] = canvas1.parentNode; + + let td1 = tr.insertCell(); + td1.appendChild(canvas1); + let td2; + td2 = tr.insertCell(); + td2.style.verticalAlign = "top"; + td2.appendChild(controls); + + div.appendChild(tr); + document.body.appendChild(div); + } + toggleFullscreen(div); + ev_supersample(); +} +mov.onclick = function (_) { + movescene = !movescene; + if (!movescene) { + mov.innerText = "Move Scene"; + mov.style.width = "170px"; + } else { + mov.innerText = "Move Lighting"; + mov.style.width = "180px"; + } +} +document.addEventListener("webkitfullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +document.addEventListener("fullscreenchange", () => { + if (!btntoggled && fullscreen) bnfs.onclick("no"); + btntoggled = false; +}); +clrsel.onclick = function (_) { + setUniform("1i", "sel", -1); + selected = false; + selection = -1; +}; +glsy.onclick = function(_){ + glassy_changed = true; + glassy = !glassy; +}; +reset.onclick = function (_) { + glassy = !glassy; + clrsel.onclick(); + if (!animating) + pause_resume(); + flags = 0; + moving = false; + donut_eaten = false; + //slider_dl = 0; + mousedx = mousedy = mousedz = 0; + positionsupdated = true; + for (let i = 0; i < ns; ++i) { + SphTr[i] = matrix_identity(); + SphDletaR[i] = 0; + } + sRotation = matrix_identity(); + startTime = lastTime = Date.now(); + uTime = rotTime = 0; + delta_l = [0, 0]; + facing = 1; + rtx.src = './RTXon.svg'; + setUniform('1i', 'flags', flags); +} +bns.onclick = function (e) { + if (ins.value > 0 && ins.value <= ns && cns != ins.value) { + cns = ins.value; + fragmentShaderDefs = '\n const int cns = ' + cns + ';'; + if (typeof canvas1.setShaders === "function") + canvas1.setShaders(vs, editor.getSession().getValue()); + } +} +bnsamp.onclick = ev_supersample; +// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. +ace.require("ace/ext/language_tools"); +var editor = ace.edit("ace", { + mode: "ace/mode/glsl", + theme: "ace/theme/crimson_editor" +}); +editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + fontSize: 14, + fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro", + fixedWidthGutter: true, + showGutter: true, + showPrintMargin: false, +}); +editor.setAutoScrollEditorIntoView(true); +if (fs != undefined) + editor.getSession().setValue(fs); +editor.session.on('change', function (delta) { + if (typeof canvas1.setShaders === "function") { + canvas1.setShaders(vs, editor.getSession().getValue()); + setUniform('1i', 'flags', flags); + } +}); +// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. +delete editor.KeyBinding; +let ttt = -1 +let pause_resume = function () { + ttt += .1; + if (animating) + lastTime = Date.now(); + else + startTime += Date.now() - lastTime; + animating = !animating; +}; +canvas1.addEventListener('click', function (ev) { + if (!(shift && alt) && lastClick && Date.now() - lastClick < 100) + pause_resume(); + lastClick = Date.now(); +}); +pause.onclick = _=>{channel = channel == 5? 0:5 + if (channel == 5) rtx.src='./RTXon.svg'; + else rtx.src = 'RTXoff.svg'; +}//pause_resume; +canvas1.addEventListener('mouseover', function (e) { + over = true; + if (e.offsetX > 0 && e.offsetY > 0) + { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + } + else over = false; +}); +canvas1.addEventListener('mousedown', function (e) { + moving = true; + rotTime = uTime; + presentation = false; + mouselastX = mouselastY = undefined; + if(channel_disp == 5){ + let i = hitTest([2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1-2 * e.offsetY / parseInt(canvas1.style.height), -1]); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + controls_hitTest(curr_mouse_pos, true); +}); +canvas1.addEventListener('mousemove', function (e) { + curr_mouse_pos = [2 * e.offsetX / parseInt(canvas1.style.width) - 1, + 1 - 2 * e.offsetY / parseInt(canvas1.style.height), 0 + ]; + controls_hitTest(curr_mouse_pos); + let dx, dy; + if(!(mouselastX == undefined || mouselastY == undefined)){ + dx = (mouselastX - e.offsetX), + dy = (mouselastY - e.offsetY); + int_fxydL += sqrt(dx*dx + dy*dy); + if(int_fxydL > 20) + { + int_fxydL = 0; + trace.push([curr_mouse_pos[0], curr_mouse_pos[1], 1]); + } + } + + if (!(mouselastX == undefined || mouselastY == undefined) && moving && !near_magician) { + if (movescene) { + sRotation = matrix_multiply(matrix_rotateX(-dy / 60), sRotation); + sRotation = matrix_multiply(matrix_rotateY(-dx / 60), sRotation); + } else if (!selected) { + mousedx -= dx / 60; + mousedy -= dy / 60; + positionsupdated = true; + } else if (dragging) { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + let dv = matrix_multiply(m, [2 * -dx / parseInt(canvas1.style.width), + 2 * dy / parseInt(canvas1.style.height), 0, 1 + ]).slice(0, 3); + SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2])); + } + } + mouselastX = e.offsetX; + mouselastY = e.offsetY; +}); +canvas1.addEventListener('mouseup', function (e) { + moving = false; + dragging = false; + slider.dragging = false; +}); +canvas1.addEventListener('mouseout', function (e) { + const mask = 0x8; + flags &= !mask; + setUniform('1i', 'flags', flags); + over = false; + moving = false; +}); +canvas1.addEventListener('wheel', function (e) { + if(channel_disp != 5) + { + if (!selected) { + mousedz += e.wheelDelta / 600; + positionsupdated = true; + } else { + SphDletaR[selection] += e.wheelDelta / 800; + } + } +}); +canvas1.scroll(function (e) { + e.stopPropagation(); +}); +rtx.style.cursor = "pointer"; +let rtswitch = function () { + if(channel_disp != 4){ + channel = 4 + rtx.src = './RTXon.svg'; + } + else { + channel = 2; + rtx.src = './RTXoff.svg'; + } +} +rtx.addEventListener('click', rtswitch); +var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; +let fpscounter = function (time) { + if (start === undefined) + start = time; + else + fps.innerHTML = Math.round(10000 / (time - start)) / 10 + ' fps'; + start = time; + if (fpson) + ; //requestAnimationFrame(fpscounter); + else { + start = undefined; + fps.innerHTML = ''; + } +}; +document.addEventListener('keydown', (e) => { + if (e.code.startsWith('Shift')) + shift = true; + if (e.code.startsWith('Control')) + ctrl = true; + if (e.code.startsWith('Alt')) + alt = true; + else if (ctrl && alt && e.code == 'KeyT') { + const mask = 0x1; + flags = flags & !mask | (!(flags & mask) ? mask : 0); + setUniform('1i', 'flags', flags); + } else if (ctrl && e.code == 'KeyS') { + let a = document.createElement('a'); + a.href = "data:text/plain," + encodeURIComponent(editor.getSession().getValue()); + a.download = 'shader.frag'; + a.click(); + } else if (ctrl && alt && e.code == 'KeyR') + rtswitch(); + else if (ctrl && alt && e.code == 'KeyN') { + reset.onclick(); + } else if (ctrl && alt && e.code == 'KeyP') + pause_resume(); + else if (ctrl && alt && e.code == 'KeyF') + if (!fpson) { + fpson = true; + requestAnimationFrame(fpscounter); + } + else + fpson = false; + + if (e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code == 'ArrowLeft' || + e.code == 'ArrowRight' || e.code == 'KeyW' || e.code == 'KeyS') { + let oldfacing = facing; + switch (e.code) { + case 'ArrowUp': + facing = -2; + break; + case 'ArrowDown': + facing = 2; + break; + case 'ArrowLeft': + facing = -1; + break; + case 'ArrowRight': + facing = 1; + break; + case 'KeyW': + break; + case 'KeyS': + break; + } + if(facing !=2) + figure_rot = (pi*(facing/2)); + else figure_rot = 0; + if(!keyhold || oldfacing != facing) + { + running = 20; + dir_sdl = 0; + keyhold = true; + } + else + running = running > 5? running:5; + rebuild = true; + } + if (fullscreen && selected) { + if (e.code == 'KeyF' || e.code == 'KeyB') { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + switch (e.code) { + case 'KeyB': + var dv = matrix_multiply(m, [0, 0, -0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + case 'KeyF': + var dv = matrix_multiply(m, [0, 0, 0.1, 1]).slice(0, 3); + m = matrix_translate(dv[0], dv[1], dv[2]); + break; + } + SphTr[selection] = matrix_multiply(SphTr[selection], m); + } + + } +}); + +document.addEventListener('keyup', (e) => { + keyhold = false; + if (e.code.startsWith('Control')) + ctrl = false; + if (e.code.startsWith('Alt')) + alt = false; + if (e.code.startsWith('Shift')) + shift = false; +}); +let squareMesh = (w = 1, h = 1, dir = 1, id = -1)=> new Float32Array([id, -w, h, 0, 0, 0, dir, id, w, h, 0, 0, 0, dir, id, -w, -h, 0, 0, 0, dir, id, w, -h, 0, 0, 0, dir]); +let sphereMesh = createMesh(8, 8, uvToSphere, 0, 16.45); +let sphereMesh25 = createMesh(25, 25, uvToSphere, 0, 16.45); +let tubeMesh = createMesh(64, 2, uvToTube, 0, 1); +let diskMesh = createMesh(16, 2, uvToDisk, 0, 1); +let tubeMesh2 = createMesh(16, 2, uvToTube, 0, 2); +let diskNMesh2 = createMesh(16, 2, uvToDisk, -1, 2); +let diskPMesh2 = createMesh(16, 2, uvToDisk, 1, 2); +let tubeMesh3 = createMesh(16, 2, uvToTube, 0, 3); +let diskNMesh3 = createMesh(16, 2, uvToDisk, -1, 3); +let diskPMesh3 = createMesh(16, 2, uvToDisk, 1, 3); +let diskNMesh = createMesh(16, 2, uvToDisk, -1, 1); +let diskPMesh = createMesh(16, 2, uvToDisk, 1, 1); +let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh); +let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2); +let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3); +let torusMash = createMesh(32, 32, uvToTorus, 1, 18); +let head = createCube(1.5, 1, 1, 4); + +let objects = []; +let addObject = (obj, mat) => { + objects.push([obj, mat]); +}; +let clearObject = () => { + delete objects; + objects = []; +}; +let delta_height = 0, + delta_l = [0, 0]; +var direction_l = [0, 0], dir_sdl = 0; +class State { + constructor() { + this.leg = true; + this.progress = 0; + this.rh = this.lh = .5 * pi; + this.lf = this.rf = 0; + } + initialize() { + this.leg = true; + this.progress = 0; + } + next() { + //return this.presentation(); + if (running <= 0) + return { + rh: .5 * pi, + lh: .5 * pi, + rf: 0, + lf: 0, + dh: 0, + dl: 0 + } + running--; + const steps = 28; + let dl = 0; + if (this.progress >= steps / 2) { + this.progress = 0; + this.leg = !this.leg; + } + let delta = [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]; + for (let i = 0; i < 4; ++i) delta[i] /= steps; + if (this.leg) { + if (this.progress < steps / 4) { + this.lh += delta[0]; + this.rh += delta[3]; + this.lf += delta[1]; + this.rf += delta[2]; + } else { + this.lh -= delta[0]; + this.rh -= delta[3]; + this.lf -= delta[1]; + this.rf -= delta[2]; + } + } else { + if (this.progress < steps / 4) { + this.lh += delta[3]; + this.rh += delta[0]; + this.lf += delta[2]; + this.rf += delta[1]; + } else { + this.lh -= delta[3]; + this.rh -= delta[0]; + this.lf -= delta[2]; + this.rf -= delta[1]; + } + } + let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6); + this.progress++; + return { + lh: this.lh, + lf: this.lf, + rh: this.rh, + rf: this.rf, + dh: delta_h, + dl: 1.8522 / steps + }; + } + // presentation(){ + // return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0}; + // } +}; + +let star1 = [], + star = []; +let sakura = [], + stars = [], + nstars = 25; +let star2 = [], + newstar, star4; +const curvex = matrix_multiply(hermiteMat, [-.3, .8, .6, .5]); +const curvey = matrix_multiply(hermiteMat, [-.2, .5, .7, .2]); +const curvez = matrix_multiply(hermiteMat, [-.5, .2, .3, .8]); +let adt = []; +var near_magician = false; +let build_objects = (state) => { + if (running === 0) + rebuild = false; + let { + lh, + lf, + rh, + rf, + dh, + dl + } = state.next(); + if(dir_sdl > 0){ + delta_l = plus(delta_l, const_multiply(dl, direction_l)); + dir_sdl -= dl; + running = 1; + }else + delta_l[abs(facing) - 1] += Math.sign(facing) * dl; + let sqlen = (a, b) => { return a*a + b*b; } + if(sqlen(delta_l[0] - 8, delta_l[1] + 6)< 6) + { + if(!near_magician){ + near_magician = true; + movescene = false; + button_magic.enabled = true; + pushStatus('Show me some magic'); + changeID(14, ground); + } + } else if(near_magician) { + restoreStatus(); + changeID(-1, ground); + near_magician = false;movescene = true;button_magic.enabled = false; + } else if(!donut_eaten && sqlen(delta_l[0] - 6, delta_l[1] - 6) < 5){ + donut_eaten = true; + pushStatus('Yum'); + } + delta_height = dh; + clearObject(); + M.save(); + + M.translate(0, 1, 0); + addObject(head, M.value()); + M.restore(); + M.save(); + M.translate(0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.53 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(-0.5, 0.2, 0.3); + M.rotateX(pi / 4); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .4); + M.rotateX(-0.55 * pi); + M.scale(0.2, 0.2, 0.4); + M.translate(0, 0, 1); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh2, M.value()); + M.restore(); + M.save(); + M.translate(0.3, -1, 0.); + M.rotateX(lh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(lf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.translate(-0.3, -1, 0.); + M.rotateX(rh); + M.translate(0, 0, .5); + M.save(); + M.translate(0, 0, .45); + M.rotateX(rf); + M.scale(0.2, 0.2, 0.6); + M.translate(0, 0, 1); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.scale(0.2, 0.2, 0.5); + addObject(cylinderMesh3, M.value()); + M.restore(); + M.save(); + M.rotateX(pi / 2); + M.scale(0.5, 0.5, 1); + addObject(cylinderMesh, M.value()); + M.restore(); + M.save(); + M.restore(); + if (running < 0) + rebuild = false; +}; + +let build_splines = () => { + star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = []; + var n = 11, + innerN = Math.ceil((paths[0].length * n) / paths[1].length); + for (let j = 0; j < paths[0].length; ++j) { + let xf = paths[0][j][0], + yf = paths[0][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + star1.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + + for (let j = 13; j >= 0; j--) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN - 1; k >= 0; --k) { + let t = k / (innerN - 1); + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let j = paths[1].length - 1; j > 12; --j) { + let xf = paths[1][j][0], + yf = paths[1][j][1]; + for (var k = innerN; k >= 0; --k) { + let t = k / innerN; + star2.push([7, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1 + ]); + } + } + for (let i = 0; i < star1.length; ++i) { + concat(star, star1[i]); + concat(star, star2[i]); + } + n = 25; + for (let l = 2; l < 6; ++l) { + adt[l - 2] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + adt[l - 2].push(10 + l, dot(xf, [t * t * t, t * t, t, 1]), + -dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + n = 20; + for (let l = 6; l < 13; ++l) { + sakura[l - 6] = []; + for (let j = 0; j < paths[l].length; ++j) { + let xf = paths[l][j][0], + yf = paths[l][j][1]; + for (var k = 0; k <= n; ++k) { + let t = k / n; + sakura[l - 6].push(10, dot(xf, [t * t * t, t * t, t, 1]), + dot(yf, [t * t * t, t * t, t, 1]), + 0, 0, 0, 1); + } + } + } + star4 = star.slice(); + newstar = star.slice() + for (let i = 0; i < star.length; i += 7) { + star4[i] = 9; + newstar[i] = 8; + } + for (let i = 0; i < nstars; ++i) { + stars.push(star.slice()); + startz.push(.6 * Math.random()); + random_rot.push(0); + random_rot_pos.push(0); + } + return false; +} +let state = new State(); +var M = new Matrix(); +var buildsplines = true; +let magician = false, + magician_neck = false, + magician_houki = false, + body = false, + leg = false, + hand = false; +let sa2 = [ + 1.6, 0.4, .9, 1., .3, .2, -.4, .8, + -.8, -.1, -1.4, .5, -1.6, -.5 + ], + fsa2 = [ + [matrix_multiply(bezierMat, [sa2[0], sa2[2], sa2[4], sa2[6]]), + matrix_multiply(bezierMat, [sa2[1], sa2[3], sa2[5], sa2[7]]) + ], + [matrix_multiply(bezierMat, [sa2[6], sa2[8], sa2[10], sa2[12]]), + matrix_multiply(bezierMat, [sa2[7], sa2[9], sa2[11], sa2[13]]) + ] + ], + random_rot = [0, 0, 0, 0, 0, 0, 0], + random_rot_pos = [0, 0, 0, 0, 0, 0, 0], + startz = []; + +let division = -1; +let changeID = (id, obj) => { + for (let i = 0; i < obj.length; i += 7) obj[i] = id; +} + +let rtx_update = () => { + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + Sph[5] = [-0.6,0.3,.5,1,.2]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } +} +let draw_magician = (gl) => { + magician = magician ? magician : createMeshFromSpline(13, 32, 4, 4, 0, (i, _, oid) => { + if (oid == 4 && i == 10) return 3; + else if (oid == 3 && i == 16) return 2; + else if (oid == 2 && i == 23) return 1; + }); + magician_neck = magician_neck ? magician_neck : createMeshFromSpline(14, 32, 4, 5, -.2); + magician_houki = magician_houki ? magician_houki : createMeshFromSpline(15, 32, 4, 6); + body = body ? body : createMeshFromSpline(16, 32, 4, 7, 0, (i, tmp) => { + if (division < 0 && i == 25) division = tmp.length; + }); + leg = leg ? leg : createMeshFromSpline(17, 32, 4, 8); + hand = hand ? hand : createMeshFromSpline(18, 32, 4, 9, .015); + + M.save(); + M.scale(0.6); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(body.slice(0, division)); + drawMesh(body.slice(division - 7), gl.LINE_LOOP); + M.save(); + M.translate(0, 0, -1.05); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician); + M.restore(); + M.save(); + M.translate(0, 0, -.53); + M.scale(.1); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_neck); + M.restore(); + M.save(); + M.translate(-1, 0, 0); + M.scale(2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(magician_houki); + M.restore(); + M.save(); + M.translate(-.06, 0, .85); + M.rotateY(0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(.06, 0, .85); + M.rotateY(-0.02); + M.scale(1.2); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(leg); + M.restore(); + M.save(); + M.translate(-.35, 0, -.1); + M.rotateY(-pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.save(); + M.translate(.35, 0, -.1); + M.rotateY(pi / 6); + M.scale(.82); + setM(matrix_multiply(sRotation, M.value())); + drawMesh(hand); + M.restore(); + M.restore(); +} +let draw_deco = (gl) => { + + // M.save(); + // M.rotateZ((uTime)); + // setM(M.value()); + // drawMesh(new Float32Array(star)); + // M.restore(); + + + // M.save(); + // M.scale(0.1); + // M.translate(6,0,1); + // M.rotateX(1); + // M.rotateY(uTime/4); + // setM(M.value()); + // drawMesh(torusMash,gl.LINES); + // M.restore(); + + M.save(); + M.translate(-.7,.75,0); + //M.rotateZ(pi); + M.scale(0.6) + setM(M.value()); + + for(let l = 2; l < 6; ++l) + drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP); + M.restore(); + + M.save(); + // M.scale(sin(uTime/2)); + M.translate(-.23, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(0.2, .7, 0); + + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.4); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore(); + M.save(); + M.translate(.7, .7, 0); + M.rotateZ(sin(uTime/6) + 0.2); + M.scale(0.6); + for(let i = 0; i < 10; i++){ + M.scale(0.995); + M.save(); + setM(M.value()); + M.restore(); + for(let l = 8; l < 13; ++l) + drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP); + } + M.restore() +} +let tv; +let make_tv = ()=>{ + let screen = squareMesh(1,.5625, 1, -2), scrtr; + let stand = tubeMesh.slice(), standtr; + let base = cylinderMesh.slice(), basetr; + + changeID(17, stand); + changeID(17, base); + M.save(); + M.translate(0,0,-5.3); + M.rotateX(pi/2); + M.scale(8); + scrtr = M.value(); + M.restore(); + M.save(); + M.scale(.07, .07 ,.9); + standtr = M.value(); + M.restore(); + + M.save(); + M.translate(0,0,.9) + M.scale(1.7, 1.7 ,.03); + basetr = M.value() + M.restore(); + tv = [[screen, scrtr], [stand, standtr], [base,basetr]]; +} +let touch_screen; +let shader1_inited = false; +let rtx_draw = (gl, mesh, m) => { + gl.useProgram(gl.shaders[1]); + gl.program = gl.shaders[1]; + if(m !== undefined) + setM(m); + else + { + m = matrix_identity(); + m[5] = -1; + setUniform('Matrix4fv', 'uMatrix', false,m); + } + + if(positionsupdated) + updatePositions(); + setUniform('1f', 'uTime', uTime); + if(selection >= 0) + setUniform("1i", "sel", selection); + else + setUniform("1i", "sel", -1); + + var offset = 0; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 0,0,0, 0.,.0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., 0,0,0, .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., 0,0,0, 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 5,5,0, 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0,0,0, 0., .85, 1.5, + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 6,6,6, 0., .85, 1.5 + ] + for(let i = 0; i < ns; i++){ + setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4)); + setUniform('3fv', 'Glow[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1)); + } + + Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15]; + Sph[1] = [0,0,0,1,.25]; + Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05]; + Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25]; + Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12]; + Sph[5] = [-0.6*cos(uTime),-0.3*sin(uTime),-.5,1,.2]; + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(invTr, matrix_multiply(SphTr[i], Sph[i])); + const ratio = (fl+1)/(fl-trsph[2]); + setUniform('3fv', 'SphCanvas['+i+']', const_multiply(ratio, [trsph[0], trsph[1], Sph[i][4]])); + } + + if(!shader1_inited){ + initTextures(gl, gl.program); + shader1_inited = true; + } + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + for(let i = 0; i < ns; ++ i) + { + let trsph = matrix_multiply(SphTr[i], Sph[i]); + trsph[3] = Sph[i][4]; + setUniform('4fv', 'Sph['+ i + ']', trsph); + } + + drawMesh(mesh); + if(glassy_changed){ + setUniform('1i', 'glassy', glassy); + glassy_changed = false; + } + gl.useProgram(gl.shaders[0]); + gl.program = gl.shaders[0]; +} +let draw_tv = (gl) => { + if(!tv) make_tv(); + if(!touch_screen) { + touch_screen = new Button(()=>{ + if(channel_disp == 4 ){ + let i = hitTest(touch_screen.P); + if (i >= 0) { + dragging = true; + selected = true; + selection = i; + } else if (selected = true) { + dragging = false; + selected = false; + selection = i; + } + } + }, tv[0][0], 'square'); + touch_screen.hover = (e)=>{ movescene = (e&&channel_disp == 4)?false:true;touch_screen.hovering =!movescene;}; + } + M.save(); + M.translate(-3, -2.2, -6); + M.rotateY(.6); + M.rotateX(pi/2); + tv.forEach((obj, i) => { + let m = matrix_multiply(sRotation, matrix_multiply(overall_ground,matrix_multiply(M.value(),obj[1]))); + if(i == 0) + touch_screen.updateMatrix(m); + if((channel_disp == 4 && i == 0)||channel_disp == 5){ + rtx_draw(gl, obj[0], m); + } + else + { + setM(m); + for(let i = 0; i < ns + 2; ++ i){ + textures[i] = i; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + } + gl.uniform1iv(gl.getUniformLocation(gl.shaders[1], 'uSampler'), textures); + + drawMesh(obj[0]); + } + }); + M.restore(); +} +let tv_ctrl = new Button(()=>{channel = 2;}); +let tv_ctrl1 = new Button(()=>{channel = 3;}); +let tv_ctrl2 = new Button(()=>{channel = 4;}); + +var ground = squareMesh(); +let trace_star; +let drawTrace = (gl)=>{ + if(!trace_star) trace_star = new Float32Array(star); + let newtrace = []; + trace.forEach((tr, _)=>{ + M.save(); + M.translate(tr[0], tr[1], 0); + M.scale(tr[2]*.07); + const newid = 16 + (1-pow(10, tr[2])/10)*.5 + for(let i = 0; i < trace_star.length; i += 7) + trace_star[i] = newid; + setM(M.value()); + drawMesh(trace_star); + M.restore(); + tr[2] -= .05; + if(tr[2] > 0) + newtrace.push(tr); + }); + trace = newtrace; +} +function setTVCtrl() { + + let setup_control = (control, str, id) =>{ + control.resetShape(sphereMesh); + changeID(id +.15, control.getShape()); + control.hover = (e = true) =>{ + if(e){ + if(status_history.len <= 2) + pushStatus(str); + else updateStatus(str); + control.hovering = true; + changeID(id, control.getShape()); + } else { + restoreStatus(); + changeID(id + .15, control.getShape()); + control.hovering = false; + } + } + } + setup_control(tv_ctrl, 'Turn TV OFF.', 8); + setup_control(tv_ctrl1, 'Play a Video.', 9); + setup_control(tv_ctrl2, 'Show Ray Tracing.', 10); +} +let drawstars = ()=>{ + for (let i = 0; i < nstars; ++i) { + M.save() + let f_idx = (uTime / (abs(sin(i * 1234)) * 8 + 10)) % 2, + t = f_idx - Math.floor(f_idx); + f_idx -= t; + let curr_mat = [t * t * t, t * t, t, 1]; + M.translate(1.2 * sin(sin(uTime / (i + 2) + i + 8) * .9 + dot(fsa2[f_idx][0], curr_mat) * 5 + startz[Math.floor(2 * startz[i] * nstars) % nstars]), 1.2 * cos(i + sin(uTime / (i / 2 + 18) + 12) * .1 + startz[nstars - i - 1] + dot(fsa2[f_idx][1], curr_mat) * startz[(nstars + i) % nstars] * .4 + i * sin(random_rot_pos[i] / 30) / pi), .65 * startz[i] + .3 * sin(random_rot_pos[i] / 20)); + let epsilon = 1 / (i * 2 + 20); + let random = () => { + var u = 0, + v = 0; + while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) + while (v === 0) v = Math.random(); + return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) / pi; + } + if ((window.performance.now() + i * (2 + abs(random()))) % 69 < 2) { + changeID(Math.ceil(Math.random() * 20) - 5, stars[i]) + } + random_rot[i] += random() * epsilon; + random_rot_pos[i] += .5 + abs(random()) / (pi + i); + //setM(M.value); + M.rotateZ(sin(random_rot_pos[i] / 10) * pi); + M.rotateX(.1 * sin(random_rot_pos[i] / (i + 50)) * pi); + M.rotateY(.1 * sin(random_rot_pos[i] / (i * 2 + 50)) * pi); + + M.scale(0.2 + startz[i] * .2); + setM(M.value()); + drawMesh(new Float32Array(stars[i])); + M.restore(); + } +} +let slider, slider_body, button_magic = new Button(()=>{ + show_magic = !show_magic; + if(show_magic) + changeID(4.1, button_magic.shape); + else + changeID(16.15, button_magic.shape); +}); +let make_slider = () => { + slider = createCube(9,.05,.05,12); + slider_body = createCube(.4,.14,.7,12); + button_magic.resetShape(sphereMesh25); + button_magic.hover=(e) => { + id = button_magic.shape[0]; + if(id < 5) + id = e?4.1:4.2; + else + id = e?16.15:16.45; + if(e){ + if(!button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = true; + } + } else + { + if(button_magic.hovering){ + changeID(id, button_magic.shape); + button_magic.hovering = false; + } + } + }; + if(!near_magician) + button_magic.enabled = false; +}; +let draw_slider = ()=>{ + if(isNaN(slider_dl)) + slider_dl = 0; + M.save(); + M.translate(2, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider); + M.restore(); + M.save(); + M.translate(-2.5 + slider_dl, -3, 7); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + drawMesh(slider_body); + M.restore(); + M.save(); + M.translate(-6, -3, 7); + button_magic.updateMatrix(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value()))); + button_magic.draw(); + M.restore(); +}; +let canvas_mesh = new Float32Array([0,-1, 1, 0,0,0,0, 0,1, 1, 0,0,0,0, 0,-1, -1, 0,0,0,0, 0,1, -1, 0,0,0,0]); + +let check_disp =_=>{ + if(channel != channel_disp){ + channel_disp = channel; + if(channel != 4) + { + for(let i = 0; i < tv[0][0].length; i+=7) + tv[0][0][i] = -channel; + rtx.src = './RTXoff.svg'; + } + else + rtx.src = './RTXon.svg'; + if(channel == 5) + movescene = false; + } +} +function animate(gl) { + buildsplines &&= build_splines() || setTVCtrl(); + if (animating) { + uTime = (Date.now() - startTime) / 1000; + } else { + uTime = (lastTime - startTime) / 1000; + //setUniform('1f', 'uTime', uTime); + } + + if(channel_disp != 5) + { + gl.enable(gl.DEPTH_TEST); + + M.save(); + M.scale(0.1); + M.rotateX(.6); + M.rotateZ(.35); + overall_ground = M.value(); + + M.translate(delta_l[0], -delta_height, delta_l[1]); + + M.rotateY(figure_rot); + overall_trans = M.value(); + M.restore(); + + + M.save(); + + M.translate(0,-3.155,0); + M.rotateX(pi/2); + M.scale(10); + + ground_m = matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())); + setM(ground_m); + drawMesh(ground); + M.restore(); + + + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + M.save(); + M.translate(8, -.5, -6); + M.scale(3); + M.applyl(overall_ground); + M.rotateX(pi/2) + draw_magician(gl); + M.restore(); + draw_tv(gl); + // M.save(); + // M.translate(-.7, .7, 0); + // M.scale(.3); + // draw_deco(gl); + // M.restore(); + // setM(M.value()); + // test = new Float32Array([10, -1,-1,ttt,0,0,0,10,1,1,ttt,0,0,0]); + // drawMesh(test, gl.LINES); + + M.save(); + M.translate(5, 4, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl.draw(); + M.restore(); + M.save(); + M.translate(5, 2., -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl1.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl1.draw(); + M.restore(); + M.save(); + M.translate(5, 0, -12); + //M.rotateY(uTime / 10); + M.scale(0.9); + tv_ctrl2.updateMatrix(matrix_multiply(sRotation, matrix_multiply(overall_ground,M.value()))); + tv_ctrl2.draw(); + M.restore(); + if(!slider) make_slider(); + if(near_magician) + draw_slider(); + if(show_magic) + drawstars(); + if(rebuild) + build_objects(state); + + M.save(); + if(donut_eaten){ + M.translate(0, -.4, 0); + M.rotateY(uTime / 5); + M.scale(0.23); + setM(matrix_multiply(sRotation,matrix_multiply(overall_trans, M.value()))); + } else { + M.translate(6, 2., 6); + M.rotateY(uTime / 10); + M.scale(0.4); + setM(matrix_multiply(sRotation,matrix_multiply(overall_ground, M.value()))); + } + drawMesh(torusMash); + M.restore(); + + for(const [obj, mat] of objects){ + let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)); + setM(m); + drawMesh(obj); + } + } + else + { + if(updateVideoTextures){ + updateVideoTexture(gl, pjsk, ns + 0); + } + rtx_draw(gl, squareMesh()); + } + check_disp(); + + gl.disable(gl.DEPTH_TEST); + drawTrace(gl); + +} + +requestAnimationFrame(fpscounter); + +pjsk.play(); +pjsk.addEventListener('timeupdate', ()=>{updateVideoTextures = true;}) \ No newline at end of file diff --git a/proj/libX.header.js b/proj/libX.header.js new file mode 100644 index 0000000..fae07a8 --- /dev/null +++ b/proj/libX.header.js @@ -0,0 +1,793 @@ +//Header file, contains global variable definitions, +// asynchronized shader loading and utility functions + +var mousedx = 0, + mousedy = 0, + mousedz = 0; +let seldx = 0, + seldy = 0, + seldz = 0; +var enableSelection = false; +var cx = 1, + cy = 1, + sx = 0, + sy = 0, + shaders = []; +var mouselastX, mouselastY + ; +var fl = 3; +let start; +var vs, fs; +var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0], + hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0], + catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0]; +var starColors = [0.9921, 0.5378, 0.7109, + 0.65, 0.56, 0.992, + 0.992, 0.7994, 0.2402, + 0.1760, 0.5094, 0.5378, + .1164, .1274, .2289, + .9784, .71, .4482, + ], + n_shapes = starColors.length / 3; +var editor = undefined +var cos = Math.cos, + sin = Math.sin, + tan = Math.tan, + acos = Math.acos, + asin = Math.asin, + atan = Math.atan, + sqrt = Math.sqrt, + pi = Math.PI, + abs = Math.abs, + pow = Math.pow, + log = Math.log; +var positionsupdated = true; +var paths = [], + origpath = [], + path_misc = []; +var canvas_controls = []; +let vsfetch = new XMLHttpRequest(); +vsfetch.open('GET', './shader.vert'); +vsfetch.onloadend = function () { + vs = vsfetch.responseText; +}; +vsfetch.send(); +//* LOADING FRAGMENT SHADER +let fsfetch = new XMLHttpRequest(); +fsfetch.open('GET', './shader.frag'); +fsfetch.onloadend = function () { + fs = (fsfetch.responseText); + //* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED. + if (editor != undefined) + editor.getSession().setValue(fs); +}; +fsfetch.send(); +let pathFetch = new XMLHttpRequest(); +pathFetch.open('GET', './paths.txt'); +pathFetch.onloadend = function () { + let text = pathFetch.responseText; + let currX = 0, + currY = 0, + maxX = -10000, + maxY = -10000, + minX = 10000, + minY = 10000; + var currShape = [], + currCurve = []; + let i = 0; + let postProcess = () => { + if (currShape.length) { + let spanX = maxX - minX; + let spanY = maxY - minY; + let span = Math.max(spanX, spanY); + let l_total = 0; + for (var k = 0; k < currShape.length; ++k) { + let funcs = []; + const curve = currShape[k]; + for (let j = 0; j < curve.length; j += 2) { + curve[j] = (curve[j] - minX) / span - spanX / (span * 2); + curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2); + origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1); + if (j % 6 == 0 && j > 5) { + let X = [], + Y = []; + for (let k = j - 6; k <= j + 1; k += 2) { + X.push(curve[k]); + Y.push(curve[k + 1]); + } + let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) + + vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) + + vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) + + vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.; + l_total += l; + funcs.push([matrix_multiply(bezierMat, X), + matrix_multiply(bezierMat, Y), l + ]); + } + + } + paths.push(funcs); + path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]); + + } + } + } + let read_num = () => { + let num = 0, + sign = 1, + accepted = 0; + + while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i; + if (text[i] == '-') { + sign = -1; + ++i; + } + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted *= 10; + accepted += n; + } + + num += accepted; + if (text[i] == '.') { + i++; + let multiplier = 0.1; + accepted = 0; + while (i < text.length && text[i] >= '0' && text[i] <= '9') { + let n = text[i++] - '0'; + accepted += n * multiplier; + multiplier /= 10; + } + num += accepted; + } + return num * sign; + } + let cRevs = [], + c_idx = 0, + prevX = 0, + prevY = 0, + getC = () => { + return cRevs[c_idx--]; + } + let get_next = (delta = false) => { + if (delta) { + currX = prevX + read_num(); + currY = prevY + read_num(); + } else { + currX = read_num(); + currY = read_num(); + } + maxX = currX > maxX ? currX : maxX; + maxY = currY > maxY ? currY : maxY; + minX = currX < minX ? currX : minX; + minY = currY < minY ? currY : minY; + + currCurve.push(currX); + currCurve.push(currY); + } + while (i < text.length) { + if (text[i] == 'z') { + currCurve.length && currShape.push(currCurve); + currCurve = []; + ++i + } else if (text[i] == 'N') { + postProcess(); + currShape = []; + maxX = -1000, maxY = -1000, minX = 1000, minY = 1000; + ++i; + } else if (text[i] == 'c') { + + prevX = currX; + prevY = currY; + + for (let j = 0; j < 3; ++j) { + get_next(true); + } + } else if (text[i] == 'C') { + for (let j = 0; j < 3; ++j) { + get_next(); + } + } else if (text[i] == 'M') { + get_next(); + } else ++i; + } +}; +pathFetch.send(); +let rtx_VFetch = new XMLHttpRequest(), rtxvshader_txt; +rtx_VFetch.open('GET', './RTX.vert'); +rtx_VFetch.onloadend = function() { + rtxvshader_txt = rtx_VFetch.responseText; +}; +rtx_VFetch.send(); +let rtx_FFetch = new XMLHttpRequest(); +rtx_FFetch.open('GET', './RTX.frag'); +rtx_FFetch.onloadend = function () { + let rtxfs_text = rtx_FFetch.responseText; + + let interval = setInterval(()=>{ + if(buildShaders && rtxvshader_txt && canvas1.gl) + { + gl.shaders[1] = buildShaders(rtxvshader_txt, rtxfs_text); + clearInterval(interval); + } + }, 1); +} +rtx_FFetch.send() +let vec_len = v => { + let len = 0; + for (let i = 0; i < v.length; ++i) + len += v[i] * v[i]; + return sqrt(len); +} +let matrix_inverse = src => { + let dst = [], + det = 0, + cofactor = (c, r) => { + let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2]; + return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) - + (s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) + + (s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3)))); + } + for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3)); + for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2]; + for (let n = 0; n < 16; n++) dst[n] /= det; + return dst; +} + +// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU +let matrix_identity = () => { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; +} +let matrix_translate = (x, y, z) => { + let m = matrix_identity(); + m[12] = x; + m[13] = y; + m[14] = z; + return m; +} +let matrix_perspective = (m) => { + let ret = [] + for (let i = 0; i < 16; i++) + ret[i] = m[i]; + for (let i = 2; i < 15; i += 4) { + ret[i] = -ret[i]; + ret[i + 1] += ret[i] / fl; + } + + return ret; +} +// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS: +let matrix_rotateX = theta => { + let m = matrix_identity(); + m[5] = cos(theta); + m[6] = sin(theta); + m[9] = -sin(theta); + m[10] = cos(theta); + return m; +} + +let matrix_rotateY = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[2] = -sin(theta); + m[8] = sin(theta); + m[10] = cos(theta); + return m; +} +let matrix_rotateZ = theta => { + let m = matrix_identity(); + m[0] = cos(theta); + m[1] = sin(theta); + m[4] = -sin(theta); + m[5] = cos(theta); + return m; +} +let matrix_scale = (x, y, z) => { + if (y === undefined) + y = z = x; + let m = matrix_identity(); + m[0] = x; + m[5] = y; + m[10] = z; + return m; +} +let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm + let res = []; + if (b.length < m * n) { //mat-vec multiply (i did this for my convenience) + for (let i = 0; i < m; ++i) { + res[i] = 0; + for (let j = 0; j < n; ++j) + res[i] += b[j] * a[m * j + i]; + } + return res; + } //otherwise mm multiply + for (let i = 0; i < m; ++i) + for (let j = 0; j < m; ++j) { + var t = 0; + for (let k = 0; k < n; ++k) + t += a[k * m + j] * b[i * n + k]; + res.push(t); + } + return res; +} +let const_multiply = (c, a, add = 0) => { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = (a[i] + add)* c; + return m; +} + +function dot(a, b) { + b=b?b:a; + let m = 0; + for (let i = 0; i < a.length; ++i) + m += a[i] * b[i]; + return m; +} + +function cross(a, b){ + let m = []; + m[0] = a[1]*b[2] - a[2]*b[1]; + m[1] = a[2]*b[0] - a[0]*b[2]; + m[2] = a[0]*b[1] - a[1]*b[0]; + return m; +} + +function plus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] + b[i]; + return m; +} + +function minus(a, b) { + let m = []; + for (let i = 0; i < a.length; ++i) + m[i] = a[i] - b[i]; + return m; +} + +function normalize(v) { + let res = []; + sum = 0; + for (let i = 0; i < v.length; ++i) + sum += v[i] * v[i]; + sum = sqrt(sum); + for (let i = 0; i < v.length; ++i) + res[i] = v[i] / sum; + return res; +} + + +let Matrix = function () { + let top = 0, + m = [matrix_identity()]; + this.identity = () => m[top] = matrix_identity(); + this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z)); + this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta)); + this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta)); + this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta)); + this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z)); + this.apply = (m1) => m[top] = matrix_multiply(m[top], m1); + this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]); + this.value = () => m[top]; + this.save = () => { + m[top + 1] = m[top].slice(); + top++; + } + this.restore = () => --top; +} +function hitTest_sphere(ray, sphere, r) { + let B = dot(ray, sphere); + let C = dot(sphere, sphere) - r*r; + let D = B * B - C; + if (D > 0.) { + //console.log(D); + //let t = -B - sqrt(D); + return 1; + } + return -1; +} +function hitTest_square(pt, invMatrix, shape){ + pt.push(1); + const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(invMatrix, pt)), + W = minus(pos, V), + A = shape.slice(1,4), + B = shape.slice(8,11), + C = shape.slice(15,18), + AB = minus(B, A), + AC = minus(C, A), + AB_AC = cross(AB, AC), + VA = minus(V, A), + t = - dot(AB_AC, VA)/dot(W, AB_AC), + P = plus(V, const_multiply(t, W)), + AP = minus(P, A), + d_AP_AC = dot(AP, AC), + d_AP_AB = dot(AP, AB); + if(0 { + let ret = []; + if(v[3]) + for(let i = 0; i < 3; ++i) + ret[i] = v[i]/v[3]; + else ret = v.slice(0,3); + return ret; +} +let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') { + this.shape = shape; + this.enabled = true; + this.outlineTy = outlineTy; + this.onClick = onclick; + this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);}; + this.updateMatrix(matrix_identity()); + this.hovering = false; + this.activated = false; + this.getShape = () => this.shape; + this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix); + setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);} + this.resetShape = (_sh) => { + if(this.shape) delete this.shape; + this.shape = new Float32Array(_sh); + let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity); + for(let i = 0; i < _sh.length; i += 7){ + const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]]; + v.forEach((c, j) => { + maxV[j] = maxV[j] > c ? maxV[j] : c; + minV[j] = minV[j] < c ? minV[j] : c; + }) + } + //build outline + switch(this.outlineTy){ + case 'sphere': + this.origin = const_multiply(.5, plus(maxV, minV)); + //this.origin.push(1); + this.radius = 0.6*vec_len(minus(maxV, minV))/2.; + break; + case 'square': + break; + case 'circle': + break; + } + switch(this.outlineTy){ + case 'sphere': + this.hitTest = (pt) => { + pt.push(1); + const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])), + pos = dot_xyz(matrix_multiply(this.invMatrix, pt)); + let W = normalize((minus(pos, V))); + let Vp = minus(V, this.origin); + return hitTest_sphere(W, Vp, this.radius); + } + break; + case 'square': + this.hitTest = (pt) => { + this.P = hitTest_square(pt, this.invMatrix, this.shape); + return this.P; + } + break; + case 'circle': + break; + } + }; + if(!shape || shape.length != 0) + this.resetShape(shape); + canvas_controls.push(this); +} +let setM = (m) => { + let mm = matrix_perspective(m); + setUniform('Matrix4fv', 'uMatrix', false, mm); + setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm)); +} +//------ CREATING MESH SHAPES + +// CREATE A MESH FROM A PARAMETRIC FUNCTION + +let createMesh = (nu, nv, f, data, oid = 0) => { + let tmp = []; + for (let v = 0; v < 1; v += 1 / nv) { + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(f(u, v, oid, data)); + tmp = tmp.concat(f(u, v + 1 / nv, oid, data)); + } + tmp = tmp.concat(f(1, v, oid, data)); + tmp = tmp.concat(f(0, v + 1 / nv, oid, data)); + } + return new Float32Array(tmp); +} +//Create a Mesh from Splines +let createMeshFromSpline = (idx, + nu, nv, oid = 16, additional_offset = 0, f = () => {}) => { + let S = paths[idx], + meta = path_misc[idx]; + const n_min = 2; + let ds = meta[0] / nv, + curr_s = 0, + curr_d = S[0][2] / Math.ceil(S[0][2] / ds), + i = 0, + s = 0; + let tmp = [], + ret = undefined; + while (s < meta[0] - 1 / 100000) { + + for (let u = 0; u <= 1; u += 1 / nu) { + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset)); + } + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset)); + tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset)); + if (ret = f(i, tmp, oid)) { + oid = ret; + } + curr_s += curr_d; + if (curr_s >= 1) { + s += S[i][2]; + ++i; + if (i >= S.length) + break; + let curr_n = Math.ceil(S[i][2] / ds); + curr_n = curr_n < n_min ? n_min : curr_n; + curr_d = 1 / curr_n; + curr_s = 0; + } + } + return new Float32Array(tmp); +} +// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH + +let glueMeshes = (a, b) => { + let c = []; + for (let i = 0; i < a.length; i++) + c.push(a[i]); // a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a + for (let i = 0; i < VERTEX_SIZE; i++) + c.push(b[i]); // + first vertex of b + for (let i = 0; i < b.length; i++) + c.push(b[i]); // + b + return new Float32Array(c); +} + + +let uvToSphere = (u, v, i) => { + let theta = 2 * Math.PI * u; + let phi = Math.PI * (v - .5); + let x = Math.cos(theta) * Math.cos(phi); + let y = Math.sin(theta) * Math.cos(phi); + let z = Math.sin(phi); + return [i, x, y, z].concat(normalize([x, y, z])); +} + +let uvToTube = (u, v, i) => { + let theta = 2 * Math.PI * u; + let x = Math.cos(theta); + let y = Math.sin(theta); + let z = 2 * v - 1; + return [i, x, y, z].concat(normalize([x, y, 0])); +} + +let uvToDisk = (u, v, i, dz) => { + if (dz === undefined) + dz = 0; + let theta = 2 * Math.PI * u; + let x = Math.cos(theta) * v; + let y = Math.sin(theta) * v; + let z = dz; + return [i, x, y, z].concat([0, 0, Math.sign(z)]); +} +let uvToTorus = (u, v, i, r) => { + let theta = 2 * pi; + let phi = theta * v; + theta *= u; + let x = 1 + r * cos(phi); + let y = sin(theta) * x; + x *= cos(theta); + let z = r * sin(phi); + let tx = -sin(theta), + ty = cos(theta), + tsx = sin(phi), + tsy = tsx * tx, + tsz = cos(phi); + tsx *= -ty; + return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx])); +} + +let createCube = (w, h, l, id) => { + let mesh = []; + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]); + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + + mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]); + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + + mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]); + mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]); + return new Float32Array(mesh); +} + +function updatePositions() { + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)); + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]); + invTr = matrix_inverse(m); + + positionsupdated = false; +} +function controls_hitTest(pos, clicked = false){ + // let iMin = -1, tMin = 10000.; + if(channel_disp == 5) + return; + canvas_controls.forEach((c, i) => { + if(c.enabled && c.hitTest && (c.hover||clicked) ){ + let t = c.hitTest(pos); + if(t != -1){ + if(clicked) + c.onClick(); + else + c.hover(true); + } + else + if(c.hovering) + c.hover(false); + } + }); + //ground + if(ground_m && ground){ + let invG = matrix_inverse(ground_m); + let P = hitTest_square(pos, invG, ground); + console.log(P); + //let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin); + if(P!=-1) + if(near_magician){ + if(slider.dragging === true){ + slider_dl += 10 * (P[0] - slider.lastPos); + if(slider_dl < 0) slider_dl = 0; + else if (slider_dl > 9) slider_dl = 9; + slider.lastPos = P[0]; + // onUpdate + let id = 1 + .45*slider_dl/9; + changeID(id, cylinderMesh); + } else if(clicked) { + const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]); + const rr = PO[0]*PO[0] + PO[1] * PO[1]; + if(rr < .003){ + slider.dragging = true; + slider.lastPos = P[0]; + } + } + } else { + if(clicked || running<=0) + { + direction_l = [P[0] - delta_l[0]/10, P[1] - delta_l[1]/10]; + if(P[1] < 0.00000000000001) + P[1] = 0.0000000000000001; + figure_rot = atan(direction_l[0]/direction_l[1]); + if(direction_l[1] < 0){ + figure_rot = pi + figure_rot; + } + } + //if(figure_rot < 0) figure_rot += pi; + //updateStatus([figure_rot, direction_l[0]/direction_l[1]]); + if(clicked) + { + dir_sdl = vec_len(direction_l)*10; + direction_l = normalize(direction_l); + rebuild = true; + running = 1; + } + } + } + //return iMin; +} +function hitTest(pos) { + + let m = matrix_rotateY(-mousedx); + m = matrix_multiply(m, matrix_rotateX(-mousedy)); + + + let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)]; + m = const_multiply((fl + 1 + mousedz) / (fl + 1), m); + + let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], pos[1], -1], 3, 3); + + let W = normalize(minus(trPos, V)); + let tMin = 10000.; + let iMin = -1; + for (let i = 0; i < cns; i++) { + let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i])); + let B = dot(W, Vp); + let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4]; + let D = B * B - C; + if (D > 0.) { + let t = -B - sqrt(D); + if (t > 0.0 && t < tMin) { + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + } + } + return iMin; +} +let matrix_transform = (m, p) => { + let x = p[0], + y = p[1], + z = p[2], + w = p[3] === undefined ? 1 : p[3]; + let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w, + m[1] * x + m[5] * y + m[9] * z + m[13] * w, + m[2] * x + m[6] * y + m[10] * z + m[14] * w, + m[3] * x + m[7] * y + m[11] * z + m[15] * w + ]; + return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q; +} +let evalSpline = (h, t) => { + // t *= (h.length - 2) / 2; + // let n = 2 * Math.floor(t); + // t = t % 1; + // let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]); + // return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3]; + return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3]; +} +let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => { + const epsilon = .001; + let z0 = evalSpline(S0, v), + z1 = evalSpline(S0, v + epsilon), + + r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset, + r1 = evalSpline(S1, v + epsilon), + + tilt = Math.atan2(r0 - r1, z1 - z0); + + let xx = cos(2 * pi * u), + yy = sin(2 * pi * u); + let x = r0 * xx, // POSITION + y = r0 * yy, + z = z0, + nx = cos(tilt), // NORMAL + ny = nx * yy, + nz = sin(tilt); + nx *= xx; + return [id, x, y, z, nx, ny, nz]; +} +let concat = (a, b) => b.forEach(e => a.push(e)); \ No newline at end of file diff --git a/proj/libX.js b/proj/libX.js new file mode 100644 index 0000000..2c9b115 --- /dev/null +++ b/proj/libX.js @@ -0,0 +1,327 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// +// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET. +// +////////////////////////////////////////////////////////////////////////////////////////// + +let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS + , 'precision highp float;', 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }','\n#define _NDEBUG\n' +].join('\n'); +var ns = 6, + cns = 6; +fragmentShaderHeader += 'const int ns = ' + ns + ';\n'; +var fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; +var status = 'The TV is a touch screen!'; +let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader + +let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; + +function getBlob(data) { + let bytes = new Array(data.length); + for (let i = 0; i < data.length; i++) { + bytes[i] = data.charCodeAt(i); + } + return new Blob([new Uint8Array(bytes)]); +} +let stack = function (){ + this.storage = ['Ready.']; + this.len = 1; + this.pop = ()=>{return this.storage[--this.len-1];} + this.push = (o)=>{this.storage[this.len++] = o;} + this.update = (o)=>{this.storage[this.len-1] = o;} + this.top = () => {return this.storage[this.len - 1];} + this.clear = () => this.len = 1; +} +let status_history = new stack(); +function updateStatus(val){ + status_history.update(status); + status = val; + errorMessage.innerHTML = val; +} +function pushStatus(val){ + status_history.push(status); + status = val; + errorMessage.innerHTML = val; +} +function restoreStatus(val){ + status = status_history.pop(); + errorMessage.innerHTML = status; +} +function resetStatus() { + status_history.clear(); + updateStatus('Ready.'); +} +var texture = [], + gl, program; +let textures = []; +let lock = false; + +function loadTexture(gl, url, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + } + const image = new Image(); + image.onload = function () { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, image); + + if (isPowerOf2(image.width) && isPowerOf2(image.height)) { + gl.generateMipmap(gl.TEXTURE_2D); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + }; + image.src = url; +} +function initTextures(gl, program){ + for (let i = 0; i < ns; ++i) { + loadTexture( gl, './' + (i + 1) + '.jpg', i); //Texture loading. + textures[i] = i; + } + textures[ns] = ns; + initVideoTexture(gl, ns + 0); + textures[ns + 1] = ns + 1; + loadTexture(gl, './starburst.png', ns + 1); + createNoiseTexture(); + textures[ns+2] = ns+2; + gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); +} +function initVideoTexture(gl, i) { + const level = 0; + const internalFormat = gl.RGBA; + const width = 1; + const height = 1; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + if (texture[i] == null) { + texture[i] = gl.createTexture(); + const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue + gl.activeTexture(gl.TEXTURE + i); + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } +} +function updateVideoTexture(gl, v_control, i){ + //return; + const level = 0; + const internalFormat = gl.RGBA; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + gl.bindTexture(gl.TEXTURE_2D, texture[i]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + srcFormat, srcType, v_control); +} +function createNoiseTexture(){ + const level = 0; + const internalFormat = gl.RGBA; + const width = 100; + const height = 100; + const border = 0; + const srcFormat = gl.RGBA; + const srcType = gl.UNSIGNED_BYTE; + texture[ns+2] = gl.createTexture(); + let random_list = []; + let get_random = ()=>Math.pow(Math.random(), .1); + let get_random2 = ()=>{ + let r = Math.random(); + if (r < .5) r = .5*Math.pow(2*r,.1); + else r = .5 + .5*Math.pow(r,10); + return r;}; + for(let i = 0; i < 40000; ++i){ + if(i%4 == 3) + random_list.push(255*get_random()); + else + random_list.push(255*get_random2()); + } + const pixel = new Uint8Array(random_list); + gl.activeTexture(gl.TEXTURE0+ns+2); + gl.bindTexture(gl.TEXTURE_2D, texture[ns+2]); + gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, + width, height, border, srcFormat, srcType, + pixel); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); +} +function isPowerOf2(value) { + return (value & (value - 1)) == 0; +} +function buildShaders(vertexShader, fragmentShader){ + let curr_program = canvas1.gl.createProgram(); + let compile_shaders = (type, src) => { + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + gl.attachShader(curr_program, shader); + }; + compile_shaders(gl.VERTEX_SHADER, vertexShader); + compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + gl.linkProgram(curr_program); + + return curr_program; +} +function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS + console.log('glstart'); + setTimeout(function () { + try { + canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD. + } catch (e) { + throw 'Sorry, your browser does not support WebGL.'; + } + + + canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders: + gl = this.gl; + program = gl.createProgram(); // Create the WebGL program. + + function addshader(type, src) { // Create and attach a WebGL shader. + function spacer(color, width, height) { + return '
 
'; + } + errorMessage.innerHTML = status; + // errorMarker.innerHTML = spacer('white', 1, 1) + '\u25B6'; + let shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + let msg = gl.getShaderInfoLog(shader); + console.log('Cannot compile shader:\n\n' + msg); + + let a = msg.substring(6, msg.length); + let line = 0; + if (a.substring(0, 3) == ' 0:') { + a = a.substring(3, a.length); + line = parseInt(a) - nfsh; + + editor.session.setAnnotations([{ + row: line, + column: 0, + text: msg, + type: "error" + }]); + } + let j = a.indexOf(':'); + a = 'line ' + (line + 1) + a.substring(j, a.length); + if ((j = a.indexOf('\n')) > 0) + a = a.substring(0, j); + errorMessage.innerHTML = a; + } else + editor.session.clearAnnotations(); + gl.attachShader(program, shader); + }; + + addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders. + addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader); + + gl.linkProgram(program); // Link the program, report any errors. + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + console.log('Could not link the shader program!'); + gl.useProgram(program); + gl.program = program; + if(!gl.shaders) + gl.shaders = [program]; + else gl.shaders[0] = program; + initTextures(gl, program); + positionsupdated = true; + let attribs = [ + .05, .05, .1, .5, .5, 1., 1., .5, .5, 20., 5,0,0, 0.,.0, 1.3, + .1, .05, .05, 1., .5, .5, 1., .5, .5, 10., .1,8,0, .3, 1., 1.3, + .1, .05, .05, .71, .71, .71, .71, .71, .71, 10., .1,0,9, 0.3, .0, 1.5, + .1, .1, .1, .71, .71, .71, .71, .71, .71, 10., 2,2,0, 0.05, 0., 1., + .0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 4,4,4, 0., .85, 1.5 + ] + var offset = 0; + for (let i = 0; i < ns; i++) { + setUniform('3fv', 'Ambient[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('3fv', 'Diffuse[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('4fv', 'Specular[' + i + ']', attribs.slice(offset, offset += 4)); + setUniform('3fv', 'Glow[' + i + ']', attribs.slice(offset, offset += 3)); + setUniform('1fv', 'ks[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kr[' + i + ']', attribs.slice(offset, offset += 1)); + setUniform('1fv', 'kf[' + i + ']', attribs.slice(offset, offset += 1)); + } + offset = 0; + for (let i = 0; i < n_shapes; i++) { + setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3)); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip + // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles. + // [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clearDepth(-1); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(oid); + gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0); + + let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(aPos); + gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4); + + let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex. + gl.enableVertexAttribArray(normal); + gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4); + } + + canvas.setShaders(vertexShader, fragmentShader); // Initialize everything, + setInterval(function () { // Start the animation loop. + gl = canvas.gl; + if (gl.startTime === undefined) // First time through, + gl.startTime = Date.now(); // record the start time. + animate(gl); + //gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square. + }, 30); + + }, 100); // Wait 100 milliseconds after page has loaded before starting WebGL. +} + +// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html. + +function animate() {} + +function setUniform(type, name, a, b, c, d, e, f) { + if (gl) { + let loc = gl.getUniformLocation(gl.program, name); + (gl['uniform' + type])(loc, a, b, c, d, e, f); + } +} + +//let VERTEX_SIZE = 3; + + +let VERTEX_SIZE = 7; + + +var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => { + gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW); + gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE); +} \ No newline at end of file diff --git a/proj/old/shader.frag b/proj/old/shader.frag new file mode 100644 index 0000000..35c5e0b --- /dev/null +++ b/proj/old/shader.frag @@ -0,0 +1,45 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +uniform sampler2D uSampler[ns + 1]; //ns + vs +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + if(id < -2.5){gl_FragColor = vec4(texture2D(uSampler[ns + 0], (1.+texPos.xy)/2.).xyz, 1.); return;} //pjsk + else if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id < 1.5) {color = vec3(1.0000, 0.3570, 0.3570);sp = 1.; df=.2; amb = .7, ex = 20.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) {color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {float alp = 2.*abs(.49999 - fract(id+.00001));gl_FragColor=vec4(1.,1.,1., alp);return;} + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alpha); +} diff --git a/proj/old/shader.vert b/proj/old/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/proj/old/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/proj/paths.txt b/proj/paths.txt new file mode 100644 index 0000000..b331549 --- /dev/null +++ b/proj/paths.txt @@ -0,0 +1,104 @@ +M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04 +c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8 +c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11 +c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43 +c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83 +C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57 +c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04 +c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14 +c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54 +c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78 +c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1 +c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55 +c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09 +c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z +N +M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8 +c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69 +c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28 +c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1 +c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45 +c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73 +c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33 +c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48 +c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28 +c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78 +c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86 +c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4 +c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22 +C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87 +c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35 +c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81 +c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79 +c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12 +c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83 +c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57 +c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85 +c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74 +c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08 +c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59 +c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28 +c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22 +c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28 +c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56 +c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19 +c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83 +c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2 +c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27 +c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54 +c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55 +c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69 +c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23 +c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21 +C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49 +c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z +N +M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z +N +M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z +N +M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z +N +M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17 + c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64 + c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17 + c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42 + c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21 + c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84 + c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59 + c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88 + c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z +N +M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32 + c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96 + c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z +N +M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17 + c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560 + c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49 + c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z +N +M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38 + c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91 + c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76 + c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28 + c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85 + c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09 + c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96 + c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95 + c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32 + c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58 + c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z +N +M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88 + c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54 + c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15 + c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17 + c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z +N +M9.22,0C6.92,4.49,4,11.35,2.55,20.09 + c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95 + c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91 + c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77 + c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z +N \ No newline at end of file diff --git a/proj/pjsk.mp4 b/proj/pjsk.mp4 new file mode 100644 index 0000000..d0c743e Binary files /dev/null and b/proj/pjsk.mp4 differ diff --git a/proj/porc.py b/proj/porc.py new file mode 100644 index 0000000..d64670b --- /dev/null +++ b/proj/porc.py @@ -0,0 +1,25 @@ +import cv2 +import numpy as np +import math +mat = cv2.imread('starburst.png') +mat2 = np.zeros((900, 1273, 3)) +print(mat.shape) +rad = 0 +for i in range(0, 900): + dt = 0 + s = math.sin(rad) + c = math.cos(rad) + for j in range(0, 1273): + # if(dt*c > 913 or dt*c < -913 or dt * s > 900 or dt*s < -900): + # break + x = 900+int(dt*s) + x = x if x < 1800 else 1799 + y = 913+int(dt*c) + y = y if y < 1826 else 1799 + + mat2[i][j] = mat[900+int(dt*s)][913+int(dt*c)] + dt += 1 + rad += .007 +cv2.imwrite('starburst3.png',mat2) +cv2.imshow('a', mat2) +cv2.waitKey(0) \ No newline at end of file diff --git a/proj/rtx2.frag b/proj/rtx2.frag new file mode 100644 index 0000000..a0bd1b8 --- /dev/null +++ b/proj/rtx2.frag @@ -0,0 +1,263 @@ + +#ifndef _NDEBUG + precision highp float; + const int ns = 5; + const int cns = 5; + float noise(vec3 v){return 1.;} +#endif +#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;} +#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1))) +vec3 foregroundColor = vec3(.0841, .5329, .9604); +vec3 groundColor = vec3(.2, .3, .5); +vec4 groundSpecular = vec4(.71, .71, .71, 10.); +uniform float uTime;// TIME, IN SECONDS +uniform vec3 Ambient[ns], Diffuse[ns]; +uniform vec4 Specular[ns]; +uniform vec3 Glow[ns]; +uniform float ks[ns], kr[ns], kf[ns]; +uniform vec4 Sph[ns]; +uniform sampler2D uSampler[ns+2]; +uniform vec3 V0; +uniform int sel; +const float kf_air = 1.000293; +varying vec3 trPos; +varying vec3 texPos; +const float pi=3.14159265359; +const float _2pi=2.*pi; + +/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/ +/*---->*/const int n_ref=15; //2^n-1 because each hit now spawn at most 2 rays. +//const int _n_swap = 3; //cosntexpr log(n_ref + 1) +/**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/ + +const int max_stack = (n_ref+1)/4; +vec3 scolor = vec3(0,0,0); +struct Ray{ + vec3 V; + vec3 W; + float kf, cumulativeK, decay; +} stack1[max_stack], stack2[max_stack]; +bool modulo2(int n){ + return n-2*(n/2) == 1; +} +vec2 getTextCoord(vec3 tex_sph, float R){ + float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r + tex_x=fract(tex_x+uTime/20.); + return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5); +} +#define clamp1(x) ((x)<0.?1.+(x):((x) > 1. ? (x) - 1.:(x))) +void main(){ + vec3 LDir=vec3(.5,.5,.5); + vec3 LCol=vec3(1.,1.,1.); + float currKf = kf_air; + vec3 color=vec3(.2, .3, .5); + vec3 V = V0; + vec3 W=(trPos-V); + const bool glassy = false; + float glassyw = 1.; + if(glassy) + { + vec2 noiseTex = (texPos.xy + 1.)/2.; + noiseTex = vec2(clamp1(noiseTex.x ), clamp1(noiseTex.y )); + vec4 Wnoise = texture2D(uSampler[ns], noiseTex); + W += (Wnoise.xyz-.5)*.4; + glassyw = Wnoise.w; + } + bool selected = false; + float currentK = 1.; + float curr_decay = 1.; + int curr_ptr = 0, curr_top = 0, next_top = 0; + bool final = false, stackswap = false, stop = false; + for(int j=0;j 0){ + Ray currR; + if(stackswap) + currR = stack1[curr]; + else + currR = stack2[curr]; + currKf = currR.kf; + currentK = currR.cumulativeK; + curr_decay = currR.decay; + if(currKf <= 0.001 || currentK <= 0.001) + skip = true; + V = currR.V; + W = currR.W; + } + else + W = normalize(W); + if(!skip){ + float tMin=10000.; + int iMin = -1; + for(int i=0;i0.){ + float t=-B-sqrt(D); + if(t >= 0.01 && t < tMin){ + tMin = t; // This is an optimization, we don't have to do lighting/tex + iMin = i; // for objects that are occuluded, which is expensive! + } + else if (t >= -0.01 && t <0.01){ + t = -(t + 2.*B); + if(t > 0.01 && t < tMin){ + tMin = t; + iMin = i; + } + } + } + } + + if(iMin >= 0){ + if(j == 0 && iMin == sel) + selected = true; + vec3 S=V+tMin*W; + if(curr_decay > .01) + curr_decay = curr_decay/(1.+tMin*tMin); + for(int i = 0; i < cns; ++ i) + if(i == iMin) + { + vec3 texture_color; + vec3 tex_sph = (S-Sph[i].xyz); + texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz; + + vec3 N=normalize(S-Sph[i].xyz); + vec3 realLDir=normalize(LDir-S); + float c1 =dot(N, W); + float eta, nextkf; + if(c1<0.){ + color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color; + if(final) //if it's the last hit + { + color += Specular[i].xyz*pow(max(0., + dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w); + if(curr_decay > .01 && Glow[i].x > .01) + scolor += currentK*curr_decay*Glow[i]; + scolor += color * currentK; + break; + } + else{ + c1 = -c1; + eta = currKf/kf[i]; + nextkf = kf[i]; + } + } + else{ + N = -N; + eta = currKf/kf_air; + nextkf = kf_air; + color = Ambient[i]; + } + float c2 = (1.-eta*eta*(1.-c1*c1)); + float nextks = currentK * ks[i], nextkr = currentK * kr[i]; + bool refl = nextks > 0.01, refr = nextkr > 0.01; + if(refl || refr) + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap){ + if(refl) + { + stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks, curr_decay); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + else + stack2[k] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + currentK -= nextkr; + next_top ++; + } + }else{ + if(refl) + { //remember, c1 = -NW now + stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks, curr_decay); //reflection + currentK -= nextks; + next_top ++; + } + if(refr) + { + if(refl) + stack1[k+1] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + else + stack1[k] = Ray(S, REFRACTION, nextkf, nextkr, curr_decay); //refraction + currentK -= nextkr; + next_top ++; + } + } + break; + } + scolor += color * currentK; + if(curr_decay > .01 && Glow[i].x > .01) + scolor += currentK*curr_decay*Glow[i]; + break; + } + } + else { + float t = -(.2+V.y)/W.y; + float sx = V.x + t* W.x, sz = V.z + t * W.z; + + if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.) + { + if(curr_decay > .01) + curr_decay = curr_decay/(1.+t*t); + vec3 S = vec3(sx, -.2, sz); + vec3 realLDir=normalize(LDir - S); + color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz; + if(final&&abs(sx)<1.5 && abs(sz+.6)<3.) + { + color += groundSpecular.xyz* //specular for ground. + pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); + scolor += currentK * color; + } + else + { + for(int k = 0; k < max_stack; ++k) + if(k == next_top){ + if(stackswap) + stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15, curr_decay); //reflection + else + stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15, curr_decay); //reflection + next_top ++; + break; + } + scolor += (currentK*.85)*color; + } + } + else{ + if(j > 0) + scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1); + else scolor = foregroundColor*0.6; + } + } + } + if(++curr_ptr >= curr_top){ + if(next_top <= 0) + stop = true; + if(next_top * 2 > max_stack) + final = true; + curr_top = next_top; + next_top = 0; + curr_ptr = 0; + stackswap = !stackswap; + } + break; + } + } + if(stop) + break; + } + if(selected) + scolor.x += 0.5; + + if(glassy) + scolor *= glassyw*glassyw; + gl_FragColor=vec4(sqrt(scolor),1.); +} diff --git a/proj/scene.mp4 b/proj/scene.mp4 new file mode 100644 index 0000000..ae68718 Binary files /dev/null and b/proj/scene.mp4 differ diff --git a/proj/shader.frag b/proj/shader.frag new file mode 100644 index 0000000..e073d1d --- /dev/null +++ b/proj/shader.frag @@ -0,0 +1,59 @@ + +vec3 foregroundColor = vec3(.0841, .5329, .9604); +uniform vec3 starColors[10]; +uniform vec3 V0; +uniform sampler2D uSampler[ns + 1]; //ns + vs +varying vec3 norm; +varying float id; +varying vec3 glpos; +varying vec3 texPos; +vec3 LDir=vec3(.5,.5,.5); +void main(){ + vec3 color =foregroundColor.xyz; + float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.; + vec3 l = vec3(1,1,1); + float alp = 2.*abs(.49999 - fract(id+.00001)); + if(id < -2.5){gl_FragColor = vec4(texture2D(uSampler[ns + 0], (1.+texPos.xy)/2.).xyz, 1.); return;} //pjsk + else if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;} + else if(id <-.5) {color = vec3(0.,1.,0.2034);} + else if(id < 1.5) {color = vec3(1.0000, 0.3570, 0.3570);sp = 1.; df=.2; amb = .7, ex = 20.;} + else if (id < 2.5) color = vec3(1.,.16,.36); + else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;} + else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;} + else if (id < 6.5) {} + else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;} + else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;} + else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;} + else if (id < 14.5) { + color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color; + if(texPos.y > .3) + color = vec3(1.,1.,1.); + } + else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;} + else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;} + else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;} + else if (id < 18.5) { + color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.; + vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(.8 * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + + if(id < 0. &&id > -1.5){ + vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.)); + // APPLY PROCEDURAL NOISE TEXTURE. + float cloud = min(0.99, max(0., 1. * noise(1. * P))); + color = (1.-cloud)*color + starColors[5] * cloud*3.; + } + vec3 V = V0; + vec3 W=normalize(glpos-V); + vec3 realLDir=normalize(LDir - glpos); + + color = color*(amb+ df*max(0.,dot(norm,realLDir))) + + sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l; + gl_FragColor=vec4(sqrt(color), alp); +} diff --git a/proj/shader.vert b/proj/shader.vert new file mode 100644 index 0000000..b03c3ed --- /dev/null +++ b/proj/shader.vert @@ -0,0 +1,18 @@ +uniform mat4 uMatrix; +uniform mat4 invMatrix; +uniform mat3 transformation; +attribute float oid; +attribute vec3 aPos; +attribute vec3 normal; +varying float id; +varying vec3 glpos; +varying vec3 norm; +varying vec3 texPos; +void main() { + vec4 pos = uMatrix * vec4(aPos, 1.); + texPos = aPos; + gl_Position = pos ; + glpos = pos.xyz; + id = oid; + norm = normalize(vec4(normal,0.)*invMatrix).xyz; +} diff --git a/proj/starburst.png b/proj/starburst.png new file mode 100644 index 0000000..8b656fb Binary files /dev/null and b/proj/starburst.png differ