void GLCanvas::keyboardCB(GLFWwindow* window, int key, int scancode, int action, int mods) {
  // store the modifier keys
  shiftKeyPressed = (GLFW_MOD_SHIFT & mods);
  controlKeyPressed = (GLFW_MOD_CONTROL & mods);
  altKeyPressed = (GLFW_MOD_ALT & mods);
  superKeyPressed = (GLFW_MOD_SUPER & mods);
  // non modifier key actions

  if (key == GLFW_KEY_ESCAPE || key == 'q' || key == 'Q') {
    glfwSetWindowShouldClose(GLCanvas::window, GL_TRUE);
  }

  // other normal ascii keys...
  if ( (action == GLFW_PRESS || action == GLFW_REPEAT) && key < 256) {
    switch (key) {
    // RAYTRACING STUFF
    case 'r':  case 'R':  case 'g':  case 'G': { 
      args->raytracing_animation = !args->raytracing_animation;
      glfwGetWindowSize(window, &args->width, &args->height);
      // animate raytracing of the scene
      if (args->raytracing_animation) {
        if (key == 'r' || key == 'R') {
          args->gather_indirect = false;
          printf ("raytracing animation started, press 'R' to stop\n");
        } else {
          args->gather_indirect = true; 
          printf ("photon mapping animation started, press 'G' to stop\n");
        }
        if (args->width <= args->height) {
          raytracing_divs_x = 10;
          raytracing_divs_y = 10 * args->height / float (args->width);
        } else {
          raytracing_divs_x = 10 * args->width / float (args->height);
          raytracing_divs_y = 10;
        }
        raytracing_x = 0;
        raytracing_y = 0;
        raytracer->resetVBOs();
      } else
        printf ("raytracing animation stopped, press 'R' to start\n");    
      break;
    }
    case 't':  case 'T': {
      // visualize the ray tree for the pixel at the current mouse position
      glfwGetWindowSize(window, &args->width, &args->height);
      RayTree::Activate();
      raytracing_divs_x = -1;
      raytracing_divs_y = -1;
      TraceRay(mouseX,args->height-mouseY);
      RayTree::Deactivate();
      glm::vec3 cp = camera->camera_position;
      glm::vec3 poi = camera->point_of_interest;
      float distance = glm::length((cp-poi)/2.0f);
      RayTree::setupVBOs(distance / 500.0); 
      radiosity->setupVBOs();
      photon_mapping->setupVBOs();
      break; }
    case 'l':  case 'L': { 
      // toggle photon rendering
      args->render_photons = !args->render_photons;
      break; }
    case 'k':  case 'K': { 
      // toggle photon rendering
      args->render_kdtree = !args->render_kdtree;
      break; }
    case 'p':  case 'P': { 
      // toggle photon rendering
      photon_mapping->TracePhotons();
      photon_mapping->setupVBOs();
      break; }
    
      // RADIOSITY STUFF
    case ' ': 
      // a single step of radiosity
      radiosity->Iterate();
      radiosity->setupVBOs();
      break;
    case 'a': case 'A':
      // animate radiosity solution
      args->radiosity_animation = !args->radiosity_animation;
      if (args->radiosity_animation) 
        printf ("radiosity animation started, press 'A' to stop\n");
      else
        printf ("radiosity animation stopped, press 'A' to start\n");
      break;
    case 's': case 'S':
      // subdivide the mesh for radiosity
      radiosity->Cleanup();
      radiosity->getMesh()->Subdivision();
      radiosity->Reset();
      radiosity->setupVBOs();
      break;
    case 'c': case 'C':
      // clear the raytracing visualization
      args->raytracing_animation = false;
      raytracer->resetVBOs();
      raytracer->setupVBOs();
      // clear the radiosity solution
      args->radiosity_animation = false;
      radiosity->Reset();
      radiosity->setupVBOs();
      break;
      
      // VISUALIZATIONS
    case 'w':  case 'W':
      // render wireframe mode
      args->wireframe = !args->wireframe;
      break;
    case 'v': case 'V':
      // toggle the different visualization modes
      args->render_mode = RENDER_MODE((args->render_mode+1)%NUM_RENDER_MODES);
      switch (args->render_mode) {
      case RENDER_MATERIALS: std::cout << "RENDER_MATERIALS\n"; fflush(stdout); break;
      case RENDER_LIGHTS: std::cout << "RENDER_LIGHTS\n"; fflush(stdout); break;
      case RENDER_UNDISTRIBUTED: std::cout << "RENDER_UNDISTRIBUTED\n"; fflush(stdout); break;
      case RENDER_ABSORBED: std::cout << "RENDER_ABSORBED\n"; fflush(stdout); break;
      case RENDER_RADIANCE: std::cout << "RENDER_RADIANCE\n"; fflush(stdout); break;
      case RENDER_FORM_FACTORS: std::cout << "RENDER_FORM_FACTORS\n"; fflush(stdout); break;
      default: assert(0); }
      radiosity->setupVBOs();
      break;
    case 'i':  case 'I':
      // interpolate patch illumination values
      args->interpolate = !args->interpolate;
      radiosity->setupVBOs();
      break;
    case 'b':  case 'B':
      // interpolate patch illumination values
      args->intersect_backfacing = !args->intersect_backfacing;
      break;
      
    case 'x':  case 'X':
      std::cout << "CURRENT CAMERA" << std::endl;
      std::cout << *camera << std::endl;
      break;

    case 'q':  case 'Q':
      // quit
      glfwSetWindowShouldClose(GLCanvas::window, GL_TRUE);
      break;
    default:
      std::cout << "UNKNOWN KEYBOARD INPUT  '" << (char)key << "'" << std::endl;
    }
    setupVBOs();
  }
}
void GLCanvas::keyboard(unsigned char key, int x, int y) {
  args->raytracing_animation = false;
  switch (key) {
    // RAYTRACING STUFF
  case 'r':  case 'R':
    // animate raytracing of the scene
    args->gather_indirect=false;
    args->raytracing_animation = !args->raytracing_animation;
    if (args->raytracing_animation) {
      raytracing_skip = my_max(args->width,args->height) / 10;
      if (raytracing_skip % 2 == 0) raytracing_skip++;
      assert (raytracing_skip >= 1);
      raytracing_x = raytracing_skip/2;
      raytracing_y = raytracing_skip/2;
      display(); // clear out any old rendering
      printf ("raytracing animation started, press 'R' to stop\n");
    } else
      printf ("raytracing animation stopped, press 'R' to start\n");    
    break;
  case 't':  case 'T': {
    // visualize the ray tree for the pixel at the current mouse position
    int i = x;
    int j = glutGet(GLUT_WINDOW_HEIGHT)-y;
    RayTree::Activate();
    raytracing_skip = 1;
    TraceRay(i,j);
    RayTree::Deactivate();
    // redraw
  RayTree::setupVBOs();
  radiosity->setupVBOs();
  photon_mapping->setupVBOs();


  glutPostRedisplay();
    break; }

  case 'l':  case 'L': { 
    // toggle photon rendering
    args->render_photons = !args->render_photons;
    glutPostRedisplay();
    break; }
  case 'k':  case 'K': { 
    // toggle photon rendering
    args->render_kdtree = !args->render_kdtree;
    glutPostRedisplay();
    break; }
  case 'p':  case 'P': { 
    // toggle photon rendering
    photon_mapping->TracePhotons();
    photon_mapping->setupVBOs();
    glutPostRedisplay();
    break; }
  case 'g':  case 'G': { 
    args->gather_indirect = true;
    args->raytracing_animation = !args->raytracing_animation;
    if (args->raytracing_animation) {
      raytracing_skip = my_max(args->width,args->height) / 10;
      if (raytracing_skip % 2 == 0) raytracing_skip++;
      assert (raytracing_skip >= 1);
      raytracing_x = raytracing_skip/2;
      raytracing_y = raytracing_skip/2;
      display(); // clear out any old rendering
      printf ("photon mapping animation started, press 'G' to stop\n");
    } else
      printf ("photon mapping animation stopped, press 'G' to start\n");    
    break; 
  }
    
    // RADIOSITY STUFF
  case ' ': 
    // a single step of radiosity
    radiosity->Iterate();
    radiosity->setupVBOs();
    glutPostRedisplay();
    break;
  case 'a': case 'A':
    // animate radiosity solution
    args->radiosity_animation = !args->radiosity_animation;
    if (args->radiosity_animation) 
      printf ("radiosity animation started, press 'A' to stop\n");
    else
      printf ("radiosity animation stopped, press 'A' to start\n");
    break;
  case 's': case 'S':
    // subdivide the mesh for radiosity
    radiosity->Cleanup();
    radiosity->getMesh()->Subdivision();
    radiosity->Reset();
    radiosity->setupVBOs();
    glutPostRedisplay();
    break;
  case 'c': case 'C':
    // clear the radiosity solution
    radiosity->Reset();
    radiosity->setupVBOs();
    glutPostRedisplay();
    break;

    // VISUALIZATIONS
  case 'w':  case 'W':
    // render wireframe mode
    args->wireframe = !args->wireframe;
    glutPostRedisplay();
    break;
  case 'v': case 'V':
    // toggle the different visualization modes
    args->render_mode = RENDER_MODE((args->render_mode+1)%NUM_RENDER_MODES);
    switch (args->render_mode) {
    case RENDER_MATERIALS: std::cout << "RENDER_MATERIALS\n"; fflush(stdout); break;
    case RENDER_LIGHTS: std::cout << "RENDER_LIGHTS\n"; fflush(stdout); break;
    case RENDER_UNDISTRIBUTED: std::cout << "RENDER_UNDISTRIBUTED\n"; fflush(stdout); break;
    case RENDER_ABSORBED: std::cout << "RENDER_ABSORBED\n"; fflush(stdout); break;
    case RENDER_RADIANCE: std::cout << "RENDER_RADIANCE\n"; fflush(stdout); break;
    case RENDER_FORM_FACTORS: std::cout << "RENDER_FORM_FACTORS\n"; fflush(stdout); break;
    default: assert(0); }
    radiosity->setupVBOs();
    glutPostRedisplay();
    break;
  case 'i':  case 'I':
    // interpolate patch illumination values
    args->interpolate = !args->interpolate;
    radiosity->setupVBOs();
    glutPostRedisplay();
    break;
  case 'b':  case 'B':
    // interpolate patch illumination values
    args->intersect_backfacing = !args->intersect_backfacing;
    glutPostRedisplay();
    break;

  case 'q':  case 'Q':
    // quit
    delete GLCanvas::photon_mapping;
    delete GLCanvas::raytracer;
    delete GLCanvas::radiosity;
    delete GLCanvas::mesh;
    exit(0);
    break;
  default:
    printf("UNKNOWN KEYBOARD INPUT  '%c'\n", key);
  }
}