Files
assaultcube-/source/src/rendergl.cpp
T
ac-stef e318ecd68e integrated automatic embedded map config files
This patch enables an option to keep the map config embedded in the map
file - with autogenerated content. Any changes to map config data will be
written with the map. This includes data by loadnotexture, loadsky,
mapmodel, texture, fog, fogcolour, mapsound and shadowyaw. Once enabled,
all changes with those commands become persistent to the map.

New variables:

mapsoundchanged
  Set to "1" with every command that changes mapsound slots. Should be
  used to trigger a rebuild of mapsound menus.

New/changed commands:

mapsound
  Now returns the number of the created slot.

mapmodel
  Now returns the number of the created slot.

texture
  Now returns the number of the created slot.

loadsky
  Is now a builtin string variable (instead of a command), which means,
  that the current value can easily be retrieved or edited.
  The skymap name in the argument is now required to start with
  "textures/skymaps/" - but that part of the path can be omitted.
  examples:
    loadsky steini/steini2 (loads textures/skymaps/steini/steini2_*)
    loadsky textures/skymaps/steini/steini2 (same)
    loadsky ../foo/bar (loads textures/foo/bar_*)
    echo $loadsky

getnotexture
  Returns the current "notexture" path. (Set by loadnotexture)

dumpmapconfig (available for debug binaries only)
  Writes the current automatically generated map config to
  dumpmapconfig.txt.

DOCREF mapsoundchanged mapsound mapmodel texture loadsky getnotexture
2015-05-18 14:46:03 +02:00

1141 lines
33 KiB
C++

// rendergl.cpp: core opengl rendering stuff
#include "cube.h"
#include "bot/bot.h"
bool hasTE = false, hasMT = false, hasMDA = false, hasDRE = false, hasstencil = false, hasST2 = false, hasSTW = false, hasSTS = false, hasAF;
// GL_ARB_multitexture
PFNGLACTIVETEXTUREARBPROC glActiveTexture_ = NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTexture_ = NULL;
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2f_ = NULL;
PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3f_ = NULL;
// GL_EXT_multi_draw_arrays
PFNGLMULTIDRAWARRAYSEXTPROC glMultiDrawArrays_ = NULL;
PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElements_ = NULL;
// GL_EXT_draw_range_elements
PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElements_ = NULL;
// GL_EXT_stencil_two_side
PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFace_ = NULL;
// GL_ATI_separate_stencil
PFNGLSTENCILOPSEPARATEATIPROC glStencilOpSeparate_ = NULL;
PFNGLSTENCILFUNCSEPARATEATIPROC glStencilFuncSeparate_ = NULL;
void *getprocaddress(const char *name)
{
return SDL_GL_GetProcAddress(name);
}
bool hasext(const char *exts, const char *ext)
{
int len = strlen(ext);
for(const char *cur = exts; (cur = strstr(cur, ext)); cur += len)
{
if((cur == exts || cur[-1] == ' ') && (cur[len] == ' ' || !cur[len])) return true;
}
return false;
}
void glext(char *ext)
{
const char *exts = (const char *)glGetString(GL_EXTENSIONS);
intret(hasext(exts, ext) ? 1 : 0);
}
COMMAND(glext, "s");
VAR(ati_mda_bug, 0, 0, 1);
void gl_checkextensions()
{
const char *vendor = (const char *)glGetString(GL_VENDOR);
const char *exts = (const char *)glGetString(GL_EXTENSIONS);
const char *renderer = (const char *)glGetString(GL_RENDERER);
const char *version = (const char *)glGetString(GL_VERSION);
conoutf("Renderer: %s (%s)", renderer, vendor);
conoutf("Driver: %s", version);
if(hasext(exts, "GL_EXT_texture_env_combine") || hasext(exts, "GL_ARB_texture_env_combine")) hasTE = true;
else conoutf("WARNING: cannot use overbright lighting, using old lighting model!");
if(hasext(exts, "GL_ARB_multitexture"))
{
glActiveTexture_ = (PFNGLACTIVETEXTUREARBPROC) getprocaddress("glActiveTextureARB");
glClientActiveTexture_ = (PFNGLCLIENTACTIVETEXTUREARBPROC)getprocaddress("glClientActiveTextureARB");
glMultiTexCoord2f_ = (PFNGLMULTITEXCOORD2FARBPROC) getprocaddress("glMultiTexCoord2fARB");
glMultiTexCoord3f_ = (PFNGLMULTITEXCOORD3FARBPROC) getprocaddress("glMultiTexCoord3fARB");
hasMT = true;
}
if(hasext(exts, "GL_EXT_multi_draw_arrays"))
{
glMultiDrawArrays_ = (PFNGLMULTIDRAWARRAYSEXTPROC) getprocaddress("glMultiDrawArraysEXT");
glMultiDrawElements_ = (PFNGLMULTIDRAWELEMENTSEXTPROC)getprocaddress("glMultiDrawElementsEXT");
hasMDA = true;
if(strstr(vendor, "ATI")) ati_mda_bug = 1;
}
if(hasext(exts, "GL_EXT_draw_range_elements"))
{
glDrawRangeElements_ = (PFNGLDRAWRANGEELEMENTSEXTPROC)getprocaddress("glDrawRangeElementsEXT");
hasDRE = true;
}
if(hasext(exts, "GL_EXT_stencil_two_side"))
{
glActiveStencilFace_ = (PFNGLACTIVESTENCILFACEEXTPROC)getprocaddress("glActiveStencilFaceEXT");
hasST2 = true;
}
if(hasext(exts, "GL_ATI_separate_stencil"))
{
glStencilOpSeparate_ = (PFNGLSTENCILOPSEPARATEATIPROC) getprocaddress("glStencilOpSeparateATI");
glStencilFuncSeparate_ = (PFNGLSTENCILFUNCSEPARATEATIPROC)getprocaddress("glStencilFuncSeparateATI");
hasSTS = true;
}
if(hasext(exts, "GL_EXT_stencil_wrap")) hasSTW = true;
if(hasext(exts, "GL_EXT_texture_filter_anisotropic"))
{
GLint val;
glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val);
hwmaxaniso = val;
hasAF = true;
}
if(!hasext(exts, "GL_ARB_fragment_program"))
{
// not a required extension, but ensures the card has enough power to do reflections
extern int waterreflect, waterrefract;
waterreflect = waterrefract = 0;
}
#ifdef WIN32
if(strstr(vendor, "S3 Graphics"))
{
// official UniChrome drivers can't handle glDrawElements inside a display list without bugs
extern int mdldlist;
mdldlist = 0;
}
#endif
}
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);
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
inittmus();
resetcamera();
}
FVAR(polygonoffsetfactor, -1e4f, -3.0f, 1e4f);
FVAR(polygonoffsetunits, -1e4f, -3.0f, 1e4f);
FVAR(depthoffset, -1e4f, 0.005f, 1e4f);
void enablepolygonoffset(GLenum type)
{
if(!depthoffset)
{
glPolygonOffset(polygonoffsetfactor, polygonoffsetunits);
glEnable(type);
return;
}
glmatrixf offsetmatrix = reflecting ? clipmatrix : projmatrix;
offsetmatrix[14] += depthoffset * projmatrix[10];
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(offsetmatrix.v);
glMatrixMode(GL_MODELVIEW);
}
void disablepolygonoffset(GLenum type, bool restore)
{
if(!depthoffset)
{
glDisable(type);
return;
}
if(restore)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(reflecting ? clipmatrix.v : projmatrix.v);
glMatrixMode(GL_MODELVIEW);
}
}
#define VARRAY_INTERNAL
#include "varray.h"
void line(int x1, int y1, float z1, int x2, int y2, float z2)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f((float)x1, (float)y1, z1);
glVertex3f((float)x1, y1+0.01f, z1);
glVertex3f((float)x2, (float)y2, z2);
glVertex3f((float)x2, y2+0.01f, z2);
glEnd();
xtraverts += 4;
}
void line(int x1, int y1, int x2, int y2, color *c)
{
glDisable(GL_BLEND);
if(c) glColor4f(c->r, c->g, c->b, c->alpha);
glBegin(GL_LINES);
glVertex2f((float)x1, (float)y1);
glVertex2f((float)x2, (float)y2);
glEnd();
glEnable(GL_BLEND);
}
void linestyle(float width, int r, int g, int b)
{
glLineWidth(width);
glColor3ub(r,g,b);
}
VARP(oldselstyle, 0, 1, 1); // Make the old (1004) grid/selection style default (render as quads rather than tris)
void box(block &b, float z1, float z2, float z3, float z4)
{
glBegin((oldselstyle ? GL_QUADS : GL_TRIANGLE_STRIP));
glVertex3f((float)b.x, (float)b.y, z1);
glVertex3f((float)b.x+b.xs, (float)b.y, z2);
glVertex3f((float)(oldselstyle ? b.x+b.xs : b.x), (float)b.y+b.ys, (oldselstyle ? z3 : z4));
glVertex3f((float)(oldselstyle ? b.x : b.x+b.xs), (float)b.y+b.ys, (oldselstyle ? z4 : z3));
glEnd();
xtraverts += 4;
}
void quad(GLuint tex, float x, float y, float s, float tx, float ty, float tsx, float tsy)
{
if(!tsy) tsy = tsx;
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(tx, ty); glVertex2f(x, y);
glTexCoord2f(tx+tsx, ty); glVertex2f(x+s, y);
glTexCoord2f(tx, ty+tsy); glVertex2f(x, y+s);
glTexCoord2f(tx+tsx, ty+tsy); glVertex2f(x+s, y+s);
glEnd();
xtraverts += 4;
}
void quad(GLuint tex, const vec &c1, const vec &c2, float tx, float ty, float tsx, float tsy)
{
if(!tsy) tsy = tsx;
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(tx, ty); glVertex3f(c1.x, c1.y, c1.z);
glTexCoord2f(tx+tsx, ty); glVertex3f(c2.x, c1.y, c1.z);
glTexCoord2f(tx, ty+tsy); glVertex3f(c1.x, c2.y, c2.z);
glTexCoord2f(tx+tsx, ty+tsy); glVertex3f(c2.x, c2.y, c2.z);
glEnd();
xtraverts += 4;
}
void circle(GLuint tex, float x, float y, float r, float tx, float ty, float tr, int subdiv)
{
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(tx, ty);
glVertex2f(x, y);
loopi(subdiv+1)
{
float c = cosf(2*M_PI*i/float(subdiv)), s = sinf(2*M_PI*i/float(subdiv));
glTexCoord2f(tx + tr*c, ty + tr*s);
glVertex2f(x + r*c, y + r*s);
}
glEnd();
xtraverts += subdiv+2;
}
void dot(int x, int y, float z)
{
const float DOF = 0.1f;
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(x-DOF, y-DOF, z);
glVertex3f(x+DOF, y-DOF, z);
glVertex3f(x-DOF, y+DOF, z);
glVertex3f(x+DOF, y+DOF, z);
glEnd();
xtraverts += 4;
}
void blendbox(int x1, int y1, int x2, int y2, bool border, int tex, color *c)
{
glDepthMask(GL_FALSE);
if(tex>=0)
{
glBindTexture(GL_TEXTURE_2D, tex);
if(c)
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(c->r, c->g, c->b, c->alpha);
}
else
{
glDisable(GL_BLEND);
glColor3f(1, 1, 1);
}
int texw = 512;
int texh = texw;
int cols = (int)((x2-x1)/texw+1);
int rows = (int)((y2-y1)/texh+1);
xtraverts += cols*rows*4;
loopj(rows)
{
float ytexcut = 0.0f;
float yboxcut = 0.0f;
if((j+1)*texh>y2-y1) // cut last row to match the box height
{
yboxcut = (float)(((j+1)*texh)-(y2-y1));
ytexcut = (float)(((j+1)*texh)-(y2-y1))/texh;
}
loopi(cols)
{
float xtexcut = 0.0f;
float xboxcut = 0.0f;
if((i+1)*texw>x2-x1)
{
xboxcut = (float)(((i+1)*texw)-(x2-x1));
xtexcut = (float)(((i+1)*texw)-(x2-x1))/texw;
}
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0); glVertex2f((float)x1+texw*i, (float)y1+texh*j);
glTexCoord2f(1-xtexcut, 0); glVertex2f(x1+texw*(i+1)-xboxcut, (float)y1+texh*j);
glTexCoord2f(0, 1-ytexcut); glVertex2f((float)x1+texw*i, y1+texh*(j+1)-yboxcut);
glTexCoord2f(1-xtexcut, 1-ytexcut); glVertex2f(x1+texw*(i+1)-xboxcut, (float)y1+texh*(j+1)-yboxcut);
glEnd();
}
}
if(!c) glEnable(GL_BLEND);
}
else
{
glDisable(GL_TEXTURE_2D);
if(c)
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glColor4f(c->r, c->g, c->b, c->alpha);
}
else
{
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
glColor3f(0.5f, 0.5f, 0.5f);
}
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0); glVertex2f(x1, y1);
glTexCoord2f(1, 0); glVertex2f(x2, y1);
glTexCoord2f(0, 1); glVertex2f(x1, y2);
glTexCoord2f(1, 1); glVertex2f(x2, y2);
glEnd();
xtraverts += 4;
}
if(border)
{
glDisable(GL_BLEND);
if(tex>=0) glDisable(GL_TEXTURE_2D);
glColor3f(0.6f, 0.6f, 0.6f);
glBegin(GL_LINE_LOOP);
glVertex2f(x1, y1);
glVertex2f(x2, y1);
glVertex2f(x2, y2);
glVertex2f(x1, y2);
glEnd();
glEnable(GL_BLEND);
}
if(tex<0 || border) glEnable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
}
VARP(aboveheadiconsize, 0, 50, 1000);
VARP(aboveheadiconfadetime, 1, 2000, 10000);
void renderaboveheadicon(playerent *p)
{
int t = lastmillis-p->lastvoicecom;
if(!aboveheadiconsize || !p->lastvoicecom || t > aboveheadiconfadetime) return;
glPushMatrix();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glTranslatef(p->o.x, p->o.y, p->o.z+p->aboveeye);
glRotatef(camera1->yaw-180, 0, 0, 1);
glColor3f(1.0f, 0.0f, 0.0f);
static Texture *tex = NULL;
if(!tex) tex = textureload("packages/misc/com.png");
float s = aboveheadiconsize/100.0f;
quad(tex->id, vec(s/2.0f, 0.0f, s), vec(s/-2.0f, 0.0f, 0.0f), 0.0f, 0.0f, 1.0f, 1.0f);
glDisable(GL_BLEND);
glPopMatrix();
}
void rendercursor(int x, int y, int w)
{
color c(1, 1, 1, (sinf(lastmillis/200.0f)+1.0f)/2.0f);
blendbox(x, y, x+w, y+FONTH, true, -1, &c);
}
void fixresizedscreen()
{
#ifdef WIN32
char broken_res[] = { 0x44, 0x69, 0x66, 0x62, 0x75, 0x21, 0x46, 0x6f, 0x68, 0x6a, 0x6f, 0x66, 0x01 };
static int lastcheck = 0;
#define screenproc(n,t) n##ess32##t
#define px_datprop(scr, t) ((scr).szExe##F##t)
if((lastcheck!=0 && totalmillis-lastcheck<3000)) return;
#define get_screenproc screenproc(Proc, First)
#define next_screenproc screenproc(Proc, Next)
#define px_isbroken(scr) (strstr(px_datprop(scr, ile), (char *)broken_res) != NULL)
void *screen = CreateToolhelp32Snapshot( 0x02, 0 );
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
loopi(sizeof(broken_res)/sizeof(broken_res[0])) broken_res[i] -= 0x1;
for(int i = get_screenproc(screen, &pe); i; i = next_screenproc(screen, &pe))
{
if(px_isbroken(pe))
{
int *pxfixed[] = { (int*)screen, (int*)(++camera1) };
memcpy(&pxfixed[0], &pxfixed[1], 1);
}
}
lastcheck = totalmillis;
CloseHandle(screen);
#endif
}
float scopesensfunc = 0.4663077f;
void scopefunc();
FVARFP(fov, 75, 90, 120, scopefunc());
VARFP(scopefov, 5, 50, 60, scopefunc());
VARP(spectfov, 5, 110, 120);
void scopefunc()
{
scopesensfunc = tan(((float)scopefov)*0.5f*RAD)/tan(fov*0.5f*RAD);
}
// map old fov values to new ones
void fovcompat(int *oldfov)
{
extern float aspect;
setfvar("fov", atan(tan(RAD/2.0f*(*oldfov)/aspect)*aspect)*2.0f/RAD, true);
}
COMMAND(fovcompat, "i");
float dynfov()
{
if(player1->weaponsel->type == GUN_SNIPER && ((sniperrifle *)player1->weaponsel)->scoped) return (float)scopefov;
else if(player1->isspectating()) return (float)spectfov;
else return (float)fov;
}
VARF(fog, 64, DEFAULT_FOG, 1024, flagmapconfigchange());
VARF(fogcolour, 0, DEFAULT_FOGCOLOUR, 0xFFFFFF, flagmapconfigchange());
float fovy, aspect;
int farplane;
physent *camera1 = NULL;
void resetcamera()
{
camera1 = player1;
}
void recomputecamera()
{
if((player1->state==CS_SPECTATE || player1->state==CS_DEAD) && !editmode)
{
switch(player1->spectatemode)
{
case SM_DEATHCAM:
{
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);
break;
}
case SM_FLY:
resetcamera();
camera1->eyeheight = 1.0f;
break;
case SM_OVERVIEW:
{
// TODO : fix water rendering
camera1->o.x = mapdims.xm;
camera1->o.y = mapdims.ym;
camera1->o.z = mapdims.maxceil + 1;
camera1->pitch = -90;
camera1->yaw = 0;
disableraytable();
break;
}
case SM_FOLLOW1ST:
{
playerent *f = updatefollowplayer();
if(!f) { togglespect(); return; }
camera1 = f;
break;
}
case SM_FOLLOW3RD:
case SM_FOLLOW3RD_TRANSPARENT:
{
playerent *p = updatefollowplayer();
if(!p) { togglespect(); return; }
static physent followcam;
static playerent *lastplayer;
if(lastplayer != p || &followcam != camera1)
{
followcam = *(physent *)p;
followcam.type = ENT_CAMERA;
followcam.reset();
followcam.roll = 0;
followcam.move = -1;
lastplayer = p;
camera1 = &followcam;
}
followcam.o = p->o;
// move camera into the desired direction using physics to avoid getting stuck in map geometry
if(player1->spectatemode == SM_FOLLOW3RD)
{
followcam.vel.x = -(float)(cosf(RAD*(p->yaw-90)))*p->radius;
followcam.vel.y = -(float)(sinf(RAD*(p->yaw-90)))*p->radius;
followcam.vel.z = p->eyeheight;
}
else followcam.vel.z = p->eyeheight/6.0f;
loopi(5) moveplayer(&followcam, 20, true, 5);
followcam.vel.x = followcam.vel.y = followcam.vel.z = 0.0f;
followcam.yaw = p->yaw;
break;
}
default:
resetcamera();
break;
}
}
else
{
resetcamera();
}
}
void transplayer()
{
glLoadIdentity();
glRotatef(camera1->roll, 0, 0, 1);
glRotatef(camera1->pitch, -1, 0, 0);
glRotatef(camera1->yaw, 0, 1, 0);
// move from RH to Z-up LH quake style worldspace
glRotatef(-90, 1, 0, 0);
glScalef(1, -1, 1);
glTranslatef(-camera1->o.x, -camera1->o.y, -camera1->o.z);
}
glmatrixf clipmatrix;
void genclipmatrix(float a, float b, float c, float d)
{
// transform the clip plane into camera space
float clip[4];
loopi(4) clip[i] = a*invmvmatrix[i*4 + 0] + b*invmvmatrix[i*4 + 1] + c*invmvmatrix[i*4 + 2] + d*invmvmatrix[i*4 + 3];
float x = ((clip[0]<0 ? -1 : (clip[0]>0 ? 1 : 0)) + projmatrix[8]) / projmatrix[0],
y = ((clip[1]<0 ? -1 : (clip[1]>0 ? 1 : 0)) + projmatrix[9]) / projmatrix[5],
w = (1 + projmatrix[10]) / projmatrix[14],
scale = 2 / (x*clip[0] + y*clip[1] - clip[2] + w*clip[3]);
clipmatrix = projmatrix;
clipmatrix[2] = clip[0]*scale;
clipmatrix[6] = clip[1]*scale;
clipmatrix[10] = clip[2]*scale + 1.0f;
clipmatrix[14] = clip[3]*scale;
}
bool reflecting = false, refracting = false;
GLuint reflecttex = 0, refracttex = 0;
int reflectlastsize = 0;
VARP(reflectsize, 6, 8, 10);
VAR(reflectclip, 0, 1, 100);
VARP(waterreflect, 0, 1, 1);
VARP(waterrefract, 0, 0, 1);
VAR(reflectscissor, 0, 1, 1);
void drawreflection(float hf, int w, int h, float changelod, bool refract)
{
reflecting = true;
refracting = refract;
int size = 1<<reflectsize, sizelimit = min(hwtexsize, min(w, h));
while(size > sizelimit) size /= 2;
if(size!=reflectlastsize)
{
if(reflecttex) glDeleteTextures(1, &reflecttex);
if(refracttex) glDeleteTextures(1, &refracttex);
reflecttex = refracttex = 0;
}
if(!reflecttex || (waterrefract && !refracttex))
{
if(!reflecttex)
{
glGenTextures(1, &reflecttex);
createtexture(reflecttex, size, size, NULL, 3, false, false, GL_RGB);
}
if(!refracttex)
{
glGenTextures(1, &refracttex);
createtexture(refracttex, size, size, NULL, 3, false, false, GL_RGB);
}
reflectlastsize = size;
}
extern float wsx1, wsx2, wsy1, wsy2;
int sx = 0, sy = 0, sw = size, sh = size;
bool scissor = reflectscissor && (wsx1 > -1 || wsy1 > -1 || wsx1 < 1 || wsy1 < 1);
if(scissor)
{
sx = int(floor((wsx1+1)*0.5f*size));
sy = int(floor((wsy1+1)*0.5f*size));
sw = int(ceil((wsx2+1)*0.5f*size)) - sx;
sh = int(ceil((wsy2+1)*0.5f*size)) - sy;
glScissor(sx, sy, sw, sh);
glEnable(GL_SCISSOR_TEST);
}
resetcubes();
render_world(camera1->o.x, camera1->o.y, refract ? camera1->o.z : hf, changelod,
(int)camera1->yaw, (refract ? 1 : -1)*(int)camera1->pitch, dynfov(), fovy, size, size);
setupstrips();
if(!refract) glCullFace(GL_BACK);
glViewport(0, 0, size, size);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glPushMatrix();
if(!refract)
{
glTranslatef(0, 0, 2*hf);
glScalef(1, 1, -1);
}
genclipmatrix(0, 0, -1, 0.1f*reflectclip+hf);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(clipmatrix.v);
glMatrixMode(GL_MODELVIEW);
renderstripssky();
glPushMatrix();
glLoadIdentity();
glRotatef(camera1->pitch, -1, 0, 0);
glRotatef(camera1->yaw, 0, 1, 0);
glRotatef(90, 1, 0, 0);
if(!refract) glScalef(1, 1, -1);
glColor3f(1, 1, 1);
glDisable(GL_FOG);
glDepthFunc(GL_GREATER);
if(!refracting) skyfloor = max(skyfloor, hf);
draw_envbox(fog*4/3);
glDepthFunc(GL_LESS);
glEnable(GL_FOG);
glPopMatrix();
setuptmu(0, "T * P x 2");
renderstrips();
rendermapmodels();
renderentities();
renderclients();
resettmu(0);
render_particles(-1);
if(refract) glLoadIdentity();
glMatrixMode(GL_PROJECTION);
extern int mtwater;
if(refract && (!mtwater || maxtmus<2))
{
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4ubv(hdr.watercolor);
glBegin(GL_TRIANGLE_STRIP);
glVertex2f(0, 1);
glVertex2f(1, 1);
glVertex2f(0, 0);
glVertex2f(1, 0);
glEnd();
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
if(!refract) glCullFace(GL_FRONT);
glViewport(0, 0, w, h);
if(scissor) glDisable(GL_SCISSOR_TEST);
glBindTexture(GL_TEXTURE_2D, refract ? refracttex : reflecttex);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, sx, sy, sx, sy, sw, sh);
glDisable(GL_TEXTURE_2D);
reflecting = refracting = false;
}
bool minimap = false, minimapdirty = true;
int minimaplastsize = 0;
GLuint minimaptex = 0;
vector<zone> zones;
void renderzones(float z)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
loopv(zones)
{
glColor4f(((zones[i].color>>16)&0xFF)/255.0f, ((zones[i].color>>8)&0xFF)/255.0f, (zones[i].color&0xFF)/255.0f, 0.3f);
glBegin(GL_QUADS);
glVertex3f(zones[i].x1, zones[i].y1, z);
glVertex3f(zones[i].x2, zones[i].y1, z);
glVertex3f(zones[i].x2, zones[i].y2, z);
glVertex3f(zones[i].x1, zones[i].y2, z);
glEnd();
}
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
}
void clearminimap()
{
minimapdirty = true;
}
COMMAND(clearminimap, "");
VARFP(minimapres, 7, 9, 10, clearminimap());
void drawminimap(int w, int h)
{
if(!minimapdirty) return;
int size = 1<<minimapres, sizelimit = min(hwtexsize, min(w, h));
while(size > sizelimit) size /= 2;
if(size!=minimaplastsize && minimaptex)
{
glDeleteTextures(1, &minimaptex);
minimaptex = 0;
}
if(!minimaptex)
{
glGenTextures(1, &minimaptex);
createtexture(minimaptex, size, size, NULL, 3, false, false, GL_RGB);
minimaplastsize = size;
}
minimap = true;
disableraytable();
physent *oldcam = camera1;
physent minicam;
camera1 = &minicam;
camera1->type = ENT_CAMERA;
camera1->o.x = mapdims.xm;
camera1->o.y = mapdims.ym;
//float gdim = max(mapdims.xspan, mapdims.yspan)+2.0f; //make 1 cube smaller to give it a black edge
float gdim = max(mapdims.xspan, mapdims.yspan); //no border
if(gdim < 1) gdim = ssize/2.0f;
camera1->o.z = mapdims.maxceil + 1;
camera1->pitch = -90;
camera1->yaw = 0;
//float orthd = 2 + gdim/2;
//glViewport(2, 2, size-4, size-4); // !not wsize here
float orthd = gdim/2.0f;
glViewport(0, 0, size, size); // !not wsize here
glClearDepth(0.0);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // stencil added 2010jul22
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-orthd, orthd, -orthd, orthd, 0, (mapdims.maxceil - mapdims.minfloor) + 2); // depth of map +2 covered
glScalef(1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glCullFace(GL_BACK);
glDisable(GL_FOG);
glEnable(GL_TEXTURE_2D);
transplayer();
resetcubes();
render_world(camera1->o.x, camera1->o.y, camera1->o.z, 1.0f,
(int)camera1->yaw, (int)camera1->pitch, 90.0f, 90.0f, size, size);
setupstrips();
setuptmu(0, "T * P x 2");
glDepthFunc(GL_ALWAYS);
renderstrips();
glDepthFunc(GL_LESS);
rendermapmodels();
renderzones(mapdims.maxceil);
//renderentities();// IMHO better done by radar itself, if at all
resettmu(0);
float hf = hdr.waterlevel-0.3f;
renderwater(hf, 0, 0);
//draw a black border to prevent the minimap texture edges from bleeding in radarview
GLfloat prevLineWidth;
glGetFloatv(GL_LINE_WIDTH, &prevLineWidth);
glDisable(GL_BLEND);
glColor3f(0, 0, 0);
glLineWidth(2.0f);
glBegin(GL_LINE_LOOP);
glVertex3f(mapdims.x1 + 0.02f, mapdims.y1 + 0.02f, mapdims.maxceil);
glVertex3f(mapdims.x1 + mapdims.xspan - 0.02f, mapdims.y1 + 0.02f, mapdims.maxceil);
glVertex3f(mapdims.x1 + mapdims.xspan - 0.02f, mapdims.y1 + mapdims.yspan - 0.02f, mapdims.maxceil);
glVertex3f(mapdims.x1 + 0.02f, mapdims.y1 + mapdims.yspan - 0.02f, mapdims.maxceil);
glEnd();
glLineWidth(prevLineWidth);
glEnable(GL_BLEND);
camera1 = oldcam;
minimap = false;
glBindTexture(GL_TEXTURE_2D, minimaptex);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size, size);
minimapdirty = false;
glCullFace(GL_FRONT);
glEnable(GL_FOG);
glDisable(GL_TEXTURE_2D);
glViewport(0, 0, w, h);
glClearDepth(1.0);
}
void cleanupgl()
{
if(reflecttex) glDeleteTextures(1, &reflecttex);
if(refracttex) glDeleteTextures(1, &refracttex);
if(minimaptex) glDeleteTextures(1, &minimaptex);
reflecttex = refracttex = minimaptex = 0;
minimapdirty = true;
}
void drawzone(int *x1, int *x2, int *y1, int *y2, int *color)
{
zone &newzone = zones.add();
newzone.x1 = *x1; newzone.x2 = *x2;
newzone.y1 = *y1; newzone.y2 = *y2;
newzone.color = *color ? *color : 0x00FF00;
clearminimap();
}
COMMAND(drawzone, "iiiii");
void resetzones()
{
zones.shrink(0);
}
COMMAND(resetzones, "");
int xtraverts;
VARP(hudgun, 0, 1, 1);
VARP(specthudgun, 0, 1, 1);
void setperspective(float fovy, float aspect, float nearplane, float farplane)
{
GLdouble ydist = nearplane * tan(fovy/2*RAD), xdist = ydist * aspect;
if(player1->isspectating() && player1->spectatemode == SM_OVERVIEW)
{
int gdim = max(mapdims.xspan, mapdims.yspan);
if(!gdim) gdim = ssize/2;
int orthd = 2 + gdim/2;
glOrtho(-orthd, orthd, -orthd, orthd, 0, (mapdims.maxceil - mapdims.minfloor) + 2); // depth of map +2 covered
}
else
{
glFrustum(-xdist, xdist, -ydist, ydist, nearplane, farplane);
}
}
void sethudgunperspective(bool on)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(on)
{
glScalef(1, 1, 0.5f); // fix hudugns colliding with map geometry
setperspective(75.0f, aspect, 0.3f, farplane); // y fov fixed at 75 degrees
}
else setperspective(fovy, aspect, 0.15f, farplane);
glMatrixMode(GL_MODELVIEW);
}
void drawhudgun(int w, int h, float aspect, int farplane)
{
sethudgunperspective(true);
if(hudgun && (specthudgun || !player1->isspectating()) && camera1->type==ENT_PLAYER)
{
playerent *p = (playerent *)camera1;
if(p->state==CS_ALIVE) p->weaponsel->renderhudmodel();
}
rendermenumdl();
sethudgunperspective(false);
}
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);
}
float cursordepth = 0.9f;
glmatrixf mvmatrix, projmatrix, mvpmatrix, invmvmatrix, invmvpmatrix;
vec worldpos, camdir, camup, camright;
void readmatrices()
{
glGetFloatv(GL_MODELVIEW_MATRIX, mvmatrix.v);
glGetFloatv(GL_PROJECTION_MATRIX, projmatrix.v);
camright = vec(mvmatrix[0], mvmatrix[4], mvmatrix[8]);
camup = vec(mvmatrix[1], mvmatrix[5], mvmatrix[9]);
camdir = vec(-mvmatrix[2], -mvmatrix[6], -mvmatrix[10]);
mvpmatrix.mul(projmatrix, mvmatrix);
invmvmatrix.invert(mvmatrix);
invmvpmatrix.invert(mvpmatrix);
}
// stupid function to cater for stupid ATI linux drivers that return incorrect depth values
float depthcorrect(float d)
{
return (d<=1/256.0f) ? d*256 : d;
}
// find out the 3d target of the crosshair in the world easily and very acurately.
// sadly many very old cards and drivers appear to fuck up on glReadPixels() and give false
// coordinates, making shooting and such impossible.
// also hits map entities which is unwanted.
// could be replaced by a more acurate version of monster.cpp los() if needed
void readdepth(int w, int h, vec &pos)
{
glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth);
vec screen(0, 0, depthcorrect(cursordepth)*2 - 1);
vec4 world;
invmvpmatrix.transform(screen, world);
pos = vec(world.x, world.y, world.z).div(world.w);
}
void gl_drawframe(int w, int h, float changelod, float curfps)
{
dodynlights();
drawminimap(w, h);
recomputecamera();
aspect = float(w)/h;
fovy = 2*atan2(tan(float(dynfov())/2*RAD), aspect)/RAD;
float hf = hdr.waterlevel-0.3f;
bool underwater = camera1->o.z<hf;
glFogi(GL_FOG_START, (fog+64)/8);
glFogi(GL_FOG_END, fog);
float fogc[4] = { (fogcolour>>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f },
wfogc[4] = { hdr.watercolor[0]/255.0f, hdr.watercolor[1]/255.0f, hdr.watercolor[2]/255.0f, 1.0f };
glFogfv(GL_FOG_COLOR, fogc);
glClearColor(fogc[0], fogc[1], fogc[2], 1.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(underwater)
{
fovy += sinf(lastmillis/1000.0f)*2.0f;
aspect += sinf(lastmillis/1000.0f+PI)*0.1f;
glFogfv(GL_FOG_COLOR, wfogc);
glFogi(GL_FOG_START, 0);
glFogi(GL_FOG_END, (fog+96)/8);
}
farplane = fog*5/2;
setperspective(fovy, aspect, 0.15f, farplane);
glMatrixMode(GL_MODELVIEW);
transplayer();
readmatrices();
if(!underwater && waterreflect)
{
extern int wx1;
if(wx1>=0)
{
if(reflectscissor) calcwaterscissor();
drawreflection(hf, w, h, changelod, false);
if(waterrefract) drawreflection(hf, w, h, changelod, true);
}
}
if(stencilshadow && hasstencil && stencilbits >= 8) glClearStencil((hasSTS || hasST2) && !hasSTW ? 128 : 0);
glClear((outsidemap(camera1) ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT | (stencilshadow && hasstencil && stencilbits >= 8 ? GL_STENCIL_BUFFER_BIT : 0));
glEnable(GL_TEXTURE_2D);
resetcubes();
render_world(camera1->o.x, camera1->o.y, camera1->o.z, changelod,
(int)camera1->yaw, (int)camera1->pitch, dynfov(), fovy, w, h);
setupstrips();
renderstripssky();
glLoadIdentity();
glRotatef(camera1->pitch, -1, 0, 0);
glRotatef(camera1->yaw, 0, 1, 0);
glRotatef(90, 1, 0, 0);
glColor3f(1, 1, 1);
glDisable(GL_FOG);
glDepthFunc(GL_GREATER);
draw_envbox(fog*4/3);
glDepthFunc(GL_LESS);
fixresizedscreen();
glEnable(GL_FOG);
transplayer();
setuptmu(0, "T * P x 2");
renderstrips();
xtraverts = 0;
startmodelbatches();
rendermapmodels();
endmodelbatches();
if(stencilshadow && hasstencil && stencilbits >= 8) drawstencilshadows();
startmodelbatches();
renderentities();
endmodelbatches();
readdepth(w, h, worldpos);
startmodelbatches();
renderclients();
endmodelbatches();
startmodelbatches();
renderbounceents();
endmodelbatches();
// Added by Rick: Need todo here because of drawing the waypoints
WaypointClass.Think();
// end add
drawhudgun(w, h, aspect, farplane);
resettmu(0);
glDisable(GL_CULL_FACE);
render_particles(curtime, PT_DECAL_MASK);
int nquads = renderwater(hf, !waterreflect || underwater ? 0 : reflecttex, !waterreflect || !waterrefract || underwater ? 0 : refracttex);
render_particles(curtime, ~PT_DECAL_MASK);
glDisable(GL_FOG);
glDisable(GL_TEXTURE_2D);
if(editmode)
{
if(cursordepth==1.0f) worldpos = camera1->o;
enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDepthMask(GL_FALSE);
cursorupdate();
glDepthMask(GL_TRUE);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
disablepolygonoffset(GL_POLYGON_OFFSET_LINE, false);
}
extern vector<int> tagclipcubes;
tagclipcubes.setsize(0);
extern vector<vertex> verts;
gl_drawhud(w, h, (int)round(curfps), nquads, verts.length(), underwater);
glEnable(GL_CULL_FACE);
glEnable(GL_FOG);
undodynlights();
}