```/// LSU EE 4702-1 (Fall 2013), GPU Programming
//
/// Homework 2 - SOLUTION
//

/// Instructions
//

// \$Id:\$

/// Purpose
//
//   Demonstrate simulation of string modeled as point masses and springs

/// What Code Does

// Simulates a string bouncing over a platform. The string is modeled
// as point masses connected by springs with a long relaxed
// length. The platform consists of tiles, some are purple-tinted
// mirrors (showing a reflection of the ball), the others show the
// course syllabus.

///  Keyboard Commands
//
/// Object (Eye, Light, Ball) Location or Push
//   Arrows, Page Up, Page Down
//        Move object or push ball, depending on mode.
//        With shift key pressed, motion is 5x faster.
//   'e': Move eye.
//   'l': Move light.
//   'b': Move head (first) ball. (Change position but not velocity.)
//
/// 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'    Switch between using "proba" and "probb" version of cone.
//  '1'    Set up scene 1.
//  '2'    Set up scene 2.
//  'p'    Pause simulation. (Press again to resume.)
//  ' '    (Space bar.) Advance simulation by 1/30 second.
//  'S- '  (Shift-space bar.) Advance simulation by one time step.
//  'k'    Freeze position of first ball. (Press again to release.)
//  't'    Freeze position of last ball. (Press again to release.)
//  's'    Stop ball.
//  'S'    Freeze ball. (Set velocity of all vertices to zero.)
//  'g'    Turn gravity on and off.
//  '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 Level of Detail - Set level of detail to be used for cone. (opt_lod)
//  VAR Spring Constant - Set spring constant.
//  VAR Air Resistance - Set air resistance.
//  VAR Light Intensity - The light intensity.
//  VAR Gravity - Gravitational acceleration. (Turn on/off using 'g'.)

#define GL_GLEXT_PROTOTYPES
#define GLX_GLXEXT_PROTOTYPES

#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
#include <GL/glxext.h>
#include <GL/glu.h>
#include <GL/freeglut.h>

#include <gp/util.h>
#include <gp/glextfuncs.h>
#include <gp/coord.h>
#include <gp/pstring.h>
#include <gp/misc.h>
#include <gp/gl-buffer.h>
#include <gp/texture-util.h>

#include "shapes.h"

int opt_lod;
bool opt_use_buffer_objects;

class Cone {
public:
Cone(){
dont_set_color = true;

bo_lod = -1;  // Set an invalid level of detail.
buffer_obj_coords = 0;
buffer_obj_norms = 0;
};
void render(pCoor base, float radius, pVect to_apex)
{
if ( opt_use_buffer_objects )
else
}
void render_proba(pCoor base, float radius, pVect to_apex)
{
/// SOLUTION:  opt_lod times as many sides.
const int sides = 10 * opt_lod;
const double delta_theta = 2 * M_PI / sides;
const double apex_height = 1;
const double vec_z = sin(alpha);
const float to_height = to_apex.mag();

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

pVect from_apex(0,0,1);
pNorm rn(from_apex,to_apex);
const float rot_angle = pangle(from_apex,to_apex);
pMatrix_Translate trans_transl(base);
pMatrix_Rotation trans_rot(rn,rot_angle);
trans_scale.rc(2,2) = to_height;
pMatrix xform = trans_transl * trans_rot * trans_scale;

glMultTransposeMatrixf(xform.a);

if ( !dont_set_color ) glColor3fv(color);

/// SOLUTION
//
//  Draw multiple (opt_lod) quad strips.
//
const float delta_height = apex_height / opt_lod;

for ( int j=0; j<opt_lod; j++ )
{
//
const float r1 = r0 + delta_rad;
const float h0 = delta_height * j;
const float h1 = h0 + delta_height;

for ( int i=0; i<=sides; i++ )
{
const double theta = delta_theta * i;
const double cos_t = cos(theta);
const double sin_t = sin(theta);
glNormal3f( cos_t, sin_t, vec_z );
glVertex3f( r1 * cos_t, r1 * sin_t, h1);
glVertex3f( r0 * cos_t, r0 * sin_t, h0);
}
glEnd();
}

glPopMatrix();
}

void render_probb(pCoor base, float radius, pVect to_apex)
{
/// SOLUTION

// First, code updates the buffer objects if the lod needed
// does not match the lod of the data stored in the buffer
// objects.
//
// Then the code renders the cone.

/// Update Buffer Object
//
if ( opt_lod != bo_lod )  // If needed lod != stored lod.
{
bo_lod = opt_lod;

// Self-sizing arrays to store coordinates and normals.
//
PStack<float> coords;
PStack<float> norms;

const int sides = 10 * opt_lod;
const double delta_theta = 2 * M_PI / sides;
const double apex_height = 1;
const double vec_z = sin(alpha);

const float delta_height = apex_height / opt_lod;

for ( int j=0; j<opt_lod; j++ )
{
const float r1 = r0 + delta_rad;
const float h0 = delta_height * j;
const float h1 = h0 + delta_height;

for ( int i=0; i<=sides; i++ )
{
const double theta = delta_theta * i;
const double cos_t = cos(theta);
const double sin_t = sin(theta);

// Add normals and coordinates to self-sizing arrays.
//
norms += cos_t;  norms += sin_t;  norms += vec_z;
norms += cos_t;  norms += sin_t;  norms += vec_z;
coords += r1 * cos_t; coords += r1 * sin_t;  coords += h1;
coords += r0 * cos_t; coords += r0 * sin_t;  coords += h0;
}
}

// Save number of coordinates. (Needed by glDrawArrays)
num_coords = coords.occ() / 3;

// Generate buffer object names if we need them.
//
if ( !buffer_obj_coords )
{
glGenBuffers(1,&buffer_obj_coords);
glGenBuffers(1,&buffer_obj_norms);
}

// Copy coordinates to buffer object.
//
glBindBuffer(GL_ARRAY_BUFFER, buffer_obj_coords);
glBufferData
(GL_ARRAY_BUFFER,
coords.occ() * sizeof(float),
coords.get_storage(),
GL_STATIC_DRAW);

// Copy normals to buffer object.
//
glBindBuffer(GL_ARRAY_BUFFER, buffer_obj_norms);
glBufferData
(GL_ARRAY_BUFFER,
norms.occ() * sizeof(float),
norms.get_storage(),
GL_STATIC_DRAW);
}

// Compute transform so generic cone matches the cone that was
// requested.
//
const float to_height = to_apex.mag();
pVect from_apex(0,0,1);
pNorm rn(from_apex,to_apex);
const float rot_angle = pangle(from_apex,to_apex);
pMatrix_Translate trans_transl(base);
pMatrix_Rotation trans_rot(rn,rot_angle);
trans_scale.rc(2,2) = to_height;
pMatrix xform = trans_transl * trans_rot * trans_scale;

// Specify our transformation to OpenGL.
//
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMultTransposeMatrixf(xform.a);

if ( !dont_set_color ) glColor3fv(color);

// Tell OpenGL to get coordinates and normals from buffer objects.
//
glBindBuffer(GL_ARRAY_BUFFER, buffer_obj_coords);
glVertexPointer( 3, GL_FLOAT, 0, NULL);
glEnableClientState(GL_VERTEX_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER, buffer_obj_norms);
glNormalPointer(GL_FLOAT,0,NULL);
glEnableClientState(GL_NORMAL_ARRAY);

// Draw the cones. (There will be minor flaws since 1 strip used.)

glBindBuffer(GL_ARRAY_BUFFER,0);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

glPopMatrix();
}

void set_color(const pColor &c) { color = c;  dont_set_color = false; }

bool dont_set_color;
pColor color;
pCoor light_pos;

GLuint buffer_obj_coords, buffer_obj_norms;
int bo_lod;
int num_coords;
};

///
/// Main Data Structures
///
//
// class World: All data about scene.

class World;

// Object Holding Ball State
//
class Ball {
public:
pCoor position;
pVect velocity;
float mass, mass_inv;
bool contact;
void push(pVect amt);
void translate(pVect amt);
void stop();
void freeze();
};

#include "hw2-graphics.cc"

void
World::init()
{
opt_lod = 1;
variable_control.insert(opt_lod,"Level of Detail",1,1);

opt_use_buffer_objects = false;

chain_length = 10;
balls = new Ball[chain_length];

distance_relaxed = 25 / chain_length;
opt_spring_constant = 1000;
variable_control.insert(opt_spring_constant,"Spring Constant");

opt_gravity_accel = 9.8;
opt_gravity = true;
gravity_accel = pVect(0,-opt_gravity_accel,0);
variable_control.insert(opt_gravity_accel,"Gravity");

opt_air_resistance = 0.001;
variable_control.insert(opt_air_resistance,"Air Resistance");

world_time = 0;
time_step_count = 0;
last_frame_wall_time = time_wall_fp();
frame_timer.work_unit_set("Steps / s");

init_graphics();

light_location = pCoor(30.8,12.8,-9.9);

cone_fixed.set_color(color_chocolate);
cone_fixed_position = pCoor(28.5,0,-8.4);
cone_fixed_height = 40;

ball_setup_2();
}

///
/// Physical Simulation Code
///

/// Initialize Simulation
//
void
World::ball_setup_1()
{
// Set initial position to a visibly interesting point.
//
pCoor next_pos(12.5,0.1,-13.7);

for ( int i=0; i<chain_length; i++ )
{
// Put the first ball on top because that one can be moved and locked.
//
Ball* const ball = &balls[chain_length-i-1];
ball->position = next_pos;
ball->velocity = pVect(0,0,0);
ball->mass = 4/3.0 * M_PI * pow(ball->radius,3);
ball->contact = false;
next_pos += pVect(0.1,distance_relaxed,0);
}
}

void
World::ball_setup_2()
{
// Arrange and size balls to form a pendulum.
//
pCoor next_pos(13.4,21.8,-9.2);

for ( int i=0; i<chain_length; i++ )
{
// Put the first ball on top because that one can be moved and locked.
//
Ball* const ball = &balls[i];
ball->position = next_pos;
ball->velocity = pVect(0,0,0);
ball->radius = i == chain_length - 1 ? 1 : 0.5;
ball->mass = 4/3.0 * M_PI * pow(ball->radius,3);
ball->contact = false;
next_pos += pVect(distance_relaxed,0,0);
}

}

void
World::ball_setup_3()
{
}

void
World::ball_setup_4()
{
}

void
World::ball_setup_5()
{
}

/// Advance Simulation State by delta_t Seconds
//
void
World::time_step_cpu(double delta_t)
{
time_step_count++;

//
/// Compute force and update velocity of each ball.
//
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];

// Skip locked balls.
//
if ( opt_head_lock && i == 0 || opt_tail_lock && i == chain_length - 1 )
{
ball->velocity = pVect(0,0,0);
continue;
}

pVect force(0,0,0);

// Gravitational Force
//
force += ball->mass * gravity_accel;

// Spring Force from Neighbor Balls
//
for ( int n_idx = i-1; n_idx <= i+1; n_idx += 2 )
{
if ( n_idx < 0 ) continue;
if ( n_idx == chain_length ) break;

Ball* const neighbor_ball = &balls[n_idx];

// Construct a normalized (Unit) Vector from ball to neighbor.
//
pNorm ball_to_neighbor(ball->position,neighbor_ball->position);

// Compute the speed of ball towards neighbor_ball.
//
pVect delta_v = neighbor_ball->velocity - ball->velocity;
float delta_s = dot( delta_v, ball_to_neighbor );

// Compute by how much the spring is stretched (positive value)
// or compressed (negative value).
//
const float spring_stretch =
ball_to_neighbor.magnitude - distance_relaxed;

// Determine whether spring is gaining energy (whether its length
// is getting further from its relaxed length).
//
const bool gaining_e = ( delta_s > 0.0 ) == ( spring_stretch > 0 );

// Use a smaller spring constant when spring is loosing energy,
// a quick and dirty way of simulating energy loss due to spring
// friction.
//
const float spring_constant =
gaining_e ? opt_spring_constant : opt_spring_constant * 0.7;

force += spring_constant * spring_stretch * ball_to_neighbor;
}

// Update Velocity
//
// This code assumes that force on ball is constant over time
// step. This is clearly wrong when balls are moving with
// respect to each other because the springs are changing
// length. This inaccuracy will make the simulation unstable
// when spring constant is large for the time step.
//
ball->velocity += ( force / ball->mass ) * delta_t;

// Air Resistance
//
const double fs = pow(1+opt_air_resistance,-delta_t);
ball->velocity *= fs;
}

///
/// Update Position of Each Ball
///

for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];

// Update Position
//
// Assume that velocity is constant.
//
ball->position += ball->velocity * delta_t;

// Possible Collision with Platform
//

// Skip if collision impossible.
//
if ( !platform_collision_possible(ball->position) ) continue;
if ( ball->position.y >= 0 ) continue;

// Snap ball position to surface.
//
ball->position.y = 0;

// Reflect y (vertical) component of velocity, with a reduction
// due to energy lost in the collision.
//
if ( ball->velocity.y < 0 )
ball->velocity.y = - 0.9 * ball->velocity.y;
}
}

bool
World::platform_collision_possible(pCoor pos)
{
// Assuming no motion in x or z axes.
//
return pos.x >= platform_xmin && pos.x <= platform_xmax
&& pos.z >= platform_zmin && pos.z <= platform_zmax;
}

/// External Modifications to State
//
//   These allow the user to play with state while simulation
//   running.

// Move the ball.
//
void Ball::translate(pVect amt) {position += amt;}

// Add velocity to the ball.
//
void Ball::push(pVect amt) {velocity += amt;}

// Set the velocity to zero.
//
void Ball::stop() {velocity = pVect(0,0,0); }

// Set the velocity and rotation (not yet supported) to zero.
//
void Ball::freeze() {velocity = pVect(0,0,0); }

void World::balls_translate(pVect amt,int b){balls[b].translate(amt);}
void World::balls_push(pVect amt,int b){balls[b].push(amt);}
void World::balls_translate(pVect amt)
{ for(int i=0;i<chain_length;i++)balls[i].translate(amt);}
void World::balls_push(pVect amt)
{ for(int i=0;i<chain_length;i++)balls[i].push(amt);}
void World::balls_stop()
{ for(int i=0;i<chain_length;i++)balls[i].stop();}
void World::balls_freeze(){balls_stop();}

void
World::frame_callback()
{
// This routine called whenever window needs to be updated.

const double time_now = time_wall_fp();

if ( !opt_pause || opt_single_frame || opt_single_time_step )
{

// Amount of time since the user saw the last frame.
//
const double wall_delta_t = time_now - last_frame_wall_time;

const double time_step_duration = 0.0001;

// Compute amount by which to advance simulation state for this frame.
//
const double duration =
opt_single_time_step ? time_step_duration :
opt_single_frame ? 1/30.0 :
wall_delta_t;

const double world_time_target = world_time + duration;

while ( world_time < world_time_target )
{
time_step_cpu(time_step_duration);
world_time += time_step_duration;
}

// Reset these, just in case they were set.
//
opt_single_frame = opt_single_time_step = false;
}

last_frame_wall_time = time_now;
render();
}

int
main(int argv, char **argc)
{
pOpenGL_Helper popengl_helper(argv,argc);
World world(popengl_helper);

popengl_helper.rate_set(30);
popengl_helper.display_cb_set(world.frame_callback_w,&world);
}
```