All,

Some "flower arranging" on Greg's version - adding adjustable FPS
target, and a checkbox for selecting the idle loop on/off.

Probably has bugs, and only tested on WinXP so far, but seems to work as
expected. Needs more tidying - in parcticular the FixedFPS and IdleFPS
methods duplicate a small chunk of code needlessly...

Note that with the timers used to set the FPS on Windows, I see distinct
values of "attainable" FPS, so there is some sort of interaction with
what we are doing and the Windows timer logic, I think. AFAIK linux and
OSX seem to be fine in this respect.

I have no idea what...


--------------------------------------------------
//
// 3-D gear wheels.  This program is in the public domain.
//
// Command line options:
//    -info      print GL implementation information
//    -exit      automatically exit after 30 seconds
//
//
// Brian Paul
//

// Conversion to GLUT by Mark J. Kilgard
// Conversion to FLTK by IMM + Greg Ercolano
// Compile as:  fltk-config --use-gl --compile fl_gears.cxx
//

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <FL/Fl.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Spinner.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Box.H>
#include <FL/gl.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

static int autoexit = 0;
static int infoflag = 0;
static float desired_frame_rate = 100.0;
static float per_frame_time = 1.0 / desired_frame_rate;

static const float pos[4]   = { 5.0, 5.0, 10.0, 0.0 };
static const float red[4]   = { 0.8, 0.1, 0.0, 1.0  };
static const float green[4] = { 0.0, 0.8, 0.2, 1.0  };
static const float blue[4]  = { 0.2, 0.2, 1.0, 1.0  };

static Fl_Output *fps_out=NULL,
                 *fps_max=NULL,
                 *fps_avg=NULL;
static Fl_Spinner *set_fps=NULL;
static Fl_Check_Button *set_idle=NULL;

class MyGlWindow : public Fl_Gl_Window {
  int gear1, gear2, gear3, frames, timerstate;
  time_t last_tod;
  float f_max, f_avg;
  float view_rotx, view_roty, view_rotz, angle;
  // Handle the redrawing of the scene (from the idle loop)
  static void IdleFPS(void *userdata) {
    MyGlWindow *glwin = (MyGlWindow*)userdata;
    static short fixed_angle = 0;
    fixed_angle += 500;
    glwin->angle = (double)(fixed_angle) * 180.0 / 32768.0;
    glwin->redraw();
  }
  // Handle fixed FPS rate timer
  static void FixedFPS(void *userdata) {
    MyGlWindow *glwin = (MyGlWindow*)userdata;
    Fl::repeat_timeout(per_frame_time, FixedFPS, userdata);
    static short fixed_angle = 0;
    fixed_angle += 500;
    glwin->angle = (double)(fixed_angle) * 180.0 / 32768.0;
    glwin->redraw();
  }
  // Handle FPS metering timer
  static void MeasureFPS(void *userdata) {
    MyGlWindow *glwin = (MyGlWindow*)userdata;
    time_t new_tod = time(NULL);
    time_t delta_tod = new_tod - glwin->last_tod;
    float fps = glwin->frames / (float)delta_tod;
    // Show rate info on console
    printf("%d frames in %d seconds = %6.3f FPS\n", glwin->frames,
(int)delta_tod, fps);
    fflush(stdout);
    // Update fps meters
    {
      char s[80];
      if (fps > glwin->f_max) {
        glwin->f_max = fps;
        sprintf(s, "%.2f", glwin->f_max); fps_max->value(s);
      }
      if (glwin->f_avg == 0 ) {
        glwin->f_avg = fps;
      } else {
        glwin->f_avg = (glwin->f_avg * 0.7) + (fps * 0.3);
      }
      glwin->last_tod = new_tod;
      sprintf(s, "%.2f", glwin->f_avg); fps_avg->value(s);
      sprintf(s, "%.2f", fps);          fps_out->value(s);
    }
    // Auto exit?
    if (autoexit) {
      autoexit = autoexit - (int)delta_tod;
      if (autoexit <= 0) exit(0);
    }
    glwin->frames = 0;  // rezero frame counter
    Fl::repeat_timeout(5.0, MeasureFPS, userdata);
  }

  // Start/Stop timers
  void Timers(int newstate) {
    // Timer state changing?
    if ( newstate == timerstate ) return;
    int do_idle = 0;
    if(set_idle) do_idle = set_idle->value();
    if ( newstate ) {
      // Enable timers?
      Fl::add_timeout(5.0, MeasureFPS, (void*)this); // compute FPS
every 5 secs
      if(do_idle) {
        Fl::add_idle(IdleFPS, (void*)this); // draw as fast as possible
      } else {
        Fl::add_timeout(per_frame_time, FixedFPS, (void*)this); // draw
at desired FPS
      }
      frames = 0;
      f_max  = 0.0;
      f_avg  = 0.0;
      last_tod = time(NULL);
    } else {
      // Disable timers?
      Fl::remove_timeout(MeasureFPS, (void*)this);
      if(do_idle) {
        Fl::remove_idle(IdleFPS, (void*)this);
      } else {
        Fl::remove_timeout(FixedFPS,(void*)this);
      }
    }
    timerstate = newstate;
  }
public:
  // CTOR
  MyGlWindow(int X,int Y,int W,int H,const char*L=0) :
Fl_Gl_Window(X,Y,W,H,L) {
    // Init vars
    frames     = 0;
    f_max      = 0.0;
    f_avg      = 0.0;
    last_tod   = 0;
    timerstate = 0;
    view_rotx  = 20.0;
    view_roty  = 30.0;
    view_rotz  = 0.0;
    angle      = 0.0;
    // Start fps timers
    Timers(1);
  }

private:
  // Draw a gear wheel.  You'll probably want to call this function when
  // building a display list since we do a lot of trig here.
  //
  //     Input:  inner_radius - radius of hole at center
  //        outer_radius - radius at center of teeth
  //        width - width of gear
  //        teeth - number of teeth
  //        tooth_depth - depth of tooth
  //
  void gear(float inner_radius,
            float outer_radius,
            float width,
            int teeth, float tooth_depth) {
    int i;
    float r0 = inner_radius;
    float r1 = outer_radius - tooth_depth / 2.0;
    float r2 = outer_radius + tooth_depth / 2.0;
    float da = 2.0 * M_PI / teeth / 4.0;
    // draw front face
    glShadeModel(GL_FLAT);
    glNormal3f(0.0, 0.0, 1.0);
    glBegin(GL_QUAD_STRIP);
    for (i = 0; i <= teeth; i++) {
      float angle = i * 2.0 * M_PI / teeth;
      glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
      glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
      if (i < teeth) {
        glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
        glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
width * 0.5);
      }
    }
    glEnd();
    // draw front sides of teeth
    glBegin(GL_QUADS);
      da = 2.0 * M_PI / teeth / 4.0;
      for (i = 0; i < teeth; i++) {
        float angle = i * 2.0 * M_PI / teeth;
        glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
        glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width *
0.5);
        glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
width * 0.5);
        glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
width * 0.5);
      }
    glEnd();
    // draw back face
    glNormal3f(0.0, 0.0, -1.0);
    glBegin(GL_QUAD_STRIP);
      for (i = 0; i <= teeth; i++) {
        float angle = i * 2.0 * M_PI / teeth;
        glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
        glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
        if (i < teeth) {
          glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
-width * 0.5);
          glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
        }
      }
    glEnd();
    // draw back sides of teeth
    glBegin(GL_QUADS);
      da = 2.0 * M_PI / teeth / 4.0;
      for (i = 0; i < teeth; i++) {
        float angle = i * 2.0 * M_PI / teeth;
        glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
-width * 0.5);
        glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
-width * 0.5);
        glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width *
0.5);
        glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
      }
    glEnd();
    // draw outward faces of teeth
    glBegin(GL_QUAD_STRIP);
      for (i = 0; i < teeth; i++) {
        float angle = i * 2.0 * M_PI / teeth;
        glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
        glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
        float u = r2 * cos(angle + da) - r1 * cos(angle);
        float v = r2 * sin(angle + da) - r1 * sin(angle);
        float len = sqrt(u * u + v * v);
        u /= len;
        v /= len;
        glNormal3f(v, -u, 0.0);
        glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width *
0.5);
        glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width *
0.5);
        glNormal3f(cos(angle), sin(angle), 0.0);
        glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
width * 0.5);
        glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
-width * 0.5);
        u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
        v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
        glNormal3f(v, -u, 0.0);
        glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
width * 0.5);
        glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
-width * 0.5);
        glNormal3f(cos(angle), sin(angle), 0.0);
      }
      glVertex3f(r1 * cos(0.0), r1 * sin(0.0), width * 0.5);
      glVertex3f(r1 * cos(0.0), r1 * sin(0.0), -width * 0.5);
    glEnd();
    // draw inside radius cylinder
    glShadeModel(GL_SMOOTH);
    glBegin(GL_QUAD_STRIP);
    for (i = 0; i <= teeth; i++) {
      float angle = i * 2.0 * M_PI / teeth;
      glNormal3f(-cos(angle), -sin(angle), 0.0);
      glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
      glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
    }
    glEnd();
  }
  void draw() {
    // First time? init viewport, etc.
    if (!valid()) {
      valid(1);
      glEnable(GL_DEPTH_TEST);
      glClearColor(0.0, 0.0, 0.4, 0.0);
      {
        GLfloat H = (float)h() / (float)w();
        glViewport(0, 0, w(), h());
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glFrustum(-1.0, 1.0, -H, H, 5.0, 60.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0.0, 0.0, -40.0);
      }
      glLightfv(GL_LIGHT0, GL_POSITION, pos);
      glEnable(GL_CULL_FACE);
      glEnable(GL_LIGHTING);
      glEnable(GL_LIGHT0);
      glEnable(GL_DEPTH_TEST);
      static int init = 0;
      if ( ! init ) {
        init = 1;
        // make gear1
        gear1 = glGenLists(1);
        glNewList(gear1, GL_COMPILE);
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
        gear(1.0, 4.0, 1.0, 20, 0.7);
        glEndList();
        // make gear2
        gear2 = glGenLists(1);
        glNewList(gear2, GL_COMPILE);
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
        gear(0.5, 2.0, 2.0, 10, 0.7);
        glEndList();
        // make gear3
        gear3 = glGenLists(1);
        glNewList(gear3, GL_COMPILE);
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
        gear(1.3, 2.0, 0.5, 10, 0.7);
        glEndList();
        // post init
        glEnable(GL_NORMALIZE);
      }
      if ( infoflag ) {
        printf("GL_RENDERER   = %s\n", (char
*)glGetString(GL_RENDERER));
        printf("GL_VERSION    = %s\n", (char *)glGetString(GL_VERSION));
        printf("GL_VENDOR     = %s\n", (char *)glGetString(GL_VENDOR));
        printf("GL_EXTENSIONS = %s\n", (char
*)glGetString(GL_EXTENSIONS));
        fflush(stdout);
      }
    }
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Draw the gears in current viewing position
    glPushMatrix();
      glRotatef(view_rotx, 1.0, 0.0, 0.0);
      glRotatef(view_roty, 0.0, 1.0, 0.0);
      glRotatef(view_rotz, 0.0, 0.0, 1.0);
      // Draw gear#1
      glPushMatrix();
        glTranslatef(-3.0, -2.0, 0.0);
        glRotatef(angle, 0.0, 0.0, 1.0);
        glCallList(gear1);
      glPopMatrix();
      // Draw gear#2
      glPushMatrix();
        glTranslatef(3.1, -2.0, 0.0);
        glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
        glCallList(gear2);
      glPopMatrix();
      // Draw gear#3
      glPushMatrix();
        glTranslatef(-3.1, 4.2, 0.0);
        glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
        glCallList(gear3);
      glPopMatrix();
    glPopMatrix();
    frames++;           // count the redraws
  }
public:
  // Fltk event handler
  int handle(int e) {
    int ret = 0;
    switch (e) {
      case FL_HIDE:  Timers(0); break;
      case FL_SHOW:  Timers(1); break;
      case FL_ENTER: ret = 1;   break;
      case FL_LEAVE: ret = 1;   break;
      case FL_FOCUS: ret = 1;   break;
      case FL_KEYBOARD:
        // Keyboard keys
        switch ( Fl::event_key() ) {
          case FL_Up:    view_rotx -= 5.0; ret = 1; break;
          case FL_Down:  view_rotx += 5.0; ret = 1; break;
          case FL_Left:  view_roty -= 5.0; ret = 1; break;
          case FL_Right: view_roty += 5.0; ret = 1; break;
          case 'z':
            if (Fl::event_state() & FL_SHIFT) view_rotz -= 5.0;
            else view_rotz += 5.0;
            ret = 1;
            break;
        }
        break;
    }
    ret |= Fl_Gl_Window::handle(e);
    return(ret);
  }
};

// FPS target setting callback
static void cb_set_fps(Fl_Spinner*, void*) {
  desired_frame_rate = set_fps->value();
  per_frame_time = 1.0 / desired_frame_rate;
} // cb_set_fps

// set idle button callback
static void cb_set_idle(Fl_Check_Button*, void *v) {
  MyGlWindow *glwin = (MyGlWindow *)v;
  int do_idle = set_idle->value();
  if(do_idle) {
    set_fps->deactivate();
    Fl::remove_timeout(glwin->FixedFPS,glwin);
    Fl::add_idle(glwin->IdleFPS,glwin);
  }
  else {
    set_fps->activate();
    Fl::remove_idle(glwin->IdleFPS,glwin);
    Fl::add_timeout(per_frame_time,glwin->FixedFPS,glwin);
  }
} // cb_set_idle

int main(int argc, char *argv[]) {
  Fl_Window window(320, 440, "Gears");
  window.begin();
    MyGlWindow glwin(10,10,300,300);
    glwin.end(); // Just in case!
    // Make a group to enclose the outputs and legends,
    // to control resizing behaviour
    Fl_Group controls(2,312,316,126);
    controls.begin();
    controls.box(FL_FLAT_BOX);
      // Create output displays for various fps metering
      fps_out = new Fl_Output( 60, 318+0,  140, 25, "FPS:");
      fps_out->value("Calculating..");
      fps_out->clear_visible_focus();
      fps_max = new Fl_Output( 60, 318+30, 140, 25, "MAX:");
      fps_max->value("Calculating..");
      fps_max->clear_visible_focus();
      fps_avg = new Fl_Output( 60, 318+60, 140, 25, "AVG:");
      fps_avg->value("Calculating..");
      fps_avg->clear_visible_focus();

      // FPS target control
      set_fps = new Fl_Spinner(140,318+90, 60, 25, "Target");
      set_fps->minimum(5);
      set_fps->maximum(200);
      set_fps->value(100);
      set_fps->callback((Fl_Callback*)cb_set_fps);
      set_fps->when(FL_WHEN_CHANGED);
      set_fps->clear_visible_focus();

      // Toggle between idle loop or target-framerate
      set_idle = new Fl_Check_Button(60,318+90,25, 25, "Idle");
      set_idle->down_box(FL_DOWN_BOX);
      set_idle->align(Fl_Align(FL_ALIGN_LEFT));
      set_idle->tooltip("Use the idle loop, rather \nthan the target
FPS");
      set_idle->callback((Fl_Callback*)cb_set_idle, (void *)&glwin);
      set_idle->when(FL_WHEN_CHANGED);
      set_idle->clear_visible_focus();

      // Legend
      Fl_Box box(210,318,100,115,"Keys:\n  Up/Dn\n  Lt/Rt\n  z/Z/Esc");
      box.align(FL_ALIGN_INSIDE | FL_ALIGN_TOP | FL_ALIGN_LEFT);
      box.box(FL_ENGRAVED_BOX);
    controls.end();
    controls.resizable(box);
  window.end();
  window.resizable(glwin);
  window.show();
  // Command line args
  for ( int i=1; i<argc; i++ ) {
    if (strcmp(argv[i], "-info")==0) { infoflag = 1; }
    else if (strcmp(argv[i], "-exit")==0) {
      autoexit = 35;
      printf("Auto Exit after approx. %i seconds.\n", autoexit);
      fflush(stdout);
    }
  }
  return(Fl::run());
}
// End of File



SELEX Galileo Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 
3EL
A company registered in England & Wales.  Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

_______________________________________________
fltk mailing list
fltk@easysw.com
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to