#if 0
:Def: Vertex Pulling
#endif
#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"
enum VTX_Spec_Method { VM_Individual, VM_Strip, VM_Array, VM_Buffer, VM_SIZE };
const char* const spec_method_str[] =
{"INDIVIDUAL", "STRIP", "ARRAY", "BUFFER"};
class World {
public:
World(pOpenGL_Helper &fb):ogl_helper(fb){init();}
void init();
static void render_w(void *moi){ ((World*)moi)->render(); }
void render();
void cb_keyboard();
void modelview_update();
pOpenGL_Helper& ogl_helper;
pVariable_Control variable_control;
pFrame_Timer frame_timer;
pCoor light_location;
float opt_light_intensity;
GLuint gpu_buffer;
bool gpu_buffer_stale;
enum { MI_Eye, MI_Light, MI_Ball, MI_Ball_V, MI_COUNT } opt_move_item;
vector<pCoor> sphere_coords;
int opt_slices;
pCoor sphere_location;
float sphere_size;
pCoor eye_location;
pVect eye_direction;
pMatrix modelview;
int opt_method;
bool opt_recompute;
bool opt_scene, opt_ambient, opt_specular, opt_diffuse, opt_emissive;
float opt_shininess;
int pf_vertices;
int pf_triangles;
int pf_fragments;
int pf_gpu_cpu_bytes;
int pf_mem_gpu_bytes;
};
void
World::init()
{
frame_timer.work_unit_set("Steps / s");
gpu_buffer = 0;
gpu_buffer_stale = true;
opt_method = 0;
opt_recompute = true;
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;
opt_slices = 20;
variable_control.insert(opt_slices,"Sphere Slices (level of detail)");
opt_scene = false;
opt_ambient = false;
opt_diffuse = true;
opt_specular = false;
opt_emissive = false;
opt_shininess = 1;
variable_control.insert(opt_light_intensity,"Light Intensity");
variable_control.insert(opt_shininess, "Shininess");
opt_move_item = MI_Eye;
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::render()
{
cb_keyboard();
frame_timer.frame_start();
glClearColor(0,0,0,0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glShadeModel(GL_SMOOTH);
const double time_now = time_wall_fp();
const bool blink_visible __attribute__((unused)) = int64_t(time_now*3) & 1;
# define BLINK(txt,pad) ( blink_visible ? txt : pad )
ogl_helper.fbprintf("%s\n",frame_timer.frame_rate_text_get());
ogl_helper.fbprintf
("Compiled: %s\n",
#ifdef __OPTIMIZE__
"WITH OPTIMIZATION"
#else
BLINK("WITHOUT OPTIMIZATION","")
#endif
);
ogl_helper.fbprintf
("Eye location: [%5.1f, %5.1f, %5.1f] "
"Eye direction: [%+.2f, %+.2f, %+.2f] "
"Light location: [%5.1f, %5.1f, %5.1f] "
"Sphere Location[%5.1f, %5.1f, %5.1f]\n",
eye_location.x, eye_location.y, eye_location.z,
eye_direction.x, eye_direction.y, eye_direction.z,
light_location.x, light_location.y, light_location.z,
sphere_location.x, sphere_location.y, sphere_location.z);
pVariable_Control_Elt* const cvar = variable_control.current;
ogl_helper.fbprintf("VAR %s = %.5f (TAB or '`' to change, +/- to adjust)\n",
cvar->name,cvar->get_val());
ogl_helper.fbprintf
("Vertex Specification Method: %s ('m' to change), Recompute Coords: %s ('r')\n",
spec_method_str[opt_method], opt_recompute ? "YES" : "NO");
ogl_helper.fbprintf
("Estimated triangles: %8s Vertices: %8s Data: %7.3f kiB/frame\n",
commaize(pf_triangles).c_str(),
commaize(pf_vertices).c_str(), pf_gpu_cpu_bytes / 1024.0);
const int win_width = ogl_helper.get_width();
const int win_height = ogl_helper.get_height();
const float aspect = float(win_width) / win_height;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLoadTransposeMatrixf(modelview);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-.8,.8,-.8/aspect,.8/aspect,1,5000);
glEnable(GL_LIGHTING);
pColor white(1,1,1);
pColor red(1,0,0);
pColor gray(0x303030);
pColor dark(0);
pColor ambient_color(0x555555);
const pColor lsu_spirit_purple(0x580da6);
const pColor lsu_spirit_gold(0xf9b237);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, light_location);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white * opt_light_intensity);
pError_Check();
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,1);
glEnable(GL_COLOR_MATERIAL);
glColor3ub( 0x58, 0x0d, 0xa6);
glBegin(GL_TRIANGLES);
pNorm tri_norm = cross(pCoor(1.5,0,-3.2),pCoor(0,5,-5),pCoor(9,6,-9));
glNormal3fv(tri_norm);
glVertex3f( 1.5, 0, -3.2 );
glVertex3f( 0, 5, -5 );
glVertex3f( 9, 6, -9 );
glEnd();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(sphere_location.x,sphere_location.y,sphere_location.z);
glScalef(sphere_size,sphere_size,sphere_size);
glColor3ub( 0x80, 0x80, 0x80);
glDisable(GL_COLOR_MATERIAL);
glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE, lsu_spirit_purple);
glEnable(GL_COLOR_MATERIAL);
if ( sphere_coords.empty() )
{
const float delta_eta = M_PI / opt_slices;
for ( float eta = 0; eta < M_PI - 0.0001 - delta_eta; eta += delta_eta )
{
const float eta1 = eta + delta_eta;
const float y0 = cosf(eta), y1 = cosf(eta1);
const float slice_r0 = sinf(eta), slice_r1 = sinf(eta1);
const float delta_theta = delta_eta * slice_r1;
sphere_coords.emplace_back( slice_r1, y1, 0 );
sphere_coords.emplace_back( slice_r0, y0, 0 );
for ( float theta = 0; theta < 2 * M_PI; theta += delta_theta )
{
const float theta1 = theta + delta_theta;
sphere_coords.emplace_back
( slice_r1 * cosf(theta1), y1, slice_r1 * sinf(theta1) );
sphere_coords.emplace_back
( slice_r0 * cosf(theta1), y0, slice_r0 * sinf(theta1) );
}
}
}
const int pf_bytes_per_vec3 = 3 * sizeof(float);
const int coords_size = sphere_coords.size();
switch ( opt_method ) {
case VM_Individual:
{
glBegin(GL_TRIANGLES);
glColor3fv(lsu_spirit_gold);
for ( int i=0; i<coords_size-2; i++ )
{
glNormal3fv( sphere_coords[i] );
glVertex3fv( sphere_coords[i + 0] );
glVertex3fv( sphere_coords[i + 1 + ( i & 1 ) ] );
glVertex3fv( sphere_coords[i + 2 - ( i & 1 ) ] );
}
glEnd();
pf_vertices = coords_size * 3;
pf_triangles = coords_size;
pf_gpu_cpu_bytes = ( 3 * pf_triangles * pf_bytes_per_vec3 + pf_triangles * pf_bytes_per_vec3 );
break;
}
case VM_Strip:
{
glBegin(GL_TRIANGLE_STRIP);
glColor3fv(lsu_spirit_gold);
for ( int i=0; i<coords_size; i++ )
{
glNormal3fv( sphere_coords[i] );
glVertex3fv( sphere_coords[i] );
}
glEnd();
pf_vertices = coords_size;
pf_triangles = coords_size;
pf_gpu_cpu_bytes = ( pf_triangles * pf_bytes_per_vec3 + pf_triangles * pf_bytes_per_vec3 );
break;
}
case VM_Array:
{
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer
( 3, GL_FLOAT, sizeof(sphere_coords[0]), sphere_coords.data());
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer( GL_FLOAT, sizeof(sphere_coords[0]),sphere_coords.data());
glColor3fv(lsu_spirit_gold);
glDrawArrays(GL_TRIANGLE_STRIP, 0, coords_size);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
pf_vertices = coords_size;
pf_triangles = coords_size;
pf_gpu_cpu_bytes = ( pf_triangles * pf_bytes_per_vec3 + pf_triangles * pf_bytes_per_vec3 );
break;
}
case VM_Buffer:
{
#ifdef XXX
glGenBuffers(n, &gpu_buffer);
glBufferData
(GL_ARRAY_BUFFER, n_bytes , data_ptr, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, gpu_buffer);
#endif
pf_vertices = coords_size;
pf_triangles = coords_size;
if ( gpu_buffer_stale )
{
if ( !gpu_buffer ) glGenBuffers(1,&gpu_buffer);
glBindBuffer(GL_ARRAY_BUFFER, gpu_buffer);
glBufferData
(GL_ARRAY_BUFFER, coords_size*sizeof(pCoor), sphere_coords.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
gpu_buffer_stale = false;
pf_gpu_cpu_bytes = ( pf_triangles * pf_bytes_per_vec3 + 0 );
}
else
{
pf_gpu_cpu_bytes = 0;
}
glBindBuffer(GL_ARRAY_BUFFER,gpu_buffer);
glVertexPointer( 3, GL_FLOAT, sizeof(pCoor), NULL);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT,sizeof(pCoor),NULL);
glEnableClientState(GL_NORMAL_ARRAY);
glColor3fv(lsu_spirit_gold);
glDrawArrays(GL_TRIANGLE_STRIP,0,coords_size);
glBindBuffer(GL_ARRAY_BUFFER,0);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
break;
default:
ASSERTS( false );
}
if ( opt_recompute )
{
sphere_coords.clear();
gpu_buffer_stale = true;
}
glPopMatrix();
insert_tetrahedron(light_location,0.5);
pError_Check();
frame_timer.frame_end();
ogl_helper.user_text_reprint();
glutSwapBuffers();
}
void
World::cb_keyboard()
{
if ( !ogl_helper.keyboard_key ) return;
pVect adjustment(0,0,0);
pVect user_rot_axis(0,0,0);
const float move_amt = 0.4;
const int slices_prev = opt_slices;
switch ( ogl_helper.keyboard_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 'b': case 'B': 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 'm': opt_method++;
if ( opt_method == VM_SIZE ) opt_method = 0;
break;
case 'M': if ( !opt_method ) opt_method = VM_SIZE;
opt_method--;
break;
case 9: 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",ogl_helper.keyboard_key); break;
}
if ( opt_slices != slices_prev )
{
sphere_coords.clear();
gpu_buffer_stale = true;
}
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();
}
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)
{
pOpenGL_Helper popengl_helper(argv,argc);
# ifdef __OPTIMIZE__
popengl_helper.ogl_debug_set(false);
# else
popengl_helper.ogl_debug_set(true);
# endif
World world(popengl_helper);
popengl_helper.rate_set(30);
popengl_helper.display_cb_set(world.render_w,&world);
}