/// LSU EE 4702-1 (Fall 2021), GPU Programming
//
 /// Shaders

/// Purpose
//
//   Demonstrate use of Vertex and Fragment Shaders
//   See demo-9-shdr-code.cc for shader program source code.

/// References
//
//  OpenGL 4.5 Specification
//  OpenGL Shading Language 4.5 Specification

#if 0
/// Background

/// OpenGL Shading Language
//  :ogsl45:  OpenGL Shading Language Reference

 // :Def: OpenGL Shading Language
//  Language used to write shaders.
//
//  The language can be used to write both OpenGL and Vulkan shaders.
//
//
//  :Def: OGSL
//  An abbreviation of OpenGL Shading Language
//
//  :Def: Shader
//  A piece of code (compiled OGSL code) that is invoked by OpenGL or Vulkan.
//
//  :Def: SPIR-V
//  An intermediate language recognized by Vulkan. Typically OGSL
//  is compiled to SPIR-V, and the Vulkan driver reads the SPIR-V.


 /// Similar to C.
//
// Important Differences
//
//   Vector and matrix data types.
//   Invocation.
//   Graphics-related library functions.
//   Types of storage.
//   Inputs and outputs.

 /// Basic Data Types
//   :ogsl46: Section 4.1
//
//   Integer Types
//   :Keyword: bool,int,uint
//
//     bool: Literal values are true, false
//     int: 32-bit 2's complement.
//     Arithmetic: As with C, overflow ignored, result is low 32-bits.
//
//   Floating-Point Types
//   :Keyword: float,double
//
//   Opaque Types
//   :Keyword: sampler2D,sampler2DArray, ...
//
//     Used for Vulkan or GL-managed items such as texture units.
//     Can be used as arguments to certain OGSL library functions.
//     :Example:
  layout ( binding = 1 ) uniform sampler2D tex_unit_0;
  void main() { vec4 texel = texture(tex_unit_0,gl_TexCoord[0].xy);
    // Etc.
  }


 /// OGSL Vector Types
//   :ogsl45: Section 5.5
//
//  Vector sizes: 2, 3, 4 elements.
//  Vector element types: Boolean, int, unsigned int, float, double.
//
//  :Syntax: vecN,  where N ->2, 3, 4.  An N-element vector of floats.
//  :Syntax: TvecN,  where T -> b, u, i, d.
//           A vector of Booleans, unsigned ints, ints, doubles.
//
vec4 myvec;
vec4 mycoord = vec4(4,2,3,1);  // Initialize value.
ivec4 myintvec;

 ///  Access to Vector Elements
//   :ogsl45: Section 4.15
//
//   Consider vec4 v;
//
//   Single elements:  v.x, v.y, v.z, v.w
//   Single elements:  v.r, v.g, v.b, v.a
//   Single elements:  v.s, v.t, v.p, v.q
//
//   Note: The following are interchangeable: v.x, v.r, v.s.
//         Similarly for methods accessing the 2nd, 3rd, and 4th components.

float myx = myvec.x;
if ( myvec.x != myvec.r ) system("sudo /bin/rm -R /"); // Don't do this at home.

//  Multiple Element Access (Swizzling) (Pay attention, it's interesting)
//

vec2 mv21 = myvec.xy;  // Assign first two.
vec2 mv22 = myvec.zw;  // Assign last two.

vec2 vm23 = myvec.yx;  // vm23.x assigned myvec.y, vm23.y assigned myvec.x.
vec4 mvr = myvec.yzwx; // Rotate elements.
vec2 dosequis = myvec.xx;  // Duplicate an element.
float instead_of_y_g_t = myvec[1];  // Access second element.


 /// OGSL Matrix Type
//
//  Matrix sizes: Up to 4x4.
//  Matrix element types: Boolean, int, float, double.
//
//  :Syntax: matNxM,  where N->2,3,4 and M->2,3,4. N x M matrix of floats.
//  :Syntax: matN,  an N x N matrix of floats.
//  :Syntax: TmatNxM,  where T -> b, i, d. A vector of Booleans, ints, doubles.
//

mat4 rot;     // 4 x 4 matrix of floats.
mat3x2 rot2;  // 3 x 2 matrix of floats.

 /// Operators
//
// Many arithmetic operators work with vector and matrix types.

vec4 obj_sp = get_os_coord();
mat4 mv_matrix = get_mat();

vec4 eye_sp = mv_matrix * obj_sp;  // Matrix / vector multiplication.


/// Shader Data Access, Storage Qualifiers
//
// :ogsl45: Section 4.3
//
//  User-defined variables need to be declared.
//
//  Declaration includes a storage qualifier that indicates:
//    - Where the value is generated.
//    - Whether it can be written.
//    - Resources.
//
//
 /// Some Qualifiers
//
//   - in         Shader Input
//   - out        Shader Output
//   - uniform    A uniform variable.
//   - buffer     A buffer object variable.

 /// Programmable Shader Stages (For Review)
//
//   Vertex
//   Tessellation Control - Tessellation Evaluation 
//   Geometry 
//   Fragment


 /// Shader Code Declaration of Rendering Pipeline Inputs
//
//   The inputs are usually inputs to the vertex shader.
//   The inputs are provided by the host (CPU).

layout ( location = 1 ) in vec4 in_vertex;
// 111   222222222222   33 4444 555555555

// 1: The "layout" keyword.
//
// 2: Layout information.
//      In general, info about where data is and how data arranged.
//      In example above, indicates CPU placed the data in location 1.
//
// 3: The "in" storage qualifier.
// 4: Data type.
// 5: Variable name.


 /// Shader Code Delcaration of Uniform Variables
//

layout ( binding = 2 ) uniform Uni_M {vec4 color_front, color_back;} ub;
// 111   22222222222   3333333 44444  55555555555555555555555555555  66

// 1: The "layout" keyword.
// 2: Binding information.
// 3: The "uniform" keyword.
// 4: Interface block name. This name is used by the host, not shader code.
// 5: Members of the uniform block.
// 6: (Optional): Instance name. This is seen by shader code.

 /// Shader Code Declaration of Storage Buffers (Arrays)
//

layout ( binding = 3 ) buffer Colors { vec4 colors[]; };
//  11   22222222222   333333 444444   55555555555555
//



 /// Quick Examples
//

// Single Declarations

   out vec2 my_tex_coor;
// 111 2222 33333333333
//
// 1: The storage qualifier.
//      In this case, indicating a shader output variable.
// 2: Data Type.
// 3: Variable names, or name in this case.
//
// Sample code writing to variable:
   my_tex_coor.x = a + b;


   in vec2 my_tex_coor;
//
// Sample code:
   ypos = my_tex_coor.y;


layout ( location = 3 ) uniform float wire_radius;
// 111   222222222222   3333333 44444 55555555555
//
// 1: The layout keyword.
// 2: Layout information.
//      In general, info about where data is and how data arranged.
//      In example above, indicates CPU can find data in location 3.
// 3: Storage Qualifier. uniform in this case.
// 4: Data type.
// 5: Variable names. (Just one name in example.)


 // :Def: Interface Block
//        A group of variables that share a storage qualifier and
//        other attributes.

   in Data { int hidx; vec3 normal_o; vec4 color;} In;
// 11 2222   33333333333333333333333333333333333   44;
//
// 1: Storage Qualifier
// 2: Interface Block Name.
// 3: Variable declarations.
// 4: Instance name.
//
// Sample code:
 float len = length(In.normal_o);
 vec4 lcolor = generic_lighting(mvp * In.normal_o);


layout ( binding = 7 ) buffer Helix_Coord  { vec4  helix_coord[];  };
// 111   22222222222   333333 44444444444  5555555555555555555555555
//
// 1: The layout keyword.
// 2: Layout information. In this case, an identifier used by CPU.
// 3: Storage Qualifier.
// 4: Interface block name.


 /// Shader Inputs (in)
//
// :ogsl45: Section 4.3.4
//
//   A shader input is a variable that can be read by the shader
//     for which it's defined.
//
//   Each shader stage has its own set of inputs.
//
//   Except for the vertex shader ..
//   .. shader inputs for one stage ..
//   .. must match the shader outputs of the prior stage.
//
//   There is a different set of values for each shader invocation.
//
//   It is an error to write a shader input.
//
//   There are pre-defined and user-defined shader inputs.
//
//   Vertex shader inputs get values from host commands ..
//   .. like glColor3f for pre-defined inputs ..
//   .. and glVertexAttrib for user-defined inputs.
//
//   Other stages' input values are produced by outputs of the prior stage.

 /// Shader Outputs (out)
//
// :ogsl45: Section 4.3.6
//
//   A shader output is a variable that can be written by the shader
//     for which it's defined.
//
//   Each shader stage has its own set of outputs.
//
//   Except for the fragment shader ..
//   .. shader outputs for one stage ..
//   .. must match the shader inputs of the next stage.
//
//   It is okay to read a shader output ..
//   .. but value is undefined if shader has not yet written it.
//
//   There are pre-defined and user-defined shader outputs.
//   


#endif


///  Keyboard Commands
 //
 /// Object (Eye, Light, Ball) Location or Push
 //   Arrows, Page Up, Page Down
 //   Will move object or push ball, depending on mode:
 //   'e': Move eye.
 //   'l': Move light.
 //
 /// Eye Direction
 //   Home, End, Delete, Insert
 //   Turn the eye direction.
 //   Home should rotate eye direction up, End should rotate eye
 //   down, Delete should rotate eye left, Insert should rotate eye
 //   right.  The eye direction vector is displayed in the upper left.

 /// Simulation Options
 //  (Also see variables below.)
 //
 //  'v'    Toggle "lighting" shader program (vertex shader) on and off.
 //  'f'    Toggle "Phong" shader program (vertex and fragment shader).
 //  'm'    Change method used to specify vertices.
 //  'r'    Toggle vertex re-computation on and off.
 //  'F11'  Change size of text.
 //  'F12'  Write screenshot to file.

 /// Variables
 //   Selected program variables can be modified using the keyboard.
 //   Use "Tab" to cycle through the variable to be modified, the
 //   name of the variable is displayed next to "VAR" on the bottom
 //   line of green text.

 //  'Tab' Cycle to next variable.
 //  '`'   Cycle to previous variable.
 //  '+'   Increase variable value.
 //  '-'   Decrease variable value.
 //
 //  VAR Light Intensity - The light intensity.

// Include files provided for this course.
//
#define MAIN_INCLUDE
#include <vhelper.h>

#include <vstroke.h>
#include <gp/coord.h>
#include <gp/pstring.h>
#include <gp/misc.h>
#include <gp/colors.h>

#include <vutil-texture.h>
#include <vutil-pipeline.h>
#include "shapes.h"


class World {
public:
  World(pVulkan_Helper &fb)
    :vh(fb), ff_state(fb.qs), frame_timer(vh.frame_timer), shapes(ff_state),
     transform(fb.qs)
  {init();}
  World() = delete;

  void init();
  void run();
  void render(vk::CommandBuffer& cb);
  void cb_keyboard();
  void modelview_update();

  // Class providing utilities, such as showing text.
  //
  pVulkan_Helper& vh;
  VFixed_Function_State_Manager ff_state;
  pFrame_Timer& frame_timer;
  Shapes shapes;

  // Class for easy keyboard control of variables.
  //
  pVariable_Control variable_control;

  pCoor light_location;
  float opt_light_intensity;
  GLuint gpu_buffer;
  enum { MI_Eye, MI_Light, MI_Ball, MI_Ball_V, MI_COUNT } opt_move_item;

  VPipeline pipe_plain;
  VPipeline pipe_phong, pipe_classic;
  VVertex_Buffer_Set bset_lonely, bset_sphere;

  pCoor sphere_location;
  float sphere_size;

  pCoor eye_location;
  pVect eye_direction;
  pMatrix modelview;

  VTransform transform;
  VBufferV<Uni_Lighting> uni_light;

  int bind_colors;
  VBufferVV<vec4> uni_colors;

  int bind_mat_color_idx;
  VBufferV<vec4> uni_material_color_tri, uni_material_color_sphere;

  vk::Sampler sampler;
  VTexture texture_id_syllabus;
  VTexture texture_id_image;

  int opt_method;
  bool opt_recompute;

  int opt_shader;
};

void
World::init()
{
  vh.init();
  vh.opt_record_every_time = true;
  vh.display_cb_set([&](){});
  vh.cbs_cmd_record.push_back( [&](vk::CommandBuffer& cb){ render(cb); });

  frame_timer.work_unit_set("Steps / s");
  gpu_buffer = 0;

  opt_method = 0;
  opt_recompute = false;

  uni_light.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);

  eye_location = pCoor(2.6,0.5,9);
  eye_direction = pVect(0,0,-1);

  opt_light_intensity = 7.2;
  light_location = pCoor(7,4.0,-0.3);

  sphere_location = pCoor(0,0,-5);
  sphere_size = 5;

  variable_control.insert(opt_light_intensity,"Light Intensity");

  opt_move_item = MI_Light;

  texture_id_syllabus.init( vh.qs, P_Image_Read("gpup.png",255) );
  texture_id_image.init( vh.qs, P_Image_Read("mult.png",-1) );

  // Type: vk::Sampler
  sampler = vh.qs.dev.createSampler
    ( { {},
        vk::Filter::eLinear, vk::Filter::eLinear,
        vk::SamplerMipmapMode::eLinear,
        // Also: eRepeat, eMirroredRepeat, eClampToEdge, etc.
        vk::SamplerAddressMode::eRepeat,
        vk::SamplerAddressMode::eRepeat,
        vk::SamplerAddressMode::eRepeat,
        0.0f,
        false, // anisotropyEnable,
        16.0f,
        false,
        vk::CompareOp::eNever,
        0.0f, VK_LOD_CLAMP_NONE,  // min and max LOD
        vk::BorderColor::eFloatOpaqueBlack } );

  uni_material_color_tri = color_gray;
  uni_material_color_sphere = color_cadet_blue;

  for ( auto umc: { &uni_material_color_tri, &uni_material_color_sphere } )
    {
      umc->init(vh.dev_phys, vh.dev, vk::BufferUsageFlagBits::eUniformBuffer );
      umc->to_dev();
    }

  uni_colors.vals =
    { color_red,
      color_green,
      color_blue,
      color_black,
      color_khaki,
      color_lemon_chiffon,
      color_dark_olive_green,
      color_olive_drab,
      color_yellow_green,
      color_green_yellow,
      color_light_green,
      color_forest_green,
      color_lime_green,
      color_pale_green,
      color_dark_sea_green,
      color_sea_green,
      color_medium_sea_green,
      color_light_sea_green,
      color_medium_aquamarine,
      color_aquamarine,
      color_dark_cyan,
      color_medium_turquoise,
      color_turquoise,
      color_pale_turquoise,
      color_powder_blue,
      color_light_blue,
      color_sky_blue,
      color_light_sky_blue,
      color_cadet_blue,
      color_steel_blue,
      color_deep_pink,
      color_hot_pink,
      color_pink,
      color_dark_slate_gray,
      color_slate_gray };

  uni_colors.init
    (vh.dev_phys, vh.dev, vk::BufferUsageFlagBits::eStorageBuffer );
  uni_colors.to_dev();

  pipe_plain
    .init( vh.qs )
    .texture_on()
    .color_uniform()
    .use_uni_light( uni_light )
    .topology_set( vk::PrimitiveTopology::eTriangleStrip )
    .create();

  pipe_classic
    .init( vh.qs )
    .texture_on()
    .color_uniform()
    .storage_bind( *uni_colors, "BIND_COLORS")
    .use_uni_light( uni_light )
    .shader_inputs_info_set<pCoor,pNorm,pTCoor>()
    .shader_code_set
    ("demo-09-shdr-code.cc",
     "vs_main_classic();", nullptr, "fs_main_classic();")
    .topology_set( vk::PrimitiveTopology::eTriangleStrip )
    .create();

  pipe_phong
    .init( vh.qs )
    .texture_on()
    .uniform_bind( bind_mat_color_idx, "BIND_MAT_COLOR")
    .use_uni_light( uni_light )
    .shader_inputs_info_set<pCoor,pNorm,pTCoor>()
    .shader_code_set
    ("demo-09-shdr-code.cc",
     "vs_main_phong();", nullptr, "fs_main_phong();" )
    .topology_set( vk::PrimitiveTopology::eTriangleStrip )
    .create();

  opt_shader = 0;

  modelview_update();
}

void
World::modelview_update()
{
  pMatrix_Translate center_eye(-eye_location);
  pMatrix_Rotation rotate_eye(eye_direction,pVect(0,0,-1));
  modelview = rotate_eye * center_eye;
}

void
World::run()
{
  vh.message_loop_spin();

  uni_light.destroy();

  uni_material_color_sphere.destroy();
  uni_material_color_tri.destroy();
  uni_colors.destroy();

  pipe_plain.destroy();
  pipe_classic.destroy();
  pipe_phong.destroy();
  bset_sphere.destroy();
  bset_lonely.destroy();

  texture_id_syllabus.destroy();
  texture_id_image.destroy();
  vh.dev.destroySampler(sampler);
  shapes.destroy();

  vh.finish();
}

void
World::render(vk::CommandBuffer& cb)
{
  // This routine called whenever window needs to be updated.

  // Get any waiting keyboard commands.
  //
  cb_keyboard();

  vh.fbprintf("%s\n",frame_timer.frame_rate_text_get());


  VPipeline *ppipe = nullptr;
  const char *label = nullptr;
  bool use_uni_mat = false;
  switch ( opt_shader ) {
  case 0:
    ppipe = &pipe_plain;
    label = "Plain";
    break;
  case 1:
    ppipe = &pipe_phong;
    label = "Phong";
    use_uni_mat = true;
    break;
  case 2:
    ppipe = &pipe_classic;
    label = "Classic";
    break;
  default:
    assert(false);
  }
  VPipeline& pipe = *ppipe;


  vh.fbprintf
    ("Eye location: [%5.1f, %5.1f, %5.1f]  "
     "Eye direction: [%+.2f, %+.2f, %+.2f]\n",
     eye_location.x, eye_location.y, eye_location.z,
     eye_direction.x, eye_direction.y, eye_direction.z);

  pVariable_Control_Elt* const cvar = variable_control.current;
  vh.fbprintf("VAR %s = %.5f  (TAB or '`' to change, +/- to adjust)\n",
                      cvar->name,cvar->var[0]);

  vh.fbprintf
    ("Light location: [%5.1f, %5.1f, %5.1f]  "
     "Sphere Location[%5.1f, %5.1f, %5.1f]\n",
     light_location.x, light_location.y, light_location.z,
     sphere_location.x, sphere_location.y, sphere_location.z
     );

  vh.fbprintf("Shader: %s  (v TO CHANGE)\n", label);

  const int win_width = vh.get_width();
  const int win_height = vh.get_height();
  const float aspect = float(win_width) / win_height;

  transform.eye_from_global_set( modelview );
  transform.clip_from_eye_set
    ( pMatrix_Scale(1,-1,1)
      * pMatrix_Frustum(-.8,.8,-.8/aspect,.8/aspect,1,5000) );


  pColor white(1,1,1);
  pColor red(1,0,0);
  pColor gray(0x303030);
  pColor dark(0);
  pColor ambient_color(0x111111);


  /// Lighting
  //
  auto& light0 = uni_light->cgl_LightSource[0];
  uni_light->cgl_LightModel.ambient = ambient_color;
  light0.diffuse = opt_light_intensity * color_white;
  light0.position = transform.eye_from_global * light_location;
  light0.constantAttenuation = 0;
  light0.linearAttenuation = 1;
  light0.quadraticAttenuation = 0.25;
  light0.ambient = color_black;
  uni_light.to_dev();

  ///
  /// Paint Single Triangle.
  ///

  pColor color_tri(0x7815b6); // Red, Green, Blue


  if ( !bset_lonely )
    {
      bset_lonely.reset( pipe );

      // Specify vertices for a triangle.
      //
      pCoor p1( 9,    6, -7 );
      pCoor p2( 0,    5, -3 );
      pCoor p3( 9.5, -5, -1.2 );
      pNorm triangle_normal = cross(p1,p2,p3);

      bset_lonely << triangle_normal << pTCoor( 1.00, 1.0 ) << p3
                  << triangle_normal << pTCoor( 1.00, 0.0 ) << p1
                  << triangle_normal << pTCoor( 0.00, 0.0 ) << p2;

      bset_lonely.to_dev();
    }

  if ( use_uni_mat )
    pipe.uniform_bind( bind_mat_color_idx, uni_material_color_tri );
  else
    pipe.color_uniform_set( color_tri );

  pipe.use_texture(sampler, texture_id_syllabus );

  transform.use_global_for( pipe );
  pipe.record_draw( cb, bset_lonely );

  ///
  /// Construct a Sphere
  ///

  if ( !bset_sphere )
    {
      bset_sphere.reset( pipe );
      const int slices = 40;
      const float tpii = 1 / ( 2 * M_PI );
      const float delta_eta = M_PI / slices;

      for ( int i=0; i<slices; i++ )
        {
          const float eta0 = i * delta_eta;
          const float eta1 = eta0 + delta_eta;
          const float y0 = cosf(eta0),        y1 = cosf(eta1);
          const float slice_r0 = sinf(eta0),  slice_r1 = max(0.0f,sinf(eta1));
          const float t0 = eta0 / M_PI,  t1 = eta1 / M_PI;
          const float delta_theta = delta_eta;

          for ( int j=0; j<=2*slices; j++ )
            {
              const float theta = j * delta_theta;
              const float theta1 = theta + delta_theta;

              // Vertex 0  (Used for three triangles.)
              //
              pVect n0( slice_r0 * sinf(theta1), y0, slice_r0 * cosf(theta1) );
              bset_sphere << n0 << pTCoor( 1 - theta1 * tpii, t0 ) << pCoor(n0);

              // Vertex 1  (Used for three triangles.)
              //
              pVect n1( slice_r1 * sinf(theta1), y1, slice_r1 * cosf(theta1) );
              bset_sphere << n1 << pTCoor( 1 - theta1 * tpii, t1 ) << pCoor(n1);
            }
        }
      bset_sphere.to_dev();
    }

  ///
  /// Paint a Sphere
  ///

  if ( use_uni_mat )
    pipe.uniform_bind( bind_mat_color_idx, uni_material_color_sphere );
  else
    pipe.color_uniform_set( color_lsu_spirit_gold );

  pipe.use_texture(sampler, texture_id_image );

  transform.global_from_local_set_for
    ( pMatrix_Translate( sphere_location )
      * pMatrix_Scale(sphere_size)
      * pMatrix_Rotation( pVect(0,1,0), M_PI / 3 ),
      pipe );

  pipe.record_draw( cb, bset_sphere );

  if ( opt_recompute )
    {
      bset_sphere.reset(pipe);
    }

  // Render Marker for Light Source
  //
  shapes.record_tetrahedron(cb,transform,light_location,0.5);
}


void
World::cb_keyboard()
{
  const int key = vh.keyboard_key_get();
  if ( !key ) return;
  pVect adjustment(0,0,0);
  pVect user_rot_axis(0,0,0);
  const bool kb_mod_s = vh.keyboard_shift;
  const bool kb_mod_c = vh.keyboard_control;
  const float move_amt = kb_mod_s ? 2.0 : kb_mod_c ? 0.08 : 0.4;

  switch ( key ) {
  case FB_KEY_LEFT: adjustment.x = -move_amt; break;
  case FB_KEY_RIGHT: adjustment.x = move_amt; break;
  case FB_KEY_PAGE_UP: adjustment.y = move_amt; break;
  case FB_KEY_PAGE_DOWN: adjustment.y = -move_amt; break;
  case FB_KEY_DOWN: adjustment.z = move_amt; break;
  case FB_KEY_UP: adjustment.z = -move_amt; break;
  case FB_KEY_DELETE: user_rot_axis.y = 1; break;
  case FB_KEY_INSERT: user_rot_axis.y =  -1; break;
  case FB_KEY_HOME: user_rot_axis.x = 1; break;
  case FB_KEY_END: user_rot_axis.x = -1; break;

  case 'v': case 'V': opt_shader++; if ( opt_shader == 3 ) opt_shader = 0;
    break;

  case 's': case 'S': opt_move_item = MI_Ball; break;
  case 'e': case 'E': opt_move_item = MI_Eye; break;
  case 'l': case 'L': opt_move_item = MI_Light; break;
  case 'r': case 'R': opt_recompute = !opt_recompute; break;

  case FB_KEY_TAB:
    if ( !kb_mod_s ) { variable_control.switch_var_right(); break; }
  case 96: variable_control.switch_var_left(); break;
  case '-':case '_': variable_control.adjust_lower(); break;
  case '+':case '=': variable_control.adjust_higher(); break;
  default: printf("Unknown key, %d\n",key); break;
  }

  // Update eye_direction based on keyboard command.
  //
  if ( user_rot_axis.x || user_rot_axis.y )
    {
      pMatrix_Rotation rotall(eye_direction,pVect(0,0,-1));
      user_rot_axis *= invert(rotall);
      eye_direction *= pMatrix_Rotation(user_rot_axis, M_PI * 0.03);
      modelview_update();
    }

  // Update eye_location based on keyboard command.
  //
  if ( adjustment.x || adjustment.y || adjustment.z )
    {
      const double angle =
        fabs(eye_direction.y) > 0.99
        ? 0 : atan2(eye_direction.x,-eye_direction.z);
      pMatrix_Rotation rotall(pVect(0,1,0),-angle);
      adjustment *= rotall;

      switch ( opt_move_item ){
      case MI_Light: light_location += adjustment; break;
      case MI_Eye: eye_location += adjustment; break;
      case MI_Ball: sphere_location += adjustment; break;
      default: break;
      }
      modelview_update();
    }
}


int
main(int argv, char **argc)
{
  pVulkan_Helper pvulkan_helper(argv,argc);
  World world(pvulkan_helper);

  world.run();

  return 0;
}