/* * render.c * * Render the scene * * (c) 2008 Thomas White * * thrust3d - a silly game * */ #ifdef HAVE_CONFIG_H #include #endif #define GL_GLEXT_PROTOTYPES 1 #include #include #include #include #include #include #include "model.h" #include "game.h" #include "render.h" #include "texture.h" #include "utils.h" /* Utility function to load and compile a shader, checking the info log */ static GLhandleARB render_load_shader(const char *filename, GLenum type) { GLhandleARB shader; char text[4096]; size_t len; FILE *fh; int l; fh = fopen(filename, "r"); if ( fh == NULL ) { fprintf(stderr, "Couldn't load shader '%s'\n", filename); return 0; } len = fread(text, 1, 4095, fh); fclose(fh); text[len] = '\0'; const GLcharARB *source = text; shader = glCreateShaderObjectARB(type); glShaderSourceARB(shader, 1, &source, NULL); glCompileShaderARB(shader); glGetInfoLogARB(shader, 4095, &l, text); if ( l > 0 ) { printf("%s\n", text); fflush(stdout); } return shader; } static void render_load_shaders(RenderContext *ctx) { ctx->lighting_vert = render_load_shader(DATADIR"/shaders/lighting.vert", GL_VERTEX_SHADER_ARB); ctx->lighting_frag = render_load_shader(DATADIR"/shaders/lighting.frag", GL_FRAGMENT_SHADER_ARB); ctx->lighting_program = glCreateProgramObjectARB(); glAttachObjectARB(ctx->lighting_program, ctx->lighting_vert); glAttachObjectARB(ctx->lighting_program, ctx->lighting_frag); glLinkProgramARB(ctx->lighting_program); } static void render_delete_shaders(RenderContext *ctx) { glDetachObjectARB(ctx->lighting_program, ctx->lighting_frag); glDetachObjectARB(ctx->lighting_program, ctx->lighting_vert); glDeleteObjectARB(ctx->lighting_vert); glDeleteObjectARB(ctx->lighting_frag); glDeleteObjectARB(ctx->lighting_program); } /* OpenGL initial setup */ RenderContext *render_setup(int width, int height) { RenderContext *ctx; ctx = malloc(sizeof(RenderContext)); if ( ctx == NULL ) return NULL; ctx->width = width; ctx->height = height; ctx->aspect = (GLfloat)width/(GLfloat)height; glClearColor(0.0, 0.0, 0.0, 1.0); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* Create a small FBO for rendering reflections with */ glGenFramebuffersEXT(1, &ctx->fbo); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->fbo); /* Add a (texture) colour buffer to the FBO */ glGenTextures(1, &ctx->fbotex); glBindTexture(GL_TEXTURE_2D, ctx->fbotex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ctx->fbotex, 0); /* Add a depth buffer to the FBO */ glGenRenderbuffersEXT(1, &ctx->fbodepth); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, ctx->fbodepth); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, 256, 256); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ctx->fbodepth); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ctx->fbodepth); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); render_load_shaders(ctx); ctx->num_textures = 0; /* Load misc texture bits */ texture_load(ctx, "radioactive"); texture_load(ctx, "fuel"); return ctx; } void render_shutdown(RenderContext *ctx) { render_delete_shaders(ctx); texture_free_all(ctx); } static GLenum render_gltype(PrimitiveType type) { switch ( type ) { case PRIMITIVE_QUADS : return GL_QUADS; case PRIMITIVE_TRIANGLES : return GL_TRIANGLES; default : break; } return GL_FALSE; } static int render_model_instance_draw(ModelInstance *instance, Uint32 t, RenderContext *ctx) { int j; Model *m; GLfloat x, y, z; GLfloat black[] = {0.0, 0.0, 0.0}; m = instance->model; if ( m == NULL ) return 0; /* No model to draw */ x = instance->x; y = instance->y; z = instance->z; for ( j=0; jnum_primitives; j++ ) { Primitive *p; p = m->primitives[j]; if ( p->attribs & ATTRIB_PULSE ) { float s; s = fabsf(cosf(t * 0.001)); GLfloat c[] = {s*p->col_r, s*p->col_g, s*p->col_b}; glMaterialfv(GL_FRONT, GL_EMISSION, c); glColor3f(0.3, 0.3, 0.3); glMaterialfv(GL_FRONT, GL_SPECULAR, black); } else if ( p->attribs & ATTRIB_COLOUR ) { glMaterialfv(GL_FRONT, GL_EMISSION, black); glMaterialfv(GL_FRONT, GL_SPECULAR, black); glColor3f(p->col_r, p->col_g, p->col_b); } else { glMaterialfv(GL_FRONT, GL_EMISSION, black); glMaterialfv(GL_FRONT, GL_SPECULAR, black); glColor3f(1.0, 1.0, 1.0); } /* Location and orientation */ glPushMatrix(); glTranslatef(x, y, z); glRotatef(rad2deg(instance->yaw), 0.0, 0.0, -1.0); /* Minus sign defines +yaw as "right" */ /* Texture */ if ( p->texture != NULL ) { Texture *texture; texture = texture_lookup(ctx, p->texture); if ( texture != NULL ) { glBindTexture(GL_TEXTURE_2D, texture->texname); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } } glVertexPointer(3, GL_FLOAT, 0, p->vertices); glNormalPointer(GL_FLOAT, 0, p->normals); glTexCoordPointer(2, GL_FLOAT, 0, p->texcoords); glDrawArrays(render_gltype(p->type), 0, p->num_vertices); glPopMatrix(); glDisable(GL_TEXTURE_2D); } return 0; } static void render_draw_line(GLfloat x1, GLfloat y1, GLfloat z1, GLfloat x2, GLfloat y2, GLfloat z2) { GLfloat red[] = {1.0, 0.0, 0.0, 1.0}; glMaterialfv(GL_FRONT, GL_EMISSION, red); glColor3f(0.0, 0.0, 0.0); glBegin(GL_LINES); glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glEnd(); } static void render_draw_stuff(Game *game, Uint32 t) { int i; glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); for ( i=0; inum_rooms; i++ ) { Room *room; int j; room = game->rooms[i]; if ( room == NULL ) return; //render_setup_lighting(game, room); for ( j=0; jnum_objects; j++ ) { GLfloat x, y, z; if ( room->objects[j] == NULL ) continue; x = room->rx - game->cur_room_x; y = room->ry - game->cur_room_y; z = room->rz - game->cur_room_z; glPushMatrix(); glTranslatef(10.0*x, 10.0*y, 10.0*z); render_model_instance_draw(room->objects[j], t, game->render); glPopMatrix(); } } glPopClientAttrib(); } static void render_setup_lighting(Game *game) { GLfloat pos[] = {-1.0, -0.8, 1.3, 0.0}; GLfloat ambient[4]; GLfloat diffuse[] = {0.8, 0.8, 0.8, 1.0}; GLfloat specular[] = {0.8, 0.8, 0.8, 1.0}; glEnable(GL_LIGHTING); ambient[0] = 0.3; ambient[1] = 0.3; ambient[2] = 0.3; ambient[3] = 1.0; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, specular); glEnable(GL_LIGHT0); } static void render_draw_2d(RenderContext *r, Game *game) { Texture *texture; GLfloat cr, cg, cb; /* Set up transforms for 2D rendering */ glClear(GL_DEPTH_BUFFER_BIT); glDisable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glScalef(0.95, 0.95, 1.0); /* Radiation symbol */ texture = texture_lookup(r, "radioactive"); if ( texture != NULL ) { glBindTexture(GL_TEXTURE_2D, texture->texname); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } glColor4f(1.0, 1.0, 1.0, 0.5); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0); /* Bottom left */ glTexCoord2f(1.0, 0.0); glVertex2f(-0.9, -1.0); /* Bottom right */ glTexCoord2f(1.0, 1.0); glVertex2f(-0.9, -1.0+0.1*r->aspect); /* Top right */ glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, -1.0+0.1*r->aspect); /* Top left */ glEnd(); glDisable(GL_TEXTURE_2D); /* Radiation meter */ cg = 1.0 - game->radiation; glBegin(GL_QUADS); glColor4f(1.0, 1.0, 0.0, 0.5); glVertex2f(-1.0, -0.8); /* Bottom left */ glVertex2f(-0.9, -0.8); /* Bottom right */ glColor4f(0.8, cg, 0.0, game->radiation); glVertex2f(-0.9, -0.8+(1.8*game->radiation)); /* Top right */ glVertex2f(-1.0, -0.8+(1.8*game->radiation)); /* Top left */ glEnd(); /* Fuel symbol */ texture = texture_lookup(r, "fuel"); if ( texture != NULL ) { glBindTexture(GL_TEXTURE_2D, texture->texname); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } glColor4f(1.0, 1.0, 1.0, 0.5); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(0.9, -1.0); /* Bottom left */ glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0); /* Bottom right */ glTexCoord2f(1.0, 1.0); glVertex2f(1.0, -1.0+0.1*r->aspect); /* Top right */ glTexCoord2f(0.0, 1.0); glVertex2f(0.9, -1.0+0.1*r->aspect); /* Top left */ glEnd(); glDisable(GL_TEXTURE_2D); /* Fuel meter */ cr = 1.0 - game->fuel; cg = game->fuel; cb = 0.2 + 0.1*game->fuel; glBegin(GL_QUADS); glColor4f(1.0, 0.0, 0.2, 0.5); glVertex2f(0.9, -0.8); /* Bottom left */ glVertex2f(1.0, -0.8); /* Bottom right */ glColor4f(cr, cg, cb, 0.5); glVertex2f(1.0, -0.8+(1.8*game->fuel)); /* Top right */ glVertex2f(0.9, -0.8+(1.8*game->fuel)); /* Top left */ glEnd(); } void render_draw(Game *game, Uint32 t) { RenderContext *r; r = game->render; /* First pass: Looking upwards */ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r->fbo); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, 256, 256); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(70.0, 1.0, 0.1, 100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(game->lander->x, game->lander->y, game->lander->z, game->lander->x, game->lander->y, game->lander->z+10.0, sqrtf(2.0)*sinf(game->lander->yaw), sqrtf(2.0)*cosf(game->lander->yaw), 0.0); render_setup_lighting(game); render_draw_stuff(game, t); /* Second pass: Main view */ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, r->width, r->height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(50.0, (GLfloat)r->width/(GLfloat)r->height, 0.1, 100.0); /* Depth buffer 10cm to 100m */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(game->lander->x-(game->view_dist*sinf(game->lander->yaw)*cosf(game->view_angle)), game->lander->y-(game->view_dist*cosf(game->lander->yaw)*cosf(game->view_angle)), game->lander->z+(game->view_dist*sinf(game->view_angle)), game->lander->x, game->lander->y, game->lander->z, sqrtf(2.0)*sinf(game->lander->yaw)*sinf(game->view_angle), sqrtf(2.0)*cosf(game->lander->yaw)*sinf(game->view_angle), sqrtf(2.0)*cosf(game->view_angle)); render_setup_lighting(game); render_draw_stuff(game, t); /* Finally, draw the lander */ glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); render_model_instance_draw(game->lander, t, r); glPopClientAttrib(); render_draw_line(game->lander->x, game->lander->y, game->lander->z, game->lander->x, game->lander->y, game->lander->z-200.0); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); render_draw_2d(r, game); SDL_GL_SwapBuffers(); }