#include <sh/sh.hpp>
#include <GL/glut.h>
#include <GL/glext.h>
#include <GL/glu.h>
#include "Camera.hpp"
using namespace SH;
ShMatrix4x4f MV, MD, VM;  // transformations
ShPoint3f light_pv;       // VCS light source position
Camera camera;  // camera object (see Camera.hpp)
ShProgram vsh, fsh; // vertex and fragment shader objects
// Glut data
int buttons[5] = GLUT_UP, GLUT_UP, GLUT_UP, GLUT_UP, GLUT_UP;
int cur_x, cur_y;  // for trackball
// initialize program objects for shaders
void init_shaders()
                                                                     
                                                                     
  vsh = SH_BEGIN_PROGRAM("gpu:vertex") 
    ShInputNormal3f nm;     // MCS normal
    ShInputPoint4f pm;      // MCS position
    ShOutputNormal3f nv;    // VCS normal
    ShOutputVector3f lv;    // VCS light vector
    ShOutputPosition4f pd;  // DCS position
    ShPoint3f pv = (MV|pm)(0,1,2); // Compute viewspace position
    lv = light_pv - pv; // Compute light direction in view space
    pd = MD|pm; // Transform position to device space
    nv = normalize((nm|VM)(0,1,2)); // Transform normal to view space
   SH_END;
  // declare and initialize diffuse color
  ShColor3f kd = ShColor3f(0.5, 0.7, 0.9);
  fsh = SH_BEGIN_PROGRAM("gpu:fragment") 
    ShInputNormal3f nv;  // VCS normal
    ShInputVector3f lv;  // VCS light vector
    ShOutputColor3f c;   // fragment color
    // normalize interpolated vectors to unit length
    nv = normalize(nv);
    lv = normalize(lv);
    // per-pixel diffuse lighting model
    c = kd * pos(nv|lv);
   SH_END;
// GLUT callback
//    draw using glut teapot (which has several faults, but)
void display ()
  // make sure Sh uniform parameters are synchronized
  shUpdate();
  // clear framebuffer
                                                                     
                                                                     
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  // draw teapot
  glFrontFace(GL_CW);
  glutSolidTeapot(2.5);
  glFrontFace(GL_CCW);
  // swap front and back buffers
  glutSwapBuffers();
void setup_view ()
  MV = camera.shModelView();
  VM = inverse(MV);
  MD = camera.shModelViewProjection(ShMatrix4x4f());
// GLUT callback
//    called on window resize
void reshape (int width, int height)
  glViewport(0, 0, width, height);
  setup_view();
// GLUT callback
//    called on mouse movement
void motion (int x, int y)
  const double factor = 20.0;
  bool changed = false;
  // process UI events
  if (buttons[GLUT_LEFT_BUTTON] == GLUT_DOWN) 
    // rotate camera orientation using left mouse button
    camera.orbit(cur_x, cur_y, x, y,
                 glutGet(GLUT_WINDOW_WIDTH),
                 glutGet(GLUT_WINDOW_HEIGHT));
    changed = true;
                                                                     
                                                                     
  
  if (buttons[GLUT_MIDDLE_BUTTON] == GLUT_DOWN) 
    // track camera forward and back using middle mouse button
    camera.move(0, 0, (y - cur_y)/factor);
    changed = true;
  
  if (buttons[GLUT_RIGHT_BUTTON] == GLUT_DOWN) 
    // pan camera using right mouse button
    camera.move((x - cur_x)/factor, (cur_y - y)/factor, 0);
    changed = true;
  
  // update everything
  if (changed) 
    setup_view();
    glutPostRedisplay();
  
void mouse (int button, int state, int x, int y)
  buttons[button] = state;
  cur_x = x;
  cur_y = y;
int main (int argc, char** argv)
  // set up GLUT
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
  glutInitWindowSize(512, 512);
  glutCreateWindow("glutex: Sh Example");
  // register callback functions
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutMotionFunc(motion);
                                                                     
                                                                     
  // initialize Sh
  shInit();
  // set up default GPU backend (optional)
  shSetBackend("arb");
  // initialize OpenGL state, turn on shader support
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_VERTEX_PROGRAM_ARB);
  glEnable(GL_FRAGMENT_PROGRAM_ARB);
  glClearColor(0.0, 0.0, 0.0, 1.0);
  setup_view();
  // Place the camera at its initial position
  camera.move(0.0, 0.0, -15.0);
  // Set up the light position
  qv = ShPoint3f(5.0, 5.0, 5.0);
  // define our shader programs
  init_shaders();
  // bind shaders
  shBind(vsh);
  shBind(fsh);
  // hand over event processing to GLUT
  glutMainLoop();