Copyright 2008-2014 Matus Chochlik. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <cmath>
namespace oglplus {
class CubeExample : public Example
{
private:
    
    shapes::Cube make_cube;
    
    shapes::DrawingInstructions cube_instr;
    
    
    Context gl;
    
    {
        vs.Source(
            "#version 330\n"
            "uniform mat4 ProjectionMatrix, CameraMatrix, ModelMatrix;"
            "in vec4 Position;"
            "in vec3 Normal;"
            "in vec3 Tangent;"
            "in vec2 TexCoord;"
            "out vec3 vertLight;"
            "out vec2 vertTexCoord;"
            "out mat3 NormalMatrix;"
            "uniform vec3 LightPos;"
            "void main(void)"
            "{"
            "   vec3 fragNormal = mat3(ModelMatrix) * Normal;"
            "   vec3 fragTangent = mat3(ModelMatrix) * Tangent;"
            "   NormalMatrix[0] = fragTangent;"
            "   NormalMatrix[1] = cross(fragNormal, fragTangent);"
            "   NormalMatrix[2] = fragNormal;"
            "   gl_Position = ModelMatrix * Position;"
            "   vertLight = LightPos - gl_Position.xyz;"
            "   vertTexCoord = TexCoord;"
            "   gl_Position = ProjectionMatrix * CameraMatrix * gl_Position;"
            "}"
        );
        vs.Compile();
        fs.Source(
            "#version 330\n"
            "uniform sampler2D ColorTex, NormalTex;"
            "in mat3 NormalMatrix;"
            "in vec3 vertLight;"
            "in vec2 vertTexCoord;"
            "out vec4 fragColor;"
            "void main(void)"
            "{"
            "   float l = dot(vertLight, vertLight);"
            "   vec3 n = texture(NormalTex, vertTexCoord).xyz;"
            "   vec3 finalNormal = NormalMatrix * n;"
            "   float d = (l > 0.0) ? dot("
            "       normalize(vertLight), "
            "       finalNormal"
            "   ) / l : 0.0;"
            "   float i = 0.2 + 4.5*max(d, 0.0);"
            "   vec4 t  = texture(ColorTex, vertTexCoord);"
            "   fragColor = vec4(t.rgb*i, 1.0);"
            "}"
        );
        fs.Compile();
        prog.AttachShader(vs);
        prog.AttachShader(fs);
        prog.Link();
        prog.Use();
        return prog;
    }
    
    Uniform<Mat4f> projection_matrix, camera_matrix, model_matrix;
    Uniform<Vec3f> light_pos;
    
    
    Buffer verts, normals, tangents, texcoords;
 
    
public:
    CubeExample(void)
     : cube_instr(make_cube.Instructions())
     , cube_indices(make_cube.Indices())
     , prog(make_prog())
     , projection_matrix(prog, "ProjectionMatrix")
     , camera_matrix(prog, "CameraMatrix")
     , model_matrix(prog, "ModelMatrix")
     , light_pos(prog, "LightPos")
    {
        gl.Bind(cube);
        gl.Bind(Buffer::Target::Array, verts);
        {
            std::vector<GLfloat> data;
            GLuint n_per_vertex = make_cube.Positions(data);
            gl.Current(Buffer::Target::Array).Data(data);
            VertexArrayAttrib attr(prog, "Position");
            attr.Setup<GLfloat>(n_per_vertex);
            attr.Enable();
        }
        gl.Bind(Buffer::Target::Array, normals);
        {
            std::vector<GLfloat> data;
            GLuint n_per_vertex = make_cube.Normals(data);
            gl.Current(Buffer::Target::Array).Data(data);
            VertexArrayAttrib attr(prog, "Normal");
            attr.Setup<GLfloat>(n_per_vertex);
            attr.Enable();
        }
        gl.Bind(Buffer::Target::Array, tangents);
        {
            std::vector<GLfloat> data;
            GLuint n_per_vertex = make_cube.Tangents(data);
            gl.Current(Buffer::Target::Array).Data(data);
            VertexArrayAttrib attr(prog, "Tangent");
            attr.Setup<GLfloat>(n_per_vertex);
            attr.Enable();
        }
        gl.Bind(Buffer::Target::Array, texcoords);
        {
            std::vector<GLfloat> data;
            GLuint n_per_vertex = make_cube.TexCoordinates(data);
            gl.Current(Buffer::Target::Array).Data(data);
            VertexArrayAttrib attr(prog, "TexCoord");
            attr.Setup<GLfloat>(n_per_vertex);
            attr.Enable();
        }
        
        {
            Texture::Active(0);
            UniformSampler(prog, "ColorTex").Set(0);
            gl.Bind(Texture::Target::_2D, colorTex);
            gl.Current(Texture::Target::_2D)
                .GenerateMipmap();
        }
        {
            Texture::Active(1);
            UniformSampler(prog, "NormalTex").Set(1);
            gl.Bind(Texture::Target::_2D, normalTex);
            gl.Current(Texture::Target::_2D)
                .Image2D(
                    images::NormalMap(
                    )
                ).GenerateMipmap();
        }
        
        gl.ClearColor(0.1f, 0.1f, 0.1f, 0.0f);
        gl.ClearDepth(1.0f);
        gl.FrontFace(make_cube.FaceWinding());
    }
    void Reshape(GLuint width, GLuint height)
    {
        gl.Viewport(width, height);
        prog.Use();
        projection_matrix.Set(
                Degrees(70),
                double(width)/height,
                1, 15
            )
        );
    }
    {
        gl.Clear().ColorBuffer().DepthBuffer();
        
        light_pos.Set(
                Cos(lightAzimuth),
                1.0f,
                Sin(lightAzimuth)
            ) * 2.0f
        );
        
        camera_matrix.Set(
                3.0f,
                Degrees(-45),
            )
        );
        
        model_matrix.Set(
        );
        cube.Bind();
        cube_instr.Draw(cube_indices);
    }
    {
        return time < 30.0;
    }
    double ScreenshotTime(void) const
    {
        return 2.0;
    }
};
void setupExample(ExampleParams& ){ }
std::unique_ptr<ExampleThread> makeExampleThread(
    Example& ,
    unsigned ,
    const ExampleParams& 
){ return std::unique_ptr<ExampleThread>(); }
std::unique_ptr<Example> makeExample(const ExampleParams& )
{
    return std::unique_ptr<Example>(new CubeExample);
}
}