/// <summary>
        /// Constructor to create a new ShaderBuilder instance.
        /// </summary>
        /// <param name="type">The shader type to generate the source for</param>
        /// <param name="twoDimensional">If true, some helper code for two dimensional shaders will be included</param>
        /// <param name="parent">Previous shader in the pipeline (if any)</param>
        public ShaderBuilder(ShaderType type, bool twoDimensional, ShaderBuilder parent = null)
        {
            Type = type;
            _twoDimensional = twoDimensional;

            // Prepare an empty list of OpenGL extensions
            _extensions = new List<String>();

            // Set up variable lists
            _uniforms = new List<ShaderVariable>();
            _attribs = new List<ShaderVariable>();
            _varyings = new List<ShaderVariable>();

            if (type == ShaderType.VertexShader && twoDimensional) {
                AddUniform(ShaderVarType.Vec2, "screen_resolution");
            }

            // If the builder is given a parent, copy any outputs
            // from that shader as inputs for this one
            if (parent != null) {
                foreach (var vary in parent._varyings) {
                    AddVarying(vary.Type, vary.Identifier);
                }
            }

            Logic = "";

            // Default fragment colour output variable identifier
            FragOutIdentifier = "out_colour";
        }
        public DepthClipShader()
        {
            ShaderBuilder vert = new ShaderBuilder(ShaderType.VertexShader, false);
            vert.AddUniform(ShaderVarType.Mat4, "vp_matrix");
            vert.AddUniform(ShaderVarType.Mat4, "transform");
            vert.AddAttribute(ShaderVarType.Vec3, "in_vertex");
            vert.Logic = @"
                void main(void)
                {
                    gl_Position = vp_matrix * (transform * vec4(in_vertex, 1.0));
                }
            ";

            ShaderBuilder frag = new ShaderBuilder(ShaderType.FragmentShader, false, vert);
            frag.Logic = @"
                void main(void)
                {
                    out_colour = vec4(0.0, 0.0, 0.0, 0.0);
                }
            ";

            VertexSource = vert.Generate(GL3);
            FragmentSource = frag.Generate(GL3);

            BeginMode = BeginMode.Triangles;

            Create();
        }
        protected override void OnAddShaderLogic(ShaderBuilder frag)
        {
            frag.Logic = @"
                float diff(float a, float b)
                {
                    float d = b - a;
                    if (d < -0.5) return d + 1.0;
                    if (d >= 0.5) return d - 1.0;
                    return d;
                }

                void main(void)
                {
                    vec2 diff = vec2(diff(depression.x, tex_pos.x), diff(depression.y, tex_pos.y));
                    float dist = length(diff) * 128.0;
                    if (dist < 1.0) {
                        float scale = min(1.0, (1.0 - dist) * depression.z);
                        float cur = texture2D(velocitymap, tex_pos).a;
                        float mag = max(cur - scale, 0.5 - scale);
                        out_colour = vec4(mag, mag, mag, mag);
                    } else {
                        discard;
                    }
                }
            ";
        }
        protected WaterEffectShader()
        {
            ShaderBuilder vert = new ShaderBuilder(ShaderType.VertexShader, true);
            vert.AddAttribute(ShaderVarType.Vec2, "in_position");
            vert.AddVarying(ShaderVarType.Vec2, "tex_pos");
            vert.AddUniform(ShaderVarType.Int, "resolution");
            vert.Logic = @"
                void main( void )
                {
                    tex_pos = in_position.xy * vec2(1.0, -1.0) / resolution;
                    gl_Position = in_position;
                }
            ";

            ShaderBuilder frag = new ShaderBuilder(ShaderType.FragmentShader, true, vert);
            OnAddShaderVariables(frag);
            OnAddShaderLogic(frag);

            VertexSource = vert.Generate(GL3);
            FragmentSource = frag.Generate(GL3);

            BeginMode = BeginMode.Quads;

            Create();
            SetScreenSize(Water.Resolution, Water.Resolution);
            _bounds = new float[] {
                0f, 0f,
                Water.Resolution, 0f,
                Water.Resolution, Water.Resolution,
                0f, Water.Resolution
            };
        }
        public SkyShader()
        {
            ShaderBuilder vert = new ShaderBuilder(ShaderType.VertexShader, false);
            vert.AddUniform(ShaderVarType.Mat4, "vp_matrix");
            vert.AddAttribute(ShaderVarType.Vec3, "in_vertex");
            vert.AddVarying(ShaderVarType.Vec3, "var_texcoord");
            vert.Logic = @"
                void main(void)
                {
                    vec4 pos = vp_matrix * vec4(in_vertex, 0.0);
                    gl_Position = pos.xyww;
                    var_texcoord = in_vertex;
                }
            ";

            ShaderBuilder frag = new ShaderBuilder(ShaderType.FragmentShader, false, vert);
            frag.AddUniform(ShaderVarType.SamplerCube, "skybox");
            frag.Logic = @"
                void main(void)
                {
                    out_colour = textureCube(skybox, var_texcoord);
                    out_colour += (vec4(0.0, 0.0, 0.0, 1.0) - out_colour) * max(0.0, -var_texcoord.y);
                }
            ";

            VertexSource = vert.Generate(GL3);
            FragmentSource = frag.Generate(GL3);

            BeginMode = BeginMode.Quads;

            Create();
        }
        protected override void OnAddShaderLogic(ShaderBuilder frag)
        {
            frag.Logic = @"
                void main(void)
                {
                    const ivec2 offsets[] = ivec2[8] (
                        ivec2(-1, -1), ivec2( 0, -1), ivec2( 1, -1),
                        ivec2(-1,  0),                ivec2( 1,  0),
                        ivec2(-1,  1), ivec2( 0,  1), ivec2( 1,  1)
                    );

                    const float weights[] = float[8] (
                        0.707, 1.0, 0.707, 1.0, 1.0, 0.707, 1.0, 0.707
                    );

                    float cur = texture2D(heightmap, tex_pos).a;
                    float avg = (0.5
                        + textureOffset(heightmap, tex_pos, offsets[0]).a * weights[0]
                        + textureOffset(heightmap, tex_pos, offsets[1]).a * weights[1]
                        + textureOffset(heightmap, tex_pos, offsets[2]).a * weights[2]
                        + textureOffset(heightmap, tex_pos, offsets[3]).a * weights[3]
                        + textureOffset(heightmap, tex_pos, offsets[4]).a * weights[4]
                        + textureOffset(heightmap, tex_pos, offsets[5]).a * weights[5]
                        + textureOffset(heightmap, tex_pos, offsets[6]).a * weights[6]
                        + textureOffset(heightmap, tex_pos, offsets[7]).a * weights[7]) / 7.828;

                    float new = min(1.0, max(0.0, texture2D(velocitymap, tex_pos).a + (avg - cur) * 1.0)) * 0.996;

                    out_colour = vec4(new, new, new, new);
                }
            ";
        }
 protected override void OnAddShaderLogic(ShaderBuilder frag)
 {
     frag.Logic = @"
         void main(void)
         {
             float cur = texture2D(heightmap, tex_pos).a;
             float vel = (texture2D(velocitymap, tex_pos).a - 0.5) / 8.0;
             float new = cur + vel;
             out_colour = vec4(new, new, new, new);
         }
     ";
 }
        protected override void OnAddShaderLogic(ShaderBuilder frag)
        {
            frag.Logic = @"
                void main(void)
                {
                    const ivec2 offsets[] = ivec2[8] (
                        ivec2(-1, -1), ivec2( 0, -1), ivec2( 1, -1),
                        ivec2(-1,  0),                ivec2( 1,  0),
                        ivec2(-1,  1), ivec2( 0,  1), ivec2( 1,  1)
                    );

                    const float weights[] = float[8] (
                        0.707, 1.0, 0.707, 1.0, 1.0, 0.707, 1.0, 0.707
                    );

                    float cur = texture2D(spraymap, tex_pos).a;
                    float mx =
                        max(
                            max(
                                max(
                                    textureOffset(spraymap, tex_pos, offsets[0]).a * weights[0],
                                    textureOffset(spraymap, tex_pos, offsets[1]).a * weights[1]
                                ),
                                max(
                                    textureOffset(spraymap, tex_pos, offsets[2]).a * weights[2],
                                    textureOffset(spraymap, tex_pos, offsets[3]).a * weights[3]
                                )
                            ),
                            max(
                                max(
                                    textureOffset(spraymap, tex_pos, offsets[4]).a * weights[4],
                                    textureOffset(spraymap, tex_pos, offsets[5]).a * weights[5]
                                ),
                                max(
                                    textureOffset(spraymap, tex_pos, offsets[6]).a * weights[6],
                                    textureOffset(spraymap, tex_pos, offsets[7]).a * weights[7]
                                )
                            )
                        );

                    float vel = pow(abs(texture2D(velocitymap, tex_pos).a - 0.5), 2.5);
                    float new = min(1.0, cur + max((mx - cur) * 0.75, 0.0) + vel) * 0.99;

                    out_colour = vec4(new, new, new, new);
                }
            ";
        }
        public ModelShader()
        {
            ShaderBuilder vert = new ShaderBuilder(ShaderType.VertexShader, false);
            vert.AddUniform(ShaderVarType.Mat4, "vp_matrix");
            vert.AddUniform(ShaderVarType.Mat4, "transform");
            vert.AddAttribute(ShaderVarType.Vec3, "in_vertex");
            vert.AddAttribute(ShaderVarType.Vec2, "in_textuv");
            vert.AddAttribute(ShaderVarType.Vec3, "in_normal");
            vert.AddVarying(ShaderVarType.Vec3, "var_normal");
            vert.AddVarying(ShaderVarType.Vec2, "var_textuv");
            vert.Logic = @"
                void main(void)
                {
                    var_normal = (transform * vec4(in_normal, 0.0)).xyz;
                    var_textuv = in_textuv;
                    gl_Position = vp_matrix * (transform * vec4(in_vertex, 1.0));
                }
            ";

            ShaderBuilder frag = new ShaderBuilder(ShaderType.FragmentShader, false, vert);
            frag.AddUniform(ShaderVarType.Vec4, "colour");
            frag.AddUniform(ShaderVarType.Sampler2D, "tex");
            frag.AddUniform(ShaderVarType.Vec3, "view_vector");
            frag.AddUniform(ShaderVarType.Vec3, "light_vector");
            frag.AddUniform(ShaderVarType.Float, "shinyness");
            frag.Logic = @"
                void main(void)
                {
                    out_colour = vec4(colour.rgb * texture2D(tex, var_textuv + vec2(0.5, 0.5)).rgb * (dot(-light_vector, var_normal) * 0.5 + 0.5), colour.a);
                    if (shinyness > 0.0) {
                        out_colour = vec4(out_colour.rgb + (vec3(1.0, 1.0, 1.0) - out_colour.rgb) * 0.5 * pow(max(0.0, dot(reflect(-light_vector, var_normal), view_vector)), shinyness), out_colour.a);
                    }
                }
            ";

            VertexSource = vert.Generate(GL3);
            FragmentSource = frag.Generate(GL3);

            BeginMode = BeginMode.Triangles;

            Create();
        }
        protected override void OnAddShaderVariables(ShaderBuilder frag)
        {
            base.OnAddShaderVariables(frag);

            frag.AddUniform(ShaderVarType.Vec3, "depression");
        }
        public WaterShader()
        {
            ShaderBuilder vert = new ShaderBuilder(ShaderType.VertexShader, false);
            vert.AddUniform(ShaderVarType.Mat4, "vp_matrix");
            vert.AddUniform(ShaderVarType.Vec3, "view_origin");
            vert.AddUniform(ShaderVarType.Vec3, "view_vector");
            vert.AddUniform(ShaderVarType.Sampler2D, "heightmap");
            vert.AddAttribute(ShaderVarType.Vec2, "in_vertex");
            vert.AddVarying(ShaderVarType.Float, "var_height");
            vert.AddVarying(ShaderVarType.Float, "var_scale");
            vert.AddVarying(ShaderVarType.Vec3, "var_position");
            vert.AddVarying(ShaderVarType.Vec2, "var_texpos");
            vert.Logic = @"
                void main(void)
                {
                    const float size = 512.0;

                    float rot = -atan(view_vector.z, view_vector.x);
                    mat2 rmat = mat2(cos(rot), -sin(rot), sin(rot), cos(rot));

                    var_scale = max(0.0, 2.0 - max(1.0, length(in_vertex * size) / 48.0));

                    vec2 offset = rmat * (in_vertex * size) + view_origin.xz;
                    var_texpos = offset / 128.0 + vec2(0.5, 0.5);
                    var_height = texture2D(heightmap, var_texpos).a * 2.0 * var_scale;

                    vec4 pos = vec4(offset.x, var_height - 1.0, offset.y, 1.0);

                    var_position = pos.xyz;
                    gl_Position = vp_matrix * pos;
                }
            ";

            ShaderBuilder frag = new ShaderBuilder(ShaderType.FragmentShader, false, vert);
            frag.AddUniform(ShaderVarType.Sampler2D, "heightmap");
            frag.AddUniform(ShaderVarType.Sampler2D, "ripplemap");
            frag.AddUniform(ShaderVarType.Sampler2D, "spraymap");
            frag.AddUniform(ShaderVarType.SamplerCube, "skybox");
            frag.AddUniform(ShaderVarType.Vec4, "colour");
            frag.AddUniform(ShaderVarType.Vec3, "view_origin");
            frag.AddUniform(ShaderVarType.Vec3, "light_vector");
            frag.Logic = @"
                void main(void)
                {
                    float l = textureOffset(heightmap, var_texpos, ivec2(-1,  0 )).a;
                    float t = textureOffset(heightmap, var_texpos, ivec2( 0, -1 )).a;
                    float r = textureOffset(heightmap, var_texpos, ivec2( 1,  0 )).a;
                    float b = textureOffset(heightmap, var_texpos, ivec2( 0,  1 )).a;

                    vec3 horz = normalize(vec3(0.0, (r - l) * 2.0 * var_scale, 1.0));
                    vec3 vert = normalize(vec3(1.0, (b - t) * 2.0 * var_scale, 0.0));
                    vec3 normal = cross(horz, vert);

                    vec3 cam_dir = normalize(var_position - view_origin);

                    vec3 reflected = normalize(reflect(cam_dir, normal));

                    out_colour = vec4(colour.rgb * max(0.0, dot(light_vector, normal)), colour.a);
                    out_colour += vec4((textureCube(skybox, reflected).rgb - out_colour.rgb) * 0.5, 0.0);

                    if (var_scale > 0.0) {
                        float ripple = texture2D(ripplemap, (var_texpos * 8.0) + normal.xz * 0.125).a;
                        float spray = texture2D(spraymap, var_texpos).a;
                        if (ripple * pow(spray, 2.0) > 0.75) {
                            out_colour += spray * 0.75 * (vec4(1.0, 1.0, 1.0, 1.0) - out_colour) * var_scale;
                        }
                    }
                }
            ";

            VertexSource = vert.Generate(GL3);
            FragmentSource = frag.Generate(GL3);

            BeginMode = BeginMode.Quads;

            Create();
        }
 protected virtual void OnAddShaderVariables(ShaderBuilder frag)
 {
     frag.AddUniform(ShaderVarType.Sampler2D, "heightmap");
     frag.AddUniform(ShaderVarType.Sampler2D, "velocitymap");
     frag.AddUniform(ShaderVarType.Sampler2D, "spraymap");
 }
 protected virtual void OnAddShaderLogic(ShaderBuilder frag)
 {
     frag.Logic = "void main(void) { discard; }";
 }