/* Simple Scene Graph ------------------------------------------------------- */ #include "ssg.h" #ifdef __APPLE__ #include #else #include #endif #include #include #include #include /* SSG constants ------------------------------------------------------------ */ #define WHEEL_UP 3 #define WHEEL_DOWN 4 #define CAMERA_DISTANCE_MIN 1.0 #define CAMERA_DISTANCE_MAX 50.0 /* SSG data structures ------------------------------------------------------ */ struct SSG_Node { void *data; render_func_t *render; struct SSG_Node *child; struct SSG_Node *sibling; }; struct SSG_Viewer { const char *title; size_t width; size_t height; int frame; double eye_x; double eye_y; double eye_z; double camera_distance; double camera_longitude; double camera_latitude; int mouse_x; int mouse_y; }; /* SSG Node functions ------------------------------------------------------- */ SSG_Node * ssg_node_create(render_func_t *render, void *data) { SSG_Node *n = NULL; n = malloc(sizeof(SSG_Node)); n->data = data; n->render = render; n->child = NULL; n->sibling = NULL; return (n); } void ssg_node_delete(SSG_Node *n) { /* TODO: delete node */ } void * ssg_node_data(SSG_Node *n) { return n->data; } void ssg_node_connect(SSG_Node *parent, SSG_Node *child) { SSG_Node *n; if (parent->child == NULL) { parent->child = child; return; } for (n = parent; n->sibling; n = n->sibling); n->sibling = child; } void ssg_node_render(SSG_Node *n) { if (n == NULL) return; glPushMatrix(); if (n->render) n->render(n); ssg_node_render(n->child); glPopMatrix(); ssg_node_render(n->sibling); } /* SSG Viewer functions ----------------------------------------------------- */ SSG_Viewer * ssg_viewer_create(const char *title, size_t width, size_t height) { SSG_Viewer *v = NULL; v = malloc(sizeof(SSG_Viewer)); memset(v, 0, sizeof(SSG_Viewer)); v->title = strdup(title); v->width = width; v->height = height; v->eye_x = 20.0; v->eye_y = 20.0; v->eye_z = 20.0; v->camera_latitude = 5.0; v->camera_longitude = 10.0; v->camera_distance = 10.0; return v; } void ssg_viewer_initialize(SSG_Viewer *v, int *argc, char *argv[]) { glutInit(argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(v->width, v->height); } static SSG_Viewer *Viewer = NULL; static SSG_Node *Root = NULL; void ssg_viewer_update_camera() { double L = Viewer->camera_distance* cos(M_PI*Viewer->camera_longitude/180.0); Viewer->eye_x = L * -sin(M_PI*Viewer->camera_latitude/180.0); Viewer->eye_y = Viewer->camera_distance * sin(M_PI*Viewer->camera_longitude/180.0); Viewer->eye_z = L * cos(M_PI*Viewer->camera_latitude/180.0); glutPostRedisplay(); } void ssg_viewer_display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(Viewer->eye_x, Viewer->eye_y, Viewer->eye_z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); ssg_node_render(Root); glutSwapBuffers(); } void ssg_viewer_reshape(int width, int height) { Viewer->width = width; Viewer->height = height; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (GLdouble)(width)/(GLdouble)(height), 0.1, 1000.0); ssg_viewer_update_camera(); } void ssg_viewer_keyboard(unsigned char key, int x, int y) { if (key == 'q' || key == 'Q') exit(EXIT_SUCCESS); } void ssg_viewer_mouse(int button, int state, int x, int y) { Viewer->mouse_x = x; Viewer->mouse_y = y; switch (button) { case WHEEL_UP: Viewer->camera_distance = (Viewer->camera_distance > CAMERA_DISTANCE_MIN ? Viewer->camera_distance - 0.5 : CAMERA_DISTANCE_MIN); break; case WHEEL_DOWN: Viewer->camera_distance = (Viewer->camera_distance < CAMERA_DISTANCE_MAX ? Viewer->camera_distance + 0.1 : CAMERA_DISTANCE_MAX); break; } ssg_viewer_update_camera(); } void ssg_viewer_motion(int x, int y) { Viewer->camera_latitude += 180.0*(double)(x - Viewer->mouse_x)/Viewer->width; Viewer->camera_longitude += 180.0*(double)(y - Viewer->mouse_y)/Viewer->height;; if (Viewer->camera_longitude < -90.0) Viewer->camera_longitude = -90.0; if (Viewer->camera_longitude > 90.0) Viewer->camera_longitude = 90.0; Viewer->mouse_x = x; Viewer->mouse_y = y; ssg_viewer_update_camera(); } void ssg_viewer_timer(int n) { glutTimerFunc(100, ssg_viewer_timer, Viewer->frame++); glutPostRedisplay(); } void ssg_viewer_show(SSG_Viewer *v, SSG_Node *n) { glutCreateWindow(v->title); glutDisplayFunc(ssg_viewer_display); glutReshapeFunc(ssg_viewer_reshape); glutKeyboardFunc(ssg_viewer_keyboard); glutMouseFunc(ssg_viewer_mouse); glutMotionFunc(ssg_viewer_motion); glutTimerFunc(100, ssg_viewer_timer, 0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); Viewer = v; Root = n; glutMainLoop(); } /* vim: set sts=4 sw=4 ts=8 ft=cpp: ----------------------------------------- */