diff --git a/README.md b/README.md index 9bd6107..749bdda 100644 --- a/README.md +++ b/README.md @@ -1,137 +1,53 @@ ------------------------------------------------------------------------------- -CIS565: Project 5: WebGL +WebGL Demos (Clike images for demos) ------------------------------------------------------------------------------- Fall 2013 ------------------------------------------------------------------------------- -Due Friday 11/08/2013 -------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -NOTE: +DEMO1: Wire Frame Waves ------------------------------------------------------------------------------- -This project requires any graphics card with support for a modern OpenGL -pipeline. Any AMD, NVIDIA, or Intel card from the past few years should work -fine, and every machine in the SIG Lab and Moore 100 is capable of running -this project. +* A sin-wave based vertex shader: +[![screen](images/Sine_Wave.png)](http://wuhao1117.github.io/WebGL-Experiments/vert_wave.html) -This project also requires a WebGL capable browser. The project is known to -have issues with Chrome on windows, but Firefox seems to run it fine. +* A simplex noise based vertex shader: +[![screen](images/Simplex_Wave.png)](http://wuhao1117.github.io/WebGL-Experiments/simplex_wave.html) -------------------------------------------------------------------------------- -INTRODUCTION: -------------------------------------------------------------------------------- -In this project, you will get introduced to the world of GLSL in two parts: -vertex shading and fragment shading. The first part of this project is the -Image Processor, and the second part of this project is a Wave Vertex Shader. -In the first part of this project, you will implement a GLSL vertex shader as -part of a WebGL demo. You will create a dynamic wave animation using code that -runs entirely on the GPU. +* Pseudo height field water +[![screen](images/heightFieldWater.png)](http://wuhao1117.github.io/WebGL-Experiments/custom_wave.html) -In the second part of this project, you will implement a GLSL fragment shader -to render an interactive globe in WebGL. This will include texture blending, -bump mapping, specular masking, and adding a cloud layer to give your globe a -uniquie feel. ------------------------------------------------------------------------------- -CONTENTS: +About Height Field Water: ------------------------------------------------------------------------------- -The Project4 root directory contains the following subdirectories: - -* part1/ contains the base code for the Wave Vertex Shader. -* part2/ contains the base code for the Globe Fragment Shader. -* resources/ contains the screenshots found in this readme file. +It was not very successful. Inspired by [this](http://madebyevan.com/webgl-water/) demo, I planned to do something similar, without the raytraced shading part. While [FFT](http://www.edxgraphics.com/2/post/2011/10/simulating-ocean-waves-with-fft-on-gpu.html) approach proved to be to hard given the time constrain for this project, I do think a "Hello World" height field water simulation is feasible in WebGL. The core part of the code is simply the following: -------------------------------------------------------------------------------- -PART 1 REQUIREMENTS: -------------------------------------------------------------------------------- - -In Part 1, you are given code for: + forall i,j do u[i,j] = u0[i,j]; v[i,j] = 0; + loop + forall i,j do v[i,j] += (u[i-1,j] + u[i+1,j] + u[i,j-1] + u[i,j+1])/4 - u[i,j]; + forall i,j do v[i,j] *= 0.99; + forall i,j do u[i,j] += v[i,j]; + endloop -* Drawing a VBO through WebGL -* Javascript code for interfacing with WebGL -* Functions for generating simplex noise +where i,j are the planar coordinates of the vertices, u is the height field and v is the vertical velocity field. Although implementing this code in CPU is trivial, to port it to GPU is not easy. Two major difficulties include: -You are required to implement the following: +* Reading the height field data and velocity data +* Writing the height field and velocity data back for next simulation step -* A sin-wave based vertex shader: +Reading through literature, one GPGPU technique seemed very promising, which is called "Ping-Ponging". The idea is supply the field data as a texture, and after manipulating with the data, render the resulting field to a separate framebuffer, where height and velocity information is stored into another texture. Before next simulation step, just swap the two texture and repeat. To store the real number data into texture, normal texture format would not be enough, instead, OES\_texture\_float extension needs to be activated and used. Also, because we are modifying the vertex positions (or heights) in vertex shader, the texture containing required data needs to be supplied to vertex shader, which in turn needs another WebGL extension: vertex texture fetches. In order for your GPU and browser support this feature, the constant GL\_MAX\_VERTEX\_TEXTUR\E_IMAGE\_UNITS has to be larger than 0. -![Example sin wave grid](resources/sinWaveGrid.png) - -* A simplex noise based vertex shader: +With all these knowlegde secured and prerequisite satisfied (which spent me 2 days, along with learning all sorts of JavaScript stuffs), there is another challenge: you cannot readback your height field and velocity data from vertex shader! But how do we do it in fragment shader? The vertices would all be transformed and data destroyed. At this point, the only solution I can think of, is somehow change the viewport to the grid plane, render it to texture, change the viewport back and render it normally. I was not sure I can code all this in java script in 1 day, given I even hadn't tried this in C++, which is much more familiar to me. So I decided to leave it for the future, maybe an possible subject for the final project. -![Example simplex noise wave grid](resources/oceanWave.png) - -* One interesting vertex shader of your choice +As for the "Height Field Water" I actually implemented, it is just superimposing multiple simplex noises with different frequency to give a fluctuating and non-periodic look. Without proper rendering, they look pretty crappy. ------------------------------------------------------------------------------- -PART 1 WALKTHROUGH: +DEMO2: Virtual Globe ------------------------------------------------------------------------------- -**Sin Wave** - -* For this assignment, you will need the latest version of Firefox. -* Begin by opening index.html. You should see a flat grid of black and white - lines on the xy plane: - -![Example boring grid](resources/emptyGrid.png) - -* In this assignment, you will animate the grid in a wave-like pattern using a - vertex shader, and determine each vertex’s color based on its height, as seen - in the example in the requirements. -* The vertex and fragment shader are located in script tags in `index.html`. -* The JavaScript code that needs to be modified is located in `index.js`. -* Required shader code modifications: - * Add a float uniform named u_time. - * Modify the vertex’s height using the following code: - - ```glsl - float s_contrib = sin(position.x*2.0*3.14159 + u_time); - float t_contrib = cos(position.y*2.0*3.14159 + u_time); - float height = s_contrib*t_contrib; - ``` - - * Use the GLSL mix function to blend together two colors of your choice based - on the vertex’s height. The lowest possible height should be assigned one - color (for example, `vec3(1.0, 0.2, 0.0)`) and the maximum height should be - another (`vec3(0.0, 0.8, 1.0)`). Use a varying variable to pass the color to - the fragment shader, where you will assign it `gl_FragColor`. - -* Required JavaScript code modifications: - * A floating-point time value should be increased every animation step. - Hint: the delta should be less than one. - * To pass the time to the vertex shader as a uniform, first query the location - of `u_time` using `context.getUniformLocation` in `initializeShader()`. - Then, the uniform’s value can be set by calling `context.uniform1f` in - `animate()`. - -**Simplex Wave** - -* Now that you have the sin wave working, create a new copy of `index.html`. - Call it `index_simplex.html`, or something similar. -* Open up `simplex.vert`, which contains a compact GLSL simplex noise - implementation, in a text editor. Copy and paste the functions included - inside into your `index_simplex.html`'s vertex shader. -* Try changing s_contrib and t_contrib to use simplex noise instead of sin/cos - functions with the following code: - -```glsl -vec2 simplexVec = vec2(u_time, position); -float s_contrib = snoise(simplexVec); -float t_contrib = snoise(vec2(s_contrib,u_time)); -``` +[![screen](images/globe.png)](http://wuhao1117.github.io/WebGL-Experiments/index.html) -**Wave Of Your Choice** -* Create another copy of `index.html`. Call it `index_custom.html`, or - something similar. -* Implement your own interesting vertex shader! In your README.md with your - submission, describe your custom vertex shader, what it does, and how it - works. - -------------------------------------------------------------------------------- -PART 2 REQUIREMENTS: -------------------------------------------------------------------------------- -In Part 2, you are given code for: +Basic features of this virtual globe demo are: * Reading and loading textures * Rendering a sphere with textures mapped on @@ -142,7 +58,7 @@ In Part 2, you are given code for: * left-click and drag moves the camera around * right-click and drag moves the camera in and out -You are required to implement: +Additionally: * Bump mapped terrain * Rim lighting to simulate atmosphere @@ -150,228 +66,27 @@ You are required to implement: * Specular mapping * Moving clouds -You are also required to pick one open-ended effect to implement: +To enhance realism, I also Implemented: -* Procedural water rendering and animation using noise -* Shade based on altitude using the height map * Cloud shadows via ray-tracing through the cloud map in the fragment shader -* Orbiting Moon with texture mapping and shadow casting onto Earth -* Draw a skybox around the entire scene for the stars. -* Your choice! Email Liam and Patrick to get approval first - -Finally in addition to your readme, you must also set up a gh-pages branch -(explained below) to expose your beautiful WebGL globe to the world. - -Some examples of what your completed globe renderer will look like: - -![Completed globe, day side](resources/globe_day.png) - -Figure 0. Completed globe renderer, daylight side. - -![Completed globe, twilight](resources/globe_twilight.png) - -Figure 1. Completed globe renderer, twilight border. - -![Completed globe, night side](resources/globe_night.png) - -Figure 2. Completed globe renderer, night side. - -------------------------------------------------------------------------------- -PART 2 WALKTHROUGH: -------------------------------------------------------------------------------- - -In part 1, we render a globe with night lights on the unlit side; a specular -map so specular highlights only occur in the ocean; animated clouds; and bump -mapping, which perturbs the surface normal to give the appears of mountains. - -Open Globe\Globe\Globe.sln in Visual Studio and run it. You’ll see a globe -with Phong lighting like the one in Figure 3. All changes you need to make -will be in the fragment shader, fs.glsl. - -![Initial globe](resources/globe_initial.png) - -Figure 3. Initial globe with diffuse and specular lighting. - -**Night Lights** - -The backside of the globe not facing the sun is completely black in the -initial globe. Use the `diffuse` lighting component to detect if a fragment -is on this side of the globe, and, if so, shade it with the color from the -night light texture, `u_Night`. Do not abruptly switch from day to night; -instead use the `GLSL mix` function to smoothly transition from day to night -over a reasonable period. The resulting globe will look like Figure 4. -Consider brightening the night lights by multiplying the value by two. - -The base code shows an example of how to gamma correct the nighttime texture: +* Spacebox -```glsl -float gammaCorrect = 1/1.2; -vec4 nightColor = pow(texture2D(u_Night, v_Texcoord), vec4(gammaCorrect)); -``` +The following image demonstrating the region on earth at dawn, where rim light is applied to gibe a feeling of atmosphere: -Feel free to play with gamma correcting the night and day textures if you -wish. Find values that you think look nice! +[![screen](images/globe_night_lights.png)](http://wuhao1117.github.io/WebGL-Experiments/index.html) -![Day/Night without specular mapping](resources/globe_nospecmap.png) +Ray traced shadow cast by clouds. Notice the misalignment between shadow and cloud is larger where the incident light angle from sun is larger: -Figure 4. Globe with night lights and day/night blending at dusk/dawn. - -**Specular Map** - -Our day/night color still shows specular highlights on landmasses, which -should only be diffuse lit. Only the ocean should receive specular highlights. -Use `u_EarthSpec` to determine if a fragment is on ocean or land, and only -include the specular component if it is in ocean. - -![Day/Night with specular mapping](resources/globe_specmap.png) - -Figure 5. Globe with specular map. Compare to Figure 4. Here, the specular -component is not used when shading the land. - -**Clouds** - -In day time, clouds should be diffuse lit. Use `u_Cloud` to determine the -cloud color, and `u_CloudTrans` and `mix` to determine how much a daytime -fragment is affected by the day diffuse map or cloud color. See Figure 6. - -In night time, clouds should obscure city lights. Use `u_CloudTrans` and `mix` -to blend between the city lights and solid black. See Figure 7. - -Animate the clouds by offseting the `s` component of `v_Texcoord` by `u_time` -when reading `u_Cloud` and `u_CloudTrans`. - -![Day with clouds](resources/globe_daycloud.png) - -Figure 6. Clouds with day time shading. - -![Night with clouds](resources/globe_nightcloud.png) - -Figure 7. Clouds observing city nights on the dark side of the globe. - -**Bump Mapping** - -Add the appearance of mountains by perturbing the normal used for diffuse -lighting the ground (not the clouds) by using the bump map texture, `u_Bump`. -This texture is 1024x512, and is zero when the fragment is at sea-level, and -one when the fragment is on the highest mountain. Read three texels from this -texture: once using `v_Texcoord`; once one texel to the right; and once one -texel above. Create a perturbed normal in tangent space: - -`normalize(vec3(center - right, center - top, 0.2))` - -Use `eastNorthUpToEyeCoordinates` to transform this normal to eye coordinates, -normalize it, then use it for diffuse lighting the ground instead of the -original normal. - -![Globe with bump mapping](resources/globe_bumpmap.png) - -Figure 8. Bump mapping brings attention to mountains. - -**Rim Lighting** - -Rim lighting is a simple post-processed lighting effect we can apply to make -the globe look as if it has an atmospheric layer catching light from the sun. -Implementing rim lighting is simple; we being by finding the dot product of -`v_Normal` and `v_Position`, and add 1 to the dot product. We call this value -our rim factor. If the rim factor is greater than 0, then we add a blue color -based on the rim factor to the current fragment color. You might use a color -something like `vec4(rim/4, rim/2, rim/2, 1)`. If our rim factor is not greater -than 0, then we leave the fragment color as is. Figures 0,1 and 2 show our -finished globe with rim lighting. - -For more information on rim lighting, -read http://www.fundza.com/rman_shaders/surface/fake_rim/fake_rim1.html. - -------------------------------------------------------------------------------- -GH-PAGES -------------------------------------------------------------------------------- -Since this assignment is in WebGL you will make your project easily viewable by -taking advantage of GitHub's project pages feature. - -Once you are done you will need to create a new branch named gh-pages: - -`git branch gh-pages` - -Switch to your new branch: - -`git checkout gh-pages` - -Create an index.html file that is either your renamed frag_globe.html or -contains a link to it, commit, and then push as usual. Now you can go to - -`.github.io/` - -to see your beautiful globe from anywhere. - -------------------------------------------------------------------------------- -README -------------------------------------------------------------------------------- -All students must replace or augment the contents of this Readme.md in a clear -manner with the following: - -* A brief description of the project and the specific features you implemented. -* At least one screenshot of your project running. -* A 30 second or longer video of your project running. To create the video you - can use http://www.microsoft.com/expression/products/Encoder4_Overview.aspx -* A performance evaluation (described in detail below). +[![screen](images/globe_shadow.png)](http://wuhao1117.github.io/WebGL-Experiments/index.html) ------------------------------------------------------------------------------- PERFORMANCE EVALUATION ------------------------------------------------------------------------------- -The performance evaluation is where you will investigate how to make your -program more efficient using the skills you've learned in class. You must have -performed at least one experiment on your code to investigate the positive or -negative effects on performance. - -We encourage you to get creative with your tweaks. Consider places in your code -that could be considered bottlenecks and try to improve them. - -Each student should provide no more than a one page summary of their -optimizations along with tables and or graphs to visually explain any -performance differences. - -------------------------------------------------------------------------------- -THIRD PARTY CODE POLICY -------------------------------------------------------------------------------- -* Use of any third-party code must be approved by asking on the Google groups. - If it is approved, all students are welcome to use it. Generally, we approve - use of third-party code that is not a core part of the project. For example, - for the ray tracer, we would approve using a third-party library for loading - models, but would not approve copying and pasting a CUDA function for doing - refraction. -* Third-party code must be credited in README.md. -* Using third-party code without its approval, including using another - student's code, is an academic integrity violation, and will result in you - receiving an F for the semester. +Curiously, adding a javascript frame rate counter shows me a constant 30FPS for every demo. Clearly something is capping the frame rate to 30 since my graphic card is way more capable of doing merely 30FPS. Will look into this issue further when I had time. ------------------------------------------------------------------------------- -SELF-GRADING +ACKNOWLEDGMENT ------------------------------------------------------------------------------- -* On the submission date, email your grade, on a scale of 0 to 100, to Liam, - liamboone@gmail.com, with a one paragraph explanation. Be concise and - realistic. Recall that we reserve 30 points as a sanity check to adjust your - grade. Your actual grade will be (0.7 * your grade) + (0.3 * our grade). We - hope to only use this in extreme cases when your grade does not realistically - reflect your work - it is either too high or too low. In most cases, we plan - to give you the exact grade you suggest. -* Projects are not weighted evenly, e.g., Project 0 doesn't count as much as - the path tracer. We will determine the weighting at the end of the semester - based on the size of each project. - - ---- -SUBMISSION ---- -As with the previous project, you should fork this project and work inside of -your fork. Upon completion, commit your finished project back to your fork, and -make a pull request to the master repository. You should include a README.md -file in the root directory detailing the following +* Skybox generation: [Spacescape](http://alexcpeterson.com/portfolio/spacescape) +* High resolution globe texture: http://planetpixelemporium.com/planets.html -* A brief description of the project and specific features you implemented -* At least one screenshot of your project running. -* A link to a video of your project running. -* Instructions for building and running your project if they differ from the - base code. -* A performance writeup as detailed above. -* A list of all third-party code used. -* This Readme file edited as described above in the README section. diff --git a/images/Simplex_Wave.png b/images/Simplex_Wave.png new file mode 100644 index 0000000..2e71aba Binary files /dev/null and b/images/Simplex_Wave.png differ diff --git a/images/Sine_Wave.png b/images/Sine_Wave.png new file mode 100644 index 0000000..b515058 Binary files /dev/null and b/images/Sine_Wave.png differ diff --git a/images/globe.png b/images/globe.png new file mode 100644 index 0000000..561d763 Binary files /dev/null and b/images/globe.png differ diff --git a/images/globe_night_lights.png b/images/globe_night_lights.png new file mode 100644 index 0000000..c7e3d00 Binary files /dev/null and b/images/globe_night_lights.png differ diff --git a/images/globe_shadow.png b/images/globe_shadow.png new file mode 100644 index 0000000..de2510f Binary files /dev/null and b/images/globe_shadow.png differ diff --git a/images/heightFieldWater.png b/images/heightFieldWater.png new file mode 100644 index 0000000..e6ad3d9 Binary files /dev/null and b/images/heightFieldWater.png differ diff --git a/part1/custom_wave.html b/part1/custom_wave.html new file mode 100644 index 0000000..1c287d9 --- /dev/null +++ b/part1/custom_wave.html @@ -0,0 +1,122 @@ + + + +Vertex Wave + + + + + +
+ + + + + + +
+ + + + + + diff --git a/part1/simplex_wave.html b/part1/simplex_wave.html new file mode 100644 index 0000000..d852c91 --- /dev/null +++ b/part1/simplex_wave.html @@ -0,0 +1,116 @@ + + + +Vertex Wave + + + + + +
+ + + + + + +
+ + + + + + diff --git a/part1/vert_wave.html b/part1/vert_wave.html index 57107ca..064ddd4 100644 --- a/part1/vert_wave.html +++ b/part1/vert_wave.html @@ -14,10 +14,17 @@ attribute vec2 position; uniform mat4 u_modelViewPerspective; - + uniform float u_time; + + varying vec3 color; + void main(void) { - float height = 0.0; + float s_contrib = sin(position.x*2.0*3.14159 + u_time); + float t_contrib = cos(position.y*2.0*3.14159 + u_time); + float height = s_contrib*t_contrib; + + color = mix(vec3(1.0, 0.2, 0.0), vec3(0.0, 0.8, 1.0), (height+1.0)/2.0); gl_Position = u_modelViewPerspective * vec4(vec3(position, height), 1.0); } @@ -25,12 +32,37 @@ + +
diff --git a/part1/vert_wave.js b/part1/vert_wave.js index b90b9cf..2eed50d 100644 --- a/part1/vert_wave.js +++ b/part1/vert_wave.js @@ -1,4 +1,4 @@ -(function() { +(function () { "use strict"; /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ /*global getShaderSource,createWebGLContext,createProgram*/ @@ -18,7 +18,7 @@ context.viewport(0, 0, canvas.width, canvas.height); context.clearColor(1.0, 1.0, 1.0, 1.0); context.enable(context.DEPTH_TEST); - + var available_extensions = context.getSupportedExtensions(); var persp = mat4.create(); mat4.perspective(45.0, 0.5, 0.1, 100.0, persp); @@ -28,18 +28,22 @@ var view = mat4.create(); mat4.lookAt(eye, center, up, view); + var time = 0.0; + var positionLocation = 0; var heightLocation = 1; var u_modelViewPerspectiveLocation; + var u_timeLocation; (function initializeShader() { var program; var vs = getShaderSource(document.getElementById("vs")); var fs = getShaderSource(document.getElementById("fs")); - var program = createProgram(context, vs, fs, message); - context.bindAttribLocation(program, positionLocation, "position"); - u_modelViewPerspectiveLocation = context.getUniformLocation(program,"u_modelViewPerspective"); + var program = createProgram(context, vs, fs, message); + context.bindAttribLocation(program, positionLocation, "position"); + u_modelViewPerspectiveLocation = context.getUniformLocation(program, "u_modelViewPerspective"); + u_timeLocation = context.getUniformLocation(program, "u_time"); context.useProgram(program); })(); @@ -56,8 +60,7 @@ context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0); context.enableVertexAttribArray(positionLocation); - if (heights) - { + if (heights) { // Heights var heightsName = context.createBuffer(); context.bindBuffer(context.ARRAY_BUFFER, heightsName); @@ -84,49 +87,45 @@ var indicesIndex = 0; var length; - for (var j = 0; j < NUM_WIDTH_PTS; ++j) - { - positions[positionsIndex++] = j /(NUM_WIDTH_PTS - 1); + for (var j = 0; j < NUM_WIDTH_PTS; ++j) { + positions[positionsIndex++] = j / (NUM_WIDTH_PTS - 1); positions[positionsIndex++] = 0.0; - if (j>=1) - { + if (j >= 1) { length = positionsIndex / 2; indices[indicesIndex++] = length - 2; indices[indicesIndex++] = length - 1; } } - for (var i = 0; i < HEIGHT_DIVISIONS; ++i) - { - var v = (i + 1) / (NUM_HEIGHT_PTS - 1); - positions[positionsIndex++] = 0.0; - positions[positionsIndex++] = v; - - length = (positionsIndex / 2); - indices[indicesIndex++] = length - 1; - indices[indicesIndex++] = length - 1 - NUM_WIDTH_PTS; - - for (var k = 0; k < WIDTH_DIVISIONS; ++k) - { - positions[positionsIndex++] = (k + 1) / (NUM_WIDTH_PTS - 1); - positions[positionsIndex++] = v; - - length = positionsIndex / 2; - var new_pt = length - 1; - indices[indicesIndex++] = new_pt - 1; // Previous side - indices[indicesIndex++] = new_pt; - - indices[indicesIndex++] = new_pt - NUM_WIDTH_PTS; // Previous bottom - indices[indicesIndex++] = new_pt; - } + for (var i = 0; i < HEIGHT_DIVISIONS; ++i) { + var v = (i + 1) / (NUM_HEIGHT_PTS - 1); + positions[positionsIndex++] = 0.0; + positions[positionsIndex++] = v; + + length = (positionsIndex / 2); + indices[indicesIndex++] = length - 1; + indices[indicesIndex++] = length - 1 - NUM_WIDTH_PTS; + + for (var k = 0; k < WIDTH_DIVISIONS; ++k) { + positions[positionsIndex++] = (k + 1) / (NUM_WIDTH_PTS - 1); + positions[positionsIndex++] = v; + + length = positionsIndex / 2; + var new_pt = length - 1; + indices[indicesIndex++] = new_pt - 1; // Previous side + indices[indicesIndex++] = new_pt; + + indices[indicesIndex++] = new_pt - NUM_WIDTH_PTS; // Previous bottom + indices[indicesIndex++] = new_pt; + } } uploadMesh(positions, heights, indices); numberOfIndices = indices.length; })(); - (function animate(){ + (function animate() { /////////////////////////////////////////////////////////////////////////// // Update @@ -138,14 +137,17 @@ var mvp = mat4.create(); mat4.multiply(persp, mv, mvp); + time += 0.01; + /////////////////////////////////////////////////////////////////////////// // Render context.clear(context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT); context.uniformMatrix4fv(u_modelViewPerspectiveLocation, false, mvp); - context.drawElements(context.LINES, numberOfIndices, context.UNSIGNED_SHORT,0); + context.uniform1f(u_timeLocation, time); + context.drawElements(context.LINES, numberOfIndices, context.UNSIGNED_SHORT, 0); - window.requestAnimFrame(animate); + window.requestAnimFrame(animate); })(); -}()); +} ()); diff --git a/part2/Hi-Res Texture Version/4096_back6.png b/part2/Hi-Res Texture Version/4096_back6.png new file mode 100644 index 0000000..f5873ca Binary files /dev/null and b/part2/Hi-Res Texture Version/4096_back6.png differ diff --git a/part2/Hi-Res Texture Version/4096_bottom4.png b/part2/Hi-Res Texture Version/4096_bottom4.png new file mode 100644 index 0000000..ed1d4c7 Binary files /dev/null and b/part2/Hi-Res Texture Version/4096_bottom4.png differ diff --git a/part2/Hi-Res Texture Version/4096_front5.png b/part2/Hi-Res Texture Version/4096_front5.png new file mode 100644 index 0000000..6842097 Binary files /dev/null and b/part2/Hi-Res Texture Version/4096_front5.png differ diff --git a/part2/Hi-Res Texture Version/4096_left2.png b/part2/Hi-Res Texture Version/4096_left2.png new file mode 100644 index 0000000..60d00f0 Binary files /dev/null and b/part2/Hi-Res Texture Version/4096_left2.png differ diff --git a/part2/Hi-Res Texture Version/4096_right1.png b/part2/Hi-Res Texture Version/4096_right1.png new file mode 100644 index 0000000..aad35a3 Binary files /dev/null and b/part2/Hi-Res Texture Version/4096_right1.png differ diff --git a/part2/Hi-Res Texture Version/4096_top3.png b/part2/Hi-Res Texture Version/4096_top3.png new file mode 100644 index 0000000..b3f389e Binary files /dev/null and b/part2/Hi-Res Texture Version/4096_top3.png differ diff --git a/part2/Hi-Res Texture Version/debug.log b/part2/Hi-Res Texture Version/debug.log new file mode 100644 index 0000000..e69de29 diff --git a/part2/Hi-Res Texture Version/earthbump10k.jpg b/part2/Hi-Res Texture Version/earthbump10k.jpg new file mode 100644 index 0000000..db38269 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthbump10k.jpg differ diff --git a/part2/earthbump1024.png b/part2/Hi-Res Texture Version/earthbump1k.png similarity index 100% rename from part2/earthbump1024.png rename to part2/Hi-Res Texture Version/earthbump1k.png diff --git a/part2/Hi-Res Texture Version/earthbump2k.jpg b/part2/Hi-Res Texture Version/earthbump2k.jpg new file mode 100644 index 0000000..80f7846 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthbump2k.jpg differ diff --git a/part2/Hi-Res Texture Version/earthbump4k.jpg b/part2/Hi-Res Texture Version/earthbump4k.jpg new file mode 100644 index 0000000..b60b670 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthbump4k.jpg differ diff --git a/part2/earthcloud1024.png b/part2/Hi-Res Texture Version/earthcloud1k.png similarity index 100% rename from part2/earthcloud1024.png rename to part2/Hi-Res Texture Version/earthcloud1k.png diff --git a/part2/earthlight1024.png b/part2/Hi-Res Texture Version/earthlight1k.png similarity index 100% rename from part2/earthlight1024.png rename to part2/Hi-Res Texture Version/earthlight1k.png diff --git a/part2/Hi-Res Texture Version/earthlights10k .jpg b/part2/Hi-Res Texture Version/earthlights10k .jpg new file mode 100644 index 0000000..b503993 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthlights10k .jpg differ diff --git a/part2/Hi-Res Texture Version/earthlights2k.jpg b/part2/Hi-Res Texture Version/earthlights2k.jpg new file mode 100644 index 0000000..0b696e4 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthlights2k.jpg differ diff --git a/part2/Hi-Res Texture Version/earthlights4k.jpg b/part2/Hi-Res Texture Version/earthlights4k.jpg new file mode 100644 index 0000000..5151352 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthlights4k.jpg differ diff --git a/part2/Hi-Res Texture Version/earthmap10k.jpg b/part2/Hi-Res Texture Version/earthmap10k.jpg new file mode 100644 index 0000000..812458c Binary files /dev/null and b/part2/Hi-Res Texture Version/earthmap10k.jpg differ diff --git a/part2/earthmap1024.png b/part2/Hi-Res Texture Version/earthmap1k.png similarity index 100% rename from part2/earthmap1024.png rename to part2/Hi-Res Texture Version/earthmap1k.png diff --git a/part2/Hi-Res Texture Version/earthmap2k.jpg b/part2/Hi-Res Texture Version/earthmap2k.jpg new file mode 100644 index 0000000..5640ac5 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthmap2k.jpg differ diff --git a/part2/Hi-Res Texture Version/earthmap4k.jpg b/part2/Hi-Res Texture Version/earthmap4k.jpg new file mode 100644 index 0000000..4d10d63 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthmap4k.jpg differ diff --git a/part2/Hi-Res Texture Version/earthmap4k.png b/part2/Hi-Res Texture Version/earthmap4k.png new file mode 100644 index 0000000..414b22c Binary files /dev/null and b/part2/Hi-Res Texture Version/earthmap4k.png differ diff --git a/part2/Hi-Res Texture Version/earthspec10k.jpg b/part2/Hi-Res Texture Version/earthspec10k.jpg new file mode 100644 index 0000000..c2eea0e Binary files /dev/null and b/part2/Hi-Res Texture Version/earthspec10k.jpg differ diff --git a/part2/earthspec1024.png b/part2/Hi-Res Texture Version/earthspec1k.png similarity index 100% rename from part2/earthspec1024.png rename to part2/Hi-Res Texture Version/earthspec1k.png diff --git a/part2/Hi-Res Texture Version/earthspec2k.jpg b/part2/Hi-Res Texture Version/earthspec2k.jpg new file mode 100644 index 0000000..4c984a8 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthspec2k.jpg differ diff --git a/part2/Hi-Res Texture Version/earthspec4k.jpg b/part2/Hi-Res Texture Version/earthspec4k.jpg new file mode 100644 index 0000000..fdac0a9 Binary files /dev/null and b/part2/Hi-Res Texture Version/earthspec4k.jpg differ diff --git a/part2/earthtrans1024.png b/part2/Hi-Res Texture Version/earthtrans1k.png similarity index 100% rename from part2/earthtrans1024.png rename to part2/Hi-Res Texture Version/earthtrans1k.png diff --git a/part2/Hi-Res Texture Version/frag_globe.js b/part2/Hi-Res Texture Version/frag_globe.js new file mode 100644 index 0000000..a07ad14 --- /dev/null +++ b/part2/Hi-Res Texture Version/frag_globe.js @@ -0,0 +1,540 @@ +var message; +var canvas; +var gl; + +// shader programs +var programGlobe; +var programSkybox; + +// handles for attributes and uniforms in globe shader pair +var positionLocation; +var normalLocation; +var texCoordLocation; + +var u_InvTransLocation; +var u_ModelLocation; +var u_ViewLocation; +var u_ModelViewInverseLocation; +var u_PerspLocation; +var u_timeLocation; +var u_CameraSpaceDirLightLocation; + +var u_DayDiffuseLocation; +var u_NightLocation; +var u_CloudLocation; +var u_CloudTransLocation; +var u_EarthSpecLocation; +var u_BumpLocation; + + +// positions and uniforms in skybox shader pair +var skyboxPositionLocation; +var skyboxNormalLocation; + +var u_CameraSpaceLightPosLocation; +var u_skyboxViewLocation; +var u_skyboxPerspLocation; +var u_skyboxInvTransLocation; + +var u_cubeTextureLocation; + +function initializeShader() { + // create programGlobe for earth shading + var vs = getShaderSource(document.getElementById("vs")); + var fs = getShaderSource(document.getElementById("fs")); + + programGlobe = createProgram(gl, vs, fs, message); + + // attributes + positionLocation = gl.getAttribLocation(programGlobe, "Position"); + normalLocation = gl.getAttribLocation(programGlobe, "Normal"); + texCoordLocation = gl.getAttribLocation(programGlobe, "Texcoord"); + // uniforms + u_ModelLocation = gl.getUniformLocation(programGlobe, "u_Model"); + u_ViewLocation = gl.getUniformLocation(programGlobe, "u_View"); + u_ModelViewInverseLocation = gl.getUniformLocation(programGlobe, "u_ModelViewInverse"); + u_PerspLocation = gl.getUniformLocation(programGlobe, "u_Persp"); + u_InvTransLocation = gl.getUniformLocation(programGlobe, "u_InvTrans"); + u_timeLocation = gl.getUniformLocation(programGlobe, "u_time"); + u_CameraSpaceDirLightLocation = gl.getUniformLocation(programGlobe, "u_CameraSpaceDirLight"); + // textures + u_DayDiffuseLocation = gl.getUniformLocation(programGlobe, "u_DayDiffuse"); + u_NightLocation = gl.getUniformLocation(programGlobe, "u_Night"); + u_CloudLocation = gl.getUniformLocation(programGlobe, "u_Cloud"); + u_CloudTransLocation = gl.getUniformLocation(programGlobe, "u_CloudTrans"); + u_EarthSpecLocation = gl.getUniformLocation(programGlobe, "u_EarthSpec"); + u_BumpLocation = gl.getUniformLocation(programGlobe, "u_Bump"); + + + // create programGlobe for skybox shading + var skyboxVS = getShaderSource(document.getElementById("skyboxVS")); + var skyboxFS = getShaderSource(document.getElementById("skyboxFS")); + + programSkybox = createProgram(gl, skyboxVS, skyboxFS, message); + skyboxPositionLocation = gl.getAttribLocation(programSkybox, "Position"); + + u_CameraSpaceLightPosLocation = gl.getUniformLocation(programSkybox, "u_CameraSpaceLightPos"); + u_skyboxViewLocation = gl.getUniformLocation(programSkybox, "u_View"); + u_skyboxPerspLocation = gl.getUniformLocation(programSkybox, "u_Persp"); + //u_skyboxInvTransLocation = gl.getUniformLocation(programSkybox, "u_InvTrans"); + + u_cubeTextureLocation = gl.getUniformLocation(programSkybox, "u_cubeTexture"); + +} + + + + + + + +var skyboxTex; + +function initSkyboxTex() { + + skyboxTex = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0); + // javaScript arrays can be of mixed types + var cubeImages = [[gl.TEXTURE_CUBE_MAP_POSITIVE_X, "4096_right1.png"], + [gl.TEXTURE_CUBE_MAP_NEGATIVE_X, "4096_left2.png"], + [gl.TEXTURE_CUBE_MAP_POSITIVE_Y, "4096_top3.png"], + [gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, "4096_bottom4.png"], + [gl.TEXTURE_CUBE_MAP_POSITIVE_Z, "4096_front5.png"], + [gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, "4096_back6.png"]]; + + // While a texture is bound, GL operations on the target to which it is + // bound affect the bound texture, and queries of the target to which it + // is bound return state from the bound texture. + gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyboxTex); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + function initLoadedCubeMap(texture, face, image) { + //alert(image.complete); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(face, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); + //message.innerHTML += image.complete + "\n"; + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); + } + + for (var i = 0; i < cubeImages.length; i++) { + var face = cubeImages[i][0]; + var image = new Image(); + image.onload = function(texture, face, image) { + return function() { + gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + }; + } (skyboxTex, face, image); + // image load functions that do not work + /*image.onload = function() { + gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyboxTex); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + };*/ + /* image.onload = function() { + return initLoadedCubeMap(skyboxTex, face, image) + };*/ + image.src = cubeImages[i][1]; + } + +} + + + +var dayTex; +var bumpTex; +var cloudTex; +var transTex; +var lightTex; +var specTex; + +function initGlobeTex(){ + dayTex = gl.createTexture(); + bumpTex = gl.createTexture(); + cloudTex = gl.createTexture(); + transTex = gl.createTexture(); + lightTex = gl.createTexture(); + specTex = gl.createTexture(); + + + function initLoadedTexture(texture) { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + gl.bindTexture(gl.TEXTURE_2D, null); + } + + function initializeTexture(texture, src) { + texture.image = new Image(); + texture.image.onload = function () { + initLoadedTexture(texture); + }; + texture.image.src = src; + } + + initializeTexture(dayTex, "earthmap4k.jpg"); + initializeTexture(bumpTex, "earthbump4k.jpg"); + initializeTexture(lightTex, "earthlights4k.jpg"); + initializeTexture(specTex, "earthspec4k.jpg"); + initializeTexture(cloudTex, "earthcloud1k.png"); + initializeTexture(transTex, "earthtrans1k.png"); +} + +var positionsName; +var indicesName; +var numberOfSkyboxIndices; + +function intializeSkybox() { + var positions = new Float32Array([ + // neg z, back + -10.0, 10.0, -10.0, -10.0, -10.0, -10.0, 10.0, -10.0, -10.0, + 10.0, -10.0, -10.0, 10.0, 10.0, -10.0, -10.0, 10.0, -10.0, + // neg x, left + -10.0, -10.0, 10.0, -10.0, -10.0, -10.0, -10.0, 10.0, -10.0, + -10.0, 10.0, -10.0, -10.0, 10.0, 10.0, -10.0, -10.0, 10.0, + // pos x, right + 10.0, -10.0, -10.0, 10.0, -10.0, 10.0, 10.0, 10.0, 10.0, + 10.0, 10.0, 10.0, 10.0, 10.0, -10.0, 10.0, -10.0, -10.0, + // pos z, front + -10.0, -10.0, 10.0, -10.0, 10.0, 10.0, 10.0, 10.0, 10.0, + 10.0, 10.0, 10.0, 10.0, -10.0, 10.0, -10.0, -10.0, 10.0, + // pos y, top + -10.0, 10.0, -10.0, 10.0, 10.0, -10.0, 10.0, 10.0, 10.0, + 10.0, 10.0, 10.0, -10.0, 10.0, 10.0, -10.0, 10.0, -10.0, + // neg y, bottom + -10.0, -10.0, -10.0, -10.0, -10.0, 10.0, 10.0, -10.0, -10.0, + 10.0, -10.0, -10.0, -10.0, -10.0, 10.0, 10.0, -10.0, 10.0 + ]); + + var indices = new Uint16Array(6 * 2 * 3); + for (var i = 0; i < indices.length; ++i) { + indices[i] = i; + } + + // Positions + positionsName = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionsName); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + // Indices + indicesName = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesName); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + + numberOfSkyboxIndices = indices.length; + +} + + +var NUM_WIDTH_PTS = 64; +var NUM_HEIGHT_PTS = 64; + +var globePosBuffer; +var globeNormBuffer; +var globeTexCoorBuffer; +var globeIndices; +var numberOfIndices; + +function initializeSphere() { + function uploadMesh(positions, texCoords, indices) { + // Positions + globePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, globePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + // Normals + globeNormBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, globeNormBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + // TextureCoords + globeTexCoorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, globeTexCoorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); + + // Indices + globeIndices = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, globeIndices); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + } + + var WIDTH_DIVISIONS = NUM_WIDTH_PTS - 1; + var HEIGHT_DIVISIONS = NUM_HEIGHT_PTS - 1; + + var numberOfPositions = NUM_WIDTH_PTS * NUM_HEIGHT_PTS; + + var positions = new Float32Array(3 * numberOfPositions); + var texCoords = new Float32Array(2 * numberOfPositions); + var indices = new Uint16Array(6 * (WIDTH_DIVISIONS * HEIGHT_DIVISIONS)); + + var positionsIndex = 0; + var texCoordsIndex = 0; + var indicesIndex = 0; + + for (var j = 0; j < NUM_HEIGHT_PTS; ++j) { + var inclination = Math.PI * (j / HEIGHT_DIVISIONS); + for (var i = 0; i < NUM_WIDTH_PTS; ++i) { + var azimuth = 2 * Math.PI * (i / WIDTH_DIVISIONS); + positions[positionsIndex++] = Math.sin(inclination) * Math.sin(azimuth); + positions[positionsIndex++] = Math.cos(inclination); + positions[positionsIndex++] = Math.sin(inclination) * Math.cos(azimuth); + texCoords[texCoordsIndex++] = i / WIDTH_DIVISIONS; + texCoords[texCoordsIndex++] = 1.0 - j / HEIGHT_DIVISIONS; + } + } + + for (var j = 0; j < HEIGHT_DIVISIONS; ++j) // a bunch of degenerate triangles at poles + { + var index = j * NUM_WIDTH_PTS; + for (var i = 0; i < WIDTH_DIVISIONS; ++i) { + indices[indicesIndex++] = index + i; + indices[indicesIndex++] = index + i + 1; + indices[indicesIndex++] = index + i + NUM_WIDTH_PTS; + indices[indicesIndex++] = index + i + NUM_WIDTH_PTS; + indices[indicesIndex++] = index + i + 1; + indices[indicesIndex++] = index + i + NUM_WIDTH_PTS + 1; + } + } + + + uploadMesh(positions, texCoords, indices); + numberOfIndices = indicesIndex; +}; + + +//time for animating globe +var time = 0; + +//Camera control +var mouseLeftDown = false; +var mouseRightDown = false; +var lastMouseX = null; +var lastMouseY = null; + +var radius = 5.0; +var azimuth = 0.0; +var zenith = Math.PI / 2.0; + +var center = [0.0, 0.0, 0.0]; +var up = [0.0, 1.0, 0.0]; + +var persp; +var eye; +var view; + +// mouse control callbacks +function handleMouseDown(event) { + if (event.button == 2) { + mouseLeftDown = false; + mouseRightDown = true; + } + else { + mouseLeftDown = true; + mouseRightDown = false; + } + lastMouseX = event.clientX; + lastMouseY = event.clientY; +} + +function handleMouseUp(event) { + mouseLeftDown = false; + mouseRightDown = false; +} + +function handleMouseMove(event) { + if (!(mouseLeftDown || mouseRightDown)) { + return; + } + var newX = event.clientX; + var newY = event.clientY; + + var deltaX = newX - lastMouseX; + var deltaY = newY - lastMouseY; + + if (mouseLeftDown) { + azimuth -= 0.01 * deltaX; + zenith -= 0.01 * deltaY; + zenith = Math.min(Math.max(zenith, 0.001), Math.PI - 0.001); + } + else { + radius += 0.01 * deltaY; + radius = Math.min(Math.max(radius, 2.0), 10.0); + } + eye = sphericalToCartesian(radius, azimuth, zenith); + view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + lastMouseX = newX; + lastMouseY = newY; +} + +function sphericalToCartesian(r, azimuth, zenith) { + var x = r * Math.sin(zenith) * Math.sin(azimuth); + var y = r * Math.cos(zenith); + var z = r * Math.sin(zenith) * Math.cos(azimuth); + + return [x, y, z]; +} + +///////////////////////////////////////////////////////////////////////////////// +function globeMain() { + + "use strict"; + message = document.getElementById("message"); + canvas = document.getElementById("canvas"); + gl = createWebGLContext(canvas, message); + if (!gl) { + return; + } + + canvas.onmousedown = handleMouseDown; + canvas.oncontextmenu = function (ev) { return false; }; + document.onmouseup = handleMouseUp; + document.onmousemove = handleMouseMove; + + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.enable(gl.DEPTH_TEST); + + persp = mat4.create(); + mat4.perspective(45.0, canvas.width / canvas.height, 0.1, 100.0, persp); + + eye = sphericalToCartesian(radius, azimuth, zenith); + view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + initializeShader(); + + initSkyboxTex(); + + intializeSkybox(); + + initGlobeTex(); + + initializeSphere(); + + + (function animate() {// Update + + (function drawGlobe() { + + gl.useProgram(programGlobe); + + // disable attributes for other programs + gl.disableVertexAttribArray(skyboxPositionLocation); + + // enable attributes for this program + gl.bindBuffer(gl.ARRAY_BUFFER, globePosBuffer); + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(positionLocation); + + gl.bindBuffer(gl.ARRAY_BUFFER, globeNormBuffer); + gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(normalLocation); + + gl.bindBuffer(gl.ARRAY_BUFFER, globeTexCoorBuffer); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(texCoordLocation); + + // calculate and pass uniforms + var model = mat4.create(); + mat4.identity(model); + mat4.rotate(model, 23.4 / 180 * Math.PI, [0.0, 0.0, 1.0]); + mat4.rotate(model, time, [0.0, 1.0, 0.0]); + var mv = mat4.create(); + mat4.multiply(view, model, mv); + + var modelViewMatrixInverse = mat4.create(); + mat4.inverse(mv, modelViewMatrixInverse); + + var invTrans = mat4.create(); + mat4.inverse(mv, invTrans); + mat4.transpose(invTrans); + + var lightdir = vec3.create([1.0, 0.0, 1.0]); + var lightdest = vec4.create(); + vec3.normalize(lightdir); + mat4.multiplyVec4(view, [lightdir[0], lightdir[1], lightdir[2], 0.0], lightdest); + lightdir = vec3.createFrom(lightdest[0], lightdest[1], lightdest[2]); + vec3.normalize(lightdir); + + gl.uniformMatrix4fv(u_ModelLocation, false, model); + gl.uniformMatrix4fv(u_ViewLocation, false, view); + gl.uniformMatrix4fv(u_ModelViewInverseLocation, false, modelViewMatrixInverse); + gl.uniformMatrix4fv(u_PerspLocation, false, persp); + gl.uniformMatrix4fv(u_InvTransLocation, false, invTrans); + + gl.uniform3fv(u_CameraSpaceDirLightLocation, lightdir); + gl.uniform1f(u_timeLocation, time); + + // pass textures + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, dayTex); + gl.uniform1i(u_DayDiffuseLocation, 0); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, bumpTex); + gl.uniform1i(u_BumpLocation, 1); + + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, cloudTex); + gl.uniform1i(u_CloudLocation, 2); + + gl.activeTexture(gl.TEXTURE3); + gl.bindTexture(gl.TEXTURE_2D, transTex); + gl.uniform1i(u_CloudTransLocation, 3); + + gl.activeTexture(gl.TEXTURE4); + gl.bindTexture(gl.TEXTURE_2D, lightTex); + gl.uniform1i(u_NightLocation, 4); + + gl.activeTexture(gl.TEXTURE5); + gl.bindTexture(gl.TEXTURE_2D, specTex); + gl.uniform1i(u_EarthSpecLocation, 5); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, globeIndices); + gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT, 0); + })(); + + (function drawSkybox(){ + gl.useProgram(programSkybox); + + // disable attributes for other programs + gl.disableVertexAttribArray(positionLocation); + gl.disableVertexAttribArray(normalLocation); + gl.disableVertexAttribArray(texCoordLocation); + + // enable attributes for this program + gl.bindBuffer(gl.ARRAY_BUFFER, positionsName); + gl.vertexAttribPointer(skyboxPositionLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(skyboxPositionLocation); + + // calculate and pass uniforms + gl.uniformMatrix4fv(u_skyboxViewLocation, false, view); + gl.uniformMatrix4fv(u_skyboxPerspLocation, false, persp); + + // pass textures + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyboxTex); + gl.uniform1i(u_cubeTextureLocation, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesName); + gl.drawElements(gl.TRIANGLES, numberOfSkyboxIndices, gl.UNSIGNED_SHORT, 0); + })(); + + time += 0.001; + + window.requestAnimFrame(animate); + })(); + +} diff --git a/part2/Hi-Res Texture Version/gl-matrix.js b/part2/Hi-Res Texture Version/gl-matrix.js new file mode 100644 index 0000000..5718055 --- /dev/null +++ b/part2/Hi-Res Texture Version/gl-matrix.js @@ -0,0 +1,3456 @@ +/** + * @fileoverview gl-matrix - High performance matrix and vector operations for WebGL + * @author Brandon Jones + * @author Colin MacKenzie IV + * @version 1.3.7 + */ + +/* + * Copyright (c) 2012 Brandon Jones, Colin MacKenzie IV + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +// Updated to use a modification of the "returnExportsGlobal" pattern from https://github.com/umdjs/umd + +(function (root, factory) { + if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like enviroments that support module.exports, + // like Node. + module.exports = factory(global); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], function () { + return factory(root); + }); + } else { + // Browser globals + factory(root); + } +}(this, function (root) { + "use strict"; + + // Tweak to your liking + var FLOAT_EPSILON = 0.000001; + + var glMath = {}; + (function() { + if (typeof(Float32Array) != 'undefined') { + var y = new Float32Array(1); + var i = new Int32Array(y.buffer); + + /** + * Fast way to calculate the inverse square root, + * see http://jsperf.com/inverse-square-root/5 + * + * If typed arrays are not available, a slower + * implementation will be used. + * + * @param {Number} number the number + * @returns {Number} Inverse square root + */ + glMath.invsqrt = function(number) { + var x2 = number * 0.5; + y[0] = number; + var threehalfs = 1.5; + + i[0] = 0x5f3759df - (i[0] >> 1); + + var number2 = y[0]; + + return number2 * (threehalfs - (x2 * number2 * number2)); + }; + } else { + glMath.invsqrt = function(number) { return 1.0 / Math.sqrt(number); }; + } + })(); + + /** + * @class System-specific optimal array type + * @name MatrixArray + */ + var MatrixArray = null; + + // explicitly sets and returns the type of array to use within glMatrix + function setMatrixArrayType(type) { + MatrixArray = type; + return MatrixArray; + } + + // auto-detects and returns the best type of array to use within glMatrix, falling + // back to Array if typed arrays are unsupported + function determineMatrixArrayType() { + MatrixArray = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + return MatrixArray; + } + + determineMatrixArrayType(); + + /** + * @class 3 Dimensional Vector + * @name vec3 + */ + var vec3 = {}; + + /** + * Creates a new instance of a vec3 using the default array type + * Any javascript array-like objects containing at least 3 numeric elements can serve as a vec3 + * + * @param {vec3} [vec] vec3 containing values to initialize with + * + * @returns {vec3} New vec3 + */ + vec3.create = function (vec) { + var dest = new MatrixArray(3); + + if (vec) { + dest[0] = vec[0]; + dest[1] = vec[1]; + dest[2] = vec[2]; + } else { + dest[0] = dest[1] = dest[2] = 0; + } + + return dest; + }; + + /** + * Creates a new instance of a vec3, initializing it with the given arguments + * + * @param {number} x X value + * @param {number} y Y value + * @param {number} z Z value + + * @returns {vec3} New vec3 + */ + vec3.createFrom = function (x, y, z) { + var dest = new MatrixArray(3); + + dest[0] = x; + dest[1] = y; + dest[2] = z; + + return dest; + }; + + /** + * Copies the values of one vec3 to another + * + * @param {vec3} vec vec3 containing values to copy + * @param {vec3} dest vec3 receiving copied values + * + * @returns {vec3} dest + */ + vec3.set = function (vec, dest) { + dest[0] = vec[0]; + dest[1] = vec[1]; + dest[2] = vec[2]; + + return dest; + }; + + /** + * Compares two vectors for equality within a certain margin of error + * + * @param {vec3} a First vector + * @param {vec3} b Second vector + * + * @returns {Boolean} True if a is equivalent to b + */ + vec3.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON && + Math.abs(a[2] - b[2]) < FLOAT_EPSILON + ); + }; + + /** + * Performs a vector addition + * + * @param {vec3} vec First operand + * @param {vec3} vec2 Second operand + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.add = function (vec, vec2, dest) { + if (!dest || vec === dest) { + vec[0] += vec2[0]; + vec[1] += vec2[1]; + vec[2] += vec2[2]; + return vec; + } + + dest[0] = vec[0] + vec2[0]; + dest[1] = vec[1] + vec2[1]; + dest[2] = vec[2] + vec2[2]; + return dest; + }; + + /** + * Performs a vector subtraction + * + * @param {vec3} vec First operand + * @param {vec3} vec2 Second operand + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.subtract = function (vec, vec2, dest) { + if (!dest || vec === dest) { + vec[0] -= vec2[0]; + vec[1] -= vec2[1]; + vec[2] -= vec2[2]; + return vec; + } + + dest[0] = vec[0] - vec2[0]; + dest[1] = vec[1] - vec2[1]; + dest[2] = vec[2] - vec2[2]; + return dest; + }; + + /** + * Performs a vector multiplication + * + * @param {vec3} vec First operand + * @param {vec3} vec2 Second operand + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.multiply = function (vec, vec2, dest) { + if (!dest || vec === dest) { + vec[0] *= vec2[0]; + vec[1] *= vec2[1]; + vec[2] *= vec2[2]; + return vec; + } + + dest[0] = vec[0] * vec2[0]; + dest[1] = vec[1] * vec2[1]; + dest[2] = vec[2] * vec2[2]; + return dest; + }; + + /** + * Negates the components of a vec3 + * + * @param {vec3} vec vec3 to negate + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.negate = function (vec, dest) { + if (!dest) { dest = vec; } + + dest[0] = -vec[0]; + dest[1] = -vec[1]; + dest[2] = -vec[2]; + return dest; + }; + + /** + * Multiplies the components of a vec3 by a scalar value + * + * @param {vec3} vec vec3 to scale + * @param {number} val Value to scale by + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.scale = function (vec, val, dest) { + if (!dest || vec === dest) { + vec[0] *= val; + vec[1] *= val; + vec[2] *= val; + return vec; + } + + dest[0] = vec[0] * val; + dest[1] = vec[1] * val; + dest[2] = vec[2] * val; + return dest; + }; + + /** + * Generates a unit vector of the same direction as the provided vec3 + * If vector length is 0, returns [0, 0, 0] + * + * @param {vec3} vec vec3 to normalize + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.normalize = function (vec, dest) { + if (!dest) { dest = vec; } + + var x = vec[0], y = vec[1], z = vec[2], + len = Math.sqrt(x * x + y * y + z * z); + + if (!len) { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + return dest; + } else if (len === 1) { + dest[0] = x; + dest[1] = y; + dest[2] = z; + return dest; + } + + len = 1 / len; + dest[0] = x * len; + dest[1] = y * len; + dest[2] = z * len; + return dest; + }; + + /** + * Generates the cross product of two vec3s + * + * @param {vec3} vec First operand + * @param {vec3} vec2 Second operand + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.cross = function (vec, vec2, dest) { + if (!dest) { dest = vec; } + + var x = vec[0], y = vec[1], z = vec[2], + x2 = vec2[0], y2 = vec2[1], z2 = vec2[2]; + + dest[0] = y * z2 - z * y2; + dest[1] = z * x2 - x * z2; + dest[2] = x * y2 - y * x2; + return dest; + }; + + /** + * Caclulates the length of a vec3 + * + * @param {vec3} vec vec3 to calculate length of + * + * @returns {number} Length of vec + */ + vec3.length = function (vec) { + var x = vec[0], y = vec[1], z = vec[2]; + return Math.sqrt(x * x + y * y + z * z); + }; + + /** + * Caclulates the squared length of a vec3 + * + * @param {vec3} vec vec3 to calculate squared length of + * + * @returns {number} Squared Length of vec + */ + vec3.squaredLength = function (vec) { + var x = vec[0], y = vec[1], z = vec[2]; + return x * x + y * y + z * z; + }; + + /** + * Caclulates the dot product of two vec3s + * + * @param {vec3} vec First operand + * @param {vec3} vec2 Second operand + * + * @returns {number} Dot product of vec and vec2 + */ + vec3.dot = function (vec, vec2) { + return vec[0] * vec2[0] + vec[1] * vec2[1] + vec[2] * vec2[2]; + }; + + /** + * Generates a unit vector pointing from one vector to another + * + * @param {vec3} vec Origin vec3 + * @param {vec3} vec2 vec3 to point to + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.direction = function (vec, vec2, dest) { + if (!dest) { dest = vec; } + + var x = vec[0] - vec2[0], + y = vec[1] - vec2[1], + z = vec[2] - vec2[2], + len = Math.sqrt(x * x + y * y + z * z); + + if (!len) { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + return dest; + } + + len = 1 / len; + dest[0] = x * len; + dest[1] = y * len; + dest[2] = z * len; + return dest; + }; + + /** + * Performs a linear interpolation between two vec3 + * + * @param {vec3} vec First vector + * @param {vec3} vec2 Second vector + * @param {number} lerp Interpolation amount between the two inputs + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.lerp = function (vec, vec2, lerp, dest) { + if (!dest) { dest = vec; } + + dest[0] = vec[0] + lerp * (vec2[0] - vec[0]); + dest[1] = vec[1] + lerp * (vec2[1] - vec[1]); + dest[2] = vec[2] + lerp * (vec2[2] - vec[2]); + + return dest; + }; + + /** + * Calculates the euclidian distance between two vec3 + * + * Params: + * @param {vec3} vec First vector + * @param {vec3} vec2 Second vector + * + * @returns {number} Distance between vec and vec2 + */ + vec3.dist = function (vec, vec2) { + var x = vec2[0] - vec[0], + y = vec2[1] - vec[1], + z = vec2[2] - vec[2]; + + return Math.sqrt(x*x + y*y + z*z); + }; + + // Pre-allocated to prevent unecessary garbage collection + var unprojectMat = null; + var unprojectVec = new MatrixArray(4); + /** + * Projects the specified vec3 from screen space into object space + * Based on the Mesa gluUnProject implementation + * + * @param {vec3} vec Screen-space vector to project + * @param {mat4} view View matrix + * @param {mat4} proj Projection matrix + * @param {vec4} viewport Viewport as given to gl.viewport [x, y, width, height] + * @param {vec3} [dest] vec3 receiving unprojected result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + vec3.unproject = function (vec, view, proj, viewport, dest) { + if (!dest) { dest = vec; } + + if(!unprojectMat) { + unprojectMat = mat4.create(); + } + + var m = unprojectMat; + var v = unprojectVec; + + v[0] = (vec[0] - viewport[0]) * 2.0 / viewport[2] - 1.0; + v[1] = (vec[1] - viewport[1]) * 2.0 / viewport[3] - 1.0; + v[2] = 2.0 * vec[2] - 1.0; + v[3] = 1.0; + + mat4.multiply(proj, view, m); + if(!mat4.inverse(m)) { return null; } + + mat4.multiplyVec4(m, v); + if(v[3] === 0.0) { return null; } + + dest[0] = v[0] / v[3]; + dest[1] = v[1] / v[3]; + dest[2] = v[2] / v[3]; + + return dest; + }; + + var xUnitVec3 = vec3.createFrom(1,0,0); + var yUnitVec3 = vec3.createFrom(0,1,0); + var zUnitVec3 = vec3.createFrom(0,0,1); + + var tmpvec3 = vec3.create(); + /** + * Generates a quaternion of rotation between two given normalized vectors + * + * @param {vec3} a Normalized source vector + * @param {vec3} b Normalized target vector + * @param {quat4} [dest] quat4 receiving operation result. + * + * @returns {quat4} dest if specified, a new quat4 otherwise + */ + vec3.rotationTo = function (a, b, dest) { + if (!dest) { dest = quat4.create(); } + + var d = vec3.dot(a, b); + var axis = tmpvec3; + if (d >= 1.0) { + quat4.set(identityQuat4, dest); + } else if (d < (0.000001 - 1.0)) { + vec3.cross(xUnitVec3, a, axis); + if (vec3.length(axis) < 0.000001) + vec3.cross(yUnitVec3, a, axis); + if (vec3.length(axis) < 0.000001) + vec3.cross(zUnitVec3, a, axis); + vec3.normalize(axis); + quat4.fromAngleAxis(Math.PI, axis, dest); + } else { + var s = Math.sqrt((1.0 + d) * 2.0); + var sInv = 1.0 / s; + vec3.cross(a, b, axis); + dest[0] = axis[0] * sInv; + dest[1] = axis[1] * sInv; + dest[2] = axis[2] * sInv; + dest[3] = s * 0.5; + quat4.normalize(dest); + } + if (dest[3] > 1.0) dest[3] = 1.0; + else if (dest[3] < -1.0) dest[3] = -1.0; + return dest; + }; + + /** + * Returns a string representation of a vector + * + * @param {vec3} vec Vector to represent as a string + * + * @returns {string} String representation of vec + */ + vec3.str = function (vec) { + return '[' + vec[0] + ', ' + vec[1] + ', ' + vec[2] + ']'; + }; + + /** + * @class 3x3 Matrix + * @name mat3 + */ + var mat3 = {}; + + /** + * Creates a new instance of a mat3 using the default array type + * Any javascript array-like object containing at least 9 numeric elements can serve as a mat3 + * + * @param {mat3} [mat] mat3 containing values to initialize with + * + * @returns {mat3} New mat3 + */ + mat3.create = function (mat) { + var dest = new MatrixArray(9); + + if (mat) { + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + dest[4] = mat[4]; + dest[5] = mat[5]; + dest[6] = mat[6]; + dest[7] = mat[7]; + dest[8] = mat[8]; + } else { + dest[0] = dest[1] = + dest[2] = dest[3] = + dest[4] = dest[5] = + dest[6] = dest[7] = + dest[8] = 0; + } + + return dest; + }; + + /** + * Creates a new instance of a mat3, initializing it with the given arguments + * + * @param {number} m00 + * @param {number} m01 + * @param {number} m02 + * @param {number} m10 + * @param {number} m11 + * @param {number} m12 + * @param {number} m20 + * @param {number} m21 + * @param {number} m22 + + * @returns {mat3} New mat3 + */ + mat3.createFrom = function (m00, m01, m02, m10, m11, m12, m20, m21, m22) { + var dest = new MatrixArray(9); + + dest[0] = m00; + dest[1] = m01; + dest[2] = m02; + dest[3] = m10; + dest[4] = m11; + dest[5] = m12; + dest[6] = m20; + dest[7] = m21; + dest[8] = m22; + + return dest; + }; + + /** + * Calculates the determinant of a mat3 + * + * @param {mat3} mat mat3 to calculate determinant of + * + * @returns {Number} determinant of mat + */ + mat3.determinant = function (mat) { + var a00 = mat[0], a01 = mat[1], a02 = mat[2], + a10 = mat[3], a11 = mat[4], a12 = mat[5], + a20 = mat[6], a21 = mat[7], a22 = mat[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + }; + + /** + * Calculates the inverse matrix of a mat3 + * + * @param {mat3} mat mat3 to calculate inverse of + * @param {mat3} [dest] mat3 receiving inverse matrix. If not specified result is written to mat + * + * @param {mat3} dest is specified, mat otherwise, null if matrix cannot be inverted + */ + mat3.inverse = function (mat, dest) { + var a00 = mat[0], a01 = mat[1], a02 = mat[2], + a10 = mat[3], a11 = mat[4], a12 = mat[5], + a20 = mat[6], a21 = mat[7], a22 = mat[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + d = a00 * b01 + a01 * b11 + a02 * b21, + id; + + if (!d) { return null; } + id = 1 / d; + + if (!dest) { dest = mat3.create(); } + + dest[0] = b01 * id; + dest[1] = (-a22 * a01 + a02 * a21) * id; + dest[2] = (a12 * a01 - a02 * a11) * id; + dest[3] = b11 * id; + dest[4] = (a22 * a00 - a02 * a20) * id; + dest[5] = (-a12 * a00 + a02 * a10) * id; + dest[6] = b21 * id; + dest[7] = (-a21 * a00 + a01 * a20) * id; + dest[8] = (a11 * a00 - a01 * a10) * id; + return dest; + }; + + /** + * Performs a matrix multiplication + * + * @param {mat3} mat First operand + * @param {mat3} mat2 Second operand + * @param {mat3} [dest] mat3 receiving operation result. If not specified result is written to mat + * + * @returns {mat3} dest if specified, mat otherwise + */ + mat3.multiply = function (mat, mat2, dest) { + if (!dest) { dest = mat; } + + + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[0], a01 = mat[1], a02 = mat[2], + a10 = mat[3], a11 = mat[4], a12 = mat[5], + a20 = mat[6], a21 = mat[7], a22 = mat[8], + + b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], + b10 = mat2[3], b11 = mat2[4], b12 = mat2[5], + b20 = mat2[6], b21 = mat2[7], b22 = mat2[8]; + + dest[0] = b00 * a00 + b01 * a10 + b02 * a20; + dest[1] = b00 * a01 + b01 * a11 + b02 * a21; + dest[2] = b00 * a02 + b01 * a12 + b02 * a22; + + dest[3] = b10 * a00 + b11 * a10 + b12 * a20; + dest[4] = b10 * a01 + b11 * a11 + b12 * a21; + dest[5] = b10 * a02 + b11 * a12 + b12 * a22; + + dest[6] = b20 * a00 + b21 * a10 + b22 * a20; + dest[7] = b20 * a01 + b21 * a11 + b22 * a21; + dest[8] = b20 * a02 + b21 * a12 + b22 * a22; + + return dest; + }; + + /** + * Transforms the vec2 according to the given mat3. + * + * @param {mat3} matrix mat3 to multiply against + * @param {vec2} vec the vector to multiply + * @param {vec2} [dest] an optional receiving vector. If not given, vec is used. + * + * @returns {vec2} The multiplication result + **/ + mat3.multiplyVec2 = function(matrix, vec, dest) { + if (!dest) dest = vec; + var x = vec[0], y = vec[1]; + dest[0] = x * matrix[0] + y * matrix[3] + matrix[6]; + dest[1] = x * matrix[1] + y * matrix[4] + matrix[7]; + return dest; + }; + + /** + * Transforms the vec3 according to the given mat3 + * + * @param {mat3} matrix mat3 to multiply against + * @param {vec3} vec the vector to multiply + * @param {vec3} [dest] an optional receiving vector. If not given, vec is used. + * + * @returns {vec3} The multiplication result + **/ + mat3.multiplyVec3 = function(matrix, vec, dest) { + if (!dest) dest = vec; + var x = vec[0], y = vec[1], z = vec[2]; + dest[0] = x * matrix[0] + y * matrix[3] + z * matrix[6]; + dest[1] = x * matrix[1] + y * matrix[4] + z * matrix[7]; + dest[2] = x * matrix[2] + y * matrix[5] + z * matrix[8]; + + return dest; + }; + + /** + * Copies the values of one mat3 to another + * + * @param {mat3} mat mat3 containing values to copy + * @param {mat3} dest mat3 receiving copied values + * + * @returns {mat3} dest + */ + mat3.set = function (mat, dest) { + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + dest[4] = mat[4]; + dest[5] = mat[5]; + dest[6] = mat[6]; + dest[7] = mat[7]; + dest[8] = mat[8]; + return dest; + }; + + /** + * Compares two matrices for equality within a certain margin of error + * + * @param {mat3} a First matrix + * @param {mat3} b Second matrix + * + * @returns {Boolean} True if a is equivalent to b + */ + mat3.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON && + Math.abs(a[2] - b[2]) < FLOAT_EPSILON && + Math.abs(a[3] - b[3]) < FLOAT_EPSILON && + Math.abs(a[4] - b[4]) < FLOAT_EPSILON && + Math.abs(a[5] - b[5]) < FLOAT_EPSILON && + Math.abs(a[6] - b[6]) < FLOAT_EPSILON && + Math.abs(a[7] - b[7]) < FLOAT_EPSILON && + Math.abs(a[8] - b[8]) < FLOAT_EPSILON + ); + }; + + /** + * Sets a mat3 to an identity matrix + * + * @param {mat3} dest mat3 to set + * + * @returns dest if specified, otherwise a new mat3 + */ + mat3.identity = function (dest) { + if (!dest) { dest = mat3.create(); } + dest[0] = 1; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + dest[4] = 1; + dest[5] = 0; + dest[6] = 0; + dest[7] = 0; + dest[8] = 1; + return dest; + }; + + /** + * Transposes a mat3 (flips the values over the diagonal) + * + * Params: + * @param {mat3} mat mat3 to transpose + * @param {mat3} [dest] mat3 receiving transposed values. If not specified result is written to mat + * + * @returns {mat3} dest is specified, mat otherwise + */ + mat3.transpose = function (mat, dest) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (!dest || mat === dest) { + var a01 = mat[1], a02 = mat[2], + a12 = mat[5]; + + mat[1] = mat[3]; + mat[2] = mat[6]; + mat[3] = a01; + mat[5] = mat[7]; + mat[6] = a02; + mat[7] = a12; + return mat; + } + + dest[0] = mat[0]; + dest[1] = mat[3]; + dest[2] = mat[6]; + dest[3] = mat[1]; + dest[4] = mat[4]; + dest[5] = mat[7]; + dest[6] = mat[2]; + dest[7] = mat[5]; + dest[8] = mat[8]; + return dest; + }; + + /** + * Copies the elements of a mat3 into the upper 3x3 elements of a mat4 + * + * @param {mat3} mat mat3 containing values to copy + * @param {mat4} [dest] mat4 receiving copied values + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + mat3.toMat4 = function (mat, dest) { + if (!dest) { dest = mat4.create(); } + + dest[15] = 1; + dest[14] = 0; + dest[13] = 0; + dest[12] = 0; + + dest[11] = 0; + dest[10] = mat[8]; + dest[9] = mat[7]; + dest[8] = mat[6]; + + dest[7] = 0; + dest[6] = mat[5]; + dest[5] = mat[4]; + dest[4] = mat[3]; + + dest[3] = 0; + dest[2] = mat[2]; + dest[1] = mat[1]; + dest[0] = mat[0]; + + return dest; + }; + + /** + * Returns a string representation of a mat3 + * + * @param {mat3} mat mat3 to represent as a string + * + * @param {string} String representation of mat + */ + mat3.str = function (mat) { + return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + + ', ' + mat[3] + ', ' + mat[4] + ', ' + mat[5] + + ', ' + mat[6] + ', ' + mat[7] + ', ' + mat[8] + ']'; + }; + + /** + * @class 4x4 Matrix + * @name mat4 + */ + var mat4 = {}; + + /** + * Creates a new instance of a mat4 using the default array type + * Any javascript array-like object containing at least 16 numeric elements can serve as a mat4 + * + * @param {mat4} [mat] mat4 containing values to initialize with + * + * @returns {mat4} New mat4 + */ + mat4.create = function (mat) { + var dest = new MatrixArray(16); + + if (mat) { + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + dest[4] = mat[4]; + dest[5] = mat[5]; + dest[6] = mat[6]; + dest[7] = mat[7]; + dest[8] = mat[8]; + dest[9] = mat[9]; + dest[10] = mat[10]; + dest[11] = mat[11]; + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + } + + return dest; + }; + + /** + * Creates a new instance of a mat4, initializing it with the given arguments + * + * @param {number} m00 + * @param {number} m01 + * @param {number} m02 + * @param {number} m03 + * @param {number} m10 + * @param {number} m11 + * @param {number} m12 + * @param {number} m13 + * @param {number} m20 + * @param {number} m21 + * @param {number} m22 + * @param {number} m23 + * @param {number} m30 + * @param {number} m31 + * @param {number} m32 + * @param {number} m33 + + * @returns {mat4} New mat4 + */ + mat4.createFrom = function (m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { + var dest = new MatrixArray(16); + + dest[0] = m00; + dest[1] = m01; + dest[2] = m02; + dest[3] = m03; + dest[4] = m10; + dest[5] = m11; + dest[6] = m12; + dest[7] = m13; + dest[8] = m20; + dest[9] = m21; + dest[10] = m22; + dest[11] = m23; + dest[12] = m30; + dest[13] = m31; + dest[14] = m32; + dest[15] = m33; + + return dest; + }; + + /** + * Copies the values of one mat4 to another + * + * @param {mat4} mat mat4 containing values to copy + * @param {mat4} dest mat4 receiving copied values + * + * @returns {mat4} dest + */ + mat4.set = function (mat, dest) { + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + dest[4] = mat[4]; + dest[5] = mat[5]; + dest[6] = mat[6]; + dest[7] = mat[7]; + dest[8] = mat[8]; + dest[9] = mat[9]; + dest[10] = mat[10]; + dest[11] = mat[11]; + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + return dest; + }; + + /** + * Compares two matrices for equality within a certain margin of error + * + * @param {mat4} a First matrix + * @param {mat4} b Second matrix + * + * @returns {Boolean} True if a is equivalent to b + */ + mat4.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON && + Math.abs(a[2] - b[2]) < FLOAT_EPSILON && + Math.abs(a[3] - b[3]) < FLOAT_EPSILON && + Math.abs(a[4] - b[4]) < FLOAT_EPSILON && + Math.abs(a[5] - b[5]) < FLOAT_EPSILON && + Math.abs(a[6] - b[6]) < FLOAT_EPSILON && + Math.abs(a[7] - b[7]) < FLOAT_EPSILON && + Math.abs(a[8] - b[8]) < FLOAT_EPSILON && + Math.abs(a[9] - b[9]) < FLOAT_EPSILON && + Math.abs(a[10] - b[10]) < FLOAT_EPSILON && + Math.abs(a[11] - b[11]) < FLOAT_EPSILON && + Math.abs(a[12] - b[12]) < FLOAT_EPSILON && + Math.abs(a[13] - b[13]) < FLOAT_EPSILON && + Math.abs(a[14] - b[14]) < FLOAT_EPSILON && + Math.abs(a[15] - b[15]) < FLOAT_EPSILON + ); + }; + + /** + * Sets a mat4 to an identity matrix + * + * @param {mat4} dest mat4 to set + * + * @returns {mat4} dest + */ + mat4.identity = function (dest) { + if (!dest) { dest = mat4.create(); } + dest[0] = 1; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + dest[4] = 0; + dest[5] = 1; + dest[6] = 0; + dest[7] = 0; + dest[8] = 0; + dest[9] = 0; + dest[10] = 1; + dest[11] = 0; + dest[12] = 0; + dest[13] = 0; + dest[14] = 0; + dest[15] = 1; + return dest; + }; + + /** + * Transposes a mat4 (flips the values over the diagonal) + * + * @param {mat4} mat mat4 to transpose + * @param {mat4} [dest] mat4 receiving transposed values. If not specified result is written to mat + * + * @param {mat4} dest is specified, mat otherwise + */ + mat4.transpose = function (mat, dest) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (!dest || mat === dest) { + var a01 = mat[1], a02 = mat[2], a03 = mat[3], + a12 = mat[6], a13 = mat[7], + a23 = mat[11]; + + mat[1] = mat[4]; + mat[2] = mat[8]; + mat[3] = mat[12]; + mat[4] = a01; + mat[6] = mat[9]; + mat[7] = mat[13]; + mat[8] = a02; + mat[9] = a12; + mat[11] = mat[14]; + mat[12] = a03; + mat[13] = a13; + mat[14] = a23; + return mat; + } + + dest[0] = mat[0]; + dest[1] = mat[4]; + dest[2] = mat[8]; + dest[3] = mat[12]; + dest[4] = mat[1]; + dest[5] = mat[5]; + dest[6] = mat[9]; + dest[7] = mat[13]; + dest[8] = mat[2]; + dest[9] = mat[6]; + dest[10] = mat[10]; + dest[11] = mat[14]; + dest[12] = mat[3]; + dest[13] = mat[7]; + dest[14] = mat[11]; + dest[15] = mat[15]; + return dest; + }; + + /** + * Calculates the determinant of a mat4 + * + * @param {mat4} mat mat4 to calculate determinant of + * + * @returns {number} determinant of mat + */ + mat4.determinant = function (mat) { + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3], + a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7], + a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11], + a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; + + return (a30 * a21 * a12 * a03 - a20 * a31 * a12 * a03 - a30 * a11 * a22 * a03 + a10 * a31 * a22 * a03 + + a20 * a11 * a32 * a03 - a10 * a21 * a32 * a03 - a30 * a21 * a02 * a13 + a20 * a31 * a02 * a13 + + a30 * a01 * a22 * a13 - a00 * a31 * a22 * a13 - a20 * a01 * a32 * a13 + a00 * a21 * a32 * a13 + + a30 * a11 * a02 * a23 - a10 * a31 * a02 * a23 - a30 * a01 * a12 * a23 + a00 * a31 * a12 * a23 + + a10 * a01 * a32 * a23 - a00 * a11 * a32 * a23 - a20 * a11 * a02 * a33 + a10 * a21 * a02 * a33 + + a20 * a01 * a12 * a33 - a00 * a21 * a12 * a33 - a10 * a01 * a22 * a33 + a00 * a11 * a22 * a33); + }; + + /** + * Calculates the inverse matrix of a mat4 + * + * @param {mat4} mat mat4 to calculate inverse of + * @param {mat4} [dest] mat4 receiving inverse matrix. If not specified result is written to mat + * + * @param {mat4} dest is specified, mat otherwise, null if matrix cannot be inverted + */ + mat4.inverse = function (mat, dest) { + if (!dest) { dest = mat; } + + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3], + a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7], + a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11], + a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), + invDet; + + // Calculate the determinant + if (!d) { return null; } + invDet = 1 / d; + + dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; + dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; + dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; + dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; + dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; + dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; + dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; + dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; + dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; + dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; + dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; + dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; + dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; + dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; + dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; + dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; + + return dest; + }; + + /** + * Copies the upper 3x3 elements of a mat4 into another mat4 + * + * @param {mat4} mat mat4 containing values to copy + * @param {mat4} [dest] mat4 receiving copied values + * + * @returns {mat4} dest is specified, a new mat4 otherwise + */ + mat4.toRotationMat = function (mat, dest) { + if (!dest) { dest = mat4.create(); } + + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + dest[4] = mat[4]; + dest[5] = mat[5]; + dest[6] = mat[6]; + dest[7] = mat[7]; + dest[8] = mat[8]; + dest[9] = mat[9]; + dest[10] = mat[10]; + dest[11] = mat[11]; + dest[12] = 0; + dest[13] = 0; + dest[14] = 0; + dest[15] = 1; + + return dest; + }; + + /** + * Copies the upper 3x3 elements of a mat4 into a mat3 + * + * @param {mat4} mat mat4 containing values to copy + * @param {mat3} [dest] mat3 receiving copied values + * + * @returns {mat3} dest is specified, a new mat3 otherwise + */ + mat4.toMat3 = function (mat, dest) { + if (!dest) { dest = mat3.create(); } + + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[4]; + dest[4] = mat[5]; + dest[5] = mat[6]; + dest[6] = mat[8]; + dest[7] = mat[9]; + dest[8] = mat[10]; + + return dest; + }; + + /** + * Calculates the inverse of the upper 3x3 elements of a mat4 and copies the result into a mat3 + * The resulting matrix is useful for calculating transformed normals + * + * Params: + * @param {mat4} mat mat4 containing values to invert and copy + * @param {mat3} [dest] mat3 receiving values + * + * @returns {mat3} dest is specified, a new mat3 otherwise, null if the matrix cannot be inverted + */ + mat4.toInverseMat3 = function (mat, dest) { + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[0], a01 = mat[1], a02 = mat[2], + a10 = mat[4], a11 = mat[5], a12 = mat[6], + a20 = mat[8], a21 = mat[9], a22 = mat[10], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + d = a00 * b01 + a01 * b11 + a02 * b21, + id; + + if (!d) { return null; } + id = 1 / d; + + if (!dest) { dest = mat3.create(); } + + dest[0] = b01 * id; + dest[1] = (-a22 * a01 + a02 * a21) * id; + dest[2] = (a12 * a01 - a02 * a11) * id; + dest[3] = b11 * id; + dest[4] = (a22 * a00 - a02 * a20) * id; + dest[5] = (-a12 * a00 + a02 * a10) * id; + dest[6] = b21 * id; + dest[7] = (-a21 * a00 + a01 * a20) * id; + dest[8] = (a11 * a00 - a01 * a10) * id; + + return dest; + }; + + /** + * Performs a matrix multiplication + * + * @param {mat4} mat First operand + * @param {mat4} mat2 Second operand + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @returns {mat4} dest if specified, mat otherwise + */ + mat4.multiply = function (mat, mat2, dest) { + if (!dest) { dest = mat; } + + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[ 0], a01 = mat[ 1], a02 = mat[ 2], a03 = mat[3]; + var a10 = mat[ 4], a11 = mat[ 5], a12 = mat[ 6], a13 = mat[7]; + var a20 = mat[ 8], a21 = mat[ 9], a22 = mat[10], a23 = mat[11]; + var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; + + // Cache only the current line of the second matrix + var b0 = mat2[0], b1 = mat2[1], b2 = mat2[2], b3 = mat2[3]; + dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = mat2[4]; + b1 = mat2[5]; + b2 = mat2[6]; + b3 = mat2[7]; + dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = mat2[8]; + b1 = mat2[9]; + b2 = mat2[10]; + b3 = mat2[11]; + dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = mat2[12]; + b1 = mat2[13]; + b2 = mat2[14]; + b3 = mat2[15]; + dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + return dest; + }; + + /** + * Transforms a vec3 with the given matrix + * 4th vector component is implicitly '1' + * + * @param {mat4} mat mat4 to transform the vector with + * @param {vec3} vec vec3 to transform + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns {vec3} dest if specified, vec otherwise + */ + mat4.multiplyVec3 = function (mat, vec, dest) { + if (!dest) { dest = vec; } + + var x = vec[0], y = vec[1], z = vec[2]; + + dest[0] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12]; + dest[1] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13]; + dest[2] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14]; + + return dest; + }; + + /** + * Transforms a vec4 with the given matrix + * + * @param {mat4} mat mat4 to transform the vector with + * @param {vec4} vec vec4 to transform + * @param {vec4} [dest] vec4 receiving operation result. If not specified result is written to vec + * + * @returns {vec4} dest if specified, vec otherwise + */ + mat4.multiplyVec4 = function (mat, vec, dest) { + if (!dest) { dest = vec; } + + var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; + + dest[0] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12] * w; + dest[1] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13] * w; + dest[2] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14] * w; + dest[3] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15] * w; + + return dest; + }; + + /** + * Translates a matrix by the given vector + * + * @param {mat4} mat mat4 to translate + * @param {vec3} vec vec3 specifying the translation + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @returns {mat4} dest if specified, mat otherwise + */ + mat4.translate = function (mat, vec, dest) { + var x = vec[0], y = vec[1], z = vec[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (!dest || mat === dest) { + mat[12] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12]; + mat[13] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13]; + mat[14] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14]; + mat[15] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15]; + return mat; + } + + a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; + a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; + a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; + + dest[0] = a00; dest[1] = a01; dest[2] = a02; dest[3] = a03; + dest[4] = a10; dest[5] = a11; dest[6] = a12; dest[7] = a13; + dest[8] = a20; dest[9] = a21; dest[10] = a22; dest[11] = a23; + + dest[12] = a00 * x + a10 * y + a20 * z + mat[12]; + dest[13] = a01 * x + a11 * y + a21 * z + mat[13]; + dest[14] = a02 * x + a12 * y + a22 * z + mat[14]; + dest[15] = a03 * x + a13 * y + a23 * z + mat[15]; + return dest; + }; + + /** + * Scales a matrix by the given vector + * + * @param {mat4} mat mat4 to scale + * @param {vec3} vec vec3 specifying the scale for each axis + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @param {mat4} dest if specified, mat otherwise + */ + mat4.scale = function (mat, vec, dest) { + var x = vec[0], y = vec[1], z = vec[2]; + + if (!dest || mat === dest) { + mat[0] *= x; + mat[1] *= x; + mat[2] *= x; + mat[3] *= x; + mat[4] *= y; + mat[5] *= y; + mat[6] *= y; + mat[7] *= y; + mat[8] *= z; + mat[9] *= z; + mat[10] *= z; + mat[11] *= z; + return mat; + } + + dest[0] = mat[0] * x; + dest[1] = mat[1] * x; + dest[2] = mat[2] * x; + dest[3] = mat[3] * x; + dest[4] = mat[4] * y; + dest[5] = mat[5] * y; + dest[6] = mat[6] * y; + dest[7] = mat[7] * y; + dest[8] = mat[8] * z; + dest[9] = mat[9] * z; + dest[10] = mat[10] * z; + dest[11] = mat[11] * z; + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + return dest; + }; + + /** + * Rotates a matrix by the given angle around the specified axis + * If rotating around a primary axis (X,Y,Z) one of the specialized rotation functions should be used instead for performance + * + * @param {mat4} mat mat4 to rotate + * @param {number} angle Angle (in radians) to rotate + * @param {vec3} axis vec3 representing the axis to rotate around + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @returns {mat4} dest if specified, mat otherwise + */ + mat4.rotate = function (mat, angle, axis, dest) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (!len) { return null; } + if (len !== 1) { + len = 1 / len; + x *= len; + y *= len; + z *= len; + } + + s = Math.sin(angle); + c = Math.cos(angle); + t = 1 - c; + + a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; + a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; + a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + if (!dest) { + dest = mat; + } else if (mat !== dest) { // If the source and destination differ, copy the unchanged last row + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + } + + // Perform rotation-specific matrix multiplication + dest[0] = a00 * b00 + a10 * b01 + a20 * b02; + dest[1] = a01 * b00 + a11 * b01 + a21 * b02; + dest[2] = a02 * b00 + a12 * b01 + a22 * b02; + dest[3] = a03 * b00 + a13 * b01 + a23 * b02; + + dest[4] = a00 * b10 + a10 * b11 + a20 * b12; + dest[5] = a01 * b10 + a11 * b11 + a21 * b12; + dest[6] = a02 * b10 + a12 * b11 + a22 * b12; + dest[7] = a03 * b10 + a13 * b11 + a23 * b12; + + dest[8] = a00 * b20 + a10 * b21 + a20 * b22; + dest[9] = a01 * b20 + a11 * b21 + a21 * b22; + dest[10] = a02 * b20 + a12 * b21 + a22 * b22; + dest[11] = a03 * b20 + a13 * b21 + a23 * b22; + return dest; + }; + + /** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} mat mat4 to rotate + * @param {number} angle Angle (in radians) to rotate + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @returns {mat4} dest if specified, mat otherwise + */ + mat4.rotateX = function (mat, angle, dest) { + var s = Math.sin(angle), + c = Math.cos(angle), + a10 = mat[4], + a11 = mat[5], + a12 = mat[6], + a13 = mat[7], + a20 = mat[8], + a21 = mat[9], + a22 = mat[10], + a23 = mat[11]; + + if (!dest) { + dest = mat; + } else if (mat !== dest) { // If the source and destination differ, copy the unchanged rows + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + } + + // Perform axis-specific matrix multiplication + dest[4] = a10 * c + a20 * s; + dest[5] = a11 * c + a21 * s; + dest[6] = a12 * c + a22 * s; + dest[7] = a13 * c + a23 * s; + + dest[8] = a10 * -s + a20 * c; + dest[9] = a11 * -s + a21 * c; + dest[10] = a12 * -s + a22 * c; + dest[11] = a13 * -s + a23 * c; + return dest; + }; + + /** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} mat mat4 to rotate + * @param {number} angle Angle (in radians) to rotate + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @returns {mat4} dest if specified, mat otherwise + */ + mat4.rotateY = function (mat, angle, dest) { + var s = Math.sin(angle), + c = Math.cos(angle), + a00 = mat[0], + a01 = mat[1], + a02 = mat[2], + a03 = mat[3], + a20 = mat[8], + a21 = mat[9], + a22 = mat[10], + a23 = mat[11]; + + if (!dest) { + dest = mat; + } else if (mat !== dest) { // If the source and destination differ, copy the unchanged rows + dest[4] = mat[4]; + dest[5] = mat[5]; + dest[6] = mat[6]; + dest[7] = mat[7]; + + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + } + + // Perform axis-specific matrix multiplication + dest[0] = a00 * c + a20 * -s; + dest[1] = a01 * c + a21 * -s; + dest[2] = a02 * c + a22 * -s; + dest[3] = a03 * c + a23 * -s; + + dest[8] = a00 * s + a20 * c; + dest[9] = a01 * s + a21 * c; + dest[10] = a02 * s + a22 * c; + dest[11] = a03 * s + a23 * c; + return dest; + }; + + /** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} mat mat4 to rotate + * @param {number} angle Angle (in radians) to rotate + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat + * + * @returns {mat4} dest if specified, mat otherwise + */ + mat4.rotateZ = function (mat, angle, dest) { + var s = Math.sin(angle), + c = Math.cos(angle), + a00 = mat[0], + a01 = mat[1], + a02 = mat[2], + a03 = mat[3], + a10 = mat[4], + a11 = mat[5], + a12 = mat[6], + a13 = mat[7]; + + if (!dest) { + dest = mat; + } else if (mat !== dest) { // If the source and destination differ, copy the unchanged last row + dest[8] = mat[8]; + dest[9] = mat[9]; + dest[10] = mat[10]; + dest[11] = mat[11]; + + dest[12] = mat[12]; + dest[13] = mat[13]; + dest[14] = mat[14]; + dest[15] = mat[15]; + } + + // Perform axis-specific matrix multiplication + dest[0] = a00 * c + a10 * s; + dest[1] = a01 * c + a11 * s; + dest[2] = a02 * c + a12 * s; + dest[3] = a03 * c + a13 * s; + + dest[4] = a00 * -s + a10 * c; + dest[5] = a01 * -s + a11 * c; + dest[6] = a02 * -s + a12 * c; + dest[7] = a03 * -s + a13 * c; + + return dest; + }; + + /** + * Generates a frustum matrix with the given bounds + * + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @param {mat4} [dest] mat4 frustum matrix will be written into + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + mat4.frustum = function (left, right, bottom, top, near, far, dest) { + if (!dest) { dest = mat4.create(); } + var rl = (right - left), + tb = (top - bottom), + fn = (far - near); + dest[0] = (near * 2) / rl; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + dest[4] = 0; + dest[5] = (near * 2) / tb; + dest[6] = 0; + dest[7] = 0; + dest[8] = (right + left) / rl; + dest[9] = (top + bottom) / tb; + dest[10] = -(far + near) / fn; + dest[11] = -1; + dest[12] = 0; + dest[13] = 0; + dest[14] = -(far * near * 2) / fn; + dest[15] = 0; + return dest; + }; + + /** + * Generates a perspective projection matrix with the given bounds + * + * @param {number} fovy Vertical field of view + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @param {mat4} [dest] mat4 frustum matrix will be written into + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + mat4.perspective = function (fovy, aspect, near, far, dest) { + var top = near * Math.tan(fovy * Math.PI / 360.0), + right = top * aspect; + return mat4.frustum(-right, right, -top, top, near, far, dest); + }; + + /** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @param {mat4} [dest] mat4 frustum matrix will be written into + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + mat4.ortho = function (left, right, bottom, top, near, far, dest) { + if (!dest) { dest = mat4.create(); } + var rl = (right - left), + tb = (top - bottom), + fn = (far - near); + dest[0] = 2 / rl; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + dest[4] = 0; + dest[5] = 2 / tb; + dest[6] = 0; + dest[7] = 0; + dest[8] = 0; + dest[9] = 0; + dest[10] = -2 / fn; + dest[11] = 0; + dest[12] = -(left + right) / rl; + dest[13] = -(top + bottom) / tb; + dest[14] = -(far + near) / fn; + dest[15] = 1; + return dest; + }; + + /** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing "up" + * @param {mat4} [dest] mat4 frustum matrix will be written into + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + mat4.lookAt = function (eye, center, up, dest) { + if (!dest) { dest = mat4.create(); } + + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (eyex === centerx && eyey === centery && eyez === centerz) { + return mat4.identity(dest); + } + + //vec3.direction(eye, center, z); + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + // normalize (no check needed for 0 because of early return) + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + //vec3.normalize(vec3.cross(up, z, x)); + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + //vec3.normalize(vec3.cross(z, x, y)); + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + dest[0] = x0; + dest[1] = y0; + dest[2] = z0; + dest[3] = 0; + dest[4] = x1; + dest[5] = y1; + dest[6] = z1; + dest[7] = 0; + dest[8] = x2; + dest[9] = y2; + dest[10] = z2; + dest[11] = 0; + dest[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + dest[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + dest[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + dest[15] = 1; + + return dest; + }; + + /** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {quat4} quat Rotation quaternion + * @param {vec3} vec Translation vector + * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to a new mat4 + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + mat4.fromRotationTranslation = function (quat, vec, dest) { + if (!dest) { dest = mat4.create(); } + + // Quaternion math + var x = quat[0], y = quat[1], z = quat[2], w = quat[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + dest[0] = 1 - (yy + zz); + dest[1] = xy + wz; + dest[2] = xz - wy; + dest[3] = 0; + dest[4] = xy - wz; + dest[5] = 1 - (xx + zz); + dest[6] = yz + wx; + dest[7] = 0; + dest[8] = xz + wy; + dest[9] = yz - wx; + dest[10] = 1 - (xx + yy); + dest[11] = 0; + dest[12] = vec[0]; + dest[13] = vec[1]; + dest[14] = vec[2]; + dest[15] = 1; + + return dest; + }; + + /** + * Returns a string representation of a mat4 + * + * @param {mat4} mat mat4 to represent as a string + * + * @returns {string} String representation of mat + */ + mat4.str = function (mat) { + return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + ', ' + mat[3] + + ', ' + mat[4] + ', ' + mat[5] + ', ' + mat[6] + ', ' + mat[7] + + ', ' + mat[8] + ', ' + mat[9] + ', ' + mat[10] + ', ' + mat[11] + + ', ' + mat[12] + ', ' + mat[13] + ', ' + mat[14] + ', ' + mat[15] + ']'; + }; + + /** + * @class Quaternion + * @name quat4 + */ + var quat4 = {}; + + /** + * Creates a new instance of a quat4 using the default array type + * Any javascript array containing at least 4 numeric elements can serve as a quat4 + * + * @param {quat4} [quat] quat4 containing values to initialize with + * + * @returns {quat4} New quat4 + */ + quat4.create = function (quat) { + var dest = new MatrixArray(4); + + if (quat) { + dest[0] = quat[0]; + dest[1] = quat[1]; + dest[2] = quat[2]; + dest[3] = quat[3]; + } else { + dest[0] = dest[1] = dest[2] = dest[3] = 0; + } + + return dest; + }; + + /** + * Creates a new instance of a quat4, initializing it with the given arguments + * + * @param {number} x X value + * @param {number} y Y value + * @param {number} z Z value + * @param {number} w W value + + * @returns {quat4} New quat4 + */ + quat4.createFrom = function (x, y, z, w) { + var dest = new MatrixArray(4); + + dest[0] = x; + dest[1] = y; + dest[2] = z; + dest[3] = w; + + return dest; + }; + + /** + * Copies the values of one quat4 to another + * + * @param {quat4} quat quat4 containing values to copy + * @param {quat4} dest quat4 receiving copied values + * + * @returns {quat4} dest + */ + quat4.set = function (quat, dest) { + dest[0] = quat[0]; + dest[1] = quat[1]; + dest[2] = quat[2]; + dest[3] = quat[3]; + + return dest; + }; + + /** + * Compares two quaternions for equality within a certain margin of error + * + * @param {quat4} a First vector + * @param {quat4} b Second vector + * + * @returns {Boolean} True if a is equivalent to b + */ + quat4.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON && + Math.abs(a[2] - b[2]) < FLOAT_EPSILON && + Math.abs(a[3] - b[3]) < FLOAT_EPSILON + ); + }; + + /** + * Creates a new identity Quat4 + * + * @param {quat4} [dest] quat4 receiving copied values + * + * @returns {quat4} dest is specified, new quat4 otherwise + */ + quat4.identity = function (dest) { + if (!dest) { dest = quat4.create(); } + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 1; + return dest; + }; + + var identityQuat4 = quat4.identity(); + + /** + * Calculates the W component of a quat4 from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat4} quat quat4 to calculate W component of + * @param {quat4} [dest] quat4 receiving calculated values. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.calculateW = function (quat, dest) { + var x = quat[0], y = quat[1], z = quat[2]; + + if (!dest || quat === dest) { + quat[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return quat; + } + dest[0] = x; + dest[1] = y; + dest[2] = z; + dest[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return dest; + }; + + /** + * Calculates the dot product of two quaternions + * + * @param {quat4} quat First operand + * @param {quat4} quat2 Second operand + * + * @return {number} Dot product of quat and quat2 + */ + quat4.dot = function(quat, quat2){ + return quat[0]*quat2[0] + quat[1]*quat2[1] + quat[2]*quat2[2] + quat[3]*quat2[3]; + }; + + /** + * Calculates the inverse of a quat4 + * + * @param {quat4} quat quat4 to calculate inverse of + * @param {quat4} [dest] quat4 receiving inverse values. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.inverse = function(quat, dest) { + var q0 = quat[0], q1 = quat[1], q2 = quat[2], q3 = quat[3], + dot = q0*q0 + q1*q1 + q2*q2 + q3*q3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + if(!dest || quat === dest) { + quat[0] *= -invDot; + quat[1] *= -invDot; + quat[2] *= -invDot; + quat[3] *= invDot; + return quat; + } + dest[0] = -quat[0]*invDot; + dest[1] = -quat[1]*invDot; + dest[2] = -quat[2]*invDot; + dest[3] = quat[3]*invDot; + return dest; + }; + + + /** + * Calculates the conjugate of a quat4 + * If the quaternion is normalized, this function is faster than quat4.inverse and produces the same result. + * + * @param {quat4} quat quat4 to calculate conjugate of + * @param {quat4} [dest] quat4 receiving conjugate values. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.conjugate = function (quat, dest) { + if (!dest || quat === dest) { + quat[0] *= -1; + quat[1] *= -1; + quat[2] *= -1; + return quat; + } + dest[0] = -quat[0]; + dest[1] = -quat[1]; + dest[2] = -quat[2]; + dest[3] = quat[3]; + return dest; + }; + + /** + * Calculates the length of a quat4 + * + * Params: + * @param {quat4} quat quat4 to calculate length of + * + * @returns Length of quat + */ + quat4.length = function (quat) { + var x = quat[0], y = quat[1], z = quat[2], w = quat[3]; + return Math.sqrt(x * x + y * y + z * z + w * w); + }; + + /** + * Generates a unit quaternion of the same direction as the provided quat4 + * If quaternion length is 0, returns [0, 0, 0, 0] + * + * @param {quat4} quat quat4 to normalize + * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.normalize = function (quat, dest) { + if (!dest) { dest = quat; } + + var x = quat[0], y = quat[1], z = quat[2], w = quat[3], + len = Math.sqrt(x * x + y * y + z * z + w * w); + if (len === 0) { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + return dest; + } + len = 1 / len; + dest[0] = x * len; + dest[1] = y * len; + dest[2] = z * len; + dest[3] = w * len; + + return dest; + }; + + /** + * Performs quaternion addition + * + * @param {quat4} quat First operand + * @param {quat4} quat2 Second operand + * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.add = function (quat, quat2, dest) { + if(!dest || quat === dest) { + quat[0] += quat2[0]; + quat[1] += quat2[1]; + quat[2] += quat2[2]; + quat[3] += quat2[3]; + return quat; + } + dest[0] = quat[0]+quat2[0]; + dest[1] = quat[1]+quat2[1]; + dest[2] = quat[2]+quat2[2]; + dest[3] = quat[3]+quat2[3]; + return dest; + }; + + /** + * Performs a quaternion multiplication + * + * @param {quat4} quat First operand + * @param {quat4} quat2 Second operand + * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.multiply = function (quat, quat2, dest) { + if (!dest) { dest = quat; } + + var qax = quat[0], qay = quat[1], qaz = quat[2], qaw = quat[3], + qbx = quat2[0], qby = quat2[1], qbz = quat2[2], qbw = quat2[3]; + + dest[0] = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + dest[1] = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + dest[2] = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + dest[3] = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + return dest; + }; + + /** + * Transforms a vec3 with the given quaternion + * + * @param {quat4} quat quat4 to transform the vector with + * @param {vec3} vec vec3 to transform + * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec + * + * @returns dest if specified, vec otherwise + */ + quat4.multiplyVec3 = function (quat, vec, dest) { + if (!dest) { dest = vec; } + + var x = vec[0], y = vec[1], z = vec[2], + qx = quat[0], qy = quat[1], qz = quat[2], qw = quat[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + dest[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + dest[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + dest[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + + return dest; + }; + + /** + * Multiplies the components of a quaternion by a scalar value + * + * @param {quat4} quat to scale + * @param {number} val Value to scale by + * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.scale = function (quat, val, dest) { + if(!dest || quat === dest) { + quat[0] *= val; + quat[1] *= val; + quat[2] *= val; + quat[3] *= val; + return quat; + } + dest[0] = quat[0]*val; + dest[1] = quat[1]*val; + dest[2] = quat[2]*val; + dest[3] = quat[3]*val; + return dest; + }; + + /** + * Calculates a 3x3 matrix from the given quat4 + * + * @param {quat4} quat quat4 to create matrix from + * @param {mat3} [dest] mat3 receiving operation result + * + * @returns {mat3} dest if specified, a new mat3 otherwise + */ + quat4.toMat3 = function (quat, dest) { + if (!dest) { dest = mat3.create(); } + + var x = quat[0], y = quat[1], z = quat[2], w = quat[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + dest[0] = 1 - (yy + zz); + dest[1] = xy + wz; + dest[2] = xz - wy; + + dest[3] = xy - wz; + dest[4] = 1 - (xx + zz); + dest[5] = yz + wx; + + dest[6] = xz + wy; + dest[7] = yz - wx; + dest[8] = 1 - (xx + yy); + + return dest; + }; + + /** + * Calculates a 4x4 matrix from the given quat4 + * + * @param {quat4} quat quat4 to create matrix from + * @param {mat4} [dest] mat4 receiving operation result + * + * @returns {mat4} dest if specified, a new mat4 otherwise + */ + quat4.toMat4 = function (quat, dest) { + if (!dest) { dest = mat4.create(); } + + var x = quat[0], y = quat[1], z = quat[2], w = quat[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + dest[0] = 1 - (yy + zz); + dest[1] = xy + wz; + dest[2] = xz - wy; + dest[3] = 0; + + dest[4] = xy - wz; + dest[5] = 1 - (xx + zz); + dest[6] = yz + wx; + dest[7] = 0; + + dest[8] = xz + wy; + dest[9] = yz - wx; + dest[10] = 1 - (xx + yy); + dest[11] = 0; + + dest[12] = 0; + dest[13] = 0; + dest[14] = 0; + dest[15] = 1; + + return dest; + }; + + /** + * Performs a spherical linear interpolation between two quat4 + * + * @param {quat4} quat First quaternion + * @param {quat4} quat2 Second quaternion + * @param {number} slerp Interpolation amount between the two inputs + * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat + * + * @returns {quat4} dest if specified, quat otherwise + */ + quat4.slerp = function (quat, quat2, slerp, dest) { + if (!dest) { dest = quat; } + + var cosHalfTheta = quat[0] * quat2[0] + quat[1] * quat2[1] + quat[2] * quat2[2] + quat[3] * quat2[3], + halfTheta, + sinHalfTheta, + ratioA, + ratioB; + + if (Math.abs(cosHalfTheta) >= 1.0) { + if (dest !== quat) { + dest[0] = quat[0]; + dest[1] = quat[1]; + dest[2] = quat[2]; + dest[3] = quat[3]; + } + return dest; + } + + halfTheta = Math.acos(cosHalfTheta); + sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + + if (Math.abs(sinHalfTheta) < 0.001) { + dest[0] = (quat[0] * 0.5 + quat2[0] * 0.5); + dest[1] = (quat[1] * 0.5 + quat2[1] * 0.5); + dest[2] = (quat[2] * 0.5 + quat2[2] * 0.5); + dest[3] = (quat[3] * 0.5 + quat2[3] * 0.5); + return dest; + } + + ratioA = Math.sin((1 - slerp) * halfTheta) / sinHalfTheta; + ratioB = Math.sin(slerp * halfTheta) / sinHalfTheta; + + dest[0] = (quat[0] * ratioA + quat2[0] * ratioB); + dest[1] = (quat[1] * ratioA + quat2[1] * ratioB); + dest[2] = (quat[2] * ratioA + quat2[2] * ratioB); + dest[3] = (quat[3] * ratioA + quat2[3] * ratioB); + + return dest; + }; + + /** + * Creates a quaternion from the given 3x3 rotation matrix. + * If dest is omitted, a new quaternion will be created. + * + * @param {mat3} mat the rotation matrix + * @param {quat4} [dest] an optional receiving quaternion + * + * @returns {quat4} the quaternion constructed from the rotation matrix + * + */ + quat4.fromRotationMatrix = function(mat, dest) { + if (!dest) dest = quat4.create(); + + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + + var fTrace = mat[0] + mat[4] + mat[8]; + var fRoot; + + if ( fTrace > 0.0 ) { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = Math.sqrt(fTrace + 1.0); // 2w + dest[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; // 1/(4w) + dest[0] = (mat[7]-mat[5])*fRoot; + dest[1] = (mat[2]-mat[6])*fRoot; + dest[2] = (mat[3]-mat[1])*fRoot; + } else { + // |w| <= 1/2 + var s_iNext = quat4.fromRotationMatrix.s_iNext = quat4.fromRotationMatrix.s_iNext || [1,2,0]; + var i = 0; + if ( mat[4] > mat[0] ) + i = 1; + if ( mat[8] > mat[i*3+i] ) + i = 2; + var j = s_iNext[i]; + var k = s_iNext[j]; + + fRoot = Math.sqrt(mat[i*3+i]-mat[j*3+j]-mat[k*3+k] + 1.0); + dest[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + dest[3] = (mat[k*3+j] - mat[j*3+k]) * fRoot; + dest[j] = (mat[j*3+i] + mat[i*3+j]) * fRoot; + dest[k] = (mat[k*3+i] + mat[i*3+k]) * fRoot; + } + + return dest; + }; + + /** + * Alias. See the description for quat4.fromRotationMatrix(). + */ + mat3.toQuat4 = quat4.fromRotationMatrix; + + (function() { + var mat = mat3.create(); + + /** + * Creates a quaternion from the 3 given vectors. They must be perpendicular + * to one another and represent the X, Y and Z axes. + * + * If dest is omitted, a new quat4 will be created. + * + * Example: The default OpenGL orientation has a view vector [0, 0, -1], + * right vector [1, 0, 0], and up vector [0, 1, 0]. A quaternion representing + * this orientation could be constructed with: + * + * quat = quat4.fromAxes([0, 0, -1], [1, 0, 0], [0, 1, 0], quat4.create()); + * + * @param {vec3} view the view vector, or direction the object is pointing in + * @param {vec3} right the right vector, or direction to the "right" of the object + * @param {vec3} up the up vector, or direction towards the object's "up" + * @param {quat4} [dest] an optional receiving quat4 + * + * @returns {quat4} dest + **/ + quat4.fromAxes = function(view, right, up, dest) { + mat[0] = right[0]; + mat[3] = right[1]; + mat[6] = right[2]; + + mat[1] = up[0]; + mat[4] = up[1]; + mat[7] = up[2]; + + mat[2] = view[0]; + mat[5] = view[1]; + mat[8] = view[2]; + + return quat4.fromRotationMatrix(mat, dest); + }; + })(); + + /** + * Sets a quat4 to the Identity and returns it. + * + * @param {quat4} [dest] quat4 to set. If omitted, a + * new quat4 will be created. + * + * @returns {quat4} dest + */ + quat4.identity = function(dest) { + if (!dest) dest = quat4.create(); + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 1; + return dest; + }; + + /** + * Sets a quat4 from the given angle and rotation axis, + * then returns it. If dest is not given, a new quat4 is created. + * + * @param {Number} angle the angle in radians + * @param {vec3} axis the axis around which to rotate + * @param {quat4} [dest] the optional quat4 to store the result + * + * @returns {quat4} dest + **/ + quat4.fromAngleAxis = function(angle, axis, dest) { + // The quaternion representing the rotation is + // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) + if (!dest) dest = quat4.create(); + + var half = angle * 0.5; + var s = Math.sin(half); + dest[3] = Math.cos(half); + dest[0] = s * axis[0]; + dest[1] = s * axis[1]; + dest[2] = s * axis[2]; + + return dest; + }; + + /** + * Stores the angle and axis in a vec4, where the XYZ components represent + * the axis and the W (4th) component is the angle in radians. + * + * If dest is not given, src will be modified in place and returned, after + * which it should not be considered not a quaternion (just an axis and angle). + * + * @param {quat4} quat the quaternion whose angle and axis to store + * @param {vec4} [dest] the optional vec4 to receive the data + * + * @returns {vec4} dest + */ + quat4.toAngleAxis = function(src, dest) { + if (!dest) dest = src; + // The quaternion representing the rotation is + // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) + + var sqrlen = src[0]*src[0]+src[1]*src[1]+src[2]*src[2]; + if (sqrlen > 0) + { + dest[3] = 2 * Math.acos(src[3]); + var invlen = glMath.invsqrt(sqrlen); + dest[0] = src[0]*invlen; + dest[1] = src[1]*invlen; + dest[2] = src[2]*invlen; + } else { + // angle is 0 (mod 2*pi), so any axis will do + dest[3] = 0; + dest[0] = 1; + dest[1] = 0; + dest[2] = 0; + } + + return dest; + }; + + /** + * Returns a string representation of a quaternion + * + * @param {quat4} quat quat4 to represent as a string + * + * @returns {string} String representation of quat + */ + quat4.str = function (quat) { + return '[' + quat[0] + ', ' + quat[1] + ', ' + quat[2] + ', ' + quat[3] + ']'; + }; + + /** + * @class 2 Dimensional Vector + * @name vec2 + */ + var vec2 = {}; + + /** + * Creates a new vec2, initializing it from vec if vec + * is given. + * + * @param {vec2} [vec] the vector's initial contents + * @returns {vec2} a new 2D vector + */ + vec2.create = function(vec) { + var dest = new MatrixArray(2); + + if (vec) { + dest[0] = vec[0]; + dest[1] = vec[1]; + } else { + dest[0] = 0; + dest[1] = 0; + } + return dest; + }; + + /** + * Creates a new instance of a vec2, initializing it with the given arguments + * + * @param {number} x X value + * @param {number} y Y value + + * @returns {vec2} New vec2 + */ + vec2.createFrom = function (x, y) { + var dest = new MatrixArray(2); + + dest[0] = x; + dest[1] = y; + + return dest; + }; + + /** + * Adds the vec2's together. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.add = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] + vecB[0]; + dest[1] = vecA[1] + vecB[1]; + return dest; + }; + + /** + * Subtracts vecB from vecA. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.subtract = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] - vecB[0]; + dest[1] = vecA[1] - vecB[1]; + return dest; + }; + + /** + * Multiplies vecA with vecB. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.multiply = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] * vecB[0]; + dest[1] = vecA[1] * vecB[1]; + return dest; + }; + + /** + * Divides vecA by vecB. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.divide = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] / vecB[0]; + dest[1] = vecA[1] / vecB[1]; + return dest; + }; + + /** + * Scales vecA by some scalar number. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecA. + * + * This is the same as multiplying each component of vecA + * by the given scalar. + * + * @param {vec2} vecA the vector to be scaled + * @param {Number} scalar the amount to scale the vector by + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.scale = function(vecA, scalar, dest) { + if (!dest) dest = vecA; + dest[0] = vecA[0] * scalar; + dest[1] = vecA[1] * scalar; + return dest; + }; + + /** + * Calculates the euclidian distance between two vec2 + * + * Params: + * @param {vec2} vecA First vector + * @param {vec2} vecB Second vector + * + * @returns {number} Distance between vecA and vecB + */ + vec2.dist = function (vecA, vecB) { + var x = vecB[0] - vecA[0], + y = vecB[1] - vecA[1]; + return Math.sqrt(x*x + y*y); + }; + + /** + * Copies the values of one vec2 to another + * + * @param {vec2} vec vec2 containing values to copy + * @param {vec2} dest vec2 receiving copied values + * + * @returns {vec2} dest + */ + vec2.set = function (vec, dest) { + dest[0] = vec[0]; + dest[1] = vec[1]; + return dest; + }; + + /** + * Compares two vectors for equality within a certain margin of error + * + * @param {vec2} a First vector + * @param {vec2} b Second vector + * + * @returns {Boolean} True if a is equivalent to b + */ + vec2.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON + ); + }; + + /** + * Negates the components of a vec2 + * + * @param {vec2} vec vec2 to negate + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vec + * + * @returns {vec2} dest if specified, vec otherwise + */ + vec2.negate = function (vec, dest) { + if (!dest) { dest = vec; } + dest[0] = -vec[0]; + dest[1] = -vec[1]; + return dest; + }; + + /** + * Normlize a vec2 + * + * @param {vec2} vec vec2 to normalize + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vec + * + * @returns {vec2} dest if specified, vec otherwise + */ + vec2.normalize = function (vec, dest) { + if (!dest) { dest = vec; } + var mag = vec[0] * vec[0] + vec[1] * vec[1]; + if (mag > 0) { + mag = Math.sqrt(mag); + dest[0] = vec[0] / mag; + dest[1] = vec[1] / mag; + } else { + dest[0] = dest[1] = 0; + } + return dest; + }; + + /** + * Computes the cross product of two vec2's. Note that the cross product must by definition + * produce a 3D vector. If a dest vector is given, it will contain the resultant 3D vector. + * Otherwise, a scalar number will be returned, representing the vector's Z coordinate, since + * its X and Y must always equal 0. + * + * Examples: + * var crossResult = vec3.create(); + * vec2.cross([1, 2], [3, 4], crossResult); + * //=> [0, 0, -2] + * + * vec2.cross([1, 2], [3, 4]); + * //=> -2 + * + * See http://stackoverflow.com/questions/243945/calculating-a-2d-vectors-cross-product + * for some interesting facts. + * + * @param {vec2} vecA left operand + * @param {vec2} vecB right operand + * @param {vec2} [dest] optional vec2 receiving result. If not specified a scalar is returned + * + */ + vec2.cross = function (vecA, vecB, dest) { + var z = vecA[0] * vecB[1] - vecA[1] * vecB[0]; + if (!dest) return z; + dest[0] = dest[1] = 0; + dest[2] = z; + return dest; + }; + + /** + * Caclulates the length of a vec2 + * + * @param {vec2} vec vec2 to calculate length of + * + * @returns {Number} Length of vec + */ + vec2.length = function (vec) { + var x = vec[0], y = vec[1]; + return Math.sqrt(x * x + y * y); + }; + + /** + * Caclulates the squared length of a vec2 + * + * @param {vec2} vec vec2 to calculate squared length of + * + * @returns {Number} Squared Length of vec + */ + vec2.squaredLength = function (vec) { + var x = vec[0], y = vec[1]; + return x * x + y * y; + }; + + /** + * Caclulates the dot product of two vec2s + * + * @param {vec2} vecA First operand + * @param {vec2} vecB Second operand + * + * @returns {Number} Dot product of vecA and vecB + */ + vec2.dot = function (vecA, vecB) { + return vecA[0] * vecB[0] + vecA[1] * vecB[1]; + }; + + /** + * Generates a 2D unit vector pointing from one vector to another + * + * @param {vec2} vecA Origin vec2 + * @param {vec2} vecB vec2 to point to + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vecA + * + * @returns {vec2} dest if specified, vecA otherwise + */ + vec2.direction = function (vecA, vecB, dest) { + if (!dest) { dest = vecA; } + + var x = vecA[0] - vecB[0], + y = vecA[1] - vecB[1], + len = x * x + y * y; + + if (!len) { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + return dest; + } + + len = 1 / Math.sqrt(len); + dest[0] = x * len; + dest[1] = y * len; + return dest; + }; + + /** + * Performs a linear interpolation between two vec2 + * + * @param {vec2} vecA First vector + * @param {vec2} vecB Second vector + * @param {Number} lerp Interpolation amount between the two inputs + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vecA + * + * @returns {vec2} dest if specified, vecA otherwise + */ + vec2.lerp = function (vecA, vecB, lerp, dest) { + if (!dest) { dest = vecA; } + dest[0] = vecA[0] + lerp * (vecB[0] - vecA[0]); + dest[1] = vecA[1] + lerp * (vecB[1] - vecA[1]); + return dest; + }; + + /** + * Returns a string representation of a vector + * + * @param {vec2} vec Vector to represent as a string + * + * @returns {String} String representation of vec + */ + vec2.str = function (vec) { + return '[' + vec[0] + ', ' + vec[1] + ']'; + }; + + /** + * @class 2x2 Matrix + * @name mat2 + */ + var mat2 = {}; + + /** + * Creates a new 2x2 matrix. If src is given, the new matrix + * is initialized to those values. + * + * @param {mat2} [src] the seed values for the new matrix, if any + * @returns {mat2} a new matrix + */ + mat2.create = function(src) { + var dest = new MatrixArray(4); + + if (src) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + } else { + dest[0] = dest[1] = dest[2] = dest[3] = 0; + } + return dest; + }; + + /** + * Creates a new instance of a mat2, initializing it with the given arguments + * + * @param {number} m00 + * @param {number} m01 + * @param {number} m10 + * @param {number} m11 + + * @returns {mat2} New mat2 + */ + mat2.createFrom = function (m00, m01, m10, m11) { + var dest = new MatrixArray(4); + + dest[0] = m00; + dest[1] = m01; + dest[2] = m10; + dest[3] = m11; + + return dest; + }; + + /** + * Copies the values of one mat2 to another + * + * @param {mat2} mat mat2 containing values to copy + * @param {mat2} dest mat2 receiving copied values + * + * @returns {mat2} dest + */ + mat2.set = function (mat, dest) { + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + return dest; + }; + + /** + * Compares two matrices for equality within a certain margin of error + * + * @param {mat2} a First matrix + * @param {mat2} b Second matrix + * + * @returns {Boolean} True if a is equivalent to b + */ + mat2.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON && + Math.abs(a[2] - b[2]) < FLOAT_EPSILON && + Math.abs(a[3] - b[3]) < FLOAT_EPSILON + ); + }; + + /** + * Sets a mat2 to an identity matrix + * + * @param {mat2} [dest] mat2 to set. If omitted a new one will be created. + * + * @returns {mat2} dest + */ + mat2.identity = function (dest) { + if (!dest) { dest = mat2.create(); } + dest[0] = 1; + dest[1] = 0; + dest[2] = 0; + dest[3] = 1; + return dest; + }; + + /** + * Transposes a mat2 (flips the values over the diagonal) + * + * @param {mat2} mat mat2 to transpose + * @param {mat2} [dest] mat2 receiving transposed values. If not specified result is written to mat + * + * @param {mat2} dest if specified, mat otherwise + */ + mat2.transpose = function (mat, dest) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (!dest || mat === dest) { + var a00 = mat[1]; + mat[1] = mat[2]; + mat[2] = a00; + return mat; + } + + dest[0] = mat[0]; + dest[1] = mat[2]; + dest[2] = mat[1]; + dest[3] = mat[3]; + return dest; + }; + + /** + * Calculates the determinant of a mat2 + * + * @param {mat2} mat mat2 to calculate determinant of + * + * @returns {Number} determinant of mat + */ + mat2.determinant = function (mat) { + return mat[0] * mat[3] - mat[2] * mat[1]; + }; + + /** + * Calculates the inverse matrix of a mat2 + * + * @param {mat2} mat mat2 to calculate inverse of + * @param {mat2} [dest] mat2 receiving inverse matrix. If not specified result is written to mat + * + * @param {mat2} dest is specified, mat otherwise, null if matrix cannot be inverted + */ + mat2.inverse = function (mat, dest) { + if (!dest) { dest = mat; } + var a0 = mat[0], a1 = mat[1], a2 = mat[2], a3 = mat[3]; + var det = a0 * a3 - a2 * a1; + if (!det) return null; + + det = 1.0 / det; + dest[0] = a3 * det; + dest[1] = -a1 * det; + dest[2] = -a2 * det; + dest[3] = a0 * det; + return dest; + }; + + /** + * Performs a matrix multiplication + * + * @param {mat2} matA First operand + * @param {mat2} matB Second operand + * @param {mat2} [dest] mat2 receiving operation result. If not specified result is written to matA + * + * @returns {mat2} dest if specified, matA otherwise + */ + mat2.multiply = function (matA, matB, dest) { + if (!dest) { dest = matA; } + var a11 = matA[0], + a12 = matA[1], + a21 = matA[2], + a22 = matA[3]; + dest[0] = a11 * matB[0] + a12 * matB[2]; + dest[1] = a11 * matB[1] + a12 * matB[3]; + dest[2] = a21 * matB[0] + a22 * matB[2]; + dest[3] = a21 * matB[1] + a22 * matB[3]; + return dest; + }; + + /** + * Rotates a 2x2 matrix by an angle + * + * @param {mat2} mat The matrix to rotate + * @param {Number} angle The angle in radians + * @param {mat2} [dest] Optional mat2 receiving the result. If omitted mat will be used. + * + * @returns {mat2} dest if specified, mat otherwise + */ + mat2.rotate = function (mat, angle, dest) { + if (!dest) { dest = mat; } + var a11 = mat[0], + a12 = mat[1], + a21 = mat[2], + a22 = mat[3], + s = Math.sin(angle), + c = Math.cos(angle); + dest[0] = a11 * c + a12 * s; + dest[1] = a11 * -s + a12 * c; + dest[2] = a21 * c + a22 * s; + dest[3] = a21 * -s + a22 * c; + return dest; + }; + + /** + * Multiplies the vec2 by the given 2x2 matrix + * + * @param {mat2} matrix the 2x2 matrix to multiply against + * @param {vec2} vec the vector to multiply + * @param {vec2} [dest] an optional receiving vector. If not given, vec is used. + * + * @returns {vec2} The multiplication result + **/ + mat2.multiplyVec2 = function(matrix, vec, dest) { + if (!dest) dest = vec; + var x = vec[0], y = vec[1]; + dest[0] = x * matrix[0] + y * matrix[1]; + dest[1] = x * matrix[2] + y * matrix[3]; + return dest; + }; + + /** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} matrix the 2x2 matrix to scale + * @param {vec2} vec the vector containing the dimensions to scale by + * @param {vec2} [dest] an optional receiving mat2. If not given, matrix is used. + * + * @returns {mat2} dest if specified, matrix otherwise + **/ + mat2.scale = function(matrix, vec, dest) { + if (!dest) { dest = matrix; } + var a11 = matrix[0], + a12 = matrix[1], + a21 = matrix[2], + a22 = matrix[3], + b11 = vec[0], + b22 = vec[1]; + dest[0] = a11 * b11; + dest[1] = a12 * b22; + dest[2] = a21 * b11; + dest[3] = a22 * b22; + return dest; + }; + + /** + * Returns a string representation of a mat2 + * + * @param {mat2} mat mat2 to represent as a string + * + * @param {String} String representation of mat + */ + mat2.str = function (mat) { + return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + ', ' + mat[3] + ']'; + }; + + /** + * @class 4 Dimensional Vector + * @name vec4 + */ + var vec4 = {}; + + /** + * Creates a new vec4, initializing it from vec if vec + * is given. + * + * @param {vec4} [vec] the vector's initial contents + * @returns {vec4} a new 2D vector + */ + vec4.create = function(vec) { + var dest = new MatrixArray(4); + + if (vec) { + dest[0] = vec[0]; + dest[1] = vec[1]; + dest[2] = vec[2]; + dest[3] = vec[3]; + } else { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + } + return dest; + }; + + /** + * Creates a new instance of a vec4, initializing it with the given arguments + * + * @param {number} x X value + * @param {number} y Y value + * @param {number} z Z value + * @param {number} w W value + + * @returns {vec4} New vec4 + */ + vec4.createFrom = function (x, y, z, w) { + var dest = new MatrixArray(4); + + dest[0] = x; + dest[1] = y; + dest[2] = z; + dest[3] = w; + + return dest; + }; + + /** + * Adds the vec4's together. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec4} vecA the first operand + * @param {vec4} vecB the second operand + * @param {vec4} [dest] the optional receiving vector + * @returns {vec4} dest + */ + vec4.add = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] + vecB[0]; + dest[1] = vecA[1] + vecB[1]; + dest[2] = vecA[2] + vecB[2]; + dest[3] = vecA[3] + vecB[3]; + return dest; + }; + + /** + * Subtracts vecB from vecA. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec4} vecA the first operand + * @param {vec4} vecB the second operand + * @param {vec4} [dest] the optional receiving vector + * @returns {vec4} dest + */ + vec4.subtract = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] - vecB[0]; + dest[1] = vecA[1] - vecB[1]; + dest[2] = vecA[2] - vecB[2]; + dest[3] = vecA[3] - vecB[3]; + return dest; + }; + + /** + * Multiplies vecA with vecB. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec4} vecA the first operand + * @param {vec4} vecB the second operand + * @param {vec4} [dest] the optional receiving vector + * @returns {vec4} dest + */ + vec4.multiply = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] * vecB[0]; + dest[1] = vecA[1] * vecB[1]; + dest[2] = vecA[2] * vecB[2]; + dest[3] = vecA[3] * vecB[3]; + return dest; + }; + + /** + * Divides vecA by vecB. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec4} vecA the first operand + * @param {vec4} vecB the second operand + * @param {vec4} [dest] the optional receiving vector + * @returns {vec4} dest + */ + vec4.divide = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] / vecB[0]; + dest[1] = vecA[1] / vecB[1]; + dest[2] = vecA[2] / vecB[2]; + dest[3] = vecA[3] / vecB[3]; + return dest; + }; + + /** + * Scales vecA by some scalar number. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecA. + * + * This is the same as multiplying each component of vecA + * by the given scalar. + * + * @param {vec4} vecA the vector to be scaled + * @param {Number} scalar the amount to scale the vector by + * @param {vec4} [dest] the optional receiving vector + * @returns {vec4} dest + */ + vec4.scale = function(vecA, scalar, dest) { + if (!dest) dest = vecA; + dest[0] = vecA[0] * scalar; + dest[1] = vecA[1] * scalar; + dest[2] = vecA[2] * scalar; + dest[3] = vecA[3] * scalar; + return dest; + }; + + /** + * Copies the values of one vec4 to another + * + * @param {vec4} vec vec4 containing values to copy + * @param {vec4} dest vec4 receiving copied values + * + * @returns {vec4} dest + */ + vec4.set = function (vec, dest) { + dest[0] = vec[0]; + dest[1] = vec[1]; + dest[2] = vec[2]; + dest[3] = vec[3]; + return dest; + }; + + /** + * Compares two vectors for equality within a certain margin of error + * + * @param {vec4} a First vector + * @param {vec4} b Second vector + * + * @returns {Boolean} True if a is equivalent to b + */ + vec4.equal = function (a, b) { + return a === b || ( + Math.abs(a[0] - b[0]) < FLOAT_EPSILON && + Math.abs(a[1] - b[1]) < FLOAT_EPSILON && + Math.abs(a[2] - b[2]) < FLOAT_EPSILON && + Math.abs(a[3] - b[3]) < FLOAT_EPSILON + ); + }; + + /** + * Negates the components of a vec4 + * + * @param {vec4} vec vec4 to negate + * @param {vec4} [dest] vec4 receiving operation result. If not specified result is written to vec + * + * @returns {vec4} dest if specified, vec otherwise + */ + vec4.negate = function (vec, dest) { + if (!dest) { dest = vec; } + dest[0] = -vec[0]; + dest[1] = -vec[1]; + dest[2] = -vec[2]; + dest[3] = -vec[3]; + return dest; + }; + + /** + * Caclulates the length of a vec2 + * + * @param {vec2} vec vec2 to calculate length of + * + * @returns {Number} Length of vec + */ + vec4.length = function (vec) { + var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; + return Math.sqrt(x * x + y * y + z * z + w * w); + }; + + /** + * Caclulates the squared length of a vec4 + * + * @param {vec4} vec vec4 to calculate squared length of + * + * @returns {Number} Squared Length of vec + */ + vec4.squaredLength = function (vec) { + var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; + return x * x + y * y + z * z + w * w; + }; + + /** + * Performs a linear interpolation between two vec4 + * + * @param {vec4} vecA First vector + * @param {vec4} vecB Second vector + * @param {Number} lerp Interpolation amount between the two inputs + * @param {vec4} [dest] vec4 receiving operation result. If not specified result is written to vecA + * + * @returns {vec4} dest if specified, vecA otherwise + */ + vec4.lerp = function (vecA, vecB, lerp, dest) { + if (!dest) { dest = vecA; } + dest[0] = vecA[0] + lerp * (vecB[0] - vecA[0]); + dest[1] = vecA[1] + lerp * (vecB[1] - vecA[1]); + dest[2] = vecA[2] + lerp * (vecB[2] - vecA[2]); + dest[3] = vecA[3] + lerp * (vecB[3] - vecA[3]); + return dest; + }; + + /** + * Returns a string representation of a vector + * + * @param {vec4} vec Vector to represent as a string + * + * @returns {String} String representation of vec + */ + vec4.str = function (vec) { + return '[' + vec[0] + ', ' + vec[1] + ', ' + vec[2] + ', ' + vec[3] + ']'; + }; + + /* + * Exports + */ + + if(root) { + root.glMatrixArrayType = MatrixArray; + root.MatrixArray = MatrixArray; + root.setMatrixArrayType = setMatrixArrayType; + root.determineMatrixArrayType = determineMatrixArrayType; + root.glMath = glMath; + root.vec2 = vec2; + root.vec3 = vec3; + root.vec4 = vec4; + root.mat2 = mat2; + root.mat3 = mat3; + root.mat4 = mat4; + root.quat4 = quat4; + } + + return { + glMatrixArrayType: MatrixArray, + MatrixArray: MatrixArray, + setMatrixArrayType: setMatrixArrayType, + determineMatrixArrayType: determineMatrixArrayType, + glMath: glMath, + vec2: vec2, + vec3: vec3, + vec4: vec4, + mat2: mat2, + mat3: mat3, + mat4: mat4, + quat4: quat4 + }; +})); diff --git a/part2/Hi-Res Texture Version/index.html b/part2/Hi-Res Texture Version/index.html new file mode 100644 index 0000000..2abb12b --- /dev/null +++ b/part2/Hi-Res Texture Version/index.html @@ -0,0 +1,251 @@ + + +Fragment Globe + + + + + + + + + + + + + + + +
+ + + + + diff --git a/part2/Hi-Res Texture Version/moonbump1k .jpg b/part2/Hi-Res Texture Version/moonbump1k .jpg new file mode 100644 index 0000000..405cb50 Binary files /dev/null and b/part2/Hi-Res Texture Version/moonbump1k .jpg differ diff --git a/part2/Hi-Res Texture Version/moonbump2k.jpg b/part2/Hi-Res Texture Version/moonbump2k.jpg new file mode 100644 index 0000000..728d5c4 Binary files /dev/null and b/part2/Hi-Res Texture Version/moonbump2k.jpg differ diff --git a/part2/Hi-Res Texture Version/moonbump4k.jpg b/part2/Hi-Res Texture Version/moonbump4k.jpg new file mode 100644 index 0000000..f6024f5 Binary files /dev/null and b/part2/Hi-Res Texture Version/moonbump4k.jpg differ diff --git a/part2/Hi-Res Texture Version/moonmap1k.jpg b/part2/Hi-Res Texture Version/moonmap1k.jpg new file mode 100644 index 0000000..1892119 Binary files /dev/null and b/part2/Hi-Res Texture Version/moonmap1k.jpg differ diff --git a/part2/Hi-Res Texture Version/moonmap2k .jpg b/part2/Hi-Res Texture Version/moonmap2k .jpg new file mode 100644 index 0000000..230daf2 Binary files /dev/null and b/part2/Hi-Res Texture Version/moonmap2k .jpg differ diff --git a/part2/Hi-Res Texture Version/moonmap4k.jpg b/part2/Hi-Res Texture Version/moonmap4k.jpg new file mode 100644 index 0000000..b597dfe Binary files /dev/null and b/part2/Hi-Res Texture Version/moonmap4k.jpg differ diff --git a/part2/Hi-Res Texture Version/webGLUtility.js b/part2/Hi-Res Texture Version/webGLUtility.js new file mode 100644 index 0000000..ae19646 --- /dev/null +++ b/part2/Hi-Res Texture Version/webGLUtility.js @@ -0,0 +1,112 @@ +(function(exports) { + "use strict"; + /*global window*/ + + exports = exports || window; + + /////////////////////////////////////////////////////////////////////////// + // Shim from http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + + exports.requestAnimFrame = + window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function( callback ){ + window.setTimeout(callback, 1000 / 60); + }; + + /////////////////////////////////////////////////////////////////////////// + // getShader based on http://learningwebgl.com/cookbook/index.php/Loading_shaders_from_HTML_script_tags + + exports.getShaderSource = function(script) { + var str = ""; + var k = script.firstChild; + while (k) { + if (k.nodeType == 3) { + str += k.textContent; + } + k = k.nextSibling; + } + + return str; + }; + + /////////////////////////////////////////////////////////////////////////// + + exports.createWebGLContext = function(canvas, message) { + if (!window.WebGLRenderingContext) { + message.innerText = "The browser does not support WebGL. Visit http://get.webgl.org."; + return undefined; + } + var options = { alpha: false }; + var context = canvas.getContext("experimental-webgl", options); + + if (!context && message) { + message.innerText = "The browser supports WebGL, but initialization failed."; + } + + return context; + }; + + exports.createProgram = function(context, vertexShaderSource, fragmentShaderSource, message) { + var program = context.createProgram(); + var vs = context.createShader(context.VERTEX_SHADER); + var fs = context.createShader(context.FRAGMENT_SHADER); + + context.attachShader(program, vs); + context.attachShader(program, fs); + + // Mark shader for deletion when the program is deleted + context.deleteShader(vs); + context.deleteShader(fs); + + context.shaderSource(vs, vertexShaderSource); + context.compileShader(vs); + if (!context.getShaderParameter(vs, context.COMPILE_STATUS)) { + if (message) { + message.innerText += context.getShaderInfoLog(vs) + "\n"; + alert(context.getShaderInfoLog(vs)); + } + else + { + alert(context.getShaderInfoLog(vs)); + } + context.deleteProgram(program); + return; + } + + context.shaderSource(fs, fragmentShaderSource); + context.compileShader(fs); + if (!context.getShaderParameter(fs, context.COMPILE_STATUS)) { + if (message) { + message.innerText += context.getShaderInfoLog(fs) + "\n"; + alert(context.getShaderInfoLog(fs)); + } + else + { + alert(context.getShaderInfoLog(fs)); + } + context.deleteProgram(program); + return; + } + + context.linkProgram(program); + if (!context.getProgramParameter(program, context.LINK_STATUS)) { + if (message) { + message.innerText += context.getProgramInfoLog(program) + "\n"; + alert(context.getShaderInfoLog(program)); + } + else + { + alert(context.getShaderInfoLog(program)); + } + context.deleteProgram(program); + return; + } + + return program; + }; + +}()); diff --git a/part2/back.png b/part2/back.png new file mode 100644 index 0000000..722e4f3 Binary files /dev/null and b/part2/back.png differ diff --git a/part2/bottom.png b/part2/bottom.png new file mode 100644 index 0000000..4350176 Binary files /dev/null and b/part2/bottom.png differ diff --git a/part2/earthbump10k.jpg b/part2/earthbump10k.jpg new file mode 100644 index 0000000..db38269 Binary files /dev/null and b/part2/earthbump10k.jpg differ diff --git a/part2/earthbump1k.png b/part2/earthbump1k.png new file mode 100644 index 0000000..149c2ea Binary files /dev/null and b/part2/earthbump1k.png differ diff --git a/part2/earthbump2k.jpg b/part2/earthbump2k.jpg new file mode 100644 index 0000000..3791de7 Binary files /dev/null and b/part2/earthbump2k.jpg differ diff --git a/part2/earthbump4k.jpg b/part2/earthbump4k.jpg new file mode 100644 index 0000000..c19b442 Binary files /dev/null and b/part2/earthbump4k.jpg differ diff --git a/part2/earthbump4kpo2.jpg b/part2/earthbump4kpo2.jpg new file mode 100644 index 0000000..b60b670 Binary files /dev/null and b/part2/earthbump4kpo2.jpg differ diff --git a/part2/earthcloud1k.png b/part2/earthcloud1k.png new file mode 100644 index 0000000..7120515 Binary files /dev/null and b/part2/earthcloud1k.png differ diff --git a/part2/earthlight1k.png b/part2/earthlight1k.png new file mode 100644 index 0000000..3532082 Binary files /dev/null and b/part2/earthlight1k.png differ diff --git a/part2/earthlights10k .jpg b/part2/earthlights10k .jpg new file mode 100644 index 0000000..b503993 Binary files /dev/null and b/part2/earthlights10k .jpg differ diff --git a/part2/earthlights2k.jpg b/part2/earthlights2k.jpg new file mode 100644 index 0000000..2d8b816 Binary files /dev/null and b/part2/earthlights2k.jpg differ diff --git a/part2/earthlights4k.jpg b/part2/earthlights4k.jpg new file mode 100644 index 0000000..5151352 Binary files /dev/null and b/part2/earthlights4k.jpg differ diff --git a/part2/earthmap10k.jpg b/part2/earthmap10k.jpg new file mode 100644 index 0000000..812458c Binary files /dev/null and b/part2/earthmap10k.jpg differ diff --git a/part2/earthmap1k.png b/part2/earthmap1k.png new file mode 100644 index 0000000..144a648 Binary files /dev/null and b/part2/earthmap1k.png differ diff --git a/part2/earthmap2k.jpg b/part2/earthmap2k.jpg new file mode 100644 index 0000000..ed2c2be Binary files /dev/null and b/part2/earthmap2k.jpg differ diff --git a/part2/earthmap4k.jpg b/part2/earthmap4k.jpg new file mode 100644 index 0000000..31dd3e4 Binary files /dev/null and b/part2/earthmap4k.jpg differ diff --git a/part2/earthmap4k.png b/part2/earthmap4k.png new file mode 100644 index 0000000..414b22c Binary files /dev/null and b/part2/earthmap4k.png differ diff --git a/part2/earthspec10k.jpg b/part2/earthspec10k.jpg new file mode 100644 index 0000000..c2eea0e Binary files /dev/null and b/part2/earthspec10k.jpg differ diff --git a/part2/earthspec1k.png b/part2/earthspec1k.png new file mode 100644 index 0000000..559662b Binary files /dev/null and b/part2/earthspec1k.png differ diff --git a/part2/earthspec2k.jpg b/part2/earthspec2k.jpg new file mode 100644 index 0000000..14f8550 Binary files /dev/null and b/part2/earthspec2k.jpg differ diff --git a/part2/earthspec4k.jpg b/part2/earthspec4k.jpg new file mode 100644 index 0000000..b3a412d Binary files /dev/null and b/part2/earthspec4k.jpg differ diff --git a/part2/earthtrans1k.png b/part2/earthtrans1k.png new file mode 100644 index 0000000..7d0647b Binary files /dev/null and b/part2/earthtrans1k.png differ diff --git a/part2/frag_globe.html b/part2/frag_globe.html deleted file mode 100644 index 6aa5609..0000000 --- a/part2/frag_globe.html +++ /dev/null @@ -1,119 +0,0 @@ - - - -Fragment Globe - - - - - -
- - - - - - - - - - - - diff --git a/part2/frag_globe.js b/part2/frag_globe.js index 6e667be..e9dc097 100644 --- a/part2/frag_globe.js +++ b/part2/frag_globe.js @@ -1,319 +1,540 @@ -(function() { - "use strict"; - /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ - /*global getShaderSource,createWebGLContext,createProgram*/ +var message; +var canvas; +var gl; + +// shader programs +var programGlobe; +var programSkybox; + +// handles for attributes and uniforms in globe shader pair +var positionLocation; +var normalLocation; +var texCoordLocation; + +var u_InvTransLocation; +var u_ModelLocation; +var u_ViewLocation; +var u_ModelViewInverseLocation; +var u_PerspLocation; +var u_timeLocation; +var u_CameraSpaceDirLightLocation; + +var u_DayDiffuseLocation; +var u_NightLocation; +var u_CloudLocation; +var u_CloudTransLocation; +var u_EarthSpecLocation; +var u_BumpLocation; + + +// positions and uniforms in skybox shader pair +var skyboxPositionLocation; +var skyboxNormalLocation; + +var u_CameraSpaceLightPosLocation; +var u_skyboxViewLocation; +var u_skyboxPerspLocation; +var u_skyboxInvTransLocation; + +var u_cubeTextureLocation; + +function initializeShader() { + // create programGlobe for earth shading + var vs = getShaderSource(document.getElementById("vs")); + var fs = getShaderSource(document.getElementById("fs")); + + programGlobe = createProgram(gl, vs, fs, message); + + // attributes + positionLocation = gl.getAttribLocation(programGlobe, "Position"); + normalLocation = gl.getAttribLocation(programGlobe, "Normal"); + texCoordLocation = gl.getAttribLocation(programGlobe, "Texcoord"); + // uniforms + u_ModelLocation = gl.getUniformLocation(programGlobe, "u_Model"); + u_ViewLocation = gl.getUniformLocation(programGlobe, "u_View"); + u_ModelViewInverseLocation = gl.getUniformLocation(programGlobe, "u_ModelViewInverse"); + u_PerspLocation = gl.getUniformLocation(programGlobe, "u_Persp"); + u_InvTransLocation = gl.getUniformLocation(programGlobe, "u_InvTrans"); + u_timeLocation = gl.getUniformLocation(programGlobe, "u_time"); + u_CameraSpaceDirLightLocation = gl.getUniformLocation(programGlobe, "u_CameraSpaceDirLight"); + // textures + u_DayDiffuseLocation = gl.getUniformLocation(programGlobe, "u_DayDiffuse"); + u_NightLocation = gl.getUniformLocation(programGlobe, "u_Night"); + u_CloudLocation = gl.getUniformLocation(programGlobe, "u_Cloud"); + u_CloudTransLocation = gl.getUniformLocation(programGlobe, "u_CloudTrans"); + u_EarthSpecLocation = gl.getUniformLocation(programGlobe, "u_EarthSpec"); + u_BumpLocation = gl.getUniformLocation(programGlobe, "u_Bump"); + + + // create programGlobe for skybox shading + var skyboxVS = getShaderSource(document.getElementById("skyboxVS")); + var skyboxFS = getShaderSource(document.getElementById("skyboxFS")); + + programSkybox = createProgram(gl, skyboxVS, skyboxFS, message); + skyboxPositionLocation = gl.getAttribLocation(programSkybox, "Position"); + + u_CameraSpaceLightPosLocation = gl.getUniformLocation(programSkybox, "u_CameraSpaceLightPos"); + u_skyboxViewLocation = gl.getUniformLocation(programSkybox, "u_View"); + u_skyboxPerspLocation = gl.getUniformLocation(programSkybox, "u_Persp"); + //u_skyboxInvTransLocation = gl.getUniformLocation(programSkybox, "u_InvTrans"); + + u_cubeTextureLocation = gl.getUniformLocation(programSkybox, "u_cubeTexture"); + +} + + + + + + + +var skyboxTex; + +function initSkyboxTex() { + + skyboxTex = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0); + // javaScript arrays can be of mixed types + var cubeImages = [[gl.TEXTURE_CUBE_MAP_POSITIVE_X, "right.png"], + [gl.TEXTURE_CUBE_MAP_NEGATIVE_X, "left.png"], + [gl.TEXTURE_CUBE_MAP_POSITIVE_Y, "top.png"], + [gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, "bottom.png"], + [gl.TEXTURE_CUBE_MAP_POSITIVE_Z, "front.png"], + [gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, "back.png"] ]; + + // While a texture is bound, GL operations on the target to which it is + // bound affect the bound texture, and queries of the target to which it + // is bound return state from the bound texture. + gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyboxTex); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + function initLoadedCubeMap(texture, face, image) { + //alert(image.complete); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(face, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); + //message.innerHTML += image.complete + "\n"; + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); + } + + for (var i = 0; i < cubeImages.length; i++) { + var face = cubeImages[i][0]; + var image = new Image(); + image.onload = function(texture, face, image) { + return function() { + gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + }; + } (skyboxTex, face, image); + // image load functions that do not work + /*image.onload = function() { + gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyboxTex); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + };*/ + /* image.onload = function() { + return initLoadedCubeMap(skyboxTex, face, image) + };*/ + image.src = cubeImages[i][1]; + } - function sphericalToCartesian( r, a, e ) { - var x = r * Math.cos(e) * Math.cos(a); - var y = r * Math.sin(e); - var z = r * Math.cos(e) * Math.sin(a); +} + + + +var dayTex; +var bumpTex; +var cloudTex; +var transTex; +var lightTex; +var specTex; + +function initGlobeTex(){ + dayTex = gl.createTexture(); + bumpTex = gl.createTexture(); + cloudTex = gl.createTexture(); + transTex = gl.createTexture(); + lightTex = gl.createTexture(); + specTex = gl.createTexture(); + + + function initLoadedTexture(texture) { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + gl.bindTexture(gl.TEXTURE_2D, null); + } - return [x,y,z]; + function initializeTexture(texture, src) { + texture.image = new Image(); + texture.image.onload = function () { + initLoadedTexture(texture); + }; + texture.image.src = src; } - var NUM_WIDTH_PTS = 64; - var NUM_HEIGHT_PTS = 64; + initializeTexture(dayTex, "earthmap1k.png"); + initializeTexture(bumpTex, "earthbump1k.png"); + initializeTexture(lightTex, "earthlight1k.png"); + initializeTexture(specTex, "earthspec1k.png"); + initializeTexture(cloudTex, "earthcloud1k.png"); + initializeTexture(transTex, "earthtrans1k.png"); +} + +var positionsName; +var indicesName; +var numberOfSkyboxIndices; + +function intializeSkybox() { + var positions = new Float32Array([ + // neg z, back + -10.0, 10.0, -10.0, -10.0, -10.0, -10.0, 10.0, -10.0, -10.0, + 10.0, -10.0, -10.0, 10.0, 10.0, -10.0, -10.0, 10.0, -10.0, + // neg x, left + -10.0, -10.0, 10.0, -10.0, -10.0, -10.0, -10.0, 10.0, -10.0, + -10.0, 10.0, -10.0, -10.0, 10.0, 10.0, -10.0, -10.0, 10.0, + // pos x, right + 10.0, -10.0, -10.0, 10.0, -10.0, 10.0, 10.0, 10.0, 10.0, + 10.0, 10.0, 10.0, 10.0, 10.0, -10.0, 10.0, -10.0, -10.0, + // pos z, front + -10.0, -10.0, 10.0, -10.0, 10.0, 10.0, 10.0, 10.0, 10.0, + 10.0, 10.0, 10.0, 10.0, -10.0, 10.0, -10.0, -10.0, 10.0, + // pos y, top + -10.0, 10.0, -10.0, 10.0, 10.0, -10.0, 10.0, 10.0, 10.0, + 10.0, 10.0, 10.0, -10.0, 10.0, 10.0, -10.0, 10.0, -10.0, + // neg y, bottom + -10.0, -10.0, -10.0, -10.0, -10.0, 10.0, 10.0, -10.0, -10.0, + 10.0, -10.0, -10.0, -10.0, -10.0, 10.0, 10.0, -10.0, 10.0 + ]); + + var indices = new Uint16Array(6 * 2 * 3); + for (var i = 0; i < indices.length; ++i) { + indices[i] = i; + } + + // Positions + positionsName = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionsName); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + // Indices + indicesName = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesName); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + + numberOfSkyboxIndices = indices.length; + +} + + +var NUM_WIDTH_PTS = 64; +var NUM_HEIGHT_PTS = 64; + +var globePosBuffer; +var globeNormBuffer; +var globeTexCoorBuffer; +var globeIndices; +var numberOfIndices; + +function initializeSphere() { + function uploadMesh(positions, texCoords, indices) { + // Positions + globePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, globePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + // Normals + globeNormBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, globeNormBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + // TextureCoords + globeTexCoorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, globeTexCoorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); + + // Indices + globeIndices = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, globeIndices); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + } - var message = document.getElementById("message"); - var canvas = document.getElementById("canvas"); - var gl = createWebGLContext(canvas, message); - if (!gl) { - return; + var WIDTH_DIVISIONS = NUM_WIDTH_PTS - 1; + var HEIGHT_DIVISIONS = NUM_HEIGHT_PTS - 1; + + var numberOfPositions = NUM_WIDTH_PTS * NUM_HEIGHT_PTS; + + var positions = new Float32Array(3 * numberOfPositions); + var texCoords = new Float32Array(2 * numberOfPositions); + var indices = new Uint16Array(6 * (WIDTH_DIVISIONS * HEIGHT_DIVISIONS)); + + var positionsIndex = 0; + var texCoordsIndex = 0; + var indicesIndex = 0; + + for (var j = 0; j < NUM_HEIGHT_PTS; ++j) { + var inclination = Math.PI * (j / HEIGHT_DIVISIONS); + for (var i = 0; i < NUM_WIDTH_PTS; ++i) { + var azimuth = 2 * Math.PI * (i / WIDTH_DIVISIONS); + positions[positionsIndex++] = Math.sin(inclination) * Math.sin(azimuth); + positions[positionsIndex++] = Math.cos(inclination); + positions[positionsIndex++] = Math.sin(inclination) * Math.cos(azimuth); + texCoords[texCoordsIndex++] = i / WIDTH_DIVISIONS; + texCoords[texCoordsIndex++] = 1.0 - j / HEIGHT_DIVISIONS; + } + } + + for (var j = 0; j < HEIGHT_DIVISIONS; ++j) // a bunch of degenerate triangles at poles + { + var index = j * NUM_WIDTH_PTS; + for (var i = 0; i < WIDTH_DIVISIONS; ++i) { + indices[indicesIndex++] = index + i; + indices[indicesIndex++] = index + i + 1; + indices[indicesIndex++] = index + i + NUM_WIDTH_PTS; + indices[indicesIndex++] = index + i + NUM_WIDTH_PTS; + indices[indicesIndex++] = index + i + 1; + indices[indicesIndex++] = index + i + NUM_WIDTH_PTS + 1; + } } + - /////////////////////////////////////////////////////////////////////////// + uploadMesh(positions, texCoords, indices); + numberOfIndices = indicesIndex; +}; - gl.viewport(0, 0, canvas.width, canvas.height); - gl.clearColor(0.0, 0.0, 0.0, 1.0); - gl.enable(gl.DEPTH_TEST); - var persp = mat4.create(); - mat4.perspective(45.0, canvas.width/canvas.height, 0.1, 100.0, persp); +//time for animating globe +var time = 0; - var radius = 5.0; - var azimuth = Math.PI; - var elevation = 0.0001; +//Camera control +var mouseLeftDown = false; +var mouseRightDown = false; +var lastMouseX = null; +var lastMouseY = null; - var eye = sphericalToCartesian(radius, azimuth, elevation); - var center = [0.0, 0.0, 0.0]; - var up = [0.0, 1.0, 0.0]; - var view = mat4.create(); - mat4.lookAt(eye, center, up, view); +var radius = 5.0; +var azimuth = 0.0; +var zenith = Math.PI / 2.0; - var positionLocation = 0; - var normalLocation = 1; - var texCoordLocation = 2; - var u_InvTransLocation; - var u_ModelLocation; - var u_ViewLocation; - var u_PerspLocation; - var u_CameraSpaceDirLightLocation; - var u_DayDiffuseLocation; - var u_NightLocation; - var u_CloudLocation; - var u_CloudTransLocation; - var u_EarthSpecLocation; - var u_BumpLocation; - var u_timeLocation; - - (function initializeShader() { - var program; - var vs = getShaderSource(document.getElementById("vs")); - var fs = getShaderSource(document.getElementById("fs")); - - var program = createProgram(gl, vs, fs, message); - gl.bindAttribLocation(program, positionLocation, "Position"); - u_ModelLocation = gl.getUniformLocation(program,"u_Model"); - u_ViewLocation = gl.getUniformLocation(program,"u_View"); - u_PerspLocation = gl.getUniformLocation(program,"u_Persp"); - u_InvTransLocation = gl.getUniformLocation(program,"u_InvTrans"); - u_DayDiffuseLocation = gl.getUniformLocation(program,"u_DayDiffuse"); - u_NightLocation = gl.getUniformLocation(program,"u_Night"); - u_CloudLocation = gl.getUniformLocation(program,"u_Cloud"); - u_CloudTransLocation = gl.getUniformLocation(program,"u_CloudTrans"); - u_EarthSpecLocation = gl.getUniformLocation(program,"u_EarthSpec"); - u_BumpLocation = gl.getUniformLocation(program,"u_Bump"); - u_timeLocation = gl.getUniformLocation(program,"u_time"); - u_CameraSpaceDirLightLocation = gl.getUniformLocation(program,"u_CameraSpaceDirLight"); - - gl.useProgram(program); - })(); +var center = [0.0, 0.0, 0.0]; +var up = [0.0, 1.0, 0.0]; - var dayTex = gl.createTexture(); - var bumpTex = gl.createTexture(); - var cloudTex = gl.createTexture(); - var transTex = gl.createTexture(); - var lightTex = gl.createTexture(); - var specTex = gl.createTexture(); - - (function initTextures() { - function initLoadedTexture(texture){ - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, - gl.UNSIGNED_BYTE, texture.image); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, - gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, - gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, - gl.REPEAT); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, - gl.REPEAT); - gl.bindTexture(gl.TEXTURE_2D, null); - } - - function initializeTexture(texture, src) { - texture.image = new Image(); - texture.image.onload = function() { - initLoadedTexture(texture); - } - texture.image.src = src; - } - - initializeTexture(dayTex, "earthmap1024.png"); - initializeTexture(bumpTex, "earthbump1024.png"); - initializeTexture(cloudTex, "earthcloud1024.png"); - initializeTexture(transTex, "earthtrans1024.png"); - initializeTexture(lightTex, "earthlight1024.png"); - initializeTexture(specTex, "earthspec1024.png"); - })(); +var persp; +var eye; +var view; - var numberOfIndices; +// mouse control callbacks +function handleMouseDown(event) { + if (event.button == 2) { + mouseLeftDown = false; + mouseRightDown = true; + } + else { + mouseLeftDown = true; + mouseRightDown = false; + } + lastMouseX = event.clientX; + lastMouseY = event.clientY; +} - (function initializeSphere() { - function uploadMesh(positions, texCoords, indices) { - // Positions - var positionsName = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, positionsName); - gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); - gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(positionLocation); - - // Normals - var normalsName = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, normalsName); - gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); - gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(normalLocation); - - // TextureCoords - var texCoordsName = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsName); - gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); - gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(texCoordLocation); - - // Indices - var indicesName = gl.createBuffer(); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesName); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); - } - - var WIDTH_DIVISIONS = NUM_WIDTH_PTS - 1; - var HEIGHT_DIVISIONS = NUM_HEIGHT_PTS - 1; - - var numberOfPositions = NUM_WIDTH_PTS * NUM_HEIGHT_PTS; - - var positions = new Float32Array(3 * numberOfPositions); - var texCoords = new Float32Array(2 * numberOfPositions); - var indices = new Uint16Array(6 * (WIDTH_DIVISIONS * HEIGHT_DIVISIONS)); - - var positionsIndex = 0; - var texCoordsIndex = 0; - var indicesIndex = 0; - var length; - - for( var j = 0; j < NUM_HEIGHT_PTS; ++j ) - { - var inclination = Math.PI * (j / HEIGHT_DIVISIONS); - for( var i = 0; i < NUM_WIDTH_PTS; ++i ) - { - var azimuth = 2 * Math.PI * (i / WIDTH_DIVISIONS); - positions[positionsIndex++] = Math.sin(inclination)*Math.cos(azimuth); - positions[positionsIndex++] = Math.cos(inclination); - positions[positionsIndex++] = Math.sin(inclination)*Math.sin(azimuth); - texCoords[texCoordsIndex++] = i / WIDTH_DIVISIONS; - texCoords[texCoordsIndex++] = j / HEIGHT_DIVISIONS; - } - } - - for( var j = 0; j < HEIGHT_DIVISIONS; ++j ) - { - var index = j*NUM_WIDTH_PTS; - for( var i = 0; i < WIDTH_DIVISIONS; ++i ) - { - indices[indicesIndex++] = index + i; - indices[indicesIndex++] = index + i+1; - indices[indicesIndex++] = index + i+NUM_WIDTH_PTS; - indices[indicesIndex++] = index + i+NUM_WIDTH_PTS; - indices[indicesIndex++] = index + i+1; - indices[indicesIndex++] = index + i+NUM_WIDTH_PTS+1; - } - } - - uploadMesh(positions, texCoords, indices); - numberOfIndices = indicesIndex; - })(); +function handleMouseUp(event) { + mouseLeftDown = false; + mouseRightDown = false; +} - var time = 0; - var mouseLeftDown = false; - var mouseRightDown = false; - var lastMouseX = null; - var lastMouseY = null; - - function handleMouseDown(event) { - if( event.button == 2 ) { - mouseLeftDown = false; - mouseRightDown = true; - } - else { - mouseLeftDown = true; - mouseRightDown = false; - } - lastMouseX = event.clientX; - lastMouseY = event.clientY; +function handleMouseMove(event) { + if (!(mouseLeftDown || mouseRightDown)) { + return; } + var newX = event.clientX; + var newY = event.clientY; - function handleMouseUp(event) { - mouseLeftDown = false; - mouseRightDown = false; + var deltaX = newX - lastMouseX; + var deltaY = newY - lastMouseY; + + if (mouseLeftDown) { + azimuth -= 0.01 * deltaX; + zenith -= 0.01 * deltaY; + zenith = Math.min(Math.max(zenith, 0.001), Math.PI - 0.001); } + else { + radius += 0.01 * deltaY; + radius = Math.min(Math.max(radius, 2.0), 10.0); + } + eye = sphericalToCartesian(radius, azimuth, zenith); + view = mat4.create(); + mat4.lookAt(eye, center, up, view); - function handleMouseMove(event) { - if (!(mouseLeftDown || mouseRightDown)) { - return; - } - var newX = event.clientX; - var newY = event.clientY; + lastMouseX = newX; + lastMouseY = newY; +} - var deltaX = newX - lastMouseX; - var deltaY = newY - lastMouseY; - - if( mouseLeftDown ) - { - azimuth += 0.01 * deltaX; - elevation += 0.01 * deltaY; - elevation = Math.min(Math.max(elevation, -Math.PI/2+0.001), Math.PI/2-0.001); - } - else - { - radius += 0.01 * deltaY; - radius = Math.min(Math.max(radius, 2.0), 10.0); - } - eye = sphericalToCartesian(radius, azimuth, elevation); - view = mat4.create(); - mat4.lookAt(eye, center, up, view); - - lastMouseX = newX; - lastMouseY = newY; - } +function sphericalToCartesian(r, azimuth, zenith) { + var x = r * Math.sin(zenith) * Math.sin(azimuth); + var y = r * Math.cos(zenith); + var z = r * Math.sin(zenith) * Math.cos(azimuth); + + return [x, y, z]; +} +///////////////////////////////////////////////////////////////////////////////// +function globeMain() { + + "use strict"; + message = document.getElementById("message"); + canvas = document.getElementById("canvas"); + gl = createWebGLContext(canvas, message); + if (!gl) { + return; + } + canvas.onmousedown = handleMouseDown; - canvas.oncontextmenu = function(ev) {return false;}; + canvas.oncontextmenu = function (ev) { return false; }; document.onmouseup = handleMouseUp; document.onmousemove = handleMouseMove; + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.enable(gl.DEPTH_TEST); + + persp = mat4.create(); + mat4.perspective(45.0, canvas.width / canvas.height, 0.1, 100.0, persp); + + eye = sphericalToCartesian(radius, azimuth, zenith); + view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + initializeShader(); - (function animate(){ - /////////////////////////////////////////////////////////////////////////// - // Update - - var model = mat4.create(); - mat4.identity(model); - mat4.rotate(model, 23.4/180*Math.PI, [0.0, 0.0, 1.0]); - mat4.rotate(model, Math.PI, [1.0, 0.0, 0.0]); - mat4.rotate(model, -time, [0.0, 1.0, 0.0]); - var mv = mat4.create(); - mat4.multiply(view, model, mv); - - var invTrans = mat4.create(); - mat4.inverse(mv, invTrans); - mat4.transpose(invTrans); - - var lightdir = vec3.create([1.0, 0.0, 1.0]); - var lightdest = vec4.create(); - vec3.normalize(lightdir); - mat4.multiplyVec4(view, [lightdir[0], lightdir[1], lightdir[2], 0.0], lightdest); - lightdir = vec3.createFrom(lightdest[0],lightdest[1],lightdest[2]); - vec3.normalize(lightdir); - - - - /////////////////////////////////////////////////////////////////////////// - // Render - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - - gl.uniformMatrix4fv(u_ModelLocation, false, model); - gl.uniformMatrix4fv(u_ViewLocation, false, view); - gl.uniformMatrix4fv(u_PerspLocation, false, persp); - gl.uniformMatrix4fv(u_InvTransLocation, false, invTrans); - - gl.uniform3fv(u_CameraSpaceDirLightLocation, lightdir); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, dayTex); - gl.uniform1i(u_DayDiffuseLocation, 0); - gl.activeTexture(gl.TEXTURE1); - gl.bindTexture(gl.TEXTURE_2D, bumpTex); - gl.uniform1i(u_BumpLocation, 1); - gl.activeTexture(gl.TEXTURE2); - gl.bindTexture(gl.TEXTURE_2D, cloudTex); - gl.uniform1i(u_CloudLocation, 2); - gl.activeTexture(gl.TEXTURE3); - gl.bindTexture(gl.TEXTURE_2D, transTex); - gl.uniform1i(u_CloudTransLocation, 3); - gl.activeTexture(gl.TEXTURE4); - gl.bindTexture(gl.TEXTURE_2D, lightTex); - gl.uniform1i(u_NightLocation, 4); - gl.activeTexture(gl.TEXTURE5); - gl.bindTexture(gl.TEXTURE_2D, specTex); - gl.uniform1i(u_EarthSpecLocation, 5); - - gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT,0); + initSkyboxTex(); + + intializeSkybox(); + + initGlobeTex(); + + initializeSphere(); + + (function animate() {// Update + + (function drawGlobe() { + + gl.useProgram(programGlobe); + + // disable attributes for other programs + gl.disableVertexAttribArray(skyboxPositionLocation); + + // enable attributes for this program + gl.bindBuffer(gl.ARRAY_BUFFER, globePosBuffer); + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(positionLocation); + + gl.bindBuffer(gl.ARRAY_BUFFER, globeNormBuffer); + gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(normalLocation); + + gl.bindBuffer(gl.ARRAY_BUFFER, globeTexCoorBuffer); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(texCoordLocation); + + // calculate and pass uniforms + var model = mat4.create(); + mat4.identity(model); + mat4.rotate(model, 23.4 / 180 * Math.PI, [0.0, 0.0, 1.0]); + mat4.rotate(model, time, [0.0, 1.0, 0.0]); + var mv = mat4.create(); + mat4.multiply(view, model, mv); + + var modelViewMatrixInverse = mat4.create(); + mat4.inverse(mv, modelViewMatrixInverse); + + var invTrans = mat4.create(); + mat4.inverse(mv, invTrans); + mat4.transpose(invTrans); + + var lightdir = vec3.create([1.0, 0.0, 1.0]); + var lightdest = vec4.create(); + vec3.normalize(lightdir); + mat4.multiplyVec4(view, [lightdir[0], lightdir[1], lightdir[2], 0.0], lightdest); + lightdir = vec3.createFrom(lightdest[0], lightdest[1], lightdest[2]); + vec3.normalize(lightdir); + + gl.uniformMatrix4fv(u_ModelLocation, false, model); + gl.uniformMatrix4fv(u_ViewLocation, false, view); + gl.uniformMatrix4fv(u_ModelViewInverseLocation, false, modelViewMatrixInverse); + gl.uniformMatrix4fv(u_PerspLocation, false, persp); + gl.uniformMatrix4fv(u_InvTransLocation, false, invTrans); + + gl.uniform3fv(u_CameraSpaceDirLightLocation, lightdir); + gl.uniform1f(u_timeLocation, time); + + // pass textures + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, dayTex); + gl.uniform1i(u_DayDiffuseLocation, 0); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, bumpTex); + gl.uniform1i(u_BumpLocation, 1); + + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, cloudTex); + gl.uniform1i(u_CloudLocation, 2); + + gl.activeTexture(gl.TEXTURE3); + gl.bindTexture(gl.TEXTURE_2D, transTex); + gl.uniform1i(u_CloudTransLocation, 3); + + gl.activeTexture(gl.TEXTURE4); + gl.bindTexture(gl.TEXTURE_2D, lightTex); + gl.uniform1i(u_NightLocation, 4); + + gl.activeTexture(gl.TEXTURE5); + gl.bindTexture(gl.TEXTURE_2D, specTex); + gl.uniform1i(u_EarthSpecLocation, 5); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, globeIndices); + gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT, 0); + })(); + + (function drawSkybox(){ + gl.useProgram(programSkybox); + + // disable attributes for other programs + gl.disableVertexAttribArray(positionLocation); + gl.disableVertexAttribArray(normalLocation); + gl.disableVertexAttribArray(texCoordLocation); + + // enable attributes for this program + gl.bindBuffer(gl.ARRAY_BUFFER, positionsName); + gl.vertexAttribPointer(skyboxPositionLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(skyboxPositionLocation); + + // calculate and pass uniforms + gl.uniformMatrix4fv(u_skyboxViewLocation, false, view); + gl.uniformMatrix4fv(u_skyboxPerspLocation, false, persp); + + // pass textures + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyboxTex); + gl.uniform1i(u_cubeTextureLocation, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesName); + gl.drawElements(gl.TRIANGLES, numberOfSkyboxIndices, gl.UNSIGNED_SHORT, 0); + })(); + time += 0.001; - window.requestAnimFrame(animate); + + window.requestAnimFrame(animate); })(); -}()); +} diff --git a/part2/front.png b/part2/front.png new file mode 100644 index 0000000..4fa8bdc Binary files /dev/null and b/part2/front.png differ diff --git a/part2/index.html b/part2/index.html new file mode 100644 index 0000000..2b03ffc --- /dev/null +++ b/part2/index.html @@ -0,0 +1,250 @@ + + +Fragment Globe + + + + + + + + + + + + + + + +
+ + + + + diff --git a/part2/left.png b/part2/left.png new file mode 100644 index 0000000..f4062f9 Binary files /dev/null and b/part2/left.png differ diff --git a/part2/moonbump1k .jpg b/part2/moonbump1k .jpg new file mode 100644 index 0000000..405cb50 Binary files /dev/null and b/part2/moonbump1k .jpg differ diff --git a/part2/moonbump2k.jpg b/part2/moonbump2k.jpg new file mode 100644 index 0000000..728d5c4 Binary files /dev/null and b/part2/moonbump2k.jpg differ diff --git a/part2/moonbump4k.jpg b/part2/moonbump4k.jpg new file mode 100644 index 0000000..f6024f5 Binary files /dev/null and b/part2/moonbump4k.jpg differ diff --git a/part2/moonmap1k.jpg b/part2/moonmap1k.jpg new file mode 100644 index 0000000..1892119 Binary files /dev/null and b/part2/moonmap1k.jpg differ diff --git a/part2/moonmap2k .jpg b/part2/moonmap2k .jpg new file mode 100644 index 0000000..230daf2 Binary files /dev/null and b/part2/moonmap2k .jpg differ diff --git a/part2/moonmap4k.jpg b/part2/moonmap4k.jpg new file mode 100644 index 0000000..b597dfe Binary files /dev/null and b/part2/moonmap4k.jpg differ diff --git a/part2/right.png b/part2/right.png new file mode 100644 index 0000000..fd59324 Binary files /dev/null and b/part2/right.png differ diff --git a/part2/top.png b/part2/top.png new file mode 100644 index 0000000..3dc4fa8 Binary files /dev/null and b/part2/top.png differ diff --git a/resources/back.png b/resources/back.png new file mode 100644 index 0000000..722e4f3 Binary files /dev/null and b/resources/back.png differ diff --git a/resources/bottom.png b/resources/bottom.png new file mode 100644 index 0000000..4350176 Binary files /dev/null and b/resources/bottom.png differ diff --git a/resources/emptyGrid.png b/resources/emptyGrid.png deleted file mode 100644 index 2ee870f..0000000 Binary files a/resources/emptyGrid.png and /dev/null differ diff --git a/resources/front.png b/resources/front.png new file mode 100644 index 0000000..4fa8bdc Binary files /dev/null and b/resources/front.png differ diff --git a/resources/globe_bumpmap.png b/resources/globe_bumpmap.png deleted file mode 100644 index fa91a9f..0000000 Binary files a/resources/globe_bumpmap.png and /dev/null differ diff --git a/resources/globe_day.png b/resources/globe_day.png deleted file mode 100644 index e3cbf1f..0000000 Binary files a/resources/globe_day.png and /dev/null differ diff --git a/resources/globe_daycloud.png b/resources/globe_daycloud.png deleted file mode 100644 index ff00096..0000000 Binary files a/resources/globe_daycloud.png and /dev/null differ diff --git a/resources/globe_initial.png b/resources/globe_initial.png deleted file mode 100644 index 1e3bde5..0000000 Binary files a/resources/globe_initial.png and /dev/null differ diff --git a/resources/globe_night.png b/resources/globe_night.png deleted file mode 100644 index 6401768..0000000 Binary files a/resources/globe_night.png and /dev/null differ diff --git a/resources/globe_nightcloud.png b/resources/globe_nightcloud.png deleted file mode 100644 index 781aec0..0000000 Binary files a/resources/globe_nightcloud.png and /dev/null differ diff --git a/resources/globe_nospecmap.png b/resources/globe_nospecmap.png deleted file mode 100644 index c370735..0000000 Binary files a/resources/globe_nospecmap.png and /dev/null differ diff --git a/resources/globe_specmap.png b/resources/globe_specmap.png deleted file mode 100644 index 7ff01a5..0000000 Binary files a/resources/globe_specmap.png and /dev/null differ diff --git a/resources/globe_twilight.png b/resources/globe_twilight.png deleted file mode 100644 index ac5ea5c..0000000 Binary files a/resources/globe_twilight.png and /dev/null differ diff --git a/resources/left.png b/resources/left.png new file mode 100644 index 0000000..f4062f9 Binary files /dev/null and b/resources/left.png differ diff --git a/resources/oceanWave.png b/resources/oceanWave.png deleted file mode 100644 index 73b65d5..0000000 Binary files a/resources/oceanWave.png and /dev/null differ diff --git a/resources/right.png b/resources/right.png new file mode 100644 index 0000000..fd59324 Binary files /dev/null and b/resources/right.png differ diff --git a/resources/sinWaveGrid.png b/resources/sinWaveGrid.png deleted file mode 100644 index 733f8d8..0000000 Binary files a/resources/sinWaveGrid.png and /dev/null differ diff --git a/resources/top.png b/resources/top.png new file mode 100644 index 0000000..3dc4fa8 Binary files /dev/null and b/resources/top.png differ