// rendergl.cpp: core opengl rendering stuff #include "cube.h" #include "bot/bot.h" #ifdef __APPLE__ #define GL_COMBINE_EXT GL_COMBINE_ARB #define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB #define GL_SOURCE0_RGB_EXT GL_SOURCE0_RGB_ARB #define GL_SOURCE1_RGB_EXT GL_SOURCE1_RGB_ARB #define GL_RGB_SCALE_EXT GL_RGB_SCALE_ARB #define GL_PRIMARY_COLOR_EXT GL_PRIMARY_COLOR_ARB #endif bool hasoverbright = false; VAR(maxtexsize, 0, 0, 4096); void gl_init(int w, int h, int bpp, int depth, int fsaa) { //#define fogvalues 0.5f, 0.6f, 0.7f, 1.0f glViewport(0, 0, w, h); glClearDepth(1.0); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_DENSITY, 0.25); glHint(GL_FOG_HINT, GL_NICEST); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glEnable(GL_POLYGON_OFFSET_LINE); glPolygonOffset(-3.0, -3.0); glCullFace(GL_FRONT); glEnable(GL_CULL_FACE); char *exts = (char *)glGetString(GL_EXTENSIONS); if(strstr(exts, "GL_EXT_texture_env_combine")) hasoverbright = true; else if(strstr(exts, "GL_ARB_texture_env_combine")) hasoverbright = true; else conoutf("WARNING: cannot use overbright lighting, using old lighting model!"); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); GLUquadricObj *qsphere = gluNewQuadric(); if(!qsphere) fatal("glu sphere"); gluQuadricDrawStyle(qsphere, GLU_FILL); gluQuadricOrientation(qsphere, GLU_INSIDE); gluQuadricTexture(qsphere, GL_TRUE); glNewList(1, GL_COMPILE); gluSphere(qsphere, 1, 12, 6); glEndList(); gluDeleteQuadric(qsphere); if(fsaa) glEnable(GL_MULTISAMPLE); camera1 = player1; }; Texture *crosshair = NULL; hashtable textures; void cleangl() { }; VAR(warnqual, 0, 0, 1); GLuint installtex(const char *texname, int &xs, int &ys, bool clamp) { SDL_Surface *s = IMG_Load(texname); if(!s) { conoutf("couldn't load texture %s", texname); return 0; }; if(s->format->BitsPerPixel!=24 && s->format->BitsPerPixel!=32) { SDL_FreeSurface(s); conoutf("texture must be 24bpp or 32bpp: %s", texname); return 0; }; // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; }; GLuint tnum; glGenTextures(1, &tnum); glBindTexture(GL_TEXTURE_2D, tnum); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); int w = (xs = s->w), h = (ys = s->h); if(maxtexsize) while(w>maxtexsize || h>maxtexsize) { w /= 2; h /= 2; }; int mode = s->format->BitsPerPixel==24 ? GL_RGB : GL_RGBA; if(w!=s->w) { if(warnqual) conoutf("warning: quality loss: scaling %s", texname); // for voodoo cards under linux if(gluScaleImage(mode, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels, w, h, GL_UNSIGNED_BYTE, s->pixels)) { w = s->w; h = s->h; }; }; if(gluBuild2DMipmaps(GL_TEXTURE_2D, mode, w, h, mode, GL_UNSIGNED_BYTE, s->pixels)) fatal("could not build mipmaps"); SDL_FreeSurface(s); return tnum; }; // management of texture slots // each texture slot can have multople texture frames, of which currently only the first is used // additional frames can be used for various shaders Texture *textureload(const char *name, bool clamp) { string pname; s_strcpy(pname, name); path(pname); Texture *t = textures.access(pname); if(t) return t; int xs, ys; GLuint id = installtex(pname, xs, ys, clamp); if(!id) return crosshair; char *key = newstring(pname); t = &textures[key]; t->name = key; t->xs = xs; t->ys = ys; t->id = id; return t; }; struct Slot { string name; Texture *tex; bool loaded; }; vector slots; void texturereset() { slots.setsizenodelete(0); }; void texture(char *aframe, char *name) { Slot &s = slots.add(); s_strcpy(s.name, name); path(s.name); s.tex = NULL; s.loaded = false; }; COMMAND(texturereset, ARG_NONE); COMMAND(texture, ARG_2STR); int lookuptexture(int tex, int &xs, int &ys) { Texture *t = crosshair; if(slots.inrange(tex)) { Slot &s = slots[tex]; if(!s.loaded) { s_sprintfd(pname)("packages/textures/%s", s.name); s.tex = textureload(pname); s.loaded = true; }; if(s.tex) t = s.tex; }; xs = t->xs; ys = t->ys; return t->id; }; void setupworld() { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); setarraypointers(); if(hasoverbright) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); }; }; struct strip { int type, start, num; }; struct stripbatch { int tex; vector strips; }; vector skystrips; stripbatch stripbatches[256]; uchar renderedtex[256]; int renderedtexs = 0; void renderstripssky() { if(skystrips.empty()) return; int xs, ys; glBindTexture(GL_TEXTURE_2D, lookuptexture(DEFAULT_SKY, xs, ys)); loopv(skystrips) glDrawArrays(skystrips[i].type, skystrips[i].start, skystrips[i].num); skystrips.setsizenodelete(0); }; void renderstrips() { int xs, ys; loopj(renderedtexs) { stripbatch &sb = stripbatches[j]; glBindTexture(GL_TEXTURE_2D, lookuptexture(sb.tex, xs, ys)); loopv(sb.strips) glDrawArrays(sb.strips[i].type, sb.strips[i].start, sb.strips[i].num); sb.strips.setsizenodelete(0); }; renderedtexs = 0; }; void overbright(float amount) { if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount ); }; void addstrip(int type, int tex, int start, int n) { vector *strips; if(tex==DEFAULT_SKY) strips = &skystrips; else { stripbatch *sb = &stripbatches[renderedtex[tex]]; if(sb->tex!=tex || sb>=&stripbatches[renderedtexs]) { sb = &stripbatches[renderedtex[tex] = renderedtexs++]; sb->tex = tex; }; strips = &sb->strips; }; if(type!=GL_TRIANGLE_STRIP && !strips->empty()) { strip &last = strips->last(); if(last.type==type && last.start+last.num==start) { last.num += n; return; }; }; strip &s = strips->add(); s.type = type; s.start = start; s.num = n; }; VARFP(gamma, 30, 100, 300, { float f = gamma/100.0f; if(SDL_SetGamma(f,f,f)==-1) { conoutf("Could not set gamma (card/driver doesn't support it?)"); conoutf("sdl: %s", SDL_GetError()); }; }); physent *camera1 = NULL; void trypitch(int i) { camera1->pitch = (float)i; } COMMAND(trypitch, ARG_1INT); void recomputecamera() { if(editmode || player1->state!=CS_DEAD) { camera1 = player1; } else { static physent deathcam; if(camera1==&deathcam) return; deathcam = *(physent *)player1; deathcam.reset(); deathcam.type = ENT_CAMERA; deathcam.roll = 0; deathcam.move = -1; camera1 = &deathcam; loopi(10) moveplayer(camera1, 10, true, 50); }; }; void transplayer() { glLoadIdentity(); glRotated(camera1->roll,0.0,0.0,1.0); glRotated(camera1->pitch,-1.0,0.0,0.0); glRotated(camera1->yaw,0.0,1.0,0.0); glTranslated(-camera1->o.x, -camera1->o.z, -camera1->o.y); }; VARP(fov, 90, 100, 120); int xtraverts; VAR(fog, 64, 180, 1024); VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF); VARP(hudgun,0,1,1); char *hudgunnames[] = { "knife", "pistol", "shotgun", "subgun", "sniper", "assault", "grenade"}; void drawhudgun(int w, int h, float aspect, int farplane) { if(scoped && player1->gunselect==GUN_SNIPER) return; glEnable(GL_CULL_FACE); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective((float)100.0f*h/w, aspect, 0.3f, farplane); // fov fixed at 100° glMatrixMode(GL_MODELVIEW); if(hudgun && player1->state!=CS_DEAD) renderhudgun(); rendermenumdl(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective((float)fov*h/w, aspect, 0.15f, farplane); glMatrixMode(GL_MODELVIEW); glDisable(GL_CULL_FACE); }; bool outsidemap(physent *pl) { if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) return true; sqr *s = S((int)pl->o.x, (int)pl->o.y); return SOLID(s) || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); }; void gl_drawframe(int w, int h, float changelod, float curfps) { recomputecamera(); float hf = hdr.waterlevel-0.3f; float fovy = (float)fov*h/w; float aspect = w/(float)h; bool underwater = camera1->o.z>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f }; glFogfv(GL_FOG_COLOR, fogc); glClearColor(fogc[0], fogc[1], fogc[2], 1.0f); if(underwater) { fovy += sinf(lastmillis/1000.0f)*2.0f; aspect += sinf(lastmillis/1000.0f+PI)*0.1f; glFogi(GL_FOG_START, 0); glFogi(GL_FOG_END, (fog+96)/8); }; glClear((outsidemap(camera1) ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); int farplane = fog*5/2; gluPerspective(fovy, aspect, 0.15f, farplane); glMatrixMode(GL_MODELVIEW); transplayer(); glEnable(GL_TEXTURE_2D); resetcubes(); render_world(camera1->o.x, camera1->o.y, camera1->o.z, changelod, (int)camera1->yaw, (int)camera1->pitch, (float)fov, w, h); finishstrips(); setupworld(); renderstripssky(); glLoadIdentity(); glRotated(camera1->pitch, -1.0, 0.0, 0.0); glRotated(camera1->yaw, 0.0, 1.0, 0.0); glRotated(90.0, 1.0, 0.0, 0.0); glColor3f(1.0f, 1.0f, 1.0f); glDisable(GL_FOG); glDepthFunc(GL_GREATER); draw_envbox(fog*4/3); glDepthFunc(GL_LESS); glEnable(GL_FOG); transplayer(); overbright(2); renderstrips(); xtraverts = 0; renderentities(); readdepth(w, h); renderclients(); if(player1->state==CS_ALIVE) readdepth(w, h, hitpos); renderspheres(curtime); renderents(); renderbounceents(); rendershotlines(); glDisable(GL_CULL_FACE); // Added by Rick: Need todo here because of drawing the waypoints glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); WaypointClass.Think(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // end add drawhudgun(w, h, aspect, farplane); overbright(1); int nquads = renderwater(hf); overbright(2); render_particles(curtime); overbright(1); glDisable(GL_FOG); glDisable(GL_TEXTURE_2D); extern vector verts; gl_drawhud(w, h, (int)curfps, nquads, verts.length(), underwater); glEnable(GL_CULL_FACE); glEnable(GL_FOG); };