/* Simple Scene Graph ------------------------------------------------------- */ #include "ssg.h" #include #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; int type; 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; }; struct SSG_Shader { GLuint vertex; GLuint fragment; GLuint program; }; /* Internal function prototypes --------------------------------------------- */ char * ssg_shader_read(const char *path); void ssg_shader_print_shader_log(GLuint id); void ssg_shader_print_program_log(GLuint id); /* SSG Node functions ------------------------------------------------------- */ SSG_Node * ssg_node_create(render_func_t *render, int type, void *data) { SSG_Node *n = NULL; n = malloc(sizeof(SSG_Node)); n->data = data; n->type = type; 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) { SSG_Shader *s = NULL; if (n == NULL) return; if (n->type == SSG_NODE_SHADER) { s = (SSG_Shader *)(n->data); glUseProgram(s->program); } glPushMatrix(); if (n->render) n->render(n); ssg_node_render(n->child); glPopMatrix(); if (n->type == SSG_NODE_SHADER) { //ssg_shader_print_program_log(s->program); glUseProgram(0); } 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[]) { GLenum result; glutInit(argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(v->width, v->height); glutCreateWindow(v->title); result = glewInit(); if (result != GLEW_OK) { fprintf(stderr, "unable to initialize glew: %s\n", glewGetErrorString(result)); exit(EXIT_FAILURE); } if (!glewIsSupported("GL_VERSION_2_0")) { fprintf(stderr, "OpenGL 2.0 is not supported\n"); exit(EXIT_FAILURE); } } 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) { 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(); } /* SSG Render functions ----------------------------------------------------- */ void ssg_cube_render(SSG_Node *n) { glPushMatrix(); glColor3f(1.0, 1.0, 1.0); glutSolidCube(1.0); glPopMatrix(); } void ssg_teapot_render(SSG_Node *n) { glPushMatrix(); glColor3f(1.0, 1.0, 1.0); glFrontFace(GL_CW); glutSolidTeapot(1.0); glFrontFace(GL_CCW); glPopMatrix(); } /* SSG Shader functions ----------------------------------------------------- */ SSG_Shader * ssg_shader_create(const char *vert_src_path, const char *frag_src_path) { SSG_Shader *s; char *vs; char *fs; s = malloc(sizeof(SSG_Shader)); s->vertex = glCreateShader(GL_VERTEX_SHADER); s->fragment = glCreateShader(GL_FRAGMENT_SHADER); s->program = glCreateProgram(); if (vert_src_path) { vs = ssg_shader_read(vert_src_path); glShaderSource(s->vertex, 1, (const char **)&vs, NULL); glCompileShader(s->vertex); glAttachShader(s->program, s->vertex); free(vs); ssg_shader_print_shader_log(s->vertex); } if (frag_src_path) { fs = ssg_shader_read(frag_src_path); glShaderSource(s->fragment, 1, (const char **)&fs, NULL); glCompileShader(s->fragment); glAttachShader(s->program, s->fragment); free(fs); ssg_shader_print_shader_log(s->fragment); } glLinkProgram(s->program); return s; } GLuint ssg_shader_program(SSG_Shader *s) { return s->program; } char * ssg_shader_read(const char *path) { FILE *fs = NULL; char *buffer = NULL; size_t fsize; fs = fopen(path, "r"); if (fs == NULL) { fprintf(stderr, "unable to open shader file %s: %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } fseek(fs, 0, SEEK_END); fsize = ftell(fs); fseek(fs, 0, SEEK_SET); if (fsize > 0) { buffer = malloc(sizeof(char) * (fsize + 1)); fsize = fread(buffer, sizeof(char), fsize, fs); buffer[fsize] = '\0'; } fclose(fs); return buffer; } void ssg_shader_print_shader_log(GLuint id) { char *buffer; GLint buffer_written; GLint buffer_size; glGetShaderiv(id, GL_INFO_LOG_LENGTH, &buffer_size); if (buffer_size > 0) { buffer = malloc(sizeof(char) * buffer_size); glGetShaderInfoLog(id, buffer_size, &buffer_written, buffer); fprintf(stderr, "Shader %u Log: %s\n", id, buffer); free(buffer); } } void ssg_shader_print_program_log(GLuint id) { char *buffer; GLint buffer_written; GLint buffer_size; glGetProgramiv(id, GL_INFO_LOG_LENGTH, &buffer_size); if (buffer_size > 0) { buffer = malloc(sizeof(char) * buffer_size); glGetProgramInfoLog(id, buffer_size, &buffer_written, buffer); fprintf(stderr, "Program %u Log: %s\n", id, buffer); free(buffer); } } /* vim: set sts=4 sw=4 ts=8 ft=cpp: ----------------------------------------- */