CALLBACK.CXX

/* 
* (c) Copyright 1993, Silicon Graphics, Inc.
* 1993-1995 Microsoft Corporation
*
* ALL RIGHTS RESERVED
*
* Please refer to OpenGL/readme.txt for additional information
*
*/

#include "glos.h"

#ifdef X11
#include <X11/Intrinsic.h>
#include <X11/keysym.h>
#include <Xm/Xm.h>

#include <GL/glx.h>
#include <GLwMDrawA.h>
#endif

#include <GL/glu.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef X11
#include <sys/time.h>
#else
#include <time.h>
#endif

#ifdef WIN32
#include "stonehen.h"
#endif

#include "atmosphe.h"
#include "scene.h"

#include "callback.h"

int cb_demo_mode = 0;
float demo_time;

#ifdef X11
extern Widget glw;
extern XtAppContext app_context;
GLXContext glx_context;
#endif

static int needs_wp = 0;
#ifdef X11
static XtWorkProcId workproc = NULL;
#else
static int workproc = 0;
#endif

static int winx, winy;

static int button_down = 0;
int mousex, mousey;
/* What's moving */
GLint target;

/* Location of the telescope */
GLfloat tx, ty;

/* Movements of the camera position to be applied at the next redraw */
float rot_pendx = 0, rot_pendz = 0, trans_pend = 0;

/* How fast the camera is moving */
float trans_speed = 0;

/* This is how fast demo time moves relative to real time */
GLfloat time_scale = 0;

TimeDate last_update;

float last_time = 0;
const float time_fudge = 1000;
inline unsigned long current_time()
{
#ifdef X11
struct timeval time;
gettimeofday(&time, NULL);
return (time.tv_sec * 1000000 + time.tv_usec);
#else
return (GetTickCount() * 1000);
#endif
}

inline float clamp(float x, float min, float max)
{
if (x < min) return min;
else if (x > max) return max;
else return x;
}

static void add_workproc(Widget w)
{
needs_wp++;
#ifdef X11
if (workproc == NULL)
workproc = XtAppAddWorkProc(app_context, drawWP, NULL);
#endif
}

static void remove_workproc(Widget w)
{
needs_wp--;
if (needs_wp == 0) {
#ifdef X11
XtRemoveWorkProc(workproc);
workproc = NULL;
#else
workproc = 0;
#endif
} else if (needs_wp < 0) {
//fprintf(stderr, "Internal Error: No workproc to remove!\n");
needs_wp = 0;
workproc = NULL;
}
}

static void reset_viewer()
{
scene_viewer_center();
}

#ifdef X11
void intToggleCB(Widget w, XtPointer client_data, XtPointer call_data)
{
int *data;
XmToggleButtonCallbackStruct *ptr;
ptr = (XmToggleButtonCallbackStruct *)call_data;
data = (int *)client_data;
*data = ptr->set;
// This redraw may or may not be needed - do it to be safe
drawWP(NULL);
}


void initCB(Widget w)
{
Arg args[1];
XVisualInfo *vi;

glw = w;

XtSetArg(args[0], GLwNvisualInfo, &vi);
XtGetValues(w, args, 1);

glx_context = glXCreateContext(XtDisplay(w), vi, 0, GL_FALSE);
GLwDrawingAreaMakeCurrent(w, glx_context);

scene_init();

last_update.read_time();

resetViewerCB(NULL, NULL, NULL);
}

void exposeCB(Widget w)
{
drawWP(NULL);
}
#endif

void resizeCB(Widget w, XtPointer client_data, XtPointer call)
{
#ifdef X11
GLwDrawingAreaCallbackStruct *call_data;

GLwDrawingAreaMakeCurrent(w, glx_context);

call_data = (GLwDrawingAreaCallbackStruct *)call;

winx = call_data->width;
winy = call_data->height;
#else
RECT rect;
GetClientRect(w, &rect);

winx = WINDSIZEX(rect);
winy = WINDSIZEY(rect);
#endif

glViewport(0, 0, winx, winy);

aspect = (GLfloat)winx / (GLfloat)winy;
}

void inputCB(Widget w, XtPointer client_data, XtPointer call_data)
{
GLwDrawingAreaCallbackStruct *call;

int bufsize = 5;
#ifdef X11
char buffer[5];
KeySym key;
XComposeStatus compose;
#endif

float dx, dy, r1, r2;

#ifdef X11
GLwDrawingAreaMakeCurrent(w, glx_context);
#endif

call = (GLwDrawingAreaCallbackStruct *)call_data;

switch(call->event->type) {
case ButtonPress:
last_time = current_time();
button_down = call->event->xbutton.button;
mousex = call->event->xbutton.x;
mousey = call->event->xbutton.y;
/* Determine if the target should be the camera position
* or the telescope */
if (use_telescope) {
scene_get_position_telescope(&tx, &ty);
scene_get_radius_telescope(&r1);
dx = (tx + .5) - ((GLfloat)(winx - mousex)/(GLfloat)winx);
dy = (ty + .5) - ((GLfloat)(winy - mousey)/(GLfloat)winy);
r2 = sqrt(dx*dx + dy*dy);
if (r2 < r1) target = name_telescope;
else target = name_background;
} else target = name_background;
add_workproc(w);
break;
case ButtonRelease:
#ifdef X11
if (call->event->xbutton.button == Button3) {
/* Use Button3 to stop */
if (trans_speed) remove_workproc(w);
trans_speed = 0;
}
#endif
remove_workproc(w);
button_down = 0;
break;
case MotionNotify:
switch(button_down) {
case Button1:
/* Use Button1 to control the way in which the viewer is looking
* or to move the telescope around */
if (target == name_background) {
dx = (float)(call->event->xmotion.x - mousex) / (float)winx;
dy = (float)(call->event->xmotion.y - mousey) / (float)winy;
rot_pendx -= dy * fov;
rot_pendz -= dx * fov;
} else {
dx = (float)(mousex - call->event->xmotion.x) / (float)winx;
dy = (float)(mousey - call->event->xmotion.y) / (float)winy;
tx += dx;
ty += dy;
tx = clamp(tx, -.5, .5);
ty = clamp(ty, -.5, .5);
scene_position_telescope(tx, ty);
}
break;
case Button2:
/* Use Button2 to change speed */
dx = (float)(mousex - call->event->xmotion.x) /
(float)winx;
if (dx && !trans_speed) add_workproc(w);
#ifdef WIN32
else if (!dx && trans_speed) remove_workproc(w);
#endif
trans_speed += dx;
break;
}
mousex = call->event->xmotion.x;
mousey = call->event->xmotion.y;
break;

#ifdef X11 // We can handle our own keys...
case KeyPress:
XLookupString(&(call->event->xkey), buffer, bufsize, &key, &compose);
if (key == XK_Escape) exit(0);
break;
#endif

default:
break;
}
}

const float speed_t = .5;
const float speed_r = 15.;
const float speed_rx = 10.;
void demo_mode_update(float dt)
{
float t;

if (!cb_demo_mode) return;

dt /= 1000000;
demo_time += dt;

t = demo_time;

if (t < 10.5) {
trans_speed = speed_t;
return;
} else t -= 10.5;

if (t < 1) {
trans_speed = 0;
rot_pendz = 0;
return;
} else t -= 1;

if (t < 3.) {
trans_speed = 0.;
rot_pendz = dt * speed_r;
return;
} else t -= 3.;

if (t < 2.) {
rot_pendx = -dt * speed_rx;
return;
} else t -= 2.;

if (t < 2.) {
return;
} else t -= 2.;

if (t < 2.) {
rot_pendx = dt * speed_rx;
return;
} else t -= 2.;


if (t < 3.) {
trans_speed = 0.;
rot_pendz = dt * speed_r;
return;
} else t -= 3.;

if (t < 30.) {
trans_speed = speed_t;
rot_pendz = 0;
return;
} else t -= 30.;

if (t < 1.) {
trans_speed = 0;
return;
} else t -= 1.;

if (t < 1.3) {
rot_pendz = -dt * speed_r;
return;
} else t -= 1.3;

// Pan back to see entire thing
if (t < 23) {
trans_speed = speed_t;
return;
} else t -= 23;

// Hold before starting over
if (t < 20) {
trans_speed = 0;
return;
}
else t -= 20;

demo_time = 0.;
reset_viewer();
}

Boolean drawWP(Widget w)
{
/* Right now, there's two completely independent time measurements:
* one for the time of day in the demo and one for changing the camera
* position */
TimeDate t, dt;
float elapsed_time, time;
#ifdef WIN32
HDC hDC;
#endif

if (time_scale != 0.0) {
t.read_time();
dt = (t - last_update) * time_scale;
scene_inc_time(dt);
last_update = t;
}

time = current_time();
if (time - last_time > time_fudge) {
elapsed_time = time - last_time;
demo_mode_update(elapsed_time);
trans_pend = trans_speed * (elapsed_time / 1000000);
last_time = time;
}

scene_viewer_rotatex(rot_pendx);
scene_viewer_rotatez(rot_pendz);
scene_viewer_translate(trans_pend);
rot_pendx = 0;
rot_pendz = 0;
trans_pend = 0;

#ifdef X11
GLwDrawingAreaMakeCurrent(glw, glx_context);
scene_render();
/* This is a total hack */
// if (!use_antialias)
GLwDrawingAreaSwapBuffers(glw);
#else
hDC = GetDC(w);
scene_render();
SwapBuffers(hDC);
ReleaseDC(w, hDC);
#endif

return FALSE;
}

#ifdef X11
void weatherCB(Widget w, XtPointer client_data, XtPointer call_data)
{
Weather *data;
XmToggleButtonCallbackStruct *ptr;
ptr = (XmToggleButtonCallbackStruct *)call_data;
if (ptr->set) scene_set_weather(*((Weather *)client_data));
drawWP(NULL);
}
#endif

void checkTimeMenuItem(HMENU hmenu, UINT idCheckItem)
{
CheckMenuItem(hmenu, IDM_CURRENTTIME, MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_10AM, MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_NOON, MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_4PM, MF_UNCHECKED);
CheckMenuItem(hmenu, idCheckItem, MF_CHECKED);
}

void currentTimeCB(Widget w)
{
scene_set_time(TimeDate().read_time());
drawWP(w);
}

void time10amCB(Widget w)
{
scene_set_time(TimeDate(10, 0));
drawWP(w);
}

void time12pmCB(Widget w)
{
scene_set_time(TimeDate(12, 0));
drawWP(w);
}

void time4pmCB(Widget w)
{
scene_set_time(TimeDate(16, 0));
drawWP(w);
}

void timeSpeedCB(Widget w, XtPointer client_data, XtPointer call_data)
{

#ifdef X11
if (!((XmToggleButtonCallbackStruct *)call_data)->set) return;
time_scale = (int)client_data;
#endif
if (time_scale == 0.0) remove_workproc(w);
else add_workproc(w);
last_update.read_time();
}

void demo_modeCB(Widget w, XtPointer client_data, XtPointer call_data)
{
#ifdef X11
int val = ((XmToggleButtonCallbackStruct *)call_data)->set;
#else
int val = cb_demo_mode;
#endif
if (!val) {
remove_workproc(w);
resetViewerCB(w, NULL, NULL);
last_time = current_time();
} else {
reset_viewer();
add_workproc(w);
trans_speed = 0;
demo_time = 0;
rot_pendx = -5;
}
drawWP(w);
}

void resetViewerCB(Widget w, XtPointer client_data, XtPointer call_data)
{
trans_speed = 0;
rot_pendx = rot_pendz = trans_pend = 0;
rot_pendx = -5;
reset_viewer();
return;
}


void exitCB(Widget w, XtPointer client_data, XtPointer call_data)
{
exit(0);
}