#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/freeglut.h>
#include <gp/util.h>
#include <gp/coord.h>
#include <gp/shader.h>
#include <gp/pstring.h>
#include <gp/misc.h>
#include <gp/gl-buffer.h>
#include <gp/texture-util.h>
#include "shapes.h"
class World;
class Ball {
public:
pCoor position;
pVect velocity;
float mass;
float radius;
bool contact;
void push(pVect amt);
void translate(pVect amt);
void stop();
void freeze();
};
#include "demo-2-springs-graphics.cc"
void
World::init()
{
chain_length = 20;
balls = new Ball[chain_length];
distance_relaxed = 15.0 / 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_time_step_easy = false;
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();
ball_setup_2();
}
void
World::ball_setup_1()
{
pCoor bottom_pos(12.5,distance_relaxed,-13.7);
pVect ball_separation(0, distance_relaxed, 0);
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[chain_length-i-1];
ball->position = bottom_pos + i * ball_separation;
ball->velocity = pVect(0,0,0);
ball->radius = 0.3 * distance_relaxed;
ball->mass = 4/3.0 * M_PI * pow(ball->radius,3);
ball->contact = false;
}
opt_head_lock = true;
}
void
World::ball_setup_2()
{
pCoor first_ball_pos(13.4,17.8,-9.2);
pVect ball_separation(distance_relaxed, 0, 0);
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];
ball->position = first_ball_pos + i * ball_separation;
ball->velocity = pVect(0,0,0);
ball->radius = ( i == chain_length - 1 ? 0.6 : 0.3 ) * distance_relaxed;
ball->mass = 4/3.0 * M_PI * pow(ball->radius,3);
ball->contact = false;
}
opt_head_lock = true;
}
void
World::ball_setup_3()
{
}
void
World::ball_setup_4()
{
}
void
World::ball_setup_5()
{
}
void
World::time_step_cpu_easy(double delta_t)
{
time_step_count++;
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];
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);
force += ball->mass * gravity_accel;
for ( int direction: { -1, +1 } )
{
const int n_idx = i + direction;
if ( n_idx < 0 ) continue;
if ( n_idx == chain_length ) continue;
Ball* const neighbor_ball = &balls[n_idx];
#if 1
pVect v12 =
pVect( neighbor_ball->position.x - ball->position.x,
neighbor_ball->position.y - ball->position.y,
neighbor_ball->position.z - ball->position.z );
double l = sqrt( v12.x * v12.x + v12.y * v12.y + v12.z * v12.z );
pVect u12 = pVect( v12.x/l, v12.y/l, v12.z/l );
pVect u12_easy_way = pNorm( ball->position, neighbor_ball->position );
pNorm u12_easier_way( ball->position, neighbor_ball->position );
#endif
pNorm ball_to_neighbor( ball->position, neighbor_ball->position );
const float distance_between_balls = ball_to_neighbor.magnitude;
const float spring_stretch =
distance_between_balls - distance_relaxed;
force += opt_spring_constant * spring_stretch * ball_to_neighbor;
}
ball->velocity += ( force / ball->mass ) * delta_t;
const double fs = pow(1+opt_air_resistance,-delta_t);
ball->velocity *= fs;
}
#if 0
for ( int i=0; i<chain_length; i++ )
for ( int j=0; j<chain_length; j++ )
{
Ball* const balli = &balls[i];
Ball* const ballj = &balls[j];
pNorm uij(balli->position,ballj->position);
if ( uij.magnitude < balli->radius + ballj->radius ) continue;
}
#endif
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];
ball->position += ball->velocity * delta_t;
if ( !platform_collision_possible(ball->position) ) continue;
if ( ball->position.y >= 0 ) continue;
if ( ball->velocity.y < 0 )
ball->velocity.y = - 0.9 * ball->velocity.y;
}
}
void
World::time_step_cpu_full(double delta_t)
{
time_step_count++;
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];
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);
force += ball->mass * gravity_accel;
for ( int direction: { -1, +1 } )
{
const int n_idx = i + direction;
if ( n_idx < 0 ) continue;
if ( n_idx == chain_length ) break;
Ball* const neighbor_ball = &balls[n_idx];
pNorm ball_to_neighbor( ball->position, neighbor_ball->position );
const float distance_between_balls = ball_to_neighbor.magnitude;
const float spring_stretch =
distance_between_balls - distance_relaxed;
pVect delta_v = neighbor_ball->velocity - ball->velocity;
float delta_s = dot( delta_v, ball_to_neighbor );
const bool gaining_e = ( delta_s > 0.0 ) == ( spring_stretch > 0 );
const float spring_constant =
gaining_e ? opt_spring_constant : opt_spring_constant * 0.7;
force += spring_constant * spring_stretch * ball_to_neighbor;
}
ball->velocity += ( force / ball->mass ) * delta_t;
const double fs = pow(1+opt_air_resistance,-delta_t);
ball->velocity *= fs;
}
for ( int i=0; i<chain_length; i++ )
{
Ball* const ball = &balls[i];
ball->position += ball->velocity * delta_t;
if ( !platform_collision_possible(ball->position) ) continue;
if ( ball->position.y >= 0 ) continue;
ball->position.y = 0;
if ( ball->velocity.y < 0 )
ball->velocity.y = - 0.9 * ball->velocity.y;
}
}
bool
World::platform_collision_possible(pCoor pos)
{
return pos.x >= platform_xmin && pos.x <= platform_xmax
&& pos.z >= platform_zmin && pos.z <= platform_zmax;
}
void Ball::translate(pVect amt) {position += amt;}
void Ball::push(pVect amt) {velocity += amt;}
void Ball::stop() {velocity = pVect(0,0,0); }
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()
{
const double time_now = time_wall_fp();
if ( !opt_pause || opt_single_frame || opt_single_time_step )
{
const double wall_delta_t = time_now - last_frame_wall_time;
const double time_step_duration = 0.0001;
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 )
{
if ( opt_time_step_easy )
time_step_cpu_easy(time_step_duration);
else
time_step_cpu_full(time_step_duration);
world_time += time_step_duration;
}
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);
# ifdef __OPTIMIZE__
glDisable(GL_DEBUG_OUTPUT);
# else
glEnable(GL_DEBUG_OUTPUT);
# endif
glDebugMessageControl(GL_DONT_CARE,GL_DONT_CARE,
GL_DEBUG_SEVERITY_NOTIFICATION,0,NULL,false);
popengl_helper.rate_set(30);
popengl_helper.display_cb_set(world.frame_callback_w,&world);
}