diff --git a/README.md b/README.md index 388c810..21269ae 100644 --- a/README.md +++ b/README.md @@ -1,234 +1,259 @@ -------------------------------------------------------------------------------- -CIS565: Project 4: Image Processing/Vertex Shading -------------------------------------------------------------------------------- -Fall 2012 -------------------------------------------------------------------------------- -Due Friday 11/09/2012 -------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- -NOTE: -------------------------------------------------------------------------------- -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. - -The first part of this project requires Visual Studio 2010 or newer, and the second part of this project requires a WebGL capable browser, such as the latest versions of Chrome, Firefox, or Safari. - -------------------------------------------------------------------------------- -INTRODUCTION: -------------------------------------------------------------------------------- -In this project, you will get introduced to the world of GLSL in two parts: fragment shading, and vertex 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 various image processing filters using GLSL fragment shaders by rendering a viewport-aligned fullscreen quad where each fragment corresponds to one texel in a texture that stores an image. As you can guess, these filters are embarassingly parallel, as each fragment can be processed in parallel. Although we apply filters to static images in this project, the same technique can be and is widely used in post-processing dynamic scenes. - -In the second 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; before vertex shading, dynamic vertex buffers could only be implemented on the CPU, and each frame would then be uploaded to the GPU. - -------------------------------------------------------------------------------- -CONTENTS: -------------------------------------------------------------------------------- -The Project4 root directory contains the following subdirectories: - -* part1/ contains the base code for the Image Processing half of the assignment. -* part1/ImageProcessing contains a Visual Studio 2010 project for the Image Processor -* part1/shared32 contains libraries that are required to build and run the Image Processor -* part2/ contains the base code for the Wave Vertex Shader in the form of a .html file and several .js files - -The Image Processor builds and runs like any other standard Visual Studio project. The Wave Vertex Shader does not require building and can be run by opening the .html file in the web browser of your choice, such as Chrome. - -------------------------------------------------------------------------------- -PART 1 REQUIREMENTS: -------------------------------------------------------------------------------- -In Part 1, you are given code for: - -* Reading and loading an image -* Code for passing a quad to OpenGL -* Passthrough fragment and vertex shaders that take in an input and output the exact original input -* An example box blur filter fragment shader - -You are required to implement the following filters: - -* Image negative -* Gaussian blur -* Grayscale -* Edge Detection -* Toon shading - -You are also required to implement at least three of the following filters: - -* Pixelate (http://wtomandev.blogspot.com/2010/01/pixelize-effect.html) -* CMYK conversion (http://en.wikipedia.org/wiki/CMYK_color_model) -* Gamma correction (http://en.wikipedia.org/wiki/Gamma_correction) -* Brightness (http://en.wikipedia.org/wiki/Brightness) -* Contrast (http://en.wikipedia.org/wiki/Contrast_(vision)) -* Night vision (http://wtomandev.blogspot.com/2009/09/night-vision-effect.html) -* Open-ended: make up your own filter! - -You will also have to bind each filter you write to a keyboard key, such that hitting that key will trigger the associated filter. In the base code, hitting "1" will turn off filtering and use the passthrough filter, and hitting "2" will switch to the example box blur filter. - -For this project, the console window will show what shaders are loaded, and will report shader compile/link warnings and errors. The base code does not have any build or shader warnings or errors; neither should your code submission! - -![Example console window](Project4-IntroGLSL/raw/master/readmeFiles/consoleExample.png) - -IMPORTANT: You MAY NOT copy/paste code from other sources for any of your filters! If you choose to make your own filter(s), please document what they are, how they work, and how your implementation works. - -------------------------------------------------------------------------------- -PART 1 WALKTHROUGH: -------------------------------------------------------------------------------- -**Image Negative** - -Create a new fragment shader starting with `passthroughFS.glsl`, that performans an image negative when the user presses `3`. Compute the negative color as: - -`vec3(1.0) - rgb` - -![Example negative filter](Project4-IntroGLSL/raw/master/readmeFiles/negativeFilter.png) - -**Gaussian Blur** - -Create a new fragment shader starting with `boxBlurFS.glsl`, that performs a 3x3 Gaussian blur when the user presses `4`. This is similar to the box blur, except we are using a smaller kernel (3x3 instead of 7x7), and instead of weighting each texel evenly, they are weighted using a 2D Gaussian function, making the blur more subtle: - -`1/16 * [[1 2 1][2 4 2][1 2 1]]` - -![Example gaussian filter](Project4-IntroGLSL/raw/master/readmeFiles/gaussianFilter.png) - -**Grayscale** - -Create a new fragment shader starting with `passthroughFS.glsl`, that displays the image in grayscale when the user presses `5`. To create a grayscale filter, determine the luminance - -`const vec3 W = vec3(0.2125, 0.7154, 0.0721);` - -`float luminance = dot(rgb, W);` - -Then set the output `r`, `g`, and `b` components to the lumiance. - -![Example grayscale filter](Project4-IntroGLSL/raw/master/readmeFiles/grayscaleFilter.png) - -**Edge Detection** - -Build on both our Gaussian blur and Grayscale filters to create an edge detection filter when the user presses `6` using a horizontal and vertical sobel filter. Use two 3x3 kernels: - -`Sobel-horizontal = [[-1 -2 -1][0 0 0][1 2 1]]` - -`Sobel-vertical = [[-1 0 1][-2 0 2][-1 0 1]]` - -Run the kernels on the luminance of each texel, instead of the `rgb` components used in the Gaussian blur. The result is two floating-point values, one from each kernel. Create a `vec2` from these values, and set the output `rgb` components to the vector’s length. - -![Example edge filter](Project4-IntroGLSL/raw/master/readmeFiles/edgeFilter.png) - -**Toon Shading** - -Toon shading is part of non-photorealistic rendering (NPR). Instead of trying to produce a photorealistic image, the goal is to create an image with a certain artistic style. In this case, we will build on our edge detection filter to create a cartoon filter when the user presses `7`. - -First, perform edge detection. If the length of the vector is above a threshold (you determine), then output black. Otherwise, quantize the texel’s color and output it. Use the following code to quantize: - -`float quantize = // determine it` - -`rgb *= quantize;` - -`rgb += vec3(0.5);` - -`ivec3 irgb = ivec3(rgb);` - -`rgb = vec3(irgb) / quantize;` - -![Example toon filter](Project4-IntroGLSL/raw/master/readmeFiles/toonFilter.png) - -------------------------------------------------------------------------------- -PART 2 REQUIREMENTS: -------------------------------------------------------------------------------- -In Part 2, you are given code for: - -* Drawing a VBO through WebGL -* Javascript code for interfacing with WebGL -* Functions for generating simplex noise - -You are required to implement the following: - -* A sin-wave based vertex shader: - -![Example sin wave grid](Project4-IntroGLSL/raw/master/readmeFiles/sinWaveGrid.png) - -* A simplex noise based vertex shader: - -![Example simplex noise wave grid](Project4-IntroGLSL/raw/master/readmeFiles/oceanWave.png) - -* One interesting vertex shader of your choice - -------------------------------------------------------------------------------- -PART 2 WALKTHROUGH: -------------------------------------------------------------------------------- -**Sin Wave** - -* For this assignment, you will need the latest version of either Chrome, Firefox, or Safari. -* Begin by opening index.html. You should see a flat grid of black and white lines on the xy plane: - -![Example boring grid](Project4-IntroGLSL/raw/master/readmeFiles/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: - - `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: - - `vec2 simplexVec = vec2(u_time, position);` - - `float s_contrib = snoise(simplexVec);` - - `float t_contrib = snoise(vec2(s_contrib,u_time));` - -**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. - -------------------------------------------------------------------------------- -BLOG -------------------------------------------------------------------------------- -As mentioned in class, all students should have student blogs detailing progress on projects. If you already have a blog, you can use it; otherwise, please create a blog using www.blogger.com or any other tool, such as www.wordpress.org. Blog posts on your project are due on the SAME DAY as the project, and should include: - -* A brief description of the project and the specific features you implemented. -* A link to your github repo if the code is open source. -* At least one screenshot of your project running. -* A 30 second or longer video of your project running. To create the video, use http://www.microsoft.com/expression/products/Encoder4_Overview.aspx - -------------------------------------------------------------------------------- -THIRD PARTY CODE POLICY -------------------------------------------------------------------------------- -* Use of any third-party code must be approved by asking on Piazza. 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. - -------------------------------------------------------------------------------- -SELF-GRADING -------------------------------------------------------------------------------- -* On the submission date, email your grade, on a scale of 0 to 100, to Karl, yiningli@seas.upenn.edu, 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 - -* A brief description of the project and specific features you implemented -* At least one screenshot of your project running, and at least one screenshot of the final rendered output of your raytracer -* Instructions for building and running your project if they differ from the base code -* A link to your blog post detailing the project +------------------------------------------------------------------------------ +SUBMISSION +------------------------------------------------------------------------------ +Part 1: +Implemented features: +Image negative +Gaussian blur +Grayscale +Edge Detection +Toon shading +Gamma correction (http://en.wikipedia.org/wiki/Gamma_correction) +Brightness (http://en.wikipedia.org/wiki/Brightness) +Contrast (http://en.wikipedia.org/wiki/Contrast_(vision)) +Night vision (http://wtomandev.blogspot.com/2009/09/night-vision-effect.html) + +Part 2: +A sin-wave based vertex shader +simplex noise based vertex shader +Custom shader - It is ocean wave based shader. + +Blog Link: http://imageprocessingglsl.blogspot.com/ + + + + +------------------------------------------------------------------------------- +CIS565: Project 4: Image Processing/Vertex Shading +------------------------------------------------------------------------------- +Fall 2012 +------------------------------------------------------------------------------- +Due Friday 11/09/2012 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +NOTE: +------------------------------------------------------------------------------- +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. + +The first part of this project requires Visual Studio 2010 or newer, and the second part of this project requires a WebGL capable browser, such as the latest versions of Chrome, Firefox, or Safari. + +------------------------------------------------------------------------------- +INTRODUCTION: +------------------------------------------------------------------------------- +In this project, you will get introduced to the world of GLSL in two parts: fragment shading, and vertex 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 various image processing filters using GLSL fragment shaders by rendering a viewport-aligned fullscreen quad where each fragment corresponds to one texel in a texture that stores an image. As you can guess, these filters are embarassingly parallel, as each fragment can be processed in parallel. Although we apply filters to static images in this project, the same technique can be and is widely used in post-processing dynamic scenes. + +In the second 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; before vertex shading, dynamic vertex buffers could only be implemented on the CPU, and each frame would then be uploaded to the GPU. + +------------------------------------------------------------------------------- +CONTENTS: +------------------------------------------------------------------------------- +The Project4 root directory contains the following subdirectories: + +* part1/ contains the base code for the Image Processing half of the assignment. +* part1/ImageProcessing contains a Visual Studio 2010 project for the Image Processor +* part1/shared32 contains libraries that are required to build and run the Image Processor +* part2/ contains the base code for the Wave Vertex Shader in the form of a .html file and several .js files + +The Image Processor builds and runs like any other standard Visual Studio project. The Wave Vertex Shader does not require building and can be run by opening the .html file in the web browser of your choice, such as Chrome. + +------------------------------------------------------------------------------- +PART 1 REQUIREMENTS: +------------------------------------------------------------------------------- +In Part 1, you are given code for: + +* Reading and loading an image +* Code for passing a quad to OpenGL +* Passthrough fragment and vertex shaders that take in an input and output the exact original input +* An example box blur filter fragment shader + +You are required to implement the following filters: + +* Image negative +* Gaussian blur +* Grayscale +* Edge Detection +* Toon shading + +You are also required to implement at least three of the following filters: + +* Pixelate (http://wtomandev.blogspot.com/2010/01/pixelize-effect.html) +* CMYK conversion (http://en.wikipedia.org/wiki/CMYK_color_model) +* Gamma correction (http://en.wikipedia.org/wiki/Gamma_correction) +* Brightness (http://en.wikipedia.org/wiki/Brightness) +* Contrast (http://en.wikipedia.org/wiki/Contrast_(vision)) +* Night vision (http://wtomandev.blogspot.com/2009/09/night-vision-effect.html) +* Open-ended: make up your own filter! + +You will also have to bind each filter you write to a keyboard key, such that hitting that key will trigger the associated filter. In the base code, hitting "1" will turn off filtering and use the passthrough filter, and hitting "2" will switch to the example box blur filter. + +For this project, the console window will show what shaders are loaded, and will report shader compile/link warnings and errors. The base code does not have any build or shader warnings or errors; neither should your code submission! + +![Example console window](Project4-IntroGLSL/raw/master/readmeFiles/consoleExample.png) + +IMPORTANT: You MAY NOT copy/paste code from other sources for any of your filters! If you choose to make your own filter(s), please document what they are, how they work, and how your implementation works. + +------------------------------------------------------------------------------- +PART 1 WALKTHROUGH: +------------------------------------------------------------------------------- +**Image Negative** + +Create a new fragment shader starting with `passthroughFS.glsl`, that performans an image negative when the user presses `3`. Compute the negative color as: + +`vec3(1.0) - rgb` + +![Example negative filter](Project4-IntroGLSL/raw/master/readmeFiles/negativeFilter.png) + +**Gaussian Blur** + +Create a new fragment shader starting with `boxBlurFS.glsl`, that performs a 3x3 Gaussian blur when the user presses `4`. This is similar to the box blur, except we are using a smaller kernel (3x3 instead of 7x7), and instead of weighting each texel evenly, they are weighted using a 2D Gaussian function, making the blur more subtle: + +`1/16 * [[1 2 1][2 4 2][1 2 1]]` + +![Example gaussian filter](Project4-IntroGLSL/raw/master/readmeFiles/gaussianFilter.png) + +**Grayscale** + +Create a new fragment shader starting with `passthroughFS.glsl`, that displays the image in grayscale when the user presses `5`. To create a grayscale filter, determine the luminance + +`const vec3 W = vec3(0.2125, 0.7154, 0.0721);` + +`float luminance = dot(rgb, W);` + +Then set the output `r`, `g`, and `b` components to the lumiance. + +![Example grayscale filter](Project4-IntroGLSL/raw/master/readmeFiles/grayscaleFilter.png) + +**Edge Detection** + +Build on both our Gaussian blur and Grayscale filters to create an edge detection filter when the user presses `6` using a horizontal and vertical sobel filter. Use two 3x3 kernels: + +`Sobel-horizontal = [[-1 -2 -1][0 0 0][1 2 1]]` + +`Sobel-vertical = [[-1 0 1][-2 0 2][-1 0 1]]` + +Run the kernels on the luminance of each texel, instead of the `rgb` components used in the Gaussian blur. The result is two floating-point values, one from each kernel. Create a `vec2` from these values, and set the output `rgb` components to the vector’s length. + +![Example edge filter](Project4-IntroGLSL/raw/master/readmeFiles/edgeFilter.png) + +**Toon Shading** + +Toon shading is part of non-photorealistic rendering (NPR). Instead of trying to produce a photorealistic image, the goal is to create an image with a certain artistic style. In this case, we will build on our edge detection filter to create a cartoon filter when the user presses `7`. + +First, perform edge detection. If the length of the vector is above a threshold (you determine), then output black. Otherwise, quantize the texel’s color and output it. Use the following code to quantize: + +`float quantize = // determine it` + +`rgb *= quantize;` + +`rgb += vec3(0.5);` + +`ivec3 irgb = ivec3(rgb);` + +`rgb = vec3(irgb) / quantize;` + +![Example toon filter](Project4-IntroGLSL/raw/master/readmeFiles/toonFilter.png) + +------------------------------------------------------------------------------- +PART 2 REQUIREMENTS: +------------------------------------------------------------------------------- +In Part 2, you are given code for: + +* Drawing a VBO through WebGL +* Javascript code for interfacing with WebGL +* Functions for generating simplex noise + +You are required to implement the following: + +* A sin-wave based vertex shader: + +![Example sin wave grid](Project4-IntroGLSL/raw/master/readmeFiles/sinWaveGrid.png) + +* A simplex noise based vertex shader: + +![Example simplex noise wave grid](Project4-IntroGLSL/raw/master/readmeFiles/oceanWave.png) + +* One interesting vertex shader of your choice + +------------------------------------------------------------------------------- +PART 2 WALKTHROUGH: +------------------------------------------------------------------------------- +**Sin Wave** + +* For this assignment, you will need the latest version of either Chrome, Firefox, or Safari. +* Begin by opening index.html. You should see a flat grid of black and white lines on the xy plane: + +![Example boring grid](Project4-IntroGLSL/raw/master/readmeFiles/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: + + `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: + + `vec2 simplexVec = vec2(u_time, position);` + + `float s_contrib = snoise(simplexVec);` + + `float t_contrib = snoise(vec2(s_contrib,u_time));` + +**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. + +------------------------------------------------------------------------------- +BLOG +------------------------------------------------------------------------------- +As mentioned in class, all students should have student blogs detailing progress on projects. If you already have a blog, you can use it; otherwise, please create a blog using www.blogger.com or any other tool, such as www.wordpress.org. Blog posts on your project are due on the SAME DAY as the project, and should include: + +* A brief description of the project and the specific features you implemented. +* A link to your github repo if the code is open source. +* At least one screenshot of your project running. +* A 30 second or longer video of your project running. To create the video, use http://www.microsoft.com/expression/products/Encoder4_Overview.aspx + +------------------------------------------------------------------------------- +THIRD PARTY CODE POLICY +------------------------------------------------------------------------------- +* Use of any third-party code must be approved by asking on Piazza. 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. + +------------------------------------------------------------------------------- +SELF-GRADING +------------------------------------------------------------------------------- +* On the submission date, email your grade, on a scale of 0 to 100, to Karl, yiningli@seas.upenn.edu, 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 + +* A brief description of the project and specific features you implemented +* At least one screenshot of your project running, and at least one screenshot of the final rendered output of your raytracer +* Instructions for building and running your project if they differ from the base code +* A link to your blog post detailing the project * A list of all third-party code used \ No newline at end of file diff --git a/part1/ImageProcessing/ImageProcessing.sdf b/part1/ImageProcessing/ImageProcessing.sdf new file mode 100644 index 0000000..b28a362 Binary files /dev/null and b/part1/ImageProcessing/ImageProcessing.sdf differ diff --git a/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj b/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj index 554291c..1ab7981 100644 --- a/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj +++ b/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj @@ -1,110 +1,119 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - - - - - - ImageProcessing - {C53A9886-B4FD-487A-83A5-01406BA9E8BF} - Box_Blur - Win32Proj - - - - Application - Unicode - true - - - Application - Unicode - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - - - - Disabled - ..\..\shared32\glew\include;..\..\shared32\freeglut\include;..\Shader_Loading;..\SOIL;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - - Level3 - EditAndContinue - - - glew32.lib;%(AdditionalDependencies) - ..\..\shared32\glew\lib;..\..\shared32\freeglut\lib;%(AdditionalLibraryDirectories) - true - Console - MachineX86 - - - - - MaxSpeed - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - true - Console - true - true - MachineX86 - - - - - {25544c77-3b78-405f-a15d-1231d05969f3} - false - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + ImageProcessing + {C53A9886-B4FD-487A-83A5-01406BA9E8BF} + Box_Blur + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + + Disabled + ..\..\shared32\glew\include;..\..\shared32\freeglut\include;..\Shader_Loading;..\SOIL;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + glew32.lib;%(AdditionalDependencies) + ..\..\shared32\glew\lib;..\..\shared32\freeglut\lib;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + + + {25544c77-3b78-405f-a15d-1231d05969f3} + false + + + + + \ No newline at end of file diff --git a/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj.user b/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/ImageProcessing.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/part1/ImageProcessing/ImageProcessing/NightVision.glsl b/part1/ImageProcessing/ImageProcessing/NightVision.glsl new file mode 100644 index 0000000..abc9466 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/NightVision.glsl @@ -0,0 +1,14 @@ +varying vec2 v_Texcoords; + +uniform sampler2D u_image; + +void main(void) +{ + + //calculate noise + float noise = dot(texture2D(u_image, v_Texcoords).rgb, vec3(0.4, 0.4, 0.4)); + //multiply with green color + gl_FragColor = vec4(noise * vec3(0.3, 1.2, 0.3),1.0); + + +} diff --git a/part1/ImageProcessing/ImageProcessing/brightness.glsl b/part1/ImageProcessing/ImageProcessing/brightness.glsl new file mode 100644 index 0000000..7d49d9f --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/brightness.glsl @@ -0,0 +1,11 @@ +varying vec2 v_Texcoords; +uniform sampler2D u_image; + +void main(void) +{ + + float brightness = 0.3; + vec3 brightCol =texture2D(u_image, v_Texcoords).rgb + vec3(brightness); + gl_FragColor = vec4(brightCol, 1.0); + +} diff --git a/part1/ImageProcessing/ImageProcessing/contrast.glsl b/part1/ImageProcessing/ImageProcessing/contrast.glsl new file mode 100644 index 0000000..eb9c0a4 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/contrast.glsl @@ -0,0 +1,10 @@ +varying vec2 v_Texcoords; +uniform sampler2D u_image; + +void main(void) +{ + + float contrast = 1.2; + vec3 contrastCol = (texture2D(u_image, v_Texcoords).rgb - 0.5) * contrast + 0.5; + gl_FragColor = vec4(contrastCol,1.0); +} diff --git a/part1/ImageProcessing/ImageProcessing/edgeDetection.glsl b/part1/ImageProcessing/ImageProcessing/edgeDetection.glsl new file mode 100644 index 0000000..e82cac4 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/edgeDetection.glsl @@ -0,0 +1,39 @@ +varying vec2 v_Texcoords; + +uniform sampler2D u_image; +uniform vec2 u_step; + +const int KERNEL_WIDTH = 7; // Odd +const float offset = 3.0; + +mat3 Sobel_horizontal = mat3(-1, -2, -1 ,0, 0, 0, 1, 2, 1); +mat3 Sobel_vertical=mat3(-1, 0, 1 ,-2 ,0, 2 ,-1, 0, 1); + + + +void main(void) +{ + + + float accum1 = 0.0; + float accum2 = 0.0; + + + for(int i =0; i<3 ;i++){ + + for(int j =0; j<3 ;j++){ + + vec2 coord = vec2(v_Texcoords.s + ((float(i) ) *u_step.x ), v_Texcoords.t + ((float(j) ) *u_step.y ) ); + float luminance = dot(texture2D(u_image, coord).rgb, vec3(0.2125, 0.7154, 0.0721)); + accum1 += luminance * Sobel_horizontal[i][j]; + accum2 += luminance * Sobel_vertical[i][j]; + } + + } + + vec2 col = vec2(accum1, accum2); + float len = length(col); + + gl_FragColor = vec4(len,len,len,1.0); + +} diff --git a/part1/ImageProcessing/ImageProcessing/gammaCorrection.glsl b/part1/ImageProcessing/ImageProcessing/gammaCorrection.glsl new file mode 100644 index 0000000..4906544 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/gammaCorrection.glsl @@ -0,0 +1,10 @@ +varying vec2 v_Texcoords; +uniform sampler2D u_image; + +void main(void) +{ + + vec3 color = texture2D(u_image, v_Texcoords).rgb; + gl_FragColor =vec4(pow(color, vec3(1.0 / 1.5)), 1.0); + +} diff --git a/part1/ImageProcessing/ImageProcessing/gaussBlur.glsl b/part1/ImageProcessing/ImageProcessing/gaussBlur.glsl new file mode 100644 index 0000000..6a7a2e6 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/gaussBlur.glsl @@ -0,0 +1,31 @@ +varying vec2 v_Texcoords; + +uniform sampler2D u_image; +uniform vec2 u_step; + +const int KERNEL_WIDTH = 7; // Odd +const float offset = 0.4; +mat3 weight =mat3 (1.0,2.0,1.0,2.0,4.0,2.0,1.0,2.0,1.0)/16.0; + +void main(void) +{ + + vec3 accum = vec3(0.0); + + for(int i =0; i<3 ;i++){ + + for(int j =0; j<3 ;j++){ + + vec2 coord = vec2(v_Texcoords.s + ((float(i) ) *u_step.x ), v_Texcoords.t + ((float(j) ) *u_step.y ) ); + accum += texture2D(u_image, coord).rgb * weight[i][j]; + + } + + } + +// gl_FragColor = (accum/16.0); + gl_FragColor = vec4(accum, 1.0); + + + +} diff --git a/part1/ImageProcessing/ImageProcessing/greyScale.glsl b/part1/ImageProcessing/ImageProcessing/greyScale.glsl new file mode 100644 index 0000000..2cac510 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/greyScale.glsl @@ -0,0 +1,12 @@ +varying vec2 v_Texcoords; + +uniform sampler2D u_image; + +void main(void) +{ + + //grey scale + float luminance = dot(texture2D(u_image, v_Texcoords).rgb, vec3(0.2125, 0.7154, 0.0721)); + gl_FragColor = vec4(luminance ,luminance , luminance, 1.0); + +} diff --git a/part1/ImageProcessing/ImageProcessing/imageNegative.glsl b/part1/ImageProcessing/ImageProcessing/imageNegative.glsl new file mode 100644 index 0000000..cbbcaf6 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/imageNegative.glsl @@ -0,0 +1,14 @@ +varying vec2 v_Texcoords; + +uniform sampler2D u_image; + +void main(void) +{ + + //negetive image + vec4 color = texture2D(u_image, v_Texcoords); + gl_FragColor = vec4(1.0 - color.rgb, 1.0); + + + +} diff --git a/part1/ImageProcessing/ImageProcessing/main.cpp b/part1/ImageProcessing/ImageProcessing/main.cpp index 3b1444d..771c2e6 100644 --- a/part1/ImageProcessing/ImageProcessing/main.cpp +++ b/part1/ImageProcessing/ImageProcessing/main.cpp @@ -1,146 +1,193 @@ -#include -#include -#include -#include "Utility.h" -#include "SOIL.h" - -int width = 640; -int height = 480; - -GLuint positionLocation = 0; -GLuint texcoordsLocation = 1; -const char *attributeLocations[] = { "Position", "Tex" }; - -GLuint passthroughProgram; -GLuint boxBlurProgram; - -GLuint initShader(const char *vertexShaderPath, const char *fragmentShaderPath) -{ - GLuint program = Utility::createProgram(vertexShaderPath, fragmentShaderPath, attributeLocations, 2); - GLint location; - - glUseProgram(program); - - if ((location = glGetUniformLocation(program, "u_image")) != -1) - { - glUniform1i(location, 0); - } - - if ((location = glGetUniformLocation(program, "u_step")) != -1) - { - glUniform2f(location, 1.0f / (float)width, 1.0f / (float)height); - } - - return program; -} - -void initTextures() -{ - GLuint image = SOIL_load_OGL_texture("Valve.png", 3, 0, 0); - glBindTexture(GL_TEXTURE_2D, image); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); -} - -void initVAO(void) -{ - GLfloat vertices[] = - { - -1.0f, -1.0f, - 1.0f, -1.0f, - 1.0f, 1.0f, - -1.0f, 1.0f, - }; - - GLfloat texcoords[] = - { - 1.0f, 1.0f, - 0.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f - }; - - GLushort indices[] = { 0, 1, 3, 3, 1, 2 }; - - GLuint vao; - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - GLuint vertexBufferObjID[3]; - glGenBuffers(3, vertexBufferObjID); - - glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glVertexAttribPointer((GLuint)positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(positionLocation); - - glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords), texcoords, GL_STATIC_DRAW); - glVertexAttribPointer((GLuint)texcoordsLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(texcoordsLocation); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBufferObjID[2]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); -} - -void display(void) -{ - glClear(GL_COLOR_BUFFER_BIT); - - // VAO, shader program, and texture already bound - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); - - glutPostRedisplay(); - glutSwapBuffers(); -} - -void keyboard(unsigned char key, int x, int y) -{ - switch (key) - { - case '1': - glUseProgram(passthroughProgram); - break; - case '2': - glUseProgram(boxBlurProgram); - break; - } -} - -void reshape(int w, int h) -{ - glViewport(0, 0, (GLsizei)w, (GLsizei)h); -} - -int main(int argc, char* argv[]) -{ - glutInit(&argc, argv); - glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); - glutInitWindowSize(width, height); - glutCreateWindow("Image Processing"); - glewInit(); - GLenum err = glewInit(); - if (GLEW_OK != err) - { - /* Problem: glewInit failed, something is seriously wrong. */ - std::cout << "glewInit failed, aborting." << std::endl; - exit (1); - } - std::cout << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << std::endl; - std::cout << "OpenGL version " << glGetString(GL_VERSION) << " supported" << std::endl; - - initVAO(); - initTextures(); - passthroughProgram = initShader("passthroughVS.glsl", "passthroughFS.glsl"); - boxBlurProgram = initShader("passthroughVS.glsl", "boxBlurFS.glsl"); - - glutDisplayFunc(display); - glutReshapeFunc(reshape); - glutKeyboardFunc(keyboard); - - glUseProgram(passthroughProgram); - glActiveTexture(GL_TEXTURE0); - - glutMainLoop(); - return 0; +#include +#include +#include +#include "Utility.h" +#include "SOIL.h" + +int width = 640; +int height = 480; + +GLuint positionLocation = 0; +GLuint texcoordsLocation = 1; +const char *attributeLocations[] = { "Position", "Tex" }; + +GLuint passthroughProgram; +GLuint boxBlurProgram; +GLuint imageNegativeProgram; +GLuint greyScaleProgram; +GLuint gaussBlurProgram; +GLuint gammaCorrectionProgram; +GLuint contrastProgram; +GLuint edgeDetectionProgram; +GLuint toonShaderProgram; +GLuint nightVisionProgram; +GLuint brightProgram; + +GLuint initShader(const char *vertexShaderPath, const char *fragmentShaderPath) +{ + GLuint program = Utility::createProgram(vertexShaderPath, fragmentShaderPath, attributeLocations, 2); + GLint location; + + glUseProgram(program); + + if ((location = glGetUniformLocation(program, "u_image")) != -1) + { + glUniform1i(location, 0); + } + + if ((location = glGetUniformLocation(program, "u_step")) != -1) + { + glUniform2f(location, 1.0f / (float)width, 1.0f / (float)height); + } + + return program; +} + +void initTextures() +{ + GLuint image = SOIL_load_OGL_texture("Valve.png", 3, 0, 0); + glBindTexture(GL_TEXTURE_2D, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +} + +void initVAO(void) +{ + GLfloat vertices[] = + { + -1.0f, -1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f, + -1.0f, 1.0f, + }; + + GLfloat texcoords[] = + { + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f + }; + + GLushort indices[] = { 0, 1, 3, 3, 1, 2 }; + + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + GLuint vertexBufferObjID[3]; + glGenBuffers(3, vertexBufferObjID); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glVertexAttribPointer((GLuint)positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(positionLocation); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords), texcoords, GL_STATIC_DRAW); + glVertexAttribPointer((GLuint)texcoordsLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(texcoordsLocation); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBufferObjID[2]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); +} + +void display(void) +{ + glClear(GL_COLOR_BUFFER_BIT); + + // VAO, shader program, and texture already bound + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); + + glutPostRedisplay(); + glutSwapBuffers(); +} + +void keyboard(unsigned char key, int x, int y) +{ + switch (key) + { + case '1': + glUseProgram(passthroughProgram); + break; + case '2': + glUseProgram(boxBlurProgram); + break; + case '3': + glUseProgram(imageNegativeProgram); + break; + case '4': + glUseProgram(gaussBlurProgram); + break; + case '5': + glUseProgram(greyScaleProgram); + break; + case '6': + glUseProgram(edgeDetectionProgram); + break; + case '7': + glUseProgram(toonShaderProgram); + break; + case '8': + glUseProgram(gammaCorrectionProgram); + break; + case '9': + glUseProgram(contrastProgram); + break; + case '0': + glUseProgram(nightVisionProgram); + break; + case 'b': + glUseProgram(brightProgram); + break; + + + } +} + +void reshape(int w, int h) +{ + glViewport(0, 0, (GLsizei)w, (GLsizei)h); +} + +int main(int argc, char* argv[]) +{ + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); + glutInitWindowSize(width, height); + glutCreateWindow("Image Processing"); + glewInit(); + GLenum err = glewInit(); + if (GLEW_OK != err) + { + /* Problem: glewInit failed, something is seriously wrong. */ + std::cout << "glewInit failed, aborting." << std::endl; + exit (1); + } + std::cout << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << std::endl; + std::cout << "OpenGL version " << glGetString(GL_VERSION) << " supported" << std::endl; + + initVAO(); + initTextures(); + passthroughProgram = initShader("passthroughVS.glsl", "passthroughFS.glsl"); + boxBlurProgram = initShader("passthroughVS.glsl", "boxBlurFS.glsl"); + imageNegativeProgram = initShader("passthroughVS.glsl", "imageNegative.glsl"); + gaussBlurProgram = initShader("passthroughVS.glsl", "gaussBlur.glsl"); + greyScaleProgram = initShader("passthroughVS.glsl", "greyScale.glsl"); + gammaCorrectionProgram = initShader("passthroughVS.glsl", "gammaCorrection.glsl"); + contrastProgram = initShader("passthroughVS.glsl", "contrast.glsl"); + edgeDetectionProgram = initShader("passthroughVS.glsl", "edgeDetection.glsl"); + toonShaderProgram = initShader("passthroughVS.glsl", "toonShader.glsl"); + nightVisionProgram = initShader("passthroughVS.glsl", "NightVision.glsl"); + brightProgram = initShader("passthroughVS.glsl", "brightness.glsl"); + + glutDisplayFunc(display); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyboard); + + glUseProgram(passthroughProgram); + glActiveTexture(GL_TEXTURE0); + + glutMainLoop(); + return 0; } \ No newline at end of file diff --git a/part1/ImageProcessing/ImageProcessing/toonShader.glsl b/part1/ImageProcessing/ImageProcessing/toonShader.glsl new file mode 100644 index 0000000..1053e88 --- /dev/null +++ b/part1/ImageProcessing/ImageProcessing/toonShader.glsl @@ -0,0 +1,53 @@ +varying vec2 v_Texcoords; + +uniform sampler2D u_image; +uniform vec2 u_step; + +const float offset = 3.0; + +mat3 Sobel_horizontal = mat3(-1, -2, -1 ,0, 0, 0, 1, 2, 1); +mat3 Sobel_vertical=mat3(-1, 0, 1 ,-2 ,0, 2 ,-1, 0, 1); + + + +void main(void) +{ + + float accum1 = 0.0; + float accum2 = 0.0; + + + for(int i =0; i<3 ;i++){ + + for(int j =0; j<3 ;j++){ + + vec2 coord = vec2(v_Texcoords.s + ((float(i) - offset ) *u_step.x ), v_Texcoords.t + ((float(j) - offset) *u_step.y ) ); + float luminance = dot(texture2D(u_image, coord).rgb, vec3(0.2125, 0.7154, 0.0721)); + accum1 += luminance * Sobel_horizontal[i][j]; + accum2 += luminance * Sobel_vertical[i][j]; + } + + } + + vec2 col = vec2(accum1, accum2); + float len = length(col); + + if(len > 0.2){ + gl_FragColor = vec4(0.0,0.0,0.0,1.0); + }else{ + float quantize = 30.5;// determine it + vec3 rgb = texture2D(u_image, v_Texcoords).rgb; + rgb *= quantize; + + rgb += vec3(0.5); + + ivec3 irgb = ivec3(rgb); + + rgb = vec3(irgb) / quantize; + gl_FragColor = vec4(rgb,1.0); + } + + +// gl_FragColor = vec4(len,len,len,1.0); + +} diff --git a/part1/ImageProcessing/SOIL/SOIL.vcxproj.user b/part1/ImageProcessing/SOIL/SOIL.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/part1/ImageProcessing/SOIL/SOIL.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/part2/customShader/gl-matrix.js b/part2/customShader/gl-matrix.js new file mode 100644 index 0000000..2e1bdb9 --- /dev/null +++ b/part2/customShader/gl-matrix.js @@ -0,0 +1,1909 @@ +/** + * @fileOverview gl-matrix - High performance matrix and vector operations for WebGL + * @author Brandon Jones + * @version 1.2.3 + */ + +/* + * Copyright (c) 2011 Brandon Jones + * + * 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. + */ + +"use strict"; + +// Type declarations +(function() { + // account for CommonJS environments + var _global = (typeof(exports) != 'undefined') ? global : window; + _global.glMatrixArrayType = _global.MatrixArray = null; + + /** + * @class 3 Dimensional Vector + * @name vec3 + */ + _global.vec3 = {}; + + /** + * @class 3x3 Matrix + * @name mat3 + */ + _global.mat3 = {}; + + /** + * @class 4x4 Matrix + * @name mat4 + */ + _global.mat4 = {}; + + /** + * @class Quaternion + * @name quat4 + */ + _global.quat4 = {}; + + // explicitly sets and returns the type of array to use within glMatrix + _global.setMatrixArrayType = function(type) { + return glMatrixArrayType = MatrixArray = type; + }; + + // auto-detects and returns the best type of array to use within glMatrix, falling + // back to Array if typed arrays are unsupported + _global.determineMatrixArrayType = function() { + return setMatrixArrayType((typeof Float32Array !== 'undefined') ? Float32Array : Array); + }; + + determineMatrixArrayType(); +})(); + +/* + * 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; +}; + +/** + * 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; +}; + +/** + * 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 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); +}; + +/** + * 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; } + + var m = mat4.create(); + var v = new MatrixArray(4); + + 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; +}; + +/** + * 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] + ']'; +}; + +/* + * 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]; + } + + 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; +}; + +/** + * 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] + ']'; +}; + +/* + * 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; +}; + +/** + * 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; +}; + +/** + * 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], + 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 = mat2[0], b01 = mat2[1], b02 = mat2[2], b03 = mat2[3], + b10 = mat2[4], b11 = mat2[5], b12 = mat2[6], b13 = mat2[7], + b20 = mat2[8], b21 = mat2[9], b22 = mat2[10], b23 = mat2[11], + b30 = mat2[12], b31 = mat2[13], b32 = mat2[14], b33 = mat2[15]; + + dest[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; + dest[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; + dest[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; + dest[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; + dest[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; + dest[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; + dest[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; + dest[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; + dest[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; + dest[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; + dest[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; + dest[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; + dest[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; + dest[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; + dest[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; + dest[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * 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 + * @paran {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] + ']'; +}; + +/* + * 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]; + } + + 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; +}; + +/** + * 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 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; +}; + +/** + * 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; +}; + +/** + * 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] + ']'; +}; + diff --git a/part2/customShader/index.html b/part2/customShader/index.html new file mode 100644 index 0000000..20684f9 --- /dev/null +++ b/part2/customShader/index.html @@ -0,0 +1,59 @@ + + + + +Vertex Wave + + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/part2/customShader/index.js b/part2/customShader/index.js new file mode 100644 index 0000000..a365d58 --- /dev/null +++ b/part2/customShader/index.js @@ -0,0 +1,158 @@ +(function() { + "use strict"; + /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ + /*global getShaderSource,createWebGLContext,createProgram*/ + + var NUM_WIDTH_PTS = 32; + var NUM_HEIGHT_PTS = 32; + + var message = document.getElementById("message"); + var canvas = document.getElementById("canvas"); + var context = createWebGLContext(canvas, message); + if (!context) { + return; + } + + /////////////////////////////////////////////////////////////////////////// + + context.viewport(0, 0, canvas.width, canvas.height); + context.clearColor(1.0, 1.0, 1.0, 1.0); + context.enable(context.GL_DEPTH_TEST); + + var persp = mat4.create(); + mat4.perspective(45.0, 0.5, 0.1, 100.0, persp); + + var eye = [2.0, 1.0, 3.0]; + var center = [0.0, 0.0, 0.0]; + var up = [0.0, 0.0, 1.0]; + var view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + var positionLocation = 0; + var heightLocation = 1; + var u_modelViewPerspectiveLocation; + + var u_time; + + (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"); + u_time = context.getUniformLocation(program,"u_time"); + context.useProgram(program); + })(); + + var heights; + var numberOfIndices; + + (function initializeGrid() { + function uploadMesh(positions, heights, indices) { + // Positions + var positionsName = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, positionsName); + context.bufferData(context.ARRAY_BUFFER, positions, context.STATIC_DRAW); + context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0); + context.enableVertexAttribArray(positionLocation); + + if (heights) + { + // Heights + var heightsName = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, heightsName); + context.bufferData(context.ARRAY_BUFFER, heights.length * heights.BYTES_PER_ELEMENT, context.STREAM_DRAW); + context.vertexAttribPointer(heightLocation, 1, context.FLOAT, false, 0, 0); + context.enableVertexAttribArray(heightLocation); + } + + // Indices + var indicesName = context.createBuffer(); + context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, indicesName); + context.bufferData(context.ELEMENT_ARRAY_BUFFER, indices, context.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(2 * numberOfPositions); + var indices = new Uint16Array(2 * ((NUM_HEIGHT_PTS * (NUM_WIDTH_PTS - 1)) + (NUM_WIDTH_PTS * (NUM_HEIGHT_PTS - 1)))); + + var positionsIndex = 0; + var indicesIndex = 0; + var length; + + for (var j = 0; j < NUM_WIDTH_PTS; ++j) + { + positions[positionsIndex++] = j /(NUM_WIDTH_PTS - 1); + positions[positionsIndex++] = 0.0; + + 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; + } + } + + uploadMesh(positions, heights, indices); + numberOfIndices = indices.length; + })(); + + + var delta = 0.0; + + (function animate(){ + /////////////////////////////////////////////////////////////////////////// + // Update + + var model = mat4.create(); + mat4.identity(model); + mat4.translate(model, [-0.5, -0.5, 0.0]); + var mv = mat4.create(); + mat4.multiply(view, model, mv); + var mvp = mat4.create(); + mat4.multiply(persp, mv, mvp); + + /////////////////////////////////////////////////////////////////////////// + // 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); + + delta += 0.5; + context.uniform1f(u_time, delta); + window.requestAnimFrame(animate); + })(); + +}()); diff --git a/part2/customShader/noise3D.js b/part2/customShader/noise3D.js new file mode 100644 index 0000000..2d4ffd2 --- /dev/null +++ b/part2/customShader/noise3D.js @@ -0,0 +1,316 @@ +(function(exports) { + "use strict"; + /*global window,vec3*/ + + exports = exports || window; + + function step(edge, x) { + return [ + (x[0] < edge[0]) ? 0.0 : 1.0, + (x[1] < edge[1]) ? 0.0 : 1.0, + (x[2] < edge[2]) ? 0.0 : 1.0 + ]; + } + + function step_vec4(edge, x) { + return [ + (x[0] < edge[0]) ? 0.0 : 1.0, + (x[1] < edge[1]) ? 0.0 : 1.0, + (x[2] < edge[2]) ? 0.0 : 1.0, + (x[3] < edge[3]) ? 0.0 : 1.0 + ]; + } + + function min(x, y) { + return [ + y[0] < x[0] ? y[0] : x[0], + y[1] < x[1] ? y[1] : x[1], + y[2] < x[2] ? y[2] : x[2] + ]; + } + + function max(x, y) { + return [ + y[0] > x[0] ? y[0] : x[0], + y[1] > x[1] ? y[1] : x[1], + y[2] > x[2] ? y[2] : x[2] + ]; + } + + function max_vec4(x, y) { + return [ + y[0] > x[0] ? y[0] : x[0], + y[1] > x[1] ? y[1] : x[1], + y[2] > x[2] ? y[2] : x[2], + y[3] > x[3] ? y[3] : x[3] + ]; + } + + function vec4_dot(left, right) { + return left[0] * right[0] + + left[1] * right[1] + + left[2] * right[2] + + left[3] * right[3]; + } + + // + // Description : Array and textureless GLSL 2D/3D/4D simplex + // noise functions. + // Author : Ian McEwan, Ashima Arts. + // Maintainer : ijm + // Lastmod : 20110822 (ijm) + // License : Copyright (C) 2011 Ashima Arts. All rights reserved. + // Distributed under the MIT License. See LICENSE file. + // https://github.com/ashima/webgl-noise + // + function mod289_vec3(x) { + var temp = (1.0 / 289.0); + return [ + x[0] - Math.floor(x[0] * temp) * 289.0, + x[1] - Math.floor(x[1] * temp) * 289.0, + x[2] - Math.floor(x[2] * temp) * 289.0 + ]; + } + + function mod289_vec4(x) { + var temp = (1.0 / 289.0); + return [ + x[0] - Math.floor(x[0] * temp) * 289.0, + x[1] - Math.floor(x[1] * temp) * 289.0, + x[2] - Math.floor(x[2] * temp) * 289.0, + x[3] - Math.floor(x[3] * temp) * 289.0 + ]; + } + + function permute_vec4(x) { + return mod289_vec4([ + ((x[0]*34.0)+1.0)*x[0], + ((x[1]*34.0)+1.0)*x[1], + ((x[2]*34.0)+1.0)*x[2], + ((x[3]*34.0)+1.0)*x[3] + ]); + } + + function taylorInvSqrt_vec4(r) { + return [ + 1.79284291400159 - 0.85373472095314 * r[0], + 1.79284291400159 - 0.85373472095314 * r[1], + 1.79284291400159 - 0.85373472095314 * r[2], + 1.79284291400159 - 0.85373472095314 * r[3] + ]; + } + + exports.snoise = function(v) { + // const vec2 C = vec2(1.0f/6.0f, 1.0f/3.0f) ; + // const vec4 D = vec4(0.0f, 0.5f, 1.0f, 2.0f); + var C = [1.0/6.0, 1.0/3.0]; + var D = [0.0, 0.5, 1.0, 2.0]; + + // vec3 i = floor(v + dot(v, vec3(C.y, C.y, C.y)) ); + // vec3 x0 = v - i + dot(i, vec3(C.x, C.x, C.x) ); + var temp0 = vec3.create(); + var temp3 = vec3.dot(v, [C[1], C[1], C[1]]); + vec3.add(v, [temp3, temp3, temp3], temp0); + var i = [Math.floor(temp0[0]), Math.floor(temp0[1]), Math.floor(temp0[2])]; + var temp1 = vec3.create(); + vec3.subtract(v, i, temp1); + var temp2 = vec3.dot(i, [C[0], C[0], C[0]]); + var x0 = vec3.create(); + vec3.add(temp1, [temp2, temp2, temp2], x0); + + // vec3 g = step(vec3(x0.y, x0.z, x0.x), vec3(x0.x, x0.y, x0.z)); + // vec3 l = 1.0f - g; + // vec3 i1 = min( vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y) ); + // vec3 i2 = max( vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y) ); + var g = step([x0[1], x0[2], x0[0]], [x0[0], x0[1], x0[2]]); + var l = [1.0 - g[0], 1.0 - g[1], 1.0 - g[2]]; + var i1 = min([g[0], g[1], g[2]], [l[2], l[0], l[1]]); + var i2 = max([g[0], g[1], g[2]], [l[2], l[0], l[1]]); + + // vec3 x1 = x0 - i1 + vec3(C.x, C.x, C.x); + // vec3 x2 = x0 - i2 + vec3(C.y, C.y, C.y); // 2.0*C.x = 1/3 = C.y + // vec3 x3 = x0 - vec3(D.y, D.y, D.y); // -1.0+3.0*C.x = -0.5 = -D.y + var temp4 = vec3.create(); + vec3.subtract(x0, i1, temp4); + var x1 = vec3.create(); + vec3.add(temp4, [C[0], C[0], C[0]], x1); + var temp5 = vec3.create(); + vec3.subtract(x0, i2, temp5); + var x2 = vec3.create(); + vec3.add(temp5, [C[1], C[1], C[1]], x2); + var x3 = vec3.create(); + vec3.subtract(x0, [D[1], D[1], D[1]], x3); + + // i = mod289(i); + // vec4 p = permute( permute( permute( + // i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + // + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + // + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); + i = mod289_vec3(i); + var p = permute_vec4([i[2] + 0.0, i[2] + i1[2], i[2] + i2[2], i[2] + 1.0]); + p[0] += i[1] + 0.0; + p[1] += i[1] + i1[1]; + p[2] += i[1] + i2[1]; + p[3] += i[1] + 1.0; + p = permute_vec4(p); + p[0] += i[0] + 0.0; + p[1] += i[0] + i1[0]; + p[2] += i[0] + i2[0]; + p[3] += i[0] + 1.0; + p = permute_vec4(p); + + // float n_ = 0.142857142857f; // 1.0/7.0 + // vec3 ns = n_ * vec3(D.w, D.y, D.z) - vec3(D.x, D.z, D.x); +// var n_ = 0.142857142857; // 1.0/7.0 +// var ns = [ +// n_ * D[3] - D[0], +// n_ * D[1] - D[2], +// n_ * D[2] - D[0] +// ]; + var ns = [ + 0.28571430, + -0.92857140, + 0.14285715 + ]; + + // vec4 j = p - 49.0f * floor(p * ns.z * ns.z); // mod(p,7*7) + var j = [ + p[0] - 49.0 * Math.floor(p[0] * ns[2] * ns[2]), + p[1] - 49.0 * Math.floor(p[1] * ns[2] * ns[2]), + p[2] - 49.0 * Math.floor(p[2] * ns[2] * ns[2]), + p[3] - 49.0 * Math.floor(p[3] * ns[2] * ns[2]) + ]; + + // vec4 x_ = floor(j * ns.z); + // vec4 y_ = floor(j - 7.0f * x_ ); // mod(j,N) + var x_ = [ + Math.floor(j[0] * ns[2]), + Math.floor(j[1] * ns[2]), + Math.floor(j[2] * ns[2]), + Math.floor(j[3] * ns[2]) + ]; + var y_ = [ + Math.floor(j[0] - 7.0 * x_[0] ), + Math.floor(j[1] - 7.0 * x_[1] ), + Math.floor(j[2] - 7.0 * x_[2] ), + Math.floor(j[3] - 7.0 * x_[3] ) + ]; + + // vec4 x = x_ *ns.x + vec4(ns.y, ns.y, ns.y, ns.y); + // vec4 y = y_ *ns.x + vec4(ns.y, ns.y, ns.y, ns.y); + // vec4 h = 1.0f - abs(x) - abs(y); + var x = [ + x_[0] *ns[0] + ns[1], + x_[1] *ns[0] + ns[1], + x_[2] *ns[0] + ns[1], + x_[3] *ns[0] + ns[1] + ]; + var y = [ + y_[0] *ns[0] + ns[1], + y_[1] *ns[0] + ns[1], + y_[2] *ns[0] + ns[1], + y_[3] *ns[0] + ns[1] + ]; + var h = [ + 1.0 - Math.abs(x[0]) - Math.abs(y[0]), + 1.0 - Math.abs(x[1]) - Math.abs(y[1]), + 1.0 - Math.abs(x[2]) - Math.abs(y[2]), + 1.0 - Math.abs(x[3]) - Math.abs(y[3]) + ]; + + // vec4 b0 = vec4( vec2(x.x, x.y), vec2(y.x, y.y) ); + // vec4 b1 = vec4( vec2(x.z, x.w), vec2(y.z, y.w) ); + var b0 = [x[0], x[1], y[0], y[1]]; + var b1 = [x[2], x[3], y[2], y[3]]; + + // vec4 s0 = floor(b0)*2.0f + 1.0f; + // vec4 s1 = floor(b1)*2.0f + 1.0f; + // vec4 sh = -step(h, vec4(0.0)); + + var s0 = [ + Math.floor(b0[0])*2.0 + 1.0, + Math.floor(b0[1])*2.0 + 1.0, + Math.floor(b0[2])*2.0 + 1.0, + Math.floor(b0[3])*2.0 + 1.0 + ]; + var s1 = [ + Math.floor(b1[0])*2.0 + 1.0, + Math.floor(b1[1])*2.0 + 1.0, + Math.floor(b1[2])*2.0 + 1.0, + Math.floor(b1[3])*2.0 + 1.0 + ]; + var sh = step_vec4(h, [0.0, 0.0, 0.0, 0.0]); + sh[0] = -sh[0]; + sh[1] = -sh[1]; + sh[2] = -sh[2]; + sh[3] = -sh[3]; + + // vec4 a0 = vec4(b0.x, b0.z, b0.y, b0.w) + vec4(s0.x, s0.z, s0.y, s0.w) * vec4(sh.x, sh.x, sh.y, sh.y) ; + // vec4 a1 = vec4(b1.x, b1.z, b1.y, b1.w) + vec4(s1.x, s1.z, s1.y, s1.w) * vec4(sh.z, sh.z, sh.w, sh.w) ; + var a0 = [ + b0[0] + s0[0] * sh[0], + b0[2] + s0[2] * sh[0], + b0[1] + s0[1] * sh[1], + b0[3] + s0[3] * sh[1] + ]; + var a1 = [ + b1[0] + s1[0] * sh[2], + b1[2] + s1[2] * sh[2], + b1[1] + s1[1] * sh[3], + b1[3] + s1[3] * sh[3] + ]; + + // vec3 p0 = vec3(a0.x, a0.y, h.x); + // vec3 p1 = vec3(a0.z, a0.w, h.y); + // vec3 p2 = vec3(a1.x, a1.y, h.z); + // vec3 p3 = vec3(a1.z, a1.w, h.w); + var p0 = [a0[0], a0[1], h[0]]; + var p1 = [a0[2], a0[3], h[1]]; + var p2 = [a1[0], a1[1], h[2]]; + var p3 = [a1[2], a1[3], h[3]]; + + // vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + // p0 *= norm.x; + // p1 *= norm.y; + // p2 *= norm.z; + // p3 *= norm.w; + var norm = taylorInvSqrt_vec4([vec3.dot(p0,p0), vec3.dot(p1,p1), vec3.dot(p2, p2), vec3.dot(p3,p3)]); + p0 = [p0[0]*norm[0], p0[1]*norm[0], p0[2]*norm[0]]; + p1 = [p1[0]*norm[1], p1[1]*norm[1], p1[2]*norm[1]]; + p2 = [p2[0]*norm[2], p2[1]*norm[2], p2[2]*norm[2]]; + p3 = [p3[0]*norm[3], p3[1]*norm[3], p3[2]*norm[3]]; + + // vec4 m = max(0.6f - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + // m = m * m; + var m = max_vec4([ + 0.6 - vec3.dot(x0,x0), + 0.6 - vec3.dot(x1,x1), + 0.6 - vec3.dot(x2,x2), + 0.6 - vec3.dot(x3,x3) + ], [ + 0.0, + 0.0, + 0.0, + 0.0 + ]); + m[0] *= m[0]; + m[1] *= m[1]; + m[2] *= m[2]; + m[3] *= m[3]; + + // return 42.0f * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), + // dot(p2,x2), dot(p3,x3) ) ); + return 42.0 * vec4_dot([ + m[0] * m[0], + m[1] * m[1], + m[2] * m[2], + m[3] * m[3] + ], [ + vec3.dot(p0,x0), + vec3.dot(p1,x1), + vec3.dot(p2,x2), + vec3.dot(p3,x3) + ]); + }; + +}()); diff --git a/part2/customShader/simplex.vert b/part2/customShader/simplex.vert new file mode 100644 index 0000000..e1ff07a --- /dev/null +++ b/part2/customShader/simplex.vert @@ -0,0 +1,39 @@ +vec3 permute(vec3 x) { + x = ((x*34.0)+1.0)*x; + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +float simplexNoise(vec2 v) + { + const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); + + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + + i = i - floor(i * (1.0 / 289.0)) * 289.0; + + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + + i.x + vec3(0.0, i1.x, 1.0 )); + + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + + m *= inversesqrt( a0*a0 + h*h ); + + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} \ No newline at end of file diff --git a/part2/customShader/webGLUtility.js b/part2/customShader/webGLUtility.js new file mode 100644 index 0000000..c9de391 --- /dev/null +++ b/part2/customShader/webGLUtility.js @@ -0,0 +1,97 @@ +(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 context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); + + 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"; + } + 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"; + } + context.deleteProgram(program); + return; + } + + context.linkProgram(program); + if (!context.getProgramParameter(program, context.LINK_STATUS)) { + if (message) { + message.innerText += context.getProgramInfoLog(program) + "\n"; + } + context.deleteProgram(program); + return; + } + + return program; + }; + +}()); diff --git a/part2/index.html b/part2/index.html index 1e3f7e2..a826c8b 100644 --- a/part2/index.html +++ b/part2/index.html @@ -1,40 +1,50 @@ - - - - -Vertex Wave - - - - - -
- - - - - - - - - - - + + + + +Vertex Wave + + + + + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/part2/index.js b/part2/index.js index b5df3fb..98064b6 100644 --- a/part2/index.js +++ b/part2/index.js @@ -1,151 +1,158 @@ -(function() { - "use strict"; - /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ - /*global getShaderSource,createWebGLContext,createProgram*/ - - var NUM_WIDTH_PTS = 32; - var NUM_HEIGHT_PTS = 32; - - var message = document.getElementById("message"); - var canvas = document.getElementById("canvas"); - var context = createWebGLContext(canvas, message); - if (!context) { - return; - } - - /////////////////////////////////////////////////////////////////////////// - - context.viewport(0, 0, canvas.width, canvas.height); - context.clearColor(1.0, 1.0, 1.0, 1.0); - context.enable(context.GL_DEPTH_TEST); - - var persp = mat4.create(); - mat4.perspective(45.0, 0.5, 0.1, 100.0, persp); - - var eye = [2.0, 1.0, 3.0]; - var center = [0.0, 0.0, 0.0]; - var up = [0.0, 0.0, 1.0]; - var view = mat4.create(); - mat4.lookAt(eye, center, up, view); - - var positionLocation = 0; - var heightLocation = 1; - var u_modelViewPerspectiveLocation; - - (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"); - - context.useProgram(program); - })(); - - var heights; - var numberOfIndices; - - (function initializeGrid() { - function uploadMesh(positions, heights, indices) { - // Positions - var positionsName = context.createBuffer(); - context.bindBuffer(context.ARRAY_BUFFER, positionsName); - context.bufferData(context.ARRAY_BUFFER, positions, context.STATIC_DRAW); - context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0); - context.enableVertexAttribArray(positionLocation); - - if (heights) - { - // Heights - var heightsName = context.createBuffer(); - context.bindBuffer(context.ARRAY_BUFFER, heightsName); - context.bufferData(context.ARRAY_BUFFER, heights.length * heights.BYTES_PER_ELEMENT, context.STREAM_DRAW); - context.vertexAttribPointer(heightLocation, 1, context.FLOAT, false, 0, 0); - context.enableVertexAttribArray(heightLocation); - } - - // Indices - var indicesName = context.createBuffer(); - context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, indicesName); - context.bufferData(context.ELEMENT_ARRAY_BUFFER, indices, context.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(2 * numberOfPositions); - var indices = new Uint16Array(2 * ((NUM_HEIGHT_PTS * (NUM_WIDTH_PTS - 1)) + (NUM_WIDTH_PTS * (NUM_HEIGHT_PTS - 1)))); - - var positionsIndex = 0; - var indicesIndex = 0; - var length; - - for (var j = 0; j < NUM_WIDTH_PTS; ++j) - { - positions[positionsIndex++] = j /(NUM_WIDTH_PTS - 1); - positions[positionsIndex++] = 0.0; - - 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; - } - } - - uploadMesh(positions, heights, indices); - numberOfIndices = indices.length; - })(); - - (function animate(){ - /////////////////////////////////////////////////////////////////////////// - // Update - - var model = mat4.create(); - mat4.identity(model); - mat4.translate(model, [-0.5, -0.5, 0.0]); - var mv = mat4.create(); - mat4.multiply(view, model, mv); - var mvp = mat4.create(); - mat4.multiply(persp, mv, mvp); - - /////////////////////////////////////////////////////////////////////////// - // 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); - - window.requestAnimFrame(animate); - })(); - -}()); +(function() { + "use strict"; + /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ + /*global getShaderSource,createWebGLContext,createProgram*/ + + var NUM_WIDTH_PTS = 32; + var NUM_HEIGHT_PTS = 32; + + var message = document.getElementById("message"); + var canvas = document.getElementById("canvas"); + var context = createWebGLContext(canvas, message); + if (!context) { + return; + } + + /////////////////////////////////////////////////////////////////////////// + + context.viewport(0, 0, canvas.width, canvas.height); + context.clearColor(1.0, 1.0, 1.0, 1.0); + context.enable(context.GL_DEPTH_TEST); + + var persp = mat4.create(); + mat4.perspective(45.0, 0.5, 0.1, 100.0, persp); + + var eye = [2.0, 1.0, 3.0]; + var center = [0.0, 0.0, 0.0]; + var up = [0.0, 0.0, 1.0]; + var view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + var positionLocation = 0; + var heightLocation = 1; + var u_modelViewPerspectiveLocation; + + var u_time; + + (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"); + u_time = context.getUniformLocation(program,"u_time"); + context.useProgram(program); + })(); + + var heights; + var numberOfIndices; + + (function initializeGrid() { + function uploadMesh(positions, heights, indices) { + // Positions + var positionsName = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, positionsName); + context.bufferData(context.ARRAY_BUFFER, positions, context.STATIC_DRAW); + context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0); + context.enableVertexAttribArray(positionLocation); + + if (heights) + { + // Heights + var heightsName = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, heightsName); + context.bufferData(context.ARRAY_BUFFER, heights.length * heights.BYTES_PER_ELEMENT, context.STREAM_DRAW); + context.vertexAttribPointer(heightLocation, 1, context.FLOAT, false, 0, 0); + context.enableVertexAttribArray(heightLocation); + } + + // Indices + var indicesName = context.createBuffer(); + context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, indicesName); + context.bufferData(context.ELEMENT_ARRAY_BUFFER, indices, context.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(2 * numberOfPositions); + var indices = new Uint16Array(2 * ((NUM_HEIGHT_PTS * (NUM_WIDTH_PTS - 1)) + (NUM_WIDTH_PTS * (NUM_HEIGHT_PTS - 1)))); + + var positionsIndex = 0; + var indicesIndex = 0; + var length; + + for (var j = 0; j < NUM_WIDTH_PTS; ++j) + { + positions[positionsIndex++] = j /(NUM_WIDTH_PTS - 1); + positions[positionsIndex++] = 0.0; + + 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; + } + } + + uploadMesh(positions, heights, indices); + numberOfIndices = indices.length; + })(); + + + var delta = 0.0; + + (function animate(){ + /////////////////////////////////////////////////////////////////////////// + // Update + + var model = mat4.create(); + mat4.identity(model); + mat4.translate(model, [-0.5, -0.5, 0.0]); + var mv = mat4.create(); + mat4.multiply(view, model, mv); + var mvp = mat4.create(); + mat4.multiply(persp, mv, mvp); + + /////////////////////////////////////////////////////////////////////////// + // 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); + + delta += 0.005; + context.uniform1f(u_time, delta); + window.requestAnimFrame(animate); + })(); + +}()); diff --git a/part2/simplex/gl-matrix.js b/part2/simplex/gl-matrix.js new file mode 100644 index 0000000..2e1bdb9 --- /dev/null +++ b/part2/simplex/gl-matrix.js @@ -0,0 +1,1909 @@ +/** + * @fileOverview gl-matrix - High performance matrix and vector operations for WebGL + * @author Brandon Jones + * @version 1.2.3 + */ + +/* + * Copyright (c) 2011 Brandon Jones + * + * 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. + */ + +"use strict"; + +// Type declarations +(function() { + // account for CommonJS environments + var _global = (typeof(exports) != 'undefined') ? global : window; + _global.glMatrixArrayType = _global.MatrixArray = null; + + /** + * @class 3 Dimensional Vector + * @name vec3 + */ + _global.vec3 = {}; + + /** + * @class 3x3 Matrix + * @name mat3 + */ + _global.mat3 = {}; + + /** + * @class 4x4 Matrix + * @name mat4 + */ + _global.mat4 = {}; + + /** + * @class Quaternion + * @name quat4 + */ + _global.quat4 = {}; + + // explicitly sets and returns the type of array to use within glMatrix + _global.setMatrixArrayType = function(type) { + return glMatrixArrayType = MatrixArray = type; + }; + + // auto-detects and returns the best type of array to use within glMatrix, falling + // back to Array if typed arrays are unsupported + _global.determineMatrixArrayType = function() { + return setMatrixArrayType((typeof Float32Array !== 'undefined') ? Float32Array : Array); + }; + + determineMatrixArrayType(); +})(); + +/* + * 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; +}; + +/** + * 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; +}; + +/** + * 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 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); +}; + +/** + * 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; } + + var m = mat4.create(); + var v = new MatrixArray(4); + + 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; +}; + +/** + * 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] + ']'; +}; + +/* + * 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]; + } + + 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; +}; + +/** + * 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] + ']'; +}; + +/* + * 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; +}; + +/** + * 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; +}; + +/** + * 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], + 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 = mat2[0], b01 = mat2[1], b02 = mat2[2], b03 = mat2[3], + b10 = mat2[4], b11 = mat2[5], b12 = mat2[6], b13 = mat2[7], + b20 = mat2[8], b21 = mat2[9], b22 = mat2[10], b23 = mat2[11], + b30 = mat2[12], b31 = mat2[13], b32 = mat2[14], b33 = mat2[15]; + + dest[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; + dest[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; + dest[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; + dest[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; + dest[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; + dest[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; + dest[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; + dest[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; + dest[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; + dest[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; + dest[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; + dest[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; + dest[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; + dest[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; + dest[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; + dest[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * 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 + * @paran {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] + ']'; +}; + +/* + * 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]; + } + + 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; +}; + +/** + * 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 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; +}; + +/** + * 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; +}; + +/** + * 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] + ']'; +}; + diff --git a/part2/simplex/index - Simplex.html b/part2/simplex/index - Simplex.html new file mode 100644 index 0000000..4b6b05e --- /dev/null +++ b/part2/simplex/index - Simplex.html @@ -0,0 +1,90 @@ + + + + +Vertex Wave + + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/part2/simplex/index.js b/part2/simplex/index.js new file mode 100644 index 0000000..f1e8c2b --- /dev/null +++ b/part2/simplex/index.js @@ -0,0 +1,158 @@ +(function() { + "use strict"; + /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ + /*global getShaderSource,createWebGLContext,createProgram*/ + + var NUM_WIDTH_PTS = 32; + var NUM_HEIGHT_PTS = 32; + + var message = document.getElementById("message"); + var canvas = document.getElementById("canvas"); + var context = createWebGLContext(canvas, message); + if (!context) { + return; + } + + /////////////////////////////////////////////////////////////////////////// + + context.viewport(0, 0, canvas.width, canvas.height); + context.clearColor(1.0, 1.0, 1.0, 1.0); + context.enable(context.GL_DEPTH_TEST); + + var persp = mat4.create(); + mat4.perspective(45.0, 0.5, 0.1, 100.0, persp); + + var eye = [2.0, 1.0, 3.0]; + var center = [0.0, 0.0, 0.0]; + var up = [0.0, 0.0, 1.0]; + var view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + var positionLocation = 0; + var heightLocation = 1; + var u_modelViewPerspectiveLocation; + + var u_time; + + (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"); + u_time = context.getUniformLocation(program,"u_time"); + context.useProgram(program); + })(); + + var heights; + var numberOfIndices; + + (function initializeGrid() { + function uploadMesh(positions, heights, indices) { + // Positions + var positionsName = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, positionsName); + context.bufferData(context.ARRAY_BUFFER, positions, context.STATIC_DRAW); + context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0); + context.enableVertexAttribArray(positionLocation); + + if (heights) + { + // Heights + var heightsName = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, heightsName); + context.bufferData(context.ARRAY_BUFFER, heights.length * heights.BYTES_PER_ELEMENT, context.STREAM_DRAW); + context.vertexAttribPointer(heightLocation, 1, context.FLOAT, false, 0, 0); + context.enableVertexAttribArray(heightLocation); + } + + // Indices + var indicesName = context.createBuffer(); + context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, indicesName); + context.bufferData(context.ELEMENT_ARRAY_BUFFER, indices, context.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(2 * numberOfPositions); + var indices = new Uint16Array(2 * ((NUM_HEIGHT_PTS * (NUM_WIDTH_PTS - 1)) + (NUM_WIDTH_PTS * (NUM_HEIGHT_PTS - 1)))); + + var positionsIndex = 0; + var indicesIndex = 0; + var length; + + for (var j = 0; j < NUM_WIDTH_PTS; ++j) + { + positions[positionsIndex++] = j /(NUM_WIDTH_PTS - 1); + positions[positionsIndex++] = 0.0; + + 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; + } + } + + uploadMesh(positions, heights, indices); + numberOfIndices = indices.length; + })(); + + + var delta = 0.0; + + (function animate(){ + /////////////////////////////////////////////////////////////////////////// + // Update + + var model = mat4.create(); + mat4.identity(model); + mat4.translate(model, [-0.5, -0.5, 0.0]); + var mv = mat4.create(); + mat4.multiply(view, model, mv); + var mvp = mat4.create(); + mat4.multiply(persp, mv, mvp); + + /////////////////////////////////////////////////////////////////////////// + // 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); + + delta += 0.0009; + context.uniform1f(u_time, delta); + window.requestAnimFrame(animate); + })(); + +}()); diff --git a/part2/simplex/noise3D.js b/part2/simplex/noise3D.js new file mode 100644 index 0000000..2d4ffd2 --- /dev/null +++ b/part2/simplex/noise3D.js @@ -0,0 +1,316 @@ +(function(exports) { + "use strict"; + /*global window,vec3*/ + + exports = exports || window; + + function step(edge, x) { + return [ + (x[0] < edge[0]) ? 0.0 : 1.0, + (x[1] < edge[1]) ? 0.0 : 1.0, + (x[2] < edge[2]) ? 0.0 : 1.0 + ]; + } + + function step_vec4(edge, x) { + return [ + (x[0] < edge[0]) ? 0.0 : 1.0, + (x[1] < edge[1]) ? 0.0 : 1.0, + (x[2] < edge[2]) ? 0.0 : 1.0, + (x[3] < edge[3]) ? 0.0 : 1.0 + ]; + } + + function min(x, y) { + return [ + y[0] < x[0] ? y[0] : x[0], + y[1] < x[1] ? y[1] : x[1], + y[2] < x[2] ? y[2] : x[2] + ]; + } + + function max(x, y) { + return [ + y[0] > x[0] ? y[0] : x[0], + y[1] > x[1] ? y[1] : x[1], + y[2] > x[2] ? y[2] : x[2] + ]; + } + + function max_vec4(x, y) { + return [ + y[0] > x[0] ? y[0] : x[0], + y[1] > x[1] ? y[1] : x[1], + y[2] > x[2] ? y[2] : x[2], + y[3] > x[3] ? y[3] : x[3] + ]; + } + + function vec4_dot(left, right) { + return left[0] * right[0] + + left[1] * right[1] + + left[2] * right[2] + + left[3] * right[3]; + } + + // + // Description : Array and textureless GLSL 2D/3D/4D simplex + // noise functions. + // Author : Ian McEwan, Ashima Arts. + // Maintainer : ijm + // Lastmod : 20110822 (ijm) + // License : Copyright (C) 2011 Ashima Arts. All rights reserved. + // Distributed under the MIT License. See LICENSE file. + // https://github.com/ashima/webgl-noise + // + function mod289_vec3(x) { + var temp = (1.0 / 289.0); + return [ + x[0] - Math.floor(x[0] * temp) * 289.0, + x[1] - Math.floor(x[1] * temp) * 289.0, + x[2] - Math.floor(x[2] * temp) * 289.0 + ]; + } + + function mod289_vec4(x) { + var temp = (1.0 / 289.0); + return [ + x[0] - Math.floor(x[0] * temp) * 289.0, + x[1] - Math.floor(x[1] * temp) * 289.0, + x[2] - Math.floor(x[2] * temp) * 289.0, + x[3] - Math.floor(x[3] * temp) * 289.0 + ]; + } + + function permute_vec4(x) { + return mod289_vec4([ + ((x[0]*34.0)+1.0)*x[0], + ((x[1]*34.0)+1.0)*x[1], + ((x[2]*34.0)+1.0)*x[2], + ((x[3]*34.0)+1.0)*x[3] + ]); + } + + function taylorInvSqrt_vec4(r) { + return [ + 1.79284291400159 - 0.85373472095314 * r[0], + 1.79284291400159 - 0.85373472095314 * r[1], + 1.79284291400159 - 0.85373472095314 * r[2], + 1.79284291400159 - 0.85373472095314 * r[3] + ]; + } + + exports.snoise = function(v) { + // const vec2 C = vec2(1.0f/6.0f, 1.0f/3.0f) ; + // const vec4 D = vec4(0.0f, 0.5f, 1.0f, 2.0f); + var C = [1.0/6.0, 1.0/3.0]; + var D = [0.0, 0.5, 1.0, 2.0]; + + // vec3 i = floor(v + dot(v, vec3(C.y, C.y, C.y)) ); + // vec3 x0 = v - i + dot(i, vec3(C.x, C.x, C.x) ); + var temp0 = vec3.create(); + var temp3 = vec3.dot(v, [C[1], C[1], C[1]]); + vec3.add(v, [temp3, temp3, temp3], temp0); + var i = [Math.floor(temp0[0]), Math.floor(temp0[1]), Math.floor(temp0[2])]; + var temp1 = vec3.create(); + vec3.subtract(v, i, temp1); + var temp2 = vec3.dot(i, [C[0], C[0], C[0]]); + var x0 = vec3.create(); + vec3.add(temp1, [temp2, temp2, temp2], x0); + + // vec3 g = step(vec3(x0.y, x0.z, x0.x), vec3(x0.x, x0.y, x0.z)); + // vec3 l = 1.0f - g; + // vec3 i1 = min( vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y) ); + // vec3 i2 = max( vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y) ); + var g = step([x0[1], x0[2], x0[0]], [x0[0], x0[1], x0[2]]); + var l = [1.0 - g[0], 1.0 - g[1], 1.0 - g[2]]; + var i1 = min([g[0], g[1], g[2]], [l[2], l[0], l[1]]); + var i2 = max([g[0], g[1], g[2]], [l[2], l[0], l[1]]); + + // vec3 x1 = x0 - i1 + vec3(C.x, C.x, C.x); + // vec3 x2 = x0 - i2 + vec3(C.y, C.y, C.y); // 2.0*C.x = 1/3 = C.y + // vec3 x3 = x0 - vec3(D.y, D.y, D.y); // -1.0+3.0*C.x = -0.5 = -D.y + var temp4 = vec3.create(); + vec3.subtract(x0, i1, temp4); + var x1 = vec3.create(); + vec3.add(temp4, [C[0], C[0], C[0]], x1); + var temp5 = vec3.create(); + vec3.subtract(x0, i2, temp5); + var x2 = vec3.create(); + vec3.add(temp5, [C[1], C[1], C[1]], x2); + var x3 = vec3.create(); + vec3.subtract(x0, [D[1], D[1], D[1]], x3); + + // i = mod289(i); + // vec4 p = permute( permute( permute( + // i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + // + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + // + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); + i = mod289_vec3(i); + var p = permute_vec4([i[2] + 0.0, i[2] + i1[2], i[2] + i2[2], i[2] + 1.0]); + p[0] += i[1] + 0.0; + p[1] += i[1] + i1[1]; + p[2] += i[1] + i2[1]; + p[3] += i[1] + 1.0; + p = permute_vec4(p); + p[0] += i[0] + 0.0; + p[1] += i[0] + i1[0]; + p[2] += i[0] + i2[0]; + p[3] += i[0] + 1.0; + p = permute_vec4(p); + + // float n_ = 0.142857142857f; // 1.0/7.0 + // vec3 ns = n_ * vec3(D.w, D.y, D.z) - vec3(D.x, D.z, D.x); +// var n_ = 0.142857142857; // 1.0/7.0 +// var ns = [ +// n_ * D[3] - D[0], +// n_ * D[1] - D[2], +// n_ * D[2] - D[0] +// ]; + var ns = [ + 0.28571430, + -0.92857140, + 0.14285715 + ]; + + // vec4 j = p - 49.0f * floor(p * ns.z * ns.z); // mod(p,7*7) + var j = [ + p[0] - 49.0 * Math.floor(p[0] * ns[2] * ns[2]), + p[1] - 49.0 * Math.floor(p[1] * ns[2] * ns[2]), + p[2] - 49.0 * Math.floor(p[2] * ns[2] * ns[2]), + p[3] - 49.0 * Math.floor(p[3] * ns[2] * ns[2]) + ]; + + // vec4 x_ = floor(j * ns.z); + // vec4 y_ = floor(j - 7.0f * x_ ); // mod(j,N) + var x_ = [ + Math.floor(j[0] * ns[2]), + Math.floor(j[1] * ns[2]), + Math.floor(j[2] * ns[2]), + Math.floor(j[3] * ns[2]) + ]; + var y_ = [ + Math.floor(j[0] - 7.0 * x_[0] ), + Math.floor(j[1] - 7.0 * x_[1] ), + Math.floor(j[2] - 7.0 * x_[2] ), + Math.floor(j[3] - 7.0 * x_[3] ) + ]; + + // vec4 x = x_ *ns.x + vec4(ns.y, ns.y, ns.y, ns.y); + // vec4 y = y_ *ns.x + vec4(ns.y, ns.y, ns.y, ns.y); + // vec4 h = 1.0f - abs(x) - abs(y); + var x = [ + x_[0] *ns[0] + ns[1], + x_[1] *ns[0] + ns[1], + x_[2] *ns[0] + ns[1], + x_[3] *ns[0] + ns[1] + ]; + var y = [ + y_[0] *ns[0] + ns[1], + y_[1] *ns[0] + ns[1], + y_[2] *ns[0] + ns[1], + y_[3] *ns[0] + ns[1] + ]; + var h = [ + 1.0 - Math.abs(x[0]) - Math.abs(y[0]), + 1.0 - Math.abs(x[1]) - Math.abs(y[1]), + 1.0 - Math.abs(x[2]) - Math.abs(y[2]), + 1.0 - Math.abs(x[3]) - Math.abs(y[3]) + ]; + + // vec4 b0 = vec4( vec2(x.x, x.y), vec2(y.x, y.y) ); + // vec4 b1 = vec4( vec2(x.z, x.w), vec2(y.z, y.w) ); + var b0 = [x[0], x[1], y[0], y[1]]; + var b1 = [x[2], x[3], y[2], y[3]]; + + // vec4 s0 = floor(b0)*2.0f + 1.0f; + // vec4 s1 = floor(b1)*2.0f + 1.0f; + // vec4 sh = -step(h, vec4(0.0)); + + var s0 = [ + Math.floor(b0[0])*2.0 + 1.0, + Math.floor(b0[1])*2.0 + 1.0, + Math.floor(b0[2])*2.0 + 1.0, + Math.floor(b0[3])*2.0 + 1.0 + ]; + var s1 = [ + Math.floor(b1[0])*2.0 + 1.0, + Math.floor(b1[1])*2.0 + 1.0, + Math.floor(b1[2])*2.0 + 1.0, + Math.floor(b1[3])*2.0 + 1.0 + ]; + var sh = step_vec4(h, [0.0, 0.0, 0.0, 0.0]); + sh[0] = -sh[0]; + sh[1] = -sh[1]; + sh[2] = -sh[2]; + sh[3] = -sh[3]; + + // vec4 a0 = vec4(b0.x, b0.z, b0.y, b0.w) + vec4(s0.x, s0.z, s0.y, s0.w) * vec4(sh.x, sh.x, sh.y, sh.y) ; + // vec4 a1 = vec4(b1.x, b1.z, b1.y, b1.w) + vec4(s1.x, s1.z, s1.y, s1.w) * vec4(sh.z, sh.z, sh.w, sh.w) ; + var a0 = [ + b0[0] + s0[0] * sh[0], + b0[2] + s0[2] * sh[0], + b0[1] + s0[1] * sh[1], + b0[3] + s0[3] * sh[1] + ]; + var a1 = [ + b1[0] + s1[0] * sh[2], + b1[2] + s1[2] * sh[2], + b1[1] + s1[1] * sh[3], + b1[3] + s1[3] * sh[3] + ]; + + // vec3 p0 = vec3(a0.x, a0.y, h.x); + // vec3 p1 = vec3(a0.z, a0.w, h.y); + // vec3 p2 = vec3(a1.x, a1.y, h.z); + // vec3 p3 = vec3(a1.z, a1.w, h.w); + var p0 = [a0[0], a0[1], h[0]]; + var p1 = [a0[2], a0[3], h[1]]; + var p2 = [a1[0], a1[1], h[2]]; + var p3 = [a1[2], a1[3], h[3]]; + + // vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + // p0 *= norm.x; + // p1 *= norm.y; + // p2 *= norm.z; + // p3 *= norm.w; + var norm = taylorInvSqrt_vec4([vec3.dot(p0,p0), vec3.dot(p1,p1), vec3.dot(p2, p2), vec3.dot(p3,p3)]); + p0 = [p0[0]*norm[0], p0[1]*norm[0], p0[2]*norm[0]]; + p1 = [p1[0]*norm[1], p1[1]*norm[1], p1[2]*norm[1]]; + p2 = [p2[0]*norm[2], p2[1]*norm[2], p2[2]*norm[2]]; + p3 = [p3[0]*norm[3], p3[1]*norm[3], p3[2]*norm[3]]; + + // vec4 m = max(0.6f - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + // m = m * m; + var m = max_vec4([ + 0.6 - vec3.dot(x0,x0), + 0.6 - vec3.dot(x1,x1), + 0.6 - vec3.dot(x2,x2), + 0.6 - vec3.dot(x3,x3) + ], [ + 0.0, + 0.0, + 0.0, + 0.0 + ]); + m[0] *= m[0]; + m[1] *= m[1]; + m[2] *= m[2]; + m[3] *= m[3]; + + // return 42.0f * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), + // dot(p2,x2), dot(p3,x3) ) ); + return 42.0 * vec4_dot([ + m[0] * m[0], + m[1] * m[1], + m[2] * m[2], + m[3] * m[3] + ], [ + vec3.dot(p0,x0), + vec3.dot(p1,x1), + vec3.dot(p2,x2), + vec3.dot(p3,x3) + ]); + }; + +}()); diff --git a/part2/simplex/simplex.vert b/part2/simplex/simplex.vert new file mode 100644 index 0000000..e1ff07a --- /dev/null +++ b/part2/simplex/simplex.vert @@ -0,0 +1,39 @@ +vec3 permute(vec3 x) { + x = ((x*34.0)+1.0)*x; + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +float simplexNoise(vec2 v) + { + const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); + + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + + i = i - floor(i * (1.0 / 289.0)) * 289.0; + + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + + i.x + vec3(0.0, i1.x, 1.0 )); + + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + + m *= inversesqrt( a0*a0 + h*h ); + + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} \ No newline at end of file diff --git a/part2/simplex/webGLUtility.js b/part2/simplex/webGLUtility.js new file mode 100644 index 0000000..c9de391 --- /dev/null +++ b/part2/simplex/webGLUtility.js @@ -0,0 +1,97 @@ +(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 context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); + + 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"; + } + 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"; + } + context.deleteProgram(program); + return; + } + + context.linkProgram(program); + if (!context.getProgramParameter(program, context.LINK_STATUS)) { + if (message) { + message.innerText += context.getProgramInfoLog(program) + "\n"; + } + context.deleteProgram(program); + return; + } + + return program; + }; + +}());