#ifdef X11 #include #include #include #include #include #endif #include #include #include #include #include #ifdef X11 #include #else #include #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); #else workproc = SetTimer(w, 1, 1000, NULL); #endif } static void remove_workproc(Widget w) { needs_wp--; if (needs_wp == 0) { #ifdef X11 XtRemoveWorkProc(workproc); workproc = NULL; #else KillTimer(w, 1); 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 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); }