//****************************************************************************//
//******************* More GLSL Shaders - March 11th, 2019 ******************//
//**************************************************************************//

- *refuses to use lavatory before class, urea build-up intensifies*
----------------------------------------------------------------------

- Okay, we were just starting to get into shaders last week, and today we're continuing our tour through this magical land!
    - Remember: the basic idea is that we get the vertices of the polygon as input, then put it through our vertex shader, then the rasterizer, and then our fragment shader

- In GLSL, there's a nice piece of syntactic sugar called "Swizzling," which lets us address the components of a vector using dot notation
    - Specifically, we can access it using the syntax ".xyzw," or ".rgba," or .stpq
    - As an example, let's say we have the following vectors:

            vec4 v1 = vec4(4.0, -2.0, 5.0, 3.0);
        
        - From there, we can use swizzling to access these parameters, either in groups or alone:

            vec2 v2 = v1.yz;        // v2 = (-2.0, 5.0)
            float scalar = v1.w;    // scalar = 3.0

        - So, that's a nice convenience to know about

- One thing we should emphasize is that we can use vertex shaders to actually modify the 2D projection of the 3D vertices we get as input - let's try it!
    - Let's suppose we have a bunch of triangles in a triforce-like shape passed as input, on a 1024x1024 screen w/ coordinates (-512, +512)
    - We'll then write the following "twisting" vertex program:

            const twistAmt = ?;

            void main() {
                vec4 pos = gl_ModelViewProjectMatrix*gl_vertex;
                vec4 center = vec4(0, 0, 0, 0);
                vec2 diff = vec2(pos.x - center.x, pos.y - center.y);

                // rotate the point counter-clockwise, based on its distance from the center of the screen
                float angle = twistAmt*length(diff);
                float c = cos(angle);
                float s = sin(angle);
                gl_Position.x = c*pos.x - s*pos.y;
                gl_Position.y = s*pos.x + c*pos.y;

                gl_Position.z = pos.z;
                gl_Position.w = pos.w;

                gl_FrontColor = gl_BackColor = gl_Color;
            }

        - This'll turn our triangle into a shuriken-like, wavy shape - cool!
    - Why don't we just draw the geometry like this in the first place, though? Why bother with this vertex shader as a middleman?
        - Well, for one thing, it lets us handle these vertex manipulations on the GPU, which is typically much faster than dealing with it on the CPU
        - For that reason, this is commonly done for parts of the geometry that need to be interactively modified for appearance's sake, e.g. letting the user draw waves on a surface

- Let's also see how we can use a vertex program and a fragment program together to draw a texture map on an object:

        //===========================================
        // Vertex Shader
        #define PROCESSING_TEXTURE_SHADER

        varying vec2 texture_coord;
        void main() {
            gl_Position = gl_ModelViewProjectMatrix * gl_Vertex;
            texture_coord = vec2(gl_MultiTexCoord0); //hands first texture coordinate to the rasterizer; since it's varying, it'll be interpolated correctly across the surface
        }

        //=============================================
        // Fragment Shader

        // how does this variable get passed between the vertex/fragment shaders? Occasionally, some "glue code" is needed to handle that, but we can worry about that later
        varying vec2 texture_coord;
        uniform sampler2D my_texture;
        void main() {
            gl_FragColor = texture2D(my_texture, texture_coord); //gets the colors of the texture at the given coordinates, handling bilinear/trilinear interpolation for us
        }

    - So, the vertex shader sets up the texture coordinates, which are then interpolated across the polygon by the rasterizer, and the fragment shader then colors each pixel appropriately - cool!
- That's the simplest POSSIBLE texture program we could make, though; what if we wanted to do something a tad more sophisticated? Let's try to make a program that combines two different, shifted versions of a texture
    - Admittedly this isn't a particularly common use case, but roll with me here

        //=============================================
        // Vertex Shader

        varying vec2 left_coord;
        varying vec2 right_coord;
        void main() {
            gl_Position = gl_ModelViewProjectMatrix * gl_Vertex;
            vec2 texture_coord = vec2(gl_MultiTexCoord0);
            left_coord = texture_coord + vec2(-0.2, 0.0);
            right_coord = texture_coord + vec2(0.2, 0.0);
        }

        //=============================================
        // Fragment Shader
        #define PROCESSING_TEXTURE_SHADER

        varying vec2 left_coord;
        varying vec2 right_coord;
        uniform sampler2D my_texture;
        void main() {
            vec4 left_color = texture2D(my_texture, left_coord);
            vec4 right_color = texture2D(my_texture, right_coord);
            gl_FragColor = 0.5*(left_color + right_color);
        }

    - So, that's generally how you combine vertex and fragment shaders together - the vertex shader 
        - In this particular example, we COULD have done this whole thing just using the fragment shader, but the main point here was to show how you can use these 2 shaders together

- Alright, we've seen we can program the vertex and fragment shaders, the vertex shader does its thing, the rasterizer does its interpolatory magic, and then the fragment shader handles the last coloring step
    - You'll be doing stuff VERY similar to this in project 4, right after spring break

- That brings us to an interesting point in the class: the break between
    - Up until now, we've been assuming we have this interesting geometry already there on the screen, and are then trying to render them
    - But where does this geometry actually come from? How DO we make a polygonal cone, or sphere, or spaceship? That's what we're going to start talking about now!

- First up, a bit of terminology (CHECK THIS):
    - A POLYHEDRON is a surface composed of polygons
        - What's a polygon? It's any shape made up of vertices and straight-line edges
            - We'll often use many polygons in a polyhedron to approximate "smooth" surfaces
    - A MANIFOLD is a part of the surface that looks like a (possibly bent) plane, i.e. a point could travel smoothly to all points in the manifold
        - Think of a pizza; a pizza without any slices missing would be a manifold, since an ant could start in the center and walk in a spiral without issue!
            - If the pizza had 2 slices missing, though, so that one of the slices only touched the rest of the pizza at its tip, it's not a manifold; our ant would "fall off" the pizza now if it tried to reach the "island" piece
                - Similarly, 2 cones that are only touching at their points wouldn't count as a manifold
                - Many algorithms will fail when dealing with non-manifold things
        - A "manifold edge" is one edge that's entirely inside the manifold, i.e. it's part of the plane (e.g. the diagonal of a square)
            - A BOUNDARY EDGE is an edge on the manifold (e.g. the side of a flat square); it's not as nice as a manifold edge, but most algorithms can still deal with them
            - A NON-MANIFOLD EDGE is one that's "sticking out" from the plane of the manifold, like 2 intersecting planes; these are nasty to deal with
        - To make this a little more concrete, let's think of the example of a regular, 6-sided, perfectly societally-adjusted cube:
            - All the edges are manifold edges
            - All the vertices are manifold - great!
                - If we removed one of the faces of the cube, though, then those 4 edges where the face of the polygon are will now be boundary edges!

- How do we create these polyhedra? How should we represent them? How do we make them as manifold as possible? We'll keep asking all of these questions on Wednesday!