diff -rup src-2002_10_20/main.cpp src/main.cpp
--- src-2002_10_20/main.cpp	2003-01-01 20:30:50.000000000 +0200
+++ src/main.cpp	2003-01-06 20:48:21.000000000 +0200
@@ -101,7 +101,7 @@ int main(int argc, char **argv)
 
     #define log(s) puts("init: " s)
     log("sdl");
-    
+
     for(int i = 1; i<argc; i++)
     {
         if(argv[i][0]=='-') switch(argv[i][1])
@@ -120,7 +120,7 @@ int main(int argc, char **argv)
         }
         else conoutf("unknown commandline argument");
     };
-    
+
     #ifdef _DEBUG
     par = SDL_INIT_NOPARACHUTE;
     fs = 0;
@@ -133,7 +133,7 @@ int main(int argc, char **argv)
 
     initclient();
     initserver(dedicated, listen, uprate, sdesc, ip, master);  // never returns if dedicated
-      
+
     log("world");
     empty_world(7, true);
 
@@ -200,10 +200,11 @@ int main(int argc, char **argv)
         static float fps = 10.0;
         frames++;
         fps = (1000.0f/curtime+fps*50)/51;
-        computeraytable(player1->o.x, player1->o.y);
-        readdepth(scr_w, scr_h);
-        SDL_GL_SwapBuffers();
+        //computeraytable(player1->o.x, player1->o.y);
+        //readdepth(scr_w, scr_h);
+        //SDL_GL_SwapBuffers();
         gl_drawframe(scr_w, scr_h, fps<minfps ? fps/minfps : (fps>maxfps ? fps/maxfps : 1.0f), fps);
+        SDL_GL_SwapBuffers();
 
         SDL_Event event;
         int lasttype = 0, lastbut = 0;
diff -rup src-2002_10_20/renderextras.cpp src/renderextras.cpp
--- src-2002_10_20/renderextras.cpp	2003-01-01 20:30:50.000000000 +0200
+++ src/renderextras.cpp	2003-01-06 20:48:21.000000000 +0200
@@ -100,12 +100,14 @@ void newsphere(vec &o, float max, int ty
     };
 };
 
+extern int fisheye_side;
+
 void renderspheres(int time)
 {
     glDepthMask(GL_FALSE);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
-    glBindTexture(GL_TEXTURE_2D, 4);  
+    glBindTexture(GL_TEXTURE_2D, 4);
 
     for(sphere *p, **pp = &slist; p = *pp;)
     {
@@ -129,7 +131,8 @@ void renderspheres(int time)
         }
         else
         {
-            p->size += time/100.0f;   
+            if(!fisheye_side)
+                p->size += time/100.0f;
             pp = &p->next;
         };
     };
@@ -143,10 +146,10 @@ char *entnames[] =
 {
     "none?", "light", "playerstart",
     "shells", "bullets", "rockets", "riflerounds",
-    "health", "healthboost", "greenarmour", "yellowarmour", "quaddamage", 
-    "teleport", "teledest", 
-    "mapmodel", "monster", "trigger", 
-    "?", "?", "?", "?", "?", 
+    "health", "healthboost", "greenarmour", "yellowarmour", "quaddamage",
+    "teleport", "teledest",
+    "mapmodel", "monster", "trigger",
+    "?", "?", "?", "?", "?",
 };
 
 void renderents()       // show sparkly thingies for map entities in edit mode
diff -rup src-2002_10_20/rendergl.cpp src/rendergl.cpp
--- src-2002_10_20/rendergl.cpp	2003-01-01 20:30:50.000000000 +0200
+++ src/rendergl.cpp	2003-01-07 22:12:06.000000000 +0200
@@ -18,15 +18,15 @@ void gl_init(int w, int h)
     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);
     GLfloat fogcolor[4] = { fogvalues };
     glFogfv(GL_FOG_COLOR, fogcolor);
-    
+
 
     glEnable(GL_LINE_SMOOTH);
     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
@@ -37,10 +37,10 @@ void gl_init(int w, int h)
     glEnable(GL_CULL_FACE);
 
     char *exts = (char *)glGetString(GL_EXTENSIONS);
-    
+
     if(strstr(exts, "GL_EXT_texture_env_combine")) hasoverbright = true;
     else conoutf("WARNING: cannot use overbright lighting, using old lighting model!");
-        
+
     purgetextures();
 
     if(!(qsphere = gluNewQuadric())) fatal("glu sphere");
@@ -62,14 +62,14 @@ bool installtex(int tnum, char *texname,
     SDL_Surface *s = IMG_Load(texname);
     if(!s) { conoutf("couldn't load texture %s", (int)texname); return false; };
     if(s->format->BitsPerPixel!=24) { conoutf("texture must be 24bpp: %s", (int)texname); return false; };
-    // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; };  
+    // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; };
     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); //NEAREST);
-    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
     if(gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, s->w, s->h, GL_RGB, GL_UNSIGNED_BYTE, s->pixels)) fatal("could not build mipmaps");
     xs = s->w;
     ys = s->h;
@@ -168,12 +168,12 @@ void setupworld()
     glEnableClientState(GL_COLOR_ARRAY);
     glVertexPointer(3, GL_FLOAT, sizeof(vertex), &verts[0].x);
     glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &verts[0].r);
-    glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
     glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &verts[0].u);
 
     if(hasoverbright)
     {
-        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); 
+        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);
@@ -199,11 +199,11 @@ void renderstrips()
     {
         if(strips[i].tex!=lasttex)
         {
-            glBindTexture(GL_TEXTURE_2D, strips[i].tex); 
+            glBindTexture(GL_TEXTURE_2D, strips[i].tex);
             lasttex = strips[i].tex;
         };
-        glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num);  
-    };   
+        glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num);
+    };
     if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f);
 };
 
@@ -230,10 +230,20 @@ void transplayer()
     glLoadIdentity();
 
     glRotated(player1->roll,0.0,0.0,1.0);
-    glRotated(player1->pitch,-1.0,0.0,0.0);
+    //glRotated(player1->pitch,-1.0,0.0,0.0);
+    glRotated(-player1->pitch,1.0,0.0,0.0);
+    /*
+    {
+    static int i = 0;
+    if(!(++i%200))
+        printf("pitch = %3f yaw = %3f roll = %3f\n",
+            player1->pitch, player1->yaw, player1->roll);
+    }
+    */
+    //glRotated(player1->pitch,-1.0,0.0001,0.0);
     glRotated(player1->yaw,0.0,1.0,0.0);
 
-    glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y);   
+    glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y);
 };
 
 VAR(fov, 10, 105, 120);
@@ -252,14 +262,14 @@ void drawhudmodel(int start, int end, fl
 void drawhudgun(float fovy, float aspect, int farplane)
 {
     if(!hudgun || !player1->gunselect) return;
-    
+
     glEnable(GL_CULL_FACE);
-    
+
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     gluPerspective(fovy, aspect, 0.3f, farplane);
     glMatrixMode(GL_MODELVIEW);
-    
+
     //glClear(GL_DEPTH_BUFFER_BIT);
     int rtime = reloadtime(player1->gunselect);
     if(player1->lastattackgun==player1->gunselect && lastmillis-player1->lastattack<rtime)
@@ -282,7 +292,7 @@ void drawhudgun(float fovy, float aspect
 
     glDisable(GL_CULL_FACE);
 };
-
+/*
 void gl_drawframe(int w, int h, float changelod, float curfps)
 {
     glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT);
@@ -291,7 +301,7 @@ void gl_drawframe(int w, int h, float ch
     float fovy = (float)fov*h/w;
     float aspect = w/(float)h;
     bool underwater = player1->o.z<hf;
-    
+
     glFogi(GL_FOG_START, (fog+64)/8);
     glFogi(GL_FOG_END, fog);
 
@@ -302,7 +312,7 @@ void gl_drawframe(int w, int h, float ch
         glFogi(GL_FOG_START, 0);
         glFogi(GL_FOG_END, (fog+96)/8);
     };
-    
+
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     int farplane = max(fog*2, 384);
@@ -312,15 +322,15 @@ void gl_drawframe(int w, int h, float ch
     transplayer();
 
     glEnable(GL_TEXTURE_2D);
-    
+
     int xs, ys;
     skyoglid = lookuptexture(DEFAULT_SKY, xs, ys);
-   
+
     resetcubes();
-            
+
     curvert = 0;
     strips.setsize(0);
-  
+
     render_world(player1->o.x, player1->o.y, player1->o.z, changelod,
             (int)player1->yaw, (int)player1->pitch, (float)fov, w, h);
     finishstrips();
@@ -330,7 +340,8 @@ void gl_drawframe(int w, int h, float ch
     renderstripssky();
 
     glLoadIdentity();
-    glRotated(player1->pitch, -1.0, 0.0, 0.0);
+    glRotated(-player1->pitch, 1.0, 0.0, 0.0);
+    //glRotated(player1->pitch, -1.0, 0.0, 0.0);
     glRotated(player1->yaw,   0.0, 1.0, 0.0);
     glRotated(90.0, 1.0, 0.0, 0.0);
     glColor3f(1.0f, 1.0f, 1.0f);
@@ -341,7 +352,7 @@ void gl_drawframe(int w, int h, float ch
     glEnable(GL_FOG);
 
     transplayer();
-        
+
     renderstrips();
 
     xtraverts = 0;
@@ -370,4 +381,492 @@ void gl_drawframe(int w, int h, float ch
     glEnable(GL_CULL_FACE);
     glEnable(GL_FOG);
 };
+*/
+
+VAR(fisheye,0,0,1);
+
+int gl_draw_world(int w, int h, float changelod, float fovy, float aspect, int farplane, float hf)
+{
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective(fovy, aspect, 0.15f, farplane);
+    glMatrixMode(GL_MODELVIEW);
+
+    transplayer();
+
+    glEnable(GL_TEXTURE_2D);
+
+    int xs, ys;
+    skyoglid = lookuptexture(DEFAULT_SKY, xs, ys);
+
+    resetcubes();
+
+    curvert = 0;
+    strips.setsize(0);
+
+    render_world(player1->o.x, player1->o.y, player1->o.z, changelod,
+            (int)player1->yaw, (int)player1->pitch, (float)fov, w, h);
+    finishstrips();
+
+    setupworld();
+
+    renderstripssky();
+
+    glLoadIdentity();
+    if(fisheye)
+        glRotated(player1->roll,0.0,0.0,1.0);
+    glRotated(-player1->pitch, 1.0, 0.0, 0.0);
+    //glRotated(player1->pitch, -1.0, 0.0, 0.0);
+    glRotated(player1->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(14, fog*4/3);
+    glDepthFunc(GL_LESS);
+    glEnable(GL_FOG);
+
+    transplayer();
+
+    renderstrips();
+
+    xtraverts = 0;
+
+    renderclients();
+    monsterrender();
+
+    renderentities();
+
+    renderspheres(curtime);
+    render_particles(curtime);
+    renderents();
+
+    glDisable(GL_CULL_FACE);
+
+    return renderwater(hf); // nquads
+};
+
+VAR(fisheye_rect_nv_ext,0,1,1);
+VAR(ffov,1,260,360);
+VAR(fviews,1,3,6);
+VAR(fdistance,10,40,100);
+
+static GLuint fisheye_grid;
+static GLuint fisheye_tex[6];
+static GLenum fisheye_texmode;
+static GLint fisheye_texw, fisheye_texh;
+static int fisheye_step_x = 0, fisheye_step_y = 0;
+static bool fisheye_capable = false;
+static int fisheye_views = -1;
+int fisheye_side = 0;
+
+#define BOX_FRONT  0
+#define BOX_RIGHT  1
+#define BOX_LEFT   2
+#define BOX_TOP    3
+#define BOX_BOTTOM 4
+#define BOX_BEHIND 5
+
+#define CHKGLERR(s) \
+do { \
+       GLint err = glGetError(); \
+       if (err != GL_NO_ERROR) printf("%s: gl error %d\n", s, (int)err); \
+} while (0)
+
+#define NO(x) \
+do { \
+       if(x < 0) x += 360; \
+       else if(x >= 360) x -= 360; \
+} while (0)
+
+#ifndef GL_TEXTURE_RECTANGLE_NV
+#define GL_TEXTURE_RECTANGLE_NV 0x84F5
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
+#endif
+
+static bool gl_fisheye_init_once(int w, int h)
+{
+    static bool fisheye_initialized_once = false;
+
+    if(fisheye_initialized_once) return fisheye_capable;
+    fisheye_initialized_once = true;
+
+    GLint maxtex;
+    int maxdim = (w > h) ? w : h;
+
+    const char *nv_ext = "GL_NV_texture_rectangle";
+    if(fisheye_rect_nv_ext)
+    {
+        const char *exts = (const char*)glGetString(GL_EXTENSIONS);
+        if(strstr(exts, nv_ext))
+        {
+            glEnable(GL_TEXTURE_RECTANGLE_NV);
+            glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_NV, &maxtex);
+            conoutf("%s supported, max texture size %d", (int)nv_ext, maxtex); // typecast!
+            if(maxdim > maxtex)
+            {
+                conoutf("TEXTURE_RECTANGLE_NV max texture size insufficient for this video mode");
+                glDisable(GL_TEXTURE_RECTANGLE_NV);
+            }
+            else
+            {
+                fisheye_capable = true;
+                fisheye_texmode = GL_TEXTURE_RECTANGLE_NV;
+                fisheye_texw = w;
+                fisheye_texh = h;
+            }
+        }
+        else
+            conoutf("%s unsupported", (int)nv_ext);
+    }
+    if(!fisheye_capable)
+    {
+        // Should we use GL_PROXY_TEXTURE_2D instead?
+        // Manual page (XFree86 4.2.1):
+        // GL_MAX_TEXTURE_SIZE -      The value gives a rough estimate of the  largest  tex-
+        // ture  that  the  GL  can  handle.   If  the  GL  version  is  1.1 or greater, use
+        // GL_PROXY_TEXTURE_1D or GL_PROXY_TEXTURE_2D to  determine  if  a  texture  is  too
+        // large.
+        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtex);
+        conoutf("Trying standard OpenGL texture, max texture size %d", maxtex);
+        if(maxdim > maxtex)
+        {
+            conoutf("Max texture size insufficient for this video mode, try lower resolution");
+            // I'm too lazy to mess with viewport..
+            return false;
+        }
+        fisheye_capable = true;
+        fisheye_texmode = GL_TEXTURE_2D;
+        int ts;
+        for(ts = maxtex; ts >= w; ts >>= 1); ts <<= 1; fisheye_texw = ts;
+        for(ts = maxtex; ts >= h; ts >>= 1); ts <<= 1; fisheye_texh = ts;
+        conoutf("Using texture size %dx%d", fisheye_texw, fisheye_texh);
+    }
+    int grid_sizes[] = { 32, 30, 25, 24, 20, 16, 15, 0 };
+    for(int i = 0; int s = grid_sizes[i]; ++i)
+    {
+        if(!fisheye_step_x && !(w%s)) fisheye_step_x = w/s;
+        if(!fisheye_step_y && !(h%s)) fisheye_step_y = h/s;
+        if(fisheye_step_x && fisheye_step_y) break;
+    }
+    if(!fisheye_step_x || !fisheye_step_y)
+    {
+        conoutf("Cannot find grid size for your videomode %dx%d", w, h);
+        fisheye_capable = false;
+    }
+    return fisheye_capable;
+}
+
+static vec mk_vec_r1(float x, float y, int side)
+{
+    float vx, vy, vz;
+    switch(side)
+    {
+    case BOX_FRONT:  vx =  x; vy =  y; vz =  1; break;
+    case BOX_RIGHT:  vx =  1; vy =  y; vz = -x; break;
+    case BOX_LEFT:   vx = -1; vy =  y; vz =  x; break;
+    case BOX_TOP:    vx =  x; vy =  1; vz = -y; break;
+    case BOX_BOTTOM: vx =  x; vy = -1; vz =  y; break;
+    case BOX_BEHIND: vx = -x; vy =  y; vz = -1; break;
+    default: vx = vy = vz = 0; break; //prevent warning
+    }
+    float r = sqrt(x*x + y*y + 1);
+    vec v = { vx/r, vy/r, vz/r };
+    return v;
+}
+
+static void get_vertex_coord(int w, int h, float x, float y, int side, float *xs, float *ys)
+{
+    vec v = mk_vec_r1(x, y, side);
+    float yaw = acos(v.z);
+    float roll;
+    roll = atan2(v.y, v.x);
+    // scale yaw to PI/2
+    yaw *= 180.0/ffov;
+    float r = sin(yaw);
+    *xs = r*cos(roll) *w/2;
+    *ys = r*sin(roll) *h/2;
+}
+
+static bool gl_fisheye_init(int w, int h, float fovy, float aspect)
+{
+    static bool fisheye_initialized = false;
+    static int pfov = -1;
+    static int pviews = -1;
+    static int pfviews = -1;
+
+    if(fisheye_initialized && pfov == ffov && pviews == fviews)
+        return fisheye_capable;
+    if(!gl_fisheye_init_once(w, h)) return false;
+
+    if(pfov != ffov || pviews != fviews)
+    {
+        // min(fviews, real number of box plains we can see)
+        int r, n;
+        if(ffov <= 90)
+            r = 1;
+        else if(ffov <= 270)
+            r = 5;
+        else
+            r = 6;
+        n = (fviews < r) ? fviews : r;
+        if(fisheye_views != n && fisheye_initialized)
+            glDeleteTextures(fisheye_views, fisheye_tex);
+        fisheye_views = n;
+    }
+    if(pfviews != fisheye_views)
+    {
+        glEnable(fisheye_texmode);
+        glGenTextures(fisheye_views, fisheye_tex);
+        for(int i = 0; i < fisheye_views; ++i)
+        {
+            glBindTexture(fisheye_texmode, fisheye_tex[i]);
+            CHKGLERR("glBindTexture 1");
+            glTexImage2D(fisheye_texmode, 0, 3, fisheye_texw, fisheye_texh,
+                0, GL_RGB, GL_UNSIGNED_SHORT, NULL);
+            CHKGLERR ("glTexImage2D");
+            glTexParameteri(fisheye_texmode, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+            glTexParameteri(fisheye_texmode, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        }
+    }
+    if(pfov == ffov && pviews == fviews)
+    {
+        fisheye_initialized = true;
+        return true;
+    }
+    pfov = ffov;
+    pviews = fviews;
+    pfviews = fisheye_views;
+
+    if(fisheye_initialized)
+        glDeleteLists(fisheye_grid, 1);
+    fisheye_grid = glGenLists(1);
+    glNewList(fisheye_grid, GL_COMPILE);
+    glEnable(fisheye_texmode);
+    glEnable(GL_CULL_FACE);
+    glCullFace(GL_BACK);
+    // black screen instead of blue, reenable fill color later
+    glClearColor(0, 0, 0, 0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glDisable(GL_DEPTH_TEST);
+    glColor3f(1, 1, 1);
+
+    for(int side = 0; side < fisheye_views; ++side)
+    {
+        glBindTexture(fisheye_texmode, fisheye_tex[side]);
+        for(int xi = 0; xi < w; xi += fisheye_step_x) {
+            // texture coordinates
+            // trying not to hit 0.0 and w/texw, because
+            // CLAMP_TO_EDGE cannot help when texmode ==
+            // TEXTURE_2D and texw != w or texh != h
+            // this will take care of linear filter at edges
+            float tx = 0; // prevent warning
+            float txn = 0;
+            if(fisheye_texmode == GL_TEXTURE_2D)
+            {
+                tx = ((float)xi * (w-1)/w + 0.5)/fisheye_texw;
+                txn = ((float)(xi+fisheye_step_x) * (w-1)/w + 0.5)/fisheye_texw;
+            }
+            // translate to [-dimension/2; dimension/2]
+            int xm = xi - w/2;
+            // translate to [-1; 1]
+            float x  = (float)2*xm/w;
+            float xn = (float)2*(xm+fisheye_step_x)/w;
+
+            glBegin(GL_QUAD_STRIP);
+            for(int yi = 0; yi <= h; yi += fisheye_step_y) { // y inclusive, drawing quad_strip
+                // texture coordinates
+                float ty = 0;
+                if(fisheye_texmode == GL_TEXTURE_2D)
+                    ty = ((float)yi * (h-1)/h + 0.5)/fisheye_texh;
+                // translate to [-dimension/2; dimension/2]
+                int ym = yi - h/2;
+                // translate to [-1; 1]
+                float y = (float)2*ym/h;
+
+                float x1, y1, x2, y2;
+                get_vertex_coord(w, h, x,   y, side, &x1, &y1);
+                get_vertex_coord(w, h, xn,  y, side, &x2, &y2);
+                if(fisheye_texmode == GL_TEXTURE_RECTANGLE_NV) glTexCoord2i(xi, yi);
+                else glTexCoord2f(tx, ty);
+                glVertex2f(x1/aspect, y1);
+                if(fisheye_texmode == GL_TEXTURE_RECTANGLE_NV) glTexCoord2i(xi+fisheye_step_x, yi);
+                else glTexCoord2f(txn, ty);
+                glVertex2f(x2/aspect, y2);
+            }
+            glEnd();
+        }
+    }
+    glClearColor(fogvalues);
+    glCullFace(GL_FRONT);
+    glDisable(GL_CULL_FACE);
+    glEnable(GL_DEPTH_TEST);
+    if(fisheye_texmode == GL_TEXTURE_RECTANGLE_NV)
+        glDisable(GL_TEXTURE_RECTANGLE_NV);
+    glEndList();
+
+    fisheye_initialized = true;
+
+    return true;
+}
+
+int gl_render_cube_side(int side, int w, int h, float changelod, float fovy, float aspect, int farplane, float hf)
+{
+    float pitch, n_pitch;
+    float yaw, n_yaw;
+    float roll, n_roll;
+    float s_roll;
+    int nquads;
+
+    pitch = n_pitch = player1->pitch;
+    yaw  = n_yaw = player1->yaw;
+    // setting roll for now to 0, correct roll handling
+    // requre more exhaustive changes in rotation
+    // TODO: implement via matrix?
+    //roll  = n_roll = player1->roll;
+    s_roll = player1->roll;
+    roll = n_roll = 0;
+
+    switch (side) {
+    case BOX_FRONT: break;
+    case BOX_RIGHT:
+            n_pitch = -roll;
+            n_yaw += 90;
+            n_roll = pitch;
+            break;
+    case BOX_LEFT:
+            n_pitch = roll;
+            n_yaw -= 90;
+            n_roll = -pitch;
+            /*
+            static int f = 0;
+            if (!(f++%100))
+                printf("%4d %4d %4d | %4d %4d %4d\n",
+                    (int)pitch, (int)yaw, (int)roll,
+                    (int)n_pitch, (int)n_yaw, (int)n_roll);
+            */
+            break;
+    case BOX_TOP:
+            n_pitch += 90;
+            // probably top and bottom boxes should be implemented by rotating
+            // yaw 180 degrees to keep pitch [-90; 90] and apply texture in
+            // opposite directions
+            //if(n_pitch > 90.0) { n_pitch = 180.0 - n_pitch; n_yaw += 180.0; }
+            //else if(n_pitch < -90.0) { n_pitch = -180.0 - n_pitch; n_yaw += 180.0; }
+            break;
+    case BOX_BOTTOM:
+            n_pitch -= 90;
+            break;
+    case BOX_BEHIND:
+            n_pitch = -pitch;
+            n_yaw += 180;
+            break;
+    }
+    //NO(n_pitch);
+    NO(n_yaw);
+    //NO(n_roll);
+    player1->pitch = n_pitch;
+    player1->yaw = n_yaw;
+    player1->roll = n_roll;
+    computeraytable(player1->o.x, player1->o.y);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    fisheye_side = side;
+    nquads = gl_draw_world(w, h, changelod, 90, 1, farplane, hf);
+
+    glEnable(fisheye_texmode);
+    glBindTexture(fisheye_texmode, fisheye_tex[side]);
+    //CHKGLERR ("glBindTexture 2");
+    glCopyTexSubImage2D(fisheye_texmode, 0, 0, 0, 0, 0, w, h);
+    //CHKGLERR ("glCopyTexSubImage2D");
+    if(fisheye_texmode == GL_TEXTURE_RECTANGLE_NV)
+        glDisable(GL_TEXTURE_RECTANGLE_NV);
+
+    player1->pitch = pitch;
+    player1->yaw = yaw;
+    player1->roll = s_roll;
+
+    return nquads;
+}
 
+extern void readmatrices(void);
+
+void gl_drawframe(int w, int h, float changelod, float curfps)
+{
+    glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT);
+
+    int farplane = max(fog*2, 384);
+    float hf = hdr.waterlevel-0.3f;
+    float fovy = (float)fov*h/w;
+    float aspect = w/(float)h;
+    bool underwater = player1->o.z<hf;
+    int nquads = 0;
+
+    glFogi(GL_FOG_START, (fog+64)/8);
+    glFogi(GL_FOG_END, fog);
+
+    if(underwater)
+    {
+        fovy += (float)sin(lastmillis/1000.0)*2.0f;
+        aspect += (float)sin(lastmillis/1000.0+PI)*0.1f;
+        glFogi(GL_FOG_START, 0);
+        glFogi(GL_FOG_END, (fog+96)/8);
+    };
+
+    if(!fisheye)
+    {
+        computeraytable(player1->o.x, player1->o.y);
+        fisheye_side = 0;
+        nquads = gl_draw_world(w, h, changelod, fovy, aspect, farplane, hf);
+        readmatrices();
+        readdepth(w, h);
+        glDisable(GL_FOG);
+    }
+    else
+    {
+        if(gl_fisheye_init(w, h, fovy, aspect))
+        {
+            // We are playing realy nasty things with aspect here.
+            // gl_draw_world() is called with original width and height -
+            // in most cases non-square resolution to preserve quality for
+            // low ffov, fovy = 90 and aspect = 1.
+            // So it renders square picture to rectangle.
+            // Fisheye algorithm divide x coorbinate by aspect to compensate
+            // this and create 'square' grid that in turn rendered with
+            // normal screen aspect.
+            switch(fisheye_views)
+            {
+            case 6:  nquads  = gl_render_cube_side(BOX_BEHIND, w, h, changelod, fovy, aspect, farplane, hf);
+            case 5:  nquads += gl_render_cube_side(BOX_BOTTOM, w, h, changelod, fovy, aspect, farplane, hf);
+            case 4:  nquads += gl_render_cube_side(BOX_TOP,    w, h, changelod, fovy, aspect, farplane, hf);
+            case 3:  nquads += gl_render_cube_side(BOX_LEFT,   w, h, changelod, fovy, aspect, farplane, hf);
+            case 2:  nquads += gl_render_cube_side(BOX_RIGHT,  w, h, changelod, fovy, aspect, farplane, hf);
+            default: nquads += gl_render_cube_side(BOX_FRONT,  w, h, changelod, fovy, aspect, farplane, hf);
+            }
+            readmatrices();
+            readdepth(w, h);
+
+            glDisable(GL_FOG);
+            glViewport(0, 0, w, h);
+
+            glMatrixMode(GL_PROJECTION);
+            glLoadIdentity();
+            gluPerspective(fovy, aspect, 10, (w+h)/2);
+
+            glMatrixMode(GL_MODELVIEW);
+            // push matrix cause drawhudgun() needs it
+            glPushMatrix();
+            glLoadIdentity();
+            glTranslatef(0, 0, -(w+h)*(fdistance/200.0));
+            glCallList(fisheye_grid);
+            glPopMatrix();
+        }
+    }
+    drawhudgun(fovy, aspect, farplane);
+
+    glDisable(GL_TEXTURE_2D);
+
+    gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater);
+
+    glEnable(GL_CULL_FACE);
+    glEnable(GL_FOG);
+}
diff -rup src-2002_10_20/renderparticles.cpp src/renderparticles.cpp
--- src-2002_10_20/renderparticles.cpp	2003-01-01 20:30:50.000000000 +0200
+++ src/renderparticles.cpp	2003-01-06 20:48:21.000000000 +0200
@@ -39,35 +39,37 @@ vec right, up;
 
 void setorient(vec &r, vec &u) { right = r; up = u; };
 
+extern int fisheye_side;
+
 void render_particles(int time)
 {
     glDepthMask(GL_FALSE);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
-    
+
     struct parttype { float r, g, b; int gr, tex; float sz; } parttypes[] =
     {
-        { 0.7f, 0.6f, 0.3f, 2,  3, 0.06f }, // yellow: sparks 
+        { 0.7f, 0.6f, 0.3f, 2,  3, 0.06f }, // yellow: sparks
         { 0.5f, 0.5f, 0.5f, 20, 7, 0.15f }, // grey:   small smoke
         { 0.2f, 0.2f, 1.0f, 20, 3, 0.08f }, // blue:   edit mode entities
         { 1.0f, 0.1f, 0.1f, 1,  7, 0.06f }, // red:    blood spats
         { 1.0f, 0.8f, 0.8f, 20, 6, 1.2f  }, // yellow: fireball1
-        { 0.5f, 0.5f, 0.5f, 20, 7, 0.6f  }, // grey:   big smoke   
+        { 0.5f, 0.5f, 0.5f, 20, 7, 0.6f  }, // grey:   big smoke
         { 1.0f, 1.0f, 1.0f, 20, 8, 1.2f  }, // blue:   fireball2
         { 1.0f, 1.0f, 1.0f, 20, 9, 1.2f  }, // green:  fireball3
     };
-    
+
     int numrender = 0;
-    
+
     for(particle *p, **pp = &parlist; p = *pp;)
-    {       
+    {
         parttype *pt = &parttypes[p->type];
 
-        glBindTexture(GL_TEXTURE_2D, pt->tex);  
+        glBindTexture(GL_TEXTURE_2D, pt->tex);
         glBegin(GL_QUADS);
-        
+
         glColor3d(pt->r, pt->g, pt->b);
-        float sz = pt->sz; 
+        float sz = pt->sz;
         // perf varray?
         glTexCoord2f(0.0, 1.0); glVertex3d(p->o.x+(-right.x+up.x)*sz, p->o.z+(-right.y+up.y)*sz, p->o.y+(-right.z+up.z)*sz);
         glTexCoord2f(1.0, 1.0); glVertex3d(p->o.x+( right.x+up.x)*sz, p->o.z+( right.y+up.y)*sz, p->o.y+( right.z+up.z)*sz);
@@ -76,7 +78,7 @@ void render_particles(int time)
         glEnd();
         xtraverts += 4;
 
-        if(numrender++>maxparticles || (p->fade -= time)<0)
+        if(numrender++>maxparticles || (!fisheye_side && (p->fade -= time)<0))
         {
             *pp = p->next;
             p->next = parempty;
@@ -84,11 +86,14 @@ void render_particles(int time)
         }
         else
         {
-            p->o.z -= ((lastmillis-p->millis)/3.0f)*curtime/(pt->gr*10000);
-            vec a = p->d;
-            vmul(a,time);
-            vdiv(a,20000.0f);
-            vadd(p->o, a);
+            if(!fisheye_side)
+            {
+                p->o.z -= ((lastmillis-p->millis)/3.0f)*curtime/(pt->gr*10000);
+                vec a = p->d;
+                vmul(a,time);
+                vdiv(a,20000.0f);
+                vadd(p->o, a);
+            }
             pp = &p->next;
         };
     };
diff -rup src-2002_10_20/worldocull.cpp src/worldocull.cpp
--- src-2002_10_20/worldocull.cpp	2003-01-01 20:30:50.000000000 +0200
+++ src/worldocull.cpp	2003-01-06 20:48:21.000000000 +0200
@@ -21,10 +21,24 @@ void computeraytable(float vx, float vy)
 
     odist = getvar("fog")*1.5f;
 
-    float apitch = (float)fabs(player1->pitch);
-    float af = getvar("fov")/2+apitch/1.5f+3;
-    float byaw = (player1->yaw-90+af)/360*PI2;
-    float syaw = (player1->yaw-90-af)/360*PI2;
+    float ppitch = player1->pitch;
+    float pyaw = player1->yaw;
+    // keep pitch [-90; 90] just in case..
+    /*
+    if(ppitch > 90.0) { ppitch = 180.0 - ppitch; pyaw += 180.0; }
+    else if(ppitch < -90.0) { ppitch = -180.0 - ppitch; pyaw += 180.0; }
+    if(pyaw >= 360.0) { pyaw -= 360.0; }
+    */
+    //float apitch = (float)fabs(player1->pitch);
+    //float af = getvar("fov")/2+apitch/1.5f+3;
+    float apitch = (float)fabs(ppitch);
+    float af;
+    if(!getvar("fisheye"))
+        af = getvar("fov")/2+apitch/1.5f+3;
+    else
+        af = 90.0/1.4+apitch/1.5f+3;
+    float byaw = (pyaw-90+af)/360*PI2;
+    float syaw = (pyaw-90-af)/360*PI2;
 
     loopi(NUMRAYS)
     {
@@ -67,7 +81,7 @@ void computeraytable(float vx, float vy)
 // test occlusion for a cube... one of the most computationally expensive functions in the engine
 // as its done for every cube and entity, but its effect is more than worth it!
 
-inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; }; 
+inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; };
 inline float ma(float x, float y) { return x==0 ? (y>0 ? 2 : -2) : y/x; };
 
 int isoccluded(float vx, float vy, float cx, float cy, float csize)     // v = viewer, c = cube to test 
diff -rup src-2002_10_20/worldrender.cpp src/worldrender.cpp
--- src-2002_10_20/worldrender.cpp	2003-01-01 20:30:50.000000000 +0200
+++ src/worldrender.cpp	2003-01-06 20:48:21.000000000 +0200
@@ -283,7 +283,15 @@ void render_world(float vx, float vy, fl
         lodtop = lod;
         distlod(lodright, lodleft, yaw<=45 ? yaw+45 : yaw-315, widef);
     };
-    float hyfov = fov*h/w/2;
+    float hyfov;
+    if(!getvar("fisheye"))
+        hyfov = fov*h/w/2;
+    else
+    {
+        hyfov = 45;
+        if(pitch > 90) pitch = 180 - pitch;
+        else if(pitch < -90) pitch = -180 - pitch;
+    }
     render_floor = pitch<hyfov;
     render_ceil  = -pitch<hyfov;
 
