/* proj7.c - CSE 167 Project 7 source - Don Yang (uguu.org) To compile / link: Under Linux, if you have Mesa3D library setup properly, you can just use the Makefile (make). Under Windows, you will need to setup OpenGL libraries and have glut32.dll in order to execute the program. Use Makefile.w32 (nmake -f Makefile.w32). Under Sun: It's quite a pain to do things when you are not root (I wasn't). The easiest way to compile/link this program is to get Mesa3D libraries and demos, place proj7.c in demos/ directory, and modify that Makefile so that proj7 gets build along with other demos. All other ways requires knowledge of which X library to link with, too messy and I haven't gotten it to work again since project 5 :P Note that since this is an academic project, it isn't supposed to be fast and the code isn't supposed to be bug free for more than 10 minutes. Hopefully the source is useful somehow ^_^;; For optimal performance, adjust these constants (search for @@@) FLOOR_RESOLUTION FLOOR_T_RESOLUTION CEILING_COUNT PATH_STEPS 06/08/00 */ /* * Headers */ #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846264338327950288419716939937510 #endif #ifdef _WIN32 #include #endif #include #include #include /* * Types */ typedef struct /* Window data */ { const char *title; /* Window title */ const GLclampf r, g, b, a; /* Clear color */ int x, y, w, h; /* Window position/size */ int mx, my, mb; /* Mouse status */ int window; /* Window handle */ } P7Window; typedef struct /* Path point data */ { double x, y, z, t; /* Position */ double nx, ny, nz; /* Normal */ double length; /* Length */ } Path; /* * Constants */ #define WINDOW_COUNT 2 /* Number of windows */ #define MOUSE_LEFT 4 /* Mouse button bits */ #define MOUSE_MIDDLE 2 #define MOUSE_RIGHT 1 #define KEY_ESCAPE 0x1b /* Escape key */ #define PICK_BUFFER_SIZE 256 /* Pick buffer size (entries) */ #define PICK_TOLERANCE 10 /* Pick tolerance (pixels) */ #define FLOOR_SIZE 400 /* Ground width */ #define FLOOR_RESOLUTION 64 /* Ground polygon divisions @@@*/ #define FLOOR_T_RESOLUTION 128 /* Ground texel divisions @@@*/ #define FLOOR_BUMP_HEIGHT 9 /* Dynamic bump height */ #define FLOOR_BUMP_COEFF 0.2 /* Dynamic bump decay */ #define CEILING_HEIGHT 90 /* Vertical position of ceiling */ #define CEILING_SIZE 800 /* Ceiling horizontal size */ #define CEILING_SIZE2 50 /* Ceiling vertical size */ #define CEILING_COUNT 1280 /* Ceiling particle count @@@*/ #define PATH_RADIUS_X 50 /* Elliptical path radius */ #define PATH_RADIUS_Z 70 #define PATH_MAX_HEIGHT 90 /* Maximum path height */ #define PATH_STEPS 128 /* Path segment divisions @@@*/ #define PATH_CONTROL_STEPS 9 /* Number of control points */ #define PATH_WIDTH 6 /* Width of track (half) */ #define PATH_NORMAL_LENGTH 5 /* Length of path normal vector */ #define PATH_STEP_OFFSET 5 /* Camera/target index difference */ #define CAMERA_OFFSET 15 /* Camera to path distance */ #define MARGIN_LEFT 0.1 /* Margin in pick window */ #define MARGIN_RIGHT 0.1 #define MARGIN_TOP 5 #define MARGIN_BOTTOM 25 #define ROTATE_FACTOR 1.0 /* Rotation factor */ #define SCALE_FACTOR 0.005 /* Scale factor */ #define MINIMUM_SCALE 0.01 /* Minimum scale */ #define MINIMUM_WIDTH 100 /* Minimum window width */ #define MINIMUM_HEIGHT 75 /* Minimum window height */ #define SQUARE_VIEW_SIZE 30 /* Square viewport size */ #define SQUARE_VIEW_SIZE2 70 #define ORTHO_NEAR 1 /* Near clipping plane */ #define PERSP_NEAR 20 #define ORTHO_FAR 200 /* Far clipping plane */ #define PERSP_FAR 200 #define PERSP_SCALE 0.4 /* Scale factor for view frustum */ #define FRAME_DELAY (1000 / 24) /* Frame rate */ #define BITMAP_FONT GLUT_BITMAP_9_BY_15 #define STROKE_FONT GLUT_STROKE_ROMAN #ifndef FALSE /* Readability constants */ #define FALSE 0 #define TRUE (!FALSE) #endif #define W_2D 0 #define W_3D 1 enum { TRANSFORM_ROTATE, TRANSFORM_SCALE, TRANSFORM_SCULPT, PROJ_PERSPECTIVE, PROJ_ORTHO, VIEW_OMNI, VIEW_FOLLOW, VIEW_RIDE, BUMP_LOWER, BUMP_RAISE, MENU_TRACK, MENU_FLOOR, MENU_CEILING, MENU_NORMAL, MENU_LINE, MENU_PERSPECTIVE, MENU_ORTHO, MENU_VIEW, MENU_ROTATE, MENU_SCALE, MENU_SCULPT, MENU_TEXTURE, MENU_TEXTURE_FILTER, MENU_SMOOTH_LINE, MENU_SMOOTH_POLY, MENU_SMOOTH_POINT, MENU_BLEND, MENU_RESET, MENU_QUIT }; enum { OBJ_FLOOR, OBJ_FLOOR_NORMAL, OBJ_CEILING, OBJ_TRACK2, OBJ_TRACK2_LINE, OBJ_CONTROL_POINTS, OBJ_TRACK3, OBJ_TRACK3_NORMAL, OBJ_COUNT }; /* * Macros */ #define path(member) (Curve[PathPos].member) #define pathnext(member) \ (Curve[(PathPos + PATH_STEP_OFFSET) % PATH_STEPS].member) /* * Globals */ /* Windows */ static P7Window p7[WINDOW_COUNT] = { {"Project 7 - 2D", 0, 0, 0, 0, 20, 50, 320, 480, 0,0,0,0}, {"Project 7 - 3D", 0, 0, 0, 0, 360, 50, 640, 480, 0,0,0,0} }; /* Path */ static Path Curve[PATH_STEPS + 1]; static double ControlX[PATH_CONTROL_STEPS]; static double ControlY[PATH_CONTROL_STEPS]; static double PathLength, PathMinHeight, PathMaxHeight; static int CapturedPoint = -1; static int PathPos = 0; /* Objects */ static int Obj[OBJ_COUNT]; static int DisplayFloor = TRUE; static int DisplayNormal = FALSE; static int DisplayCeiling = TRUE; static int DisplayTrack = TRUE; static int DisplayLine = TRUE; /* Modes */ static int TransformMode = TRANSFORM_ROTATE; static int ViewMode = VIEW_OMNI; static int TextureMode = TRUE; static int BlendMode = TRUE; static int BilinearMode = TRUE; static int SmoothLine = FALSE, SmoothPoly = FALSE, SmoothPoint = FALSE; /* Floor */ static GLfloat LandscapeColor[4] = {1, 1, 1, 1}; static GLfloat Landscape[FLOOR_RESOLUTION][FLOOR_RESOLUTION]; static GLuint LandscapeTexture; static int TextureShift = 0; /* Track */ static GLfloat TrackColor[4] = {1, 1, 1, 0.8}; /* Roller coaster */ static GLfloat RollerCoaster[4] = {0, 1, 0, 1}; /* Pick buffer */ static GLuint PickBuffer[PICK_BUFFER_SIZE], PickHitCount; /* 3D window status */ static GLfloat SceneScale, SceneRotateX, SceneRotateY; static int Projection; /* * Prototypes */ static void Animate(void); static void GeneratePath(void); static void GeneratePathStep(Path *seg, float t); static void InitLists(void); static void Keyboard(unsigned char c, int i, int j); static void Menu(int action); static void MouseButton(int window, int button, int state, int x, int y); static void Quit(void); static void Resize(int window, int width, int height); static void TxtRaster(GLfloat x, GLfloat y, GLfloat z, char *text); /* Functions for 2D window */ static void Display2(void); static void InitGraphics2(void); static void InitTrack2(void); static void Menu2(int action); static void MouseButton2(int button, int state, int x, int y); static void MouseMotion2(int x, int y); static void Pick2(void); static void Reset2(void); static void Resize2(int width, int height); static void Visible2(int state); /* Functiosn for 3D window */ static void DeformLandscape3(unsigned int name, int dir); static void Display3(void); static void InitGraphics3(void); static void InitLandscape3(void); static void InitLandscapeTexture3(void); static void InitSky3(void); static void InitTrack3(void); static void Menu3(int action); static void MouseButton3(int button, int state, int x, int y); static void MouseMotion3(int x, int y); static void Pick3(void); static void Reset3(void); static void Resize3(int width, int height); static void Visible3(int state); /* * Code */ /******************************************************************** main */ int main(int argc, char **argv) { /* Initialize */ srand((unsigned)time(NULL)); /* Random numbers */ glutInit(&argc, argv); /* GLUT */ InitGraphics2(); /* Windows */ InitGraphics3(); InitLists(); /* Objects */ Reset2(); /* Variables */ Reset3(); GeneratePath(); /* Loop forever */ glutMainLoop(); return 0; } /* main() */ /******************************************************************* Animate Update variables. */ static void Animate(void) { if( TextureMode ) { TextureShift = (TextureShift + 1) & 63; InitLandscapeTexture3(); } PathPos = (PathPos + 1) % PATH_STEPS; glutSetWindow(p7[W_2D].window); glutPostRedisplay(); glutSetWindow(p7[W_3D].window); glutPostRedisplay(); } /* Animate() */ /************************************************************** GeneratePath Compute 8th order Bezier curve points and angles. */ static void GeneratePath(void) { double dx, dy, dz, nx, ny, nz, u; int i; /* Generate coordinates */ for(i = 0; i < PATH_STEPS; i++) GeneratePathStep(&Curve[i], ((float)i) / (PATH_STEPS - 1)); memcpy(&Curve[PATH_STEPS], &Curve[0], sizeof(Path)); /* Compute angles and path length */ PathMinHeight = PathMaxHeight = Curve[0].y; PathLength = 0; for(i = 0; i < PATH_STEPS; i++) { dx = Curve[i + 1].x - Curve[i].x; dy = Curve[i + 1].y - Curve[i].y; dz = Curve[i + 1].z - Curve[i].z; nx = Curve[i].z * dy - Curve[i].y * dz; ny = Curve[i].x * dz - Curve[i].z * dx; nz = Curve[i].y * dx - Curve[i].x * dy; u = sqrt(nx * nx + ny * ny + nz * nz); PathLength += sqrt(dx * dx + dy * dy + dz * dz); if( Curve[i].y < PathMinHeight ) PathMinHeight = Curve[i].y; if( Curve[i].y > PathMaxHeight ) PathMaxHeight = Curve[i].y; Curve[i].nx = nx / u; Curve[i].ny = ny / u; Curve[i].nz = nz / u; } memcpy(&Curve[PATH_STEPS], &Curve[0], sizeof(Path)); InitTrack2(); InitTrack3(); glutSetWindow(p7[W_2D].window); glutPostRedisplay(); glutSetWindow(p7[W_3D].window); glutPostRedisplay(); } /* GeneratePath() */ /********************************************************** GeneratePathStep Compute single path point. */ static void GeneratePathStep(Path *seg, float t) { static float t2, t3, t4, t5, t6, t7, t8; static float i2, i3, i4, i5, i6, i7, i8, i; static float c1, c2, c3, c4, c5, c6, c7; /* Exponentiate, LISP style */ t8 = t*(t7=(t*(t6=(t*(t5=(t*(t4=(t*(t3=(t*(t2=(t*t)))))))))))); i = 1 - t; i8 = i*(i7=(i*(i6=(i*(i5=(i*(i4=(i*(i3=(i*(i2=(i*i)))))))))))); /* Compute coordinate */ seg->t = ControlX[0] * i8 + ControlX[1] * (c1 = i7 * t * 8) + ControlX[2] * (c2 = i6 * t2 * 28) + ControlX[3] * (c3 = i5 * t3 * 56) + ControlX[4] * (c4 = i4 * t4 * 70) + ControlX[5] * (c5 = i3 * t5 * 56) + ControlX[6] * (c6 = i2 * t6 * 28) + ControlX[7] * (c7 = i * t7 * 8) + ControlX[8] * t8; seg->y = ControlY[0] * i8 + ControlY[1] * c1 + ControlY[2] * c2 + ControlY[3] * c3 + ControlY[4] * c4 + ControlY[5] * c5 + ControlY[6] * c6 + ControlY[7] * c7 + ControlY[8] * t8; seg->x = PATH_RADIUS_X * cos(seg->t * M_PI * 2); seg->z = PATH_RADIUS_Z * sin(seg->t * M_PI * 2); } /* GeneratePathStep() */ /****************************************************************** Keyboard Process keyboard input. */ static void Keyboard(unsigned char c, int i, int j) { int w; switch( tolower(c) ) { case '0': BlendMode = !BlendMode; break; case '1': SmoothPoint = !SmoothPoint; break; case '2': SmoothLine = !SmoothLine; break; case '3': SmoothPoly = !SmoothPoly; break; case 'h': BilinearMode = !BilinearMode; break; case 'f': DisplayFloor = !DisplayFloor; break; case 'n': DisplayNormal = !DisplayNormal; break; case 'c': DisplayCeiling = !DisplayCeiling; break; case 'k': DisplayTrack = !DisplayTrack; break; case 'l': DisplayLine = !DisplayLine; break; case 'p': Projection = PROJ_PERSPECTIVE; break; case 'o': Projection = PROJ_ORTHO; break; case 'r': TransformMode = TRANSFORM_ROTATE; break; case 's': TransformMode = TRANSFORM_SCALE; break; case 'b': TransformMode = TRANSFORM_SCULPT; break; case 't': TextureMode = !TextureMode; InitLandscape3(); break; case 'v': ViewMode = ViewMode == VIEW_OMNI ? VIEW_FOLLOW : ( ViewMode == VIEW_RIDE ? VIEW_OMNI : VIEW_RIDE ); break; case 'q': case KEY_ESCAPE: Quit(); break; default: printf(isprint(c) ? "Unrecognized key: '%c'\n" : "Unrecognized key: '0x%02x'\n", c); break; } for(w = 0; w < WINDOW_COUNT; w++) { glutSetWindow(p7[w].window); glutPostRedisplay(); } } /* Keyboard() */ /***************************************************************** InitLists Create display lists. */ static void InitLists(void) { static GLubyte TexImage[FLOOR_T_RESOLUTION][FLOOR_T_RESOLUTION][4]; int i, j; glutSetWindow(p7[W_2D].window); Obj[OBJ_TRACK2] = glGenLists(1); Obj[OBJ_TRACK2_LINE] = glGenLists(1); Obj[OBJ_CONTROL_POINTS] = glGenLists(1); glutSetWindow(p7[W_3D].window); memset(TexImage, 0, FLOOR_T_RESOLUTION * FLOOR_T_RESOLUTION * 4); glGenTextures(1, &LandscapeTexture); glBindTexture(GL_TEXTURE_2D, LandscapeTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FLOOR_T_RESOLUTION, FLOOR_T_RESOLUTION, 0, GL_RGBA, GL_UNSIGNED_BYTE, TexImage); InitLandscapeTexture3(); Obj[OBJ_FLOOR] = glGenLists(1); Obj[OBJ_FLOOR_NORMAL] = glGenLists(1); for(i = 0; i < FLOOR_RESOLUTION; i++) { for(j = 0; j < FLOOR_RESOLUTION; j++) Landscape[i][j] = 0; } InitLandscape3(); Obj[OBJ_CEILING] = glGenLists(1); InitSky3(); Obj[OBJ_TRACK3] = glGenLists(1); Obj[OBJ_TRACK3_NORMAL] = glGenLists(1); } /* InitLists() */ /********************************************************************** Menu Process menu input. */ static void Menu(int action) { switch( action ) { case MENU_BLEND: BlendMode = !BlendMode; break; case MENU_SMOOTH_LINE: SmoothLine = !SmoothLine; break; case MENU_SMOOTH_POLY: SmoothPoly = !SmoothPoly; break; case MENU_SMOOTH_POINT: SmoothPoint = !SmoothPoint; break; case MENU_TEXTURE: TextureMode = !TextureMode; InitLandscape3(); break; case MENU_QUIT: Quit(); break; default: break; } } /* Menu() */ /*************************************************************** MouseButton Process mouse button press. */ static void MouseButton(int window, int button, int state, int x, int y) { int b; switch( button ) { case GLUT_LEFT_BUTTON: b = MOUSE_LEFT; break; case GLUT_MIDDLE_BUTTON: b = MOUSE_MIDDLE; break; case GLUT_RIGHT_BUTTON: b = MOUSE_RIGHT; break; default: b = 0; printf("Unrecognized mouse button %d\n", button); } if( state == GLUT_DOWN ) p7[window].mb |= b; else p7[window].mb &= ~b; p7[window].mx = x; p7[window].my = y; } /* MouseButton() */ /********************************************************************** Quit Exit gracefully. */ static void Quit(void) { int i; glFinish(); for(i = 0; i < WINDOW_COUNT; i++) glutDestroyWindow(p7[i].window); exit(EXIT_SUCCESS); } /* Quit() */ /******************************************************************** Resize Process window resize request. */ static void Resize(int window, int width, int height) { glutSetWindow(p7[window].window); if( width < MINIMUM_WIDTH ) { if( height < MINIMUM_HEIGHT ) glutReshapeWindow(MINIMUM_WIDTH, MINIMUM_HEIGHT); else glutReshapeWindow(MINIMUM_WIDTH, height); } else if( height < MINIMUM_HEIGHT ) { glutReshapeWindow(width, MINIMUM_HEIGHT); } p7[window].w = width; p7[window].h = height; glutPostRedisplay(); } /* Resize() */ /***************************************************************** TxtRaster Draw raster string. */ static void TxtRaster(GLfloat x, GLfloat y, GLfloat z, char *text) { glRasterPos3f(x, y, z); for(; *text; text++) glutBitmapCharacter(BITMAP_FONT, *text); } /* TxtRaster() */ /* * 2D code */ /****************************************************************** Display2 Draw everything. */ static void Display2(void) { static char string[80]; /* Initialize window */ glutSetWindow(p7[W_2D].window); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_NORMALIZE); glShadeModel(GL_FLAT); /* Initialize viewport */ p7[W_2D].w = glutGet(GLUT_WINDOW_WIDTH); p7[W_2D].h = glutGet(GLUT_WINDOW_HEIGHT); glViewport(0, 0, p7[W_2D].w, p7[W_2D].h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-MARGIN_LEFT, 1 + MARGIN_RIGHT, -MARGIN_BOTTOM, PATH_MAX_HEIGHT * 2 + MARGIN_TOP); /* Initialize environment */ if( BlendMode ) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } if( SmoothLine ) { glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); } else { glDisable(GL_LINE_SMOOTH); } if( SmoothPoint ) { glEnable(GL_POINT_SMOOTH); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); } else { glDisable(GL_POINT_SMOOTH); } /* Geometry */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(0.5, 0.7, 0.6); glBegin(GL_LINES); glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glEnd(); glCallList(Obj[OBJ_CONTROL_POINTS]); glCallList(Obj[OBJ_TRACK2]); if( DisplayLine ) glCallList(Obj[OBJ_TRACK2_LINE]); /* Roller coaster */ glColor3f(0, 1, 0); glBegin(GL_POINTS); glVertex3f(path(t), path(y), 0); glEnd(); /* Text information */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glColor3f(1, 1, 1); gluOrtho2D(0, 100, 0, 100); sprintf(string, "Length = %.3f, height = %.3f", PathLength, PathMaxHeight - PathMinHeight); TxtRaster(4, 4, 0, string); /* End */ glutSwapBuffers(); glFlush(); } /* Display2() */ /************************************************************* InitGraphics2 Initialize window. */ static void InitGraphics2(void) { int MainMenu, ObjMenu, FeatureMenu; /* Setup window */ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE ); glutInitWindowSize(p7[W_2D].w, p7[W_2D].h); glutInitWindowPosition(p7[W_2D].x, p7[W_2D].y); p7[W_2D].window = glutCreateWindow(p7[W_2D].title); glutSetWindowTitle(p7[W_2D].title); glClearColor(p7[W_2D].r, p7[W_2D].g, p7[W_2D].b, p7[W_2D].a); /* Create menus */ ObjMenu = glutCreateMenu(Menu2); glutAddMenuEntry("[l] Toggle lines", MENU_LINE); FeatureMenu = glutCreateMenu(Menu2); glutAddMenuEntry("[0] Toggle blending", MENU_BLEND); glutAddMenuEntry("[1] Toggle point smoothing", MENU_SMOOTH_POINT); glutAddMenuEntry("[2] Toggle line smoothing", MENU_SMOOTH_LINE); MainMenu = glutCreateMenu(Menu2); glutAddSubMenu("Objects", ObjMenu); glutAddSubMenu("Features", FeatureMenu); glutAddMenuEntry("Reset", MENU_RESET); glutAddMenuEntry("[q] Quit", MENU_QUIT); glutAttachMenu(GLUT_RIGHT_BUTTON); /* Callback functions */ glutSetWindow(p7[W_2D].window); glutDisplayFunc(Display2); glutReshapeFunc(Resize2); glutKeyboardFunc(Keyboard); glutMouseFunc(MouseButton2); glutMotionFunc(MouseMotion2); glutPassiveMotionFunc(NULL); glutVisibilityFunc(Visible2); glutEntryFunc(NULL); glutSpecialFunc(NULL); glutSpaceballMotionFunc(NULL); glutSpaceballRotateFunc(NULL); glutSpaceballButtonFunc(NULL); glutButtonBoxFunc(NULL); glutDialsFunc(NULL); glutTabletMotionFunc(NULL); glutTabletButtonFunc(NULL); glutMenuStateFunc(NULL); glutIdleFunc(Animate); glutTimerFunc(FRAME_DELAY, NULL, 0); } /* InitGraphics2() */ /**************************************************************** InitTrack2 Initialize 2D path curve. */ static void InitTrack2(void) { int i; /* Curve */ glutSetWindow(p7[W_2D].window); glNewList(Obj[OBJ_TRACK2], GL_COMPILE); glColor3f(1, 1, 1); glLineWidth(2); glBegin(GL_LINE_STRIP); for(i = 0; i < PATH_STEPS; i++) glVertex3f((GLfloat)(Curve[i].t), (GLfloat)(Curve[i].y), 0); glEnd(); glEndList(); /* Guide */ glNewList(Obj[OBJ_TRACK2_LINE], GL_COMPILE); glColor3f(0, 0.9, 0.9); glLineWidth(1); glBegin(GL_LINE_STRIP); for(i = 0; i < PATH_CONTROL_STEPS; i++) glVertex3f((GLfloat)ControlX[i], (GLfloat)ControlY[i], 0); glEnd(); glEndList(); /* Control points */ glNewList(Obj[OBJ_CONTROL_POINTS], GL_COMPILE); glColor3f(1, 0, 0); glPointSize(10); for(i = 0; i < PATH_CONTROL_STEPS; i++) { glLoadName(i); glBegin(GL_POINTS); glVertex3f((GLfloat)ControlX[i], (GLfloat)ControlY[i], 0); glEnd(); } glEndList(); } /* InitTrack2() */ /********************************************************************* Menu2 Process menu input. */ static void Menu2(int action) { switch( action ) { case MENU_LINE: DisplayLine = !DisplayLine; break; case MENU_RESET: Reset2(); break; default: Menu(action); glutSetWindow(p7[W_3D].window); glutPostRedisplay(); break; } glutSetWindow(p7[W_2D].window); glutPostRedisplay(); } /* Menu2() */ /************************************************************** MouseButton2 Process mouse button press. */ static void MouseButton2(int button, int state, int x, int y) { MouseButton(W_2D, button, state, x, y); glSelectBuffer(PICK_BUFFER_SIZE, PickBuffer); if( button == GLUT_LEFT_BUTTON ) { if( state == GLUT_DOWN ) { /* Capture control point */ Pick2(); if( !PickHitCount ) Pick2(); if( PickHitCount ) CapturedPoint = (int)PickBuffer[3]; else CapturedPoint = -1; } else { /* Release control point */ CapturedPoint = -1; } } } /* MouseButton2() */ /************************************************************** MouseMotion2 Process mouse movement. */ static void MouseMotion2(int x, int y) { double sx, sy; if( CapturedPoint > 0 && CapturedPoint < 8 ) { sx = ((double)x) / p7[W_2D].w; sy = 1 - ((double)y) / p7[W_2D].h; ControlX[CapturedPoint] = sx * (MARGIN_LEFT + MARGIN_RIGHT + 1) - MARGIN_LEFT; if( CapturedPoint > 1 && CapturedPoint < 7 ) { ControlY[CapturedPoint] = sy * (MARGIN_BOTTOM + MARGIN_TOP + PATH_MAX_HEIGHT * 2) - MARGIN_BOTTOM; if( ControlX[CapturedPoint] < -MARGIN_LEFT ) ControlX[CapturedPoint] = -MARGIN_LEFT; if( ControlX[CapturedPoint] > (MARGIN_RIGHT + 1) ) ControlX[CapturedPoint] = MARGIN_RIGHT + 1; if( ControlY[CapturedPoint] < -MARGIN_BOTTOM ) ControlY[CapturedPoint] = -MARGIN_BOTTOM; if( ControlY[CapturedPoint] > (MARGIN_TOP + PATH_MAX_HEIGHT * 2) ) ControlY[CapturedPoint] = MARGIN_TOP + PATH_MAX_HEIGHT * 2; } else { if( ControlX[CapturedPoint] < 0 ) ControlX[CapturedPoint] = 0; if( ControlX[CapturedPoint] > 1 ) ControlX[CapturedPoint] = 1; } GeneratePath(); } p7[W_2D].mx = x; p7[W_2D].my = y; } /* MouseMotion() */ /********************************************************************* Pick2 Redraw window for picking. */ static void Pick2(void) { int viewport[4]; /* Initialize picking */ glRenderMode(GL_SELECT); glInitNames(); glPushName(0xffffffff); /* Initialize window */ glutSetWindow(p7[W_2D].window); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_NORMALIZE); glShadeModel(GL_FLAT); /* Initialize viewport */ p7[W_2D].w = glutGet(GLUT_WINDOW_WIDTH); p7[W_2D].h = glutGet(GLUT_WINDOW_HEIGHT); glViewport(0, 0, p7[W_2D].w, p7[W_2D].h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); viewport[0] = 0; viewport[1] = 0; viewport[2] = p7[W_2D].w; viewport[3] = p7[W_2D].h; gluPickMatrix(p7[W_2D].mx, p7[W_2D].h - p7[W_2D].my, PICK_TOLERANCE, PICK_TOLERANCE, viewport); gluOrtho2D(-MARGIN_LEFT, 1 + MARGIN_RIGHT, -MARGIN_BOTTOM, PATH_MAX_HEIGHT * 2 + MARGIN_TOP); /* Geometry */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glCallList(Obj[OBJ_CONTROL_POINTS]); /* End */ glFlush(); PickHitCount = glRenderMode(GL_RENDER); } /* Pick2() */ /******************************************************************** Reset2 Reset. */ static void Reset2(void) { int i; for(i = 0; i < PATH_CONTROL_STEPS; i++) { ControlX[i] = ((double)i) / (PATH_CONTROL_STEPS - 1); ControlY[i] = (PATH_MAX_HEIGHT * (rand() & 0xff)) / 256; } ControlY[0] = ControlY[1] = ControlY[7] = ControlY[8] = 0; GeneratePath(); DisplayLine = TRUE; CapturedPoint = -2; p7[W_2D].mb = 0; glutSetWindow(p7[W_2D].window); glutPostRedisplay(); } /* Reset2() */ /******************************************************************* Resize2 Process change in window size. */ static void Resize2(int width, int height) { Resize(W_2D, width, height); } /* Resize2() */ /****************************************************************** Visible2 Process change in visibility. */ static void Visible2(int state) { if( state == GLUT_VISIBLE ) { glutSetWindow(p7[W_2D].window); glutPostRedisplay(); } } /* Visible2() */ /* * 3D code */ /********************************************************** DeformLandscape3 Modify height matrix. */ static void DeformLandscape3(unsigned int name, int dir) { int x, z, cx, cz; double scale; cx = name >> 8; cz = name & 0xff; scale = (dir == BUMP_LOWER) ? -FLOOR_BUMP_HEIGHT : FLOOR_BUMP_HEIGHT; for(z = 0; z < FLOOR_RESOLUTION; z++) { for(x = 0; x < FLOOR_RESOLUTION; x++) { Landscape[z][x] += scale * exp (-FLOOR_BUMP_COEFF * ((x - cx) * (x - cx) + (z - cz) * (z - cz))); } } } /* DeformLandscape3() */ /****************************************************************** Display3 Draw everything. */ static void Display3(void) { static GLfloat LightPosition[4] = {FLOOR_SIZE, FLOOR_SIZE, FLOOR_SIZE, 0}; static GLfloat LightDirection[4] = {-1, -1, -2, 0}; static GLfloat LightAmbient[4] = {0, 0, 0, 1}; static GLfloat LightDiffuse[4] = {0.8, 0.8, 0.8, 1}; static GLfloat LightSpecular[4] = {1, 1, 1, 1}; static GLfloat MatSpecular[4] = {1, 1, 1, 1}; static GLfloat MatShininess = 20; GLfloat dw, dh, s; /* Initialize window */ glutSetWindow(p7[W_3D].window); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glShadeModel(GL_SMOOTH); /* Set environment */ if( BlendMode ) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } if( SmoothLine ) { glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); } else { glDisable(GL_LINE_SMOOTH); } if( SmoothPoly ) { glEnable(GL_POLYGON_SMOOTH); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); } else { glDisable(GL_POLYGON_SMOOTH); } if( SmoothPoint ) { glEnable(GL_POINT_SMOOTH); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); } else { glDisable(GL_POINT_SMOOTH); } if( BilinearMode ) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } /* View -> screen */ p7[W_3D].w = glutGet(GLUT_WINDOW_WIDTH); p7[W_3D].h = glutGet(GLUT_WINDOW_HEIGHT); glViewport(0, 0, p7[W_3D].w, p7[W_3D].h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); s = ViewMode == VIEW_RIDE ? SQUARE_VIEW_SIZE2 : SQUARE_VIEW_SIZE; if( p7[W_3D].w > p7[W_3D].h ) { dw = (GLfloat)(((s * p7[W_3D].w) / (float)p7[W_3D].h) / 2); dh = (GLfloat)(s / 2); } else { dw = (GLfloat)(s / 2); dh = (GLfloat)(((s * p7[W_3D].h) / (float)p7[W_3D].w) / 2); } if( Projection == PROJ_ORTHO ) { glOrtho(-dw, dw, -dh, dh, ORTHO_NEAR, ORTHO_FAR); } else { glFrustum(-dw * PERSP_SCALE, dw * PERSP_SCALE, -dh * PERSP_SCALE, dh * PERSP_SCALE, PERSP_NEAR, PERSP_FAR); } /* Model -> view */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if( ViewMode == VIEW_RIDE ) { gluLookAt(path(x) + CAMERA_OFFSET * path(nx), path(y) + CAMERA_OFFSET * path(ny), path(z) + CAMERA_OFFSET * path(nz), pathnext(x) + CAMERA_OFFSET * pathnext(nx), pathnext(y) + CAMERA_OFFSET * pathnext(ny), pathnext(z) + CAMERA_OFFSET * pathnext(nz), path(nx), path(ny), path(nz)); } else if( ViewMode == VIEW_FOLLOW ) { gluLookAt(0, PATH_MAX_HEIGHT / 3, 0, path(x) + PATH_NORMAL_LENGTH * path(nx), path(y) + PATH_NORMAL_LENGTH * path(ny), path(z) + PATH_NORMAL_LENGTH * path(nz), 0, 1, 0); } else { gluLookAt(0, 0, FLOOR_SIZE / 3, 0, 0, 0, 0, 1, 0); glRotatef(SceneRotateY, 0, 1, 0); glRotatef(SceneRotateX, 1, 0, 0); glScalef(SceneScale, SceneScale, SceneScale); } /* Light */ glEnable(GL_LIGHT1); glMaterialfv(GL_FRONT, GL_SPECULAR, MatSpecular); glMaterialf(GL_FRONT, GL_SHININESS, MatShininess); glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, LightDirection); glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, LightSpecular); /* Roller coaster ;) */ if( DisplayTrack ) { glEnable(GL_LIGHTING); glPushMatrix(); glMaterialfv(GL_FRONT, GL_DIFFUSE, RollerCoaster); glTranslatef(path(x) + PATH_NORMAL_LENGTH * path(nx), path(y) + PATH_NORMAL_LENGTH * path(ny), path(z) + PATH_NORMAL_LENGTH * path(nz)); glRotatef(-90 + (180 / M_PI) * atan2(pathnext(x) - path(x), pathnext(z) - path(z)), 0, 1, 0); glRotatef((180 / M_PI) * atan2(pathnext(y) - path(y), 10), 0, 0, 1); glutSolidTeapot(PATH_WIDTH); glPopMatrix(); glDisable(GL_LIGHTING); } /* Geometry */ if( DisplayFloor ) { glMaterialfv(GL_FRONT, GL_DIFFUSE, LandscapeColor); glCallList(Obj[OBJ_FLOOR]); if( DisplayNormal ) glCallList(Obj[OBJ_FLOOR_NORMAL]); } if( DisplayTrack ) { glMaterialfv(GL_FRONT, GL_DIFFUSE, TrackColor); glCallList(Obj[OBJ_TRACK3]); if( DisplayNormal ) glCallList(Obj[OBJ_TRACK3_NORMAL]); } if( DisplayCeiling ) { glCallList(Obj[OBJ_CEILING]); } /* End */ glutSwapBuffers(); glFlush(); } /* Display3() */ /************************************************************* InitGraphics3 Initialize window. */ static void InitGraphics3(void) { int MainMenu, ObjMenu, ProjMenu, TransformMenu, FeatureMenu; /* Setup window */ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowSize(p7[W_3D].w, p7[W_3D].h); glutInitWindowPosition(p7[W_3D].x, p7[W_3D].y); p7[W_3D].window = glutCreateWindow(p7[W_3D].title); glutSetWindowTitle(p7[W_3D].title); glClearColor(p7[W_3D].r, p7[W_3D].g, p7[W_3D].b, p7[W_3D].a); /* Create menus */ ObjMenu = glutCreateMenu(Menu3); glutAddMenuEntry("[k] Toggle track", MENU_TRACK); glutAddMenuEntry("[f] Toggle floor", MENU_FLOOR); glutAddMenuEntry("[c] Toggle ceiling", MENU_CEILING); glutAddMenuEntry("[n] Toggle normals", MENU_NORMAL); ProjMenu = glutCreateMenu(Menu3); glutAddMenuEntry("[p] Perspective", MENU_PERSPECTIVE); glutAddMenuEntry("[o] Ortho", MENU_ORTHO); TransformMenu = glutCreateMenu(Menu3); glutAddMenuEntry("[r] Rotate", MENU_ROTATE); glutAddMenuEntry("[s] Scale", MENU_SCALE); glutAddMenuEntry("[b] Sculpt", MENU_SCULPT); FeatureMenu = glutCreateMenu(Menu3); glutAddMenuEntry("[v] Change view", MENU_VIEW); glutAddMenuEntry("[t] Toggle textures", MENU_TEXTURE); glutAddMenuEntry("[0] Toggle blending", MENU_BLEND); glutAddMenuEntry("[1] Toggle point smoothing", MENU_SMOOTH_POINT); glutAddMenuEntry("[2] Toggle line smoothing", MENU_SMOOTH_LINE); glutAddMenuEntry("[3] Toggle polygon smoothing", MENU_SMOOTH_POLY); MainMenu = glutCreateMenu(Menu3); glutAddSubMenu("Objects", ObjMenu); glutAddSubMenu("Projection", ProjMenu); glutAddSubMenu("Transform", TransformMenu); glutAddSubMenu("Features", FeatureMenu); glutAddMenuEntry("Reset", MENU_RESET); glutAddMenuEntry("[q] Quit", MENU_QUIT); glutAttachMenu(GLUT_RIGHT_BUTTON); /* Callback functions */ glutSetWindow(p7[W_3D].window); glutDisplayFunc(Display3); glutReshapeFunc(Resize3); glutKeyboardFunc(Keyboard); glutMouseFunc(MouseButton3); glutMotionFunc(MouseMotion3); glutPassiveMotionFunc(NULL); glutVisibilityFunc(Visible3); glutEntryFunc(NULL); glutSpecialFunc(NULL); glutSpaceballMotionFunc(NULL); glutSpaceballRotateFunc(NULL); glutSpaceballButtonFunc(NULL); glutButtonBoxFunc(NULL); glutDialsFunc(NULL); glutTabletMotionFunc(NULL); glutTabletButtonFunc(NULL); glutMenuStateFunc(NULL); glutIdleFunc(Animate); glutTimerFunc(FRAME_DELAY, NULL, 0); } /* InitGraphics3() */ /************************************************************ InitLandscape3 Initialize landscape from height matrix. */ static void InitLandscape3(void) { double d, dyx, dyz, nx, ny, nz; float u, v; int x, z; d = (double)(FLOOR_SIZE * 2) / (FLOOR_RESOLUTION - 1); glNewList(Obj[OBJ_FLOOR], GL_COMPILE); glEnable(GL_LIGHTING); if( TextureMode ) { glBindTexture(GL_TEXTURE_2D, LandscapeTexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); } glColor3f(1, 1, 1); for(z = 1; z < FLOOR_RESOLUTION - 2; z++) { for(x = 1; x < FLOOR_RESOLUTION - 2; x++) { glLoadName((((unsigned int)x) << 8) | ((unsigned int)z) ); glBegin(GL_QUADS); #define LandscapeVertex(vi, vj) \ u = ((float)(vi)) / (FLOOR_RESOLUTION - 1); \ v = ((float)(vj)) / (FLOOR_RESOLUTION - 1); \ dyx = atan2(Landscape[vi - 1][vj] - \ Landscape[vi + 1][vj], d); \ dyz = atan2(Landscape[vi][vj - 1] - \ Landscape[vi][vj + 1], d); \ glNormal3f(cos(dyz) * sin(dyx), \ cos(dyz) * cos(dyx), \ sin(dyz)); \ glTexCoord2f(v, u); \ glVertex3f(FLOOR_SIZE * (u - 0.5), \ Landscape[vi][vj], \ FLOOR_SIZE * (v - 0.5)); LandscapeVertex(z, x + 1); LandscapeVertex(z + 1, x + 1); LandscapeVertex(z + 1, x ); LandscapeVertex(z, x ); glEnd(); } } glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glEndList(); glNewList(Obj[OBJ_FLOOR_NORMAL], GL_COMPILE); glColor3f(1, 0, 0); glLineWidth(1); glBegin(GL_LINES); for(z = 1; z < FLOOR_RESOLUTION - 2; z++) { for(x = 1; x < FLOOR_RESOLUTION - 2; x++) { u = ((float)z) / (FLOOR_RESOLUTION - 1); \ v = ((float)x) / (FLOOR_RESOLUTION - 1); \ glVertex3f((u - 0.5) * FLOOR_SIZE, Landscape[z][x], (v - 0.5) * FLOOR_SIZE); dyx = atan2(Landscape[z - 1][x] - Landscape[z + 1][x], d); dyz = atan2(Landscape[z][x - 1] - Landscape[z][x + 1], d); nx = cos(dyz) * sin(dyx); ny = cos(dyz) * cos(dyx); nz = sin(dyz); glVertex3f((u - 0.5) * FLOOR_SIZE + 3 * nx, Landscape[z][x] + 3 * ny, (v - 0.5) * FLOOR_SIZE + 3 * nz); } } glEnd(); glEndList(); } /* InitLandscape3() */ /***************************************************** InitLandscapeTexture3 Create texture for landscape. */ static void InitLandscapeTexture3(void) { static GLubyte TexImage[FLOOR_T_RESOLUTION][FLOOR_T_RESOLUTION][4]; int x, y; float r, t; /* Generate image */ for(y = 0; y < FLOOR_T_RESOLUTION; y++) { for(x = 0; x < FLOOR_T_RESOLUTION; x++) { r = sqrt ( (x - FLOOR_T_RESOLUTION / 2) * (x - FLOOR_T_RESOLUTION / 2) + (y - FLOOR_T_RESOLUTION / 2) * (y - FLOOR_T_RESOLUTION / 2) ) / FLOOR_T_RESOLUTION; t = (y == FLOOR_T_RESOLUTION / 2 && x == FLOOR_T_RESOLUTION / 2) ? 0 : atan2 (y - FLOOR_T_RESOLUTION / 2, x - FLOOR_T_RESOLUTION / 2) / M_PI + TextureShift / 32.0 + 1; TexImage[y][x][0] = 0; TexImage[y][x][1] = (GLubyte)((int)(r * 512 - t * 128) & 0xff); TexImage[y][x][2] = TexImage[y][x][3] = 255; } } /* Bind image to texture */ glBindTexture(GL_TEXTURE_2D, LandscapeTexture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, FLOOR_T_RESOLUTION, FLOOR_T_RESOLUTION, GL_RGBA, GL_UNSIGNED_BYTE, TexImage); } /* InitLandscapeTexture3() */ /****************************************************************** InitSky3 Initialize ceiling object. */ static void InitSky3(void) { int i; glNewList(Obj[OBJ_CEILING], GL_COMPILE); glDisable(GL_LIGHTING); glPointSize(2); glBegin(GL_POINTS); for(i = 0; i < CEILING_COUNT; i++) { glColor4f((rand() & 0x7ff) / 4095.0 + 0.5, (rand() & 0x3ff) / 4095.0 + 0.75, (rand() & 0x3ff) / 4095.0 + 0.75, (rand() & 0x7ff) / 4095.0 + 0.5); glVertex3f(CEILING_SIZE * ((rand() & 0xfff) / 4095.0 - 0.5), CEILING_SIZE2 * ((rand() & 0xfff) / 4095.0 - 0.5) + PATH_MAX_HEIGHT, CEILING_SIZE * ((rand() & 0xfff) / 4095.0 - 0.5)); } glEnd(); glEndList(); } /* InitSky3() */ /**************************************************************** InitTrack3 Create 3D track geometry from Bezier curve. */ static void InitTrack3(void) { static double wx[PATH_STEPS], wy[PATH_STEPS], wz[PATH_STEPS]; double dx, dy, dz, u; int i; for(i = 0; i < PATH_STEPS; i++) { wx[i] = Curve[i].x; wy[i] = Curve[i].y; wz[i] = Curve[i].z; u = PATH_WIDTH / sqrt(wx[i] * wx[i] + wy[i] * wy[i] + wz[i] * wz[i]); wx[i] *= u; wy[i] *= u; wz[i] *= u; } /* Track */ glutSetWindow(p7[W_3D].window); glNewList(Obj[OBJ_TRACK3], GL_COMPILE); glDisable(GL_LIGHTING); glLineWidth(1); glColor4f(1, 0, 0, 0.8); glBegin(GL_LINE_LOOP); for(i = 0; i < PATH_STEPS; i++) { glNormal3f(Curve[i].nx, Curve[i].ny, Curve[i].nz); glVertex3f(Curve[i].x + Curve[i].nx, Curve[i].y + Curve[i].ny, Curve[i].z + Curve[i].nz); } glEnd(); glEnable(GL_LIGHTING); glColor4f(1, 1, 1, 0.8); glBegin(GL_QUAD_STRIP); for(i = 0; i < PATH_STEPS; i++) { glNormal3f(Curve[i].nx, Curve[i].ny, Curve[i].nz); glVertex3f(Curve[i].x + wx[i], Curve[i].y + wy[i], Curve[i].z + wz[i]); glNormal3f(Curve[i].nx, Curve[i].ny, Curve[i].nz); glVertex3f(Curve[i].x - wx[i], Curve[i].y - wy[i], Curve[i].z - wz[i]); } glEnd(); glDisable(GL_LIGHTING); glEndList(); /* Normals */ glNewList(Obj[OBJ_TRACK3_NORMAL], GL_COMPILE); glColor3f(1, 0, 0); glBegin(GL_LINES); for(i = 0; i < PATH_STEPS; i++) { dx = PATH_NORMAL_LENGTH * Curve[i].nx; dy = PATH_NORMAL_LENGTH * Curve[i].ny; dz = PATH_NORMAL_LENGTH * Curve[i].nz; glVertex3f(Curve[i].x, Curve[i].y, Curve[i].z); glVertex3f(Curve[i].x + dx, Curve[i].y + dy, Curve[i].z + dz); } glEnd(); glEndList(); } /* InitTrack3() */ /********************************************************************* Menu3 Process menu action. */ static void Menu3(int action) { switch( action ) { case MENU_TRACK: DisplayTrack = !DisplayTrack; break; case MENU_FLOOR: DisplayFloor = !DisplayFloor; break; case MENU_CEILING: DisplayCeiling = !DisplayCeiling; break; case MENU_NORMAL: DisplayNormal = !DisplayNormal; break; case MENU_PERSPECTIVE: Projection = PROJ_PERSPECTIVE; break; case MENU_ORTHO: Projection = PROJ_ORTHO; break; case MENU_ROTATE: TransformMode = TRANSFORM_ROTATE; break; case MENU_SCALE: TransformMode = TRANSFORM_SCALE; break; case MENU_SCULPT: TransformMode = TRANSFORM_SCULPT; break; case MENU_VIEW: ViewMode = ViewMode == VIEW_OMNI ? VIEW_FOLLOW : ( ViewMode == VIEW_RIDE ? VIEW_OMNI : VIEW_RIDE ); break; case MENU_RESET: Reset3(); break; default: Menu(action); glutSetWindow(p7[W_2D].window); glutPostRedisplay(); break; } glutSetWindow(p7[W_3D].window); glutPostRedisplay(); } /* Menu3() */ /************************************************************** MouseButton3 Process mouse button press. */ static void MouseButton3(int button, int state, int x, int y) { MouseButton(W_3D, button, state, x, y); if( TransformMode == TRANSFORM_SCULPT && state == GLUT_DOWN ) { glSelectBuffer(PICK_BUFFER_SIZE, PickBuffer); Pick3(); if( !PickHitCount ) Pick3(); if( PickHitCount ) { DeformLandscape3(PickBuffer[3], button == GLUT_LEFT_BUTTON ? BUMP_RAISE : BUMP_LOWER); InitLandscape3(); } } } /* MouseButton3() */ /************************************************************** MouseMotion3 Process mouse drag. */ static void MouseMotion3(int x, int y) { int dx, dy; dx = x - p7[W_3D].mx; dy = y - p7[W_3D].my; if( TransformMode == TRANSFORM_ROTATE ) { if( p7[W_3D].mb & MOUSE_LEFT ) { SceneRotateX += ROTATE_FACTOR * dy; SceneRotateY += ROTATE_FACTOR * dx; } else if( p7[W_3D].mb & MOUSE_MIDDLE ) { SceneScale += SCALE_FACTOR * (dx - dy); if( SceneScale < MINIMUM_SCALE ) SceneScale = MINIMUM_SCALE; } } else if( TransformMode == TRANSFORM_SCALE ) { if( p7[W_3D].mb & MOUSE_LEFT ) { SceneScale += SCALE_FACTOR * (dx - dy); if( SceneScale < MINIMUM_SCALE ) SceneScale = MINIMUM_SCALE; } else if( p7[W_3D].mb & MOUSE_MIDDLE ) { SceneRotateX += ROTATE_FACTOR * dy; SceneRotateY += ROTATE_FACTOR * dx; } } p7[W_3D].mx = x; p7[W_3D].my = y; } /* MouseMotion3() */ /********************************************************************* Pick3 Redraw window for picking. */ static void Pick3(void) { GLfloat dw, dh, s; GLint viewport[4]; /* Initialize picking */ glRenderMode(GL_SELECT); glInitNames(); glPushName(0xffffffff); /* Initialize window */ glutSetWindow(p7[W_3D].window); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); /* View -> screen */ p7[W_3D].w = glutGet(GLUT_WINDOW_WIDTH); p7[W_3D].h = glutGet(GLUT_WINDOW_HEIGHT); glViewport(0, 0, p7[W_3D].w, p7[W_3D].h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); viewport[0] = viewport[1] = 0; viewport[2] = p7[W_3D].w; viewport[3] = p7[W_3D].h; gluPickMatrix(p7[W_3D].mx, p7[W_3D].h - p7[W_3D].my, PICK_TOLERANCE, PICK_TOLERANCE, viewport); s = ViewMode == VIEW_RIDE ? SQUARE_VIEW_SIZE2 : SQUARE_VIEW_SIZE; if( p7[W_3D].w > p7[W_3D].h ) { dw = (GLfloat)(((s * p7[W_3D].w) / (float)p7[W_3D].h) / 2); dh = (GLfloat)(s / 2); } else { dw = (GLfloat)(s / 2); dh = (GLfloat)(((s * p7[W_3D].h) / (float)p7[W_3D].w) / 2); } if( Projection == PROJ_ORTHO ) { glOrtho(-dw, dw, -dh, dh, ORTHO_NEAR, ORTHO_FAR); } else { glFrustum(-dw * PERSP_SCALE, dw * PERSP_SCALE, -dh * PERSP_SCALE, dh * PERSP_SCALE, PERSP_NEAR, PERSP_FAR); } /* Model -> view */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if( ViewMode == VIEW_RIDE ) { gluLookAt(path(x) + CAMERA_OFFSET * path(nx), path(y) + CAMERA_OFFSET * path(ny), path(z) + CAMERA_OFFSET * path(nz), pathnext(x) + CAMERA_OFFSET * pathnext(nx), pathnext(y) + CAMERA_OFFSET * pathnext(ny), pathnext(z) + CAMERA_OFFSET * pathnext(nz), path(nx), path(ny), path(nz)); } else if( ViewMode == VIEW_FOLLOW ) { gluLookAt(0, PATH_MAX_HEIGHT / 3, 0, path(x) + PATH_NORMAL_LENGTH * path(nx), path(y) + PATH_NORMAL_LENGTH * path(ny), path(z) + PATH_NORMAL_LENGTH * path(nz), 0, 1, 0); } else { gluLookAt(0, 0, FLOOR_SIZE / 3, 0, 0, 0, 0, 1, 0); glRotatef(SceneRotateY, 0, 1, 0); glRotatef(SceneRotateX, 1, 0, 0); glScalef(SceneScale, SceneScale, SceneScale); } /* Geometry */ if( DisplayFloor ) glCallList(Obj[OBJ_FLOOR]); /* End */ glFlush(); PickHitCount = glRenderMode(GL_RENDER); } /* Pick3() */ /******************************************************************** Reset3 Reset. */ static void Reset3(void) { int i, j; Projection = PROJ_PERSPECTIVE; TransformMode = TRANSFORM_ROTATE; ViewMode = VIEW_OMNI; TextureMode = TRUE; BilinearMode = TRUE; SmoothLine = SmoothPoint = SmoothPoly = FALSE; SceneScale = 0.7; SceneRotateX = 20; SceneRotateY = 0; DisplayFloor = TRUE; DisplayNormal = FALSE; DisplayCeiling = TRUE; DisplayTrack = TRUE; for(i = 0; i < FLOOR_RESOLUTION; i++) { for(j = 0; j < FLOOR_RESOLUTION; j++) Landscape[i][j] = 0; } InitLandscape3(); p7[W_3D].mb = 0; glutSetWindow(p7[W_3D].window); glutPostRedisplay(); } /* Reset3() */ /******************************************************************* Resize3 Process change in window size. */ static void Resize3(int width, int height) { Resize(W_3D, width, height); } /* Resize3() */ /****************************************************************** Visible3 Process change in visibility. */ static void Visible3(int state) { if( state == GLUT_VISIBLE ) { glutSetWindow(p7[W_3D].window); glutPostRedisplay(); } } /* Visible3() */