added DPSOFTRAST software rasterizer, a work in progress
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 22 Jan 2011 21:00:49 +0000 (21:00 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 22 Jan 2011 21:00:49 +0000 (21:00 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10717 d7cf8633-e32d-0410-b094-e92efae38249

13 files changed:
dpsoftrast.c [new file with mode: 0644]
dpsoftrast.h [new file with mode: 0644]
gl_backend.c
gl_draw.c
gl_rmain.c
gl_textures.c
makefile.inc
r_shadow.c
render.h
vid.h
vid_sdl.c
vid_shared.c
vid_wgl.c

diff --git a/dpsoftrast.c b/dpsoftrast.c
new file mode 100644 (file)
index 0000000..cbd6629
--- /dev/null
@@ -0,0 +1,2461 @@
+
+#include <memory.h>
+#include "dpsoftrast.h"
+#include <stdio.h>
+#include <math.h>
+
+#undef true
+#undef false
+#ifndef __cplusplus
+typedef enum bool {false, true} bool;
+#endif
+
+#define GL_NONE                                        0
+#define GL_FRONT_LEFT                  0x0400
+#define GL_FRONT_RIGHT                 0x0401
+#define GL_BACK_LEFT                   0x0402
+#define GL_BACK_RIGHT                  0x0403
+#define GL_FRONT                               0x0404
+#define GL_BACK                                        0x0405
+#define GL_LEFT                                        0x0406
+#define GL_RIGHT                               0x0407
+#define GL_FRONT_AND_BACK              0x0408
+#define GL_AUX0                                        0x0409
+#define GL_AUX1                                        0x040A
+#define GL_AUX2                                        0x040B
+#define GL_AUX3                                        0x040C
+
+#define GL_NEVER                               0x0200
+#define GL_LESS                                        0x0201
+#define GL_EQUAL                               0x0202
+#define GL_LEQUAL                              0x0203
+#define GL_GREATER                             0x0204
+#define GL_NOTEQUAL                            0x0205
+#define GL_GEQUAL                              0x0206
+#define GL_ALWAYS                              0x0207
+
+#define GL_ZERO                                        0x0
+#define GL_ONE                                 0x1
+#define GL_SRC_COLOR                           0x0300
+#define GL_ONE_MINUS_SRC_COLOR                 0x0301
+#define GL_DST_COLOR                           0x0306
+#define GL_ONE_MINUS_DST_COLOR                 0x0307
+#define GL_SRC_ALPHA                           0x0302
+#define GL_ONE_MINUS_SRC_ALPHA                 0x0303
+#define GL_DST_ALPHA                           0x0304
+#define GL_ONE_MINUS_DST_ALPHA                 0x0305
+#define GL_SRC_ALPHA_SATURATE                  0x0308
+#define GL_CONSTANT_COLOR                      0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR            0x8002
+#define GL_CONSTANT_ALPHA                      0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA            0x8004
+
+typedef enum DPSOFTRAST_ARRAY_e
+{
+       DPSOFTRAST_ARRAY_POSITION,
+       DPSOFTRAST_ARRAY_COLOR,
+       DPSOFTRAST_ARRAY_TEXCOORD0,
+       DPSOFTRAST_ARRAY_TEXCOORD1,
+       DPSOFTRAST_ARRAY_TEXCOORD2,
+       DPSOFTRAST_ARRAY_TEXCOORD3,
+       DPSOFTRAST_ARRAY_TEXCOORD4,
+       DPSOFTRAST_ARRAY_TEXCOORD5,
+       DPSOFTRAST_ARRAY_TEXCOORD6,
+       DPSOFTRAST_ARRAY_TEXCOORD7,
+       DPSOFTRAST_ARRAY_TOTAL
+}
+DPSOFTRAST_ARRAY;
+
+typedef struct DPSOFTRAST_Texture_s
+{
+       int flags;
+       int width;
+       int height;
+       int depth;
+       int sides;
+       DPSOFTRAST_TEXTURE_FILTER filter;
+       int mipmaps;
+       int size;
+       unsigned char *bytes;
+       int mipmap[DPSOFTRAST_MAXMIPMAPS][5];
+       int clampmin[3];
+       int clampmax[3];
+       int wrapmask[3];
+}
+DPSOFTRAST_Texture;
+
+typedef struct DPSOFTRAST_State_User_s
+{
+       int colormask[4];
+       int blendfunc[2];
+       int blendsubtract;
+       int depthmask;
+       int depthtest;
+       int depthfunc;
+       int scissortest;
+       int cullface;
+       int alphatest;
+       int alphafunc;
+       float alphavalue;
+       int scissor[4];
+       int viewport[4];
+       float depthrange[2];
+       float polygonoffset[2];
+       float color[4];
+}
+DPSOFTRAST_State_User;
+
+typedef struct DPSOFTRAST_State_Draw_Span_s
+{
+       int start; // pixel index
+       int length; // pixel count
+       int startx; // usable range (according to pixelmask)
+       int endx; // usable range (according to pixelmask)
+       unsigned char *pixelmask; // true for pixels that passed depth test, false for others
+       // [0][n][] is start interpolant values (projected)
+       // [1][n][] is end interpolant values (projected)
+       // [0][DPSOFTRAST_ARRAY_TOTAL][] is start screencoord4f
+       // [1][DPSOFTRAST_ARRAY_TOTAL][] is end screencoord4f
+       // NOTE: screencoord4f[3] is W (basically 1/Z), useful for depthbuffer
+       float data[2][DPSOFTRAST_ARRAY_TOTAL+1][4];
+}
+DPSOFTRAST_State_Draw_Span;
+
+#define DPSOFTRAST_DRAW_MAXSPANQUEUE 1024
+
+typedef struct DPSOFTRAST_State_Draw_s
+{
+       int numvertices;
+       int maxvertices;
+       float *in_array4f[DPSOFTRAST_ARRAY_TOTAL];
+       float *post_array4f[DPSOFTRAST_ARRAY_TOTAL];
+       float *screencoord4f;
+
+       // spans are queued in this structure for dispatch to the pixel shader,
+       // partly to improve cache locality, partly for batching purposes, spans
+       // are flushed before DrawTriangles returns to caller
+       int numspans;
+       DPSOFTRAST_State_Draw_Span spanqueue[DPSOFTRAST_DRAW_MAXSPANQUEUE];
+}
+DPSOFTRAST_State_Draw;
+
+#define DPSOFTRAST_VALIDATE_FB 1
+#define DPSOFTRAST_VALIDATE_DEPTHFUNC 2
+#define DPSOFTRAST_VALIDATE_BLENDFUNC 4
+#define DPSOFTRAST_VALIDATE_DRAW (DPSOFTRAST_VALIDATE_FB | DPSOFTRAST_VALIDATE_DEPTHFUNC | DPSOFTRAST_VALIDATE_BLENDFUNC)
+
+typedef enum DPSOFTRAST_BLENDMODE_e
+{
+       DPSOFTRAST_BLENDMODE_OPAQUE,
+       DPSOFTRAST_BLENDMODE_ALPHA,
+       DPSOFTRAST_BLENDMODE_ADDALPHA,
+       DPSOFTRAST_BLENDMODE_ADD,
+       DPSOFTRAST_BLENDMODE_INVMOD,
+       DPSOFTRAST_BLENDMODE_MUL,
+       DPSOFTRAST_BLENDMODE_MUL2,
+       DPSOFTRAST_BLENDMODE_SUBALPHA,
+       DPSOFTRAST_BLENDMODE_TOTAL
+}
+DPSOFTRAST_BLENDMODE;
+
+typedef struct DPSOFTRAST_State_s
+{
+       // DPSOFTRAST_VALIDATE_ flags
+       int validate;
+
+       int fb_colormask;
+       int fb_width;
+       int fb_height;
+       unsigned int *fb_depthpixels;
+       unsigned int *fb_colorpixels[4];
+
+       const float *pointer_vertex3f;
+       const float *pointer_color4f;
+       const unsigned char *pointer_color4ub;
+       const float *pointer_texcoordf[DPSOFTRAST_MAXTEXCOORDARRAYS];
+       int stride_vertex;
+       int stride_color;
+       int stride_texcoord[DPSOFTRAST_MAXTEXCOORDARRAYS];
+       int components_texcoord[DPSOFTRAST_MAXTEXCOORDARRAYS];
+       DPSOFTRAST_Texture *texbound[DPSOFTRAST_MAXTEXTUREUNITS];
+
+       int shader_mode;
+       int shader_permutation;
+       float uniform4f[DPSOFTRAST_UNIFORM_TOTAL*4];
+       int uniform1i[DPSOFTRAST_UNIFORM_TOTAL];
+
+       // derived values (DPSOFTRAST_VALIDATE_FB)
+       int fb_clearscissor[4];
+       int fb_viewport[4];
+       int fb_viewportscissor[4];
+       float fb_viewportcenter[2];
+       float fb_viewportscale[2];
+
+       // derived values (DPSOFTRAST_VALIDATE_DEPTHFUNC)
+       int fb_depthfunc;
+
+       // derived values (DPSOFTRAST_VALIDATE_BLENDFUNC)
+       int fb_blendmode;
+
+       int texture_max;
+       int texture_end;
+       int texture_firstfree;
+       DPSOFTRAST_Texture *texture;
+
+       int bigendian;
+
+       // error reporting
+       const char *errorstring;
+
+       DPSOFTRAST_State_User user;
+
+       DPSOFTRAST_State_Draw draw;
+}
+DPSOFTRAST_State;
+
+DPSOFTRAST_State dpsoftrast;
+
+#define DPSOFTRAST_DEPTHSCALE (1024.0f*1048576.0f)
+#define DPSOFTRAST_BGRA8_FROM_RGBA32F(r,g,b,a) (((int)(r * 255.0f + 0.5f) << 16) | ((int)(g * 255.0f + 0.5f) << 8) | (int)(b * 255.0f + 0.5f) | ((int)(a * 255.0f + 0.5f) << 24))
+#define DPSOFTRAST_DEPTH32_FROM_DEPTH32F(d) ((int)(DPSOFTRAST_DEPTHSCALE * (1-d)))
+#define DPSOFTRAST_DRAW_MAXSPANLENGTH 256
+
+void DPSOFTRAST_RecalcFB(void)
+{
+       // calculate framebuffer scissor, viewport, viewport clipped by scissor,
+       // and viewport projection values
+       int x1, x2, x3, x4, x5, x6;
+       int y1, y2, y3, y4, y5, y6;
+       x1 = dpsoftrast.user.scissor[0];
+       x2 = dpsoftrast.user.scissor[0] + dpsoftrast.user.scissor[2];
+       x3 = dpsoftrast.user.viewport[0];
+       x4 = dpsoftrast.user.viewport[0] + dpsoftrast.user.viewport[2];
+       y1 = dpsoftrast.fb_height - dpsoftrast.user.scissor[1] - dpsoftrast.user.scissor[3];
+       y2 = dpsoftrast.fb_height - dpsoftrast.user.scissor[1];
+       y3 = dpsoftrast.fb_height - dpsoftrast.user.viewport[1] - dpsoftrast.user.viewport[3];
+       y4 = dpsoftrast.fb_height - dpsoftrast.user.viewport[1];
+       if (!dpsoftrast.user.scissortest) {x1 = 0;y1 = 0;x2 = dpsoftrast.fb_width;y2 = dpsoftrast.fb_height;}
+       if (x1 < 0) x1 = 0;
+       if (x2 > dpsoftrast.fb_width) x2 = dpsoftrast.fb_width;
+       if (x3 < 0) x1 = 0;
+       if (x4 > dpsoftrast.fb_width) x4 = dpsoftrast.fb_width;
+       if (y1 < 0) y1 = 0;
+       if (y2 > dpsoftrast.fb_height) y2 = dpsoftrast.fb_height;
+       if (y3 < 0) y1 = 0;
+       if (y4 > dpsoftrast.fb_height) y4 = dpsoftrast.fb_height;
+       x5 = x1;if (x5 < x3) x5 = x3;
+       x6 = x2;if (x6 > x4) x4 = x4;
+       y5 = y1;if (y5 < y3) y5 = y3;
+       y6 = y2;if (y6 > y4) y6 = y4;
+       dpsoftrast.fb_clearscissor[0] = x1;
+       dpsoftrast.fb_clearscissor[1] = y1;
+       dpsoftrast.fb_clearscissor[2] = x2 - x1;
+       dpsoftrast.fb_clearscissor[3] = y2 - y1;
+       dpsoftrast.fb_viewport[0] = x3;
+       dpsoftrast.fb_viewport[1] = y3;
+       dpsoftrast.fb_viewport[2] = x4 - x3;
+       dpsoftrast.fb_viewport[3] = y4 - y3;
+       dpsoftrast.fb_viewportscissor[0] = x5;
+       dpsoftrast.fb_viewportscissor[1] = y5;
+       dpsoftrast.fb_viewportscissor[2] = x6 - x5;
+       dpsoftrast.fb_viewportscissor[3] = y6 - y5;
+       dpsoftrast.fb_viewportcenter[0] = dpsoftrast.user.viewport[0] + 0.5f * dpsoftrast.user.viewport[2] - 0.5f;
+       dpsoftrast.fb_viewportcenter[1] = dpsoftrast.fb_height - dpsoftrast.user.viewport[1] - 0.5f * dpsoftrast.user.viewport[3] - 0.5f;
+       dpsoftrast.fb_viewportscale[0] = 0.5f * dpsoftrast.user.viewport[2];
+       dpsoftrast.fb_viewportscale[1] = -0.5f * dpsoftrast.user.viewport[3];
+}
+
+void DPSOFTRAST_RecalcDepthFunc(void)
+{
+       dpsoftrast.fb_depthfunc = dpsoftrast.user.depthtest ? dpsoftrast.user.depthfunc : GL_ALWAYS;
+}
+
+int blendmodetable[][4] = 
+{
+       {DPSOFTRAST_BLENDMODE_OPAQUE, GL_ONE, GL_ZERO, false},
+       {DPSOFTRAST_BLENDMODE_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, false},
+       {DPSOFTRAST_BLENDMODE_ADDALPHA, GL_SRC_ALPHA, GL_ONE, false},
+       {DPSOFTRAST_BLENDMODE_ADD, GL_ONE, GL_ONE, false},
+       {DPSOFTRAST_BLENDMODE_INVMOD, GL_ZERO, GL_ONE_MINUS_SRC_COLOR, false},
+       {DPSOFTRAST_BLENDMODE_MUL, GL_ZERO, GL_SRC_COLOR, false},
+       {DPSOFTRAST_BLENDMODE_MUL, GL_DST_COLOR, GL_ZERO, false},
+       {DPSOFTRAST_BLENDMODE_MUL2, GL_DST_COLOR, GL_SRC_COLOR, false},
+       {DPSOFTRAST_BLENDMODE_SUBALPHA, GL_SRC_COLOR, GL_ONE, true}
+};
+
+void DPSOFTRAST_RecalcBlendFunc(void)
+{
+       int i;
+       dpsoftrast.fb_blendmode = DPSOFTRAST_BLENDMODE_OPAQUE;
+       for (i = 0;i < (int)(sizeof(blendmodetable) / sizeof(blendmodetable[0]));i++)
+       {
+               if (dpsoftrast.user.blendfunc[0] == blendmodetable[i][1] && dpsoftrast.user.blendfunc[1] == blendmodetable[i][2] && dpsoftrast.user.blendsubtract == blendmodetable[i][3])
+               {
+                       dpsoftrast.fb_blendmode = blendmodetable[i][0];
+                       break;
+               }
+       }
+}
+
+#define DPSOFTRAST_ValidateQuick(f) ((dpsoftrast.validate & (f)) ? (DPSOFTRAST_Validate(f), 0) : 0)
+
+void DPSOFTRAST_Validate(int mask)
+{
+       mask &= dpsoftrast.validate;
+       if (!mask)
+               return;
+       if (mask & DPSOFTRAST_VALIDATE_FB)
+       {
+               dpsoftrast.validate &= ~DPSOFTRAST_VALIDATE_FB;
+               DPSOFTRAST_RecalcFB();
+       }
+       if (mask & DPSOFTRAST_VALIDATE_DEPTHFUNC)
+       {
+               dpsoftrast.validate &= ~DPSOFTRAST_VALIDATE_DEPTHFUNC;
+               DPSOFTRAST_RecalcDepthFunc();
+       }
+       if (mask & DPSOFTRAST_VALIDATE_BLENDFUNC)
+       {
+               dpsoftrast.validate &= ~DPSOFTRAST_VALIDATE_BLENDFUNC;
+               DPSOFTRAST_RecalcBlendFunc();
+       }
+}
+
+DPSOFTRAST_Texture *DPSOFTRAST_Texture_GetByIndex(int index)
+{
+       if (index >= 1 && index < dpsoftrast.texture_end && dpsoftrast.texture[index].bytes)
+               return &dpsoftrast.texture[index];
+       return NULL;
+}
+
+int DPSOFTRAST_Texture_New(int flags, int width, int height, int depth)
+{
+       int w;
+       int h;
+       int d;
+       int size;
+       int s;
+       int texnum;
+       int mipmaps;
+       int sides = (flags & DPSOFTRAST_TEXTURE_FLAG_CUBEMAP) ? 6 : 1;
+       int texformat = flags & DPSOFTRAST_TEXTURE_FORMAT_COMPAREMASK;
+       DPSOFTRAST_Texture *texture;
+       if (width*height*depth < 1)
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: width, height or depth is less than 1";
+               return 0;
+       }
+       if (width > DPSOFTRAST_TEXTURE_MAXSIZE || height > DPSOFTRAST_TEXTURE_MAXSIZE || depth > DPSOFTRAST_TEXTURE_MAXSIZE)
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: texture size is too large";
+               return 0;
+       }
+       switch(texformat)
+       {
+       case DPSOFTRAST_TEXTURE_FORMAT_BGRA8:
+       case DPSOFTRAST_TEXTURE_FORMAT_RGBA8:
+       case DPSOFTRAST_TEXTURE_FORMAT_ALPHA8:
+               break;
+       case DPSOFTRAST_TEXTURE_FORMAT_DEPTH:
+               if (flags & DPSOFTRAST_TEXTURE_FLAG_CUBEMAP)
+               {
+                       dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FORMAT_DEPTH only permitted on 2D textures";
+                       return 0;
+               }
+               if (depth != 1)
+               {
+                       dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FORMAT_DEPTH only permitted on 2D textures";
+                       return 0;
+               }
+               if ((flags & DPSOFTRAST_TEXTURE_FLAG_MIPMAP) && (texformat == DPSOFTRAST_TEXTURE_FORMAT_DEPTH))
+               {
+                       dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FORMAT_DEPTH does not permit mipmaps";
+                       return 0;
+               }
+               break;
+       }
+       if (depth != 1 && (flags & DPSOFTRAST_TEXTURE_FLAG_CUBEMAP))
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FLAG_CUBEMAP can not be used on 3D textures";
+               return 0;
+       }
+       if (depth != 1 && (flags & DPSOFTRAST_TEXTURE_FLAG_MIPMAP))
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FLAG_MIPMAP can not be used on 3D textures";
+               return 0;
+       }
+       if (depth != 1 && (flags & DPSOFTRAST_TEXTURE_FLAG_MIPMAP))
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FLAG_MIPMAP can not be used on 3D textures";
+               return 0;
+       }
+       if ((flags & DPSOFTRAST_TEXTURE_FLAG_CUBEMAP) && (flags & DPSOFTRAST_TEXTURE_FLAG_MIPMAP))
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: DPSOFTRAST_TEXTURE_FLAG_MIPMAP can not be used on cubemap textures";
+               return 0;
+       }
+       if ((width & (width-1)) || (height & (height-1)) || (depth & (depth-1)))
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_New: dimensions are not power of two";
+               return 0;
+       }
+       // find first empty slot in texture array
+       for (texnum = dpsoftrast.texture_firstfree;texnum < dpsoftrast.texture_end;texnum++)
+               if (!dpsoftrast.texture[texnum].bytes)
+                       break;
+       dpsoftrast.texture_firstfree = texnum + 1;
+       if (dpsoftrast.texture_max <= texnum)
+       {
+               // expand texture array as needed
+               if (dpsoftrast.texture_max < 1024)
+                       dpsoftrast.texture_max = 1024;
+               else
+                       dpsoftrast.texture_max *= 2;
+               dpsoftrast.texture = (DPSOFTRAST_Texture *)realloc(dpsoftrast.texture, dpsoftrast.texture_max * sizeof(DPSOFTRAST_Texture));
+       }
+       if (dpsoftrast.texture_end <= texnum)
+               dpsoftrast.texture_end = texnum + 1;
+       texture = &dpsoftrast.texture[texnum];
+       memset(texture, 0, sizeof(*texture));
+       texture->flags = flags;
+       texture->width = width;
+       texture->height = height;
+       texture->depth = depth;
+       texture->sides = sides;
+       w = width;
+       h = height;
+       d = depth;
+       size = 0;
+       mipmaps = 0;
+       w = width;
+       h = height;
+       d = depth;
+       for (;;)
+       {
+               s = w * h * d * sides * 4;
+               texture->mipmap[mipmaps][0] = size;
+               texture->mipmap[mipmaps][1] = s;
+               texture->mipmap[mipmaps][2] = w;
+               texture->mipmap[mipmaps][3] = h;
+               texture->mipmap[mipmaps][4] = d;
+               size += s;
+               mipmaps++;
+               if (w * h * d == 1 || !(flags & DPSOFTRAST_TEXTURE_FLAG_MIPMAP))
+                       break;
+               if (w > 1) w >>= 1;
+               if (h > 1) h >>= 1;
+               if (d > 1) d >>= 1;
+       }
+       texture->mipmaps = mipmaps;
+       texture->size = size;
+
+       // allocate the pixels now
+       texture->bytes = calloc(1, size);
+       texture->clampmin[0] = 0;
+       texture->clampmin[1] = 0;
+       texture->clampmin[2] = 0;
+       texture->clampmax[0] = texture->width-1;
+       texture->clampmax[1] = texture->height-1;
+       texture->clampmax[2] = texture->depth-1;
+       texture->wrapmask[0] = texture->width-1;
+       texture->wrapmask[1] = texture->height-1;
+       texture->wrapmask[2] = texture->depth-1;
+
+       return texnum;
+}
+void DPSOFTRAST_Texture_Free(int index)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return;
+       if (texture->bytes)
+               free(texture->bytes);
+       texture->bytes = NULL;
+       memset(texture, 0, sizeof(*texture));
+       // adjust the free range and used range
+       if (dpsoftrast.texture_firstfree > index)
+               dpsoftrast.texture_firstfree = index;
+       while (dpsoftrast.texture_end > 0 && dpsoftrast.texture[dpsoftrast.texture_end-1].bytes == NULL)
+               dpsoftrast.texture_end--;
+}
+void DPSOFTRAST_Texture_CalculateMipmaps(int index)
+{
+       int i, x, y, z, w, layer0, layer1, row0, row1;
+       unsigned char *o, *i0, *i1, *i2, *i3;
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return;
+       if (texture->mipmaps <= 1)
+               return;
+       for (i = 1;i < texture->mipmaps;i++)
+       {
+               for (z = 0;z < texture->mipmap[i][4];z++)
+               {
+                       layer0 = z*2;
+                       layer1 = z*2+1;
+                       if (layer1 >= texture->mipmap[i-1][4])
+                               layer1 = texture->mipmap[i-1][4]-1;
+                       for (y = 0;y < texture->mipmap[i][3];y++)
+                       {
+                               row0 = y*2;
+                               row1 = y*2+1;
+                               if (row1 >= texture->mipmap[i-1][3])
+                                       row1 = texture->mipmap[i-1][3]-1;
+                               o =  texture->bytes + texture->mipmap[i  ][0] + 4*(texture->mipmap[i  ][3] * texture->mipmap[i  ][2] * z      + texture->mipmap[i  ][2] * y   );
+                               i0 = texture->bytes + texture->mipmap[i-1][0] + 4*(texture->mipmap[i-1][3] * texture->mipmap[i-1][2] * layer0 + texture->mipmap[i-1][2] * row0);
+                               i1 = texture->bytes + texture->mipmap[i-1][0] + 4*(texture->mipmap[i-1][3] * texture->mipmap[i-1][2] * layer0 + texture->mipmap[i-1][2] * row1);
+                               i2 = texture->bytes + texture->mipmap[i-1][0] + 4*(texture->mipmap[i-1][3] * texture->mipmap[i-1][2] * layer1 + texture->mipmap[i-1][2] * row0);
+                               i3 = texture->bytes + texture->mipmap[i-1][0] + 4*(texture->mipmap[i-1][3] * texture->mipmap[i-1][2] * layer1 + texture->mipmap[i-1][2] * row1);
+                               w = texture->mipmap[i][2];
+                               if (layer1 > layer0)
+                               {
+                                       if (texture->mipmap[i-1][2] > 1)
+                                       {
+                                               // average 3D texture
+                                               for (x = 0;x < w;x++)
+                                               {
+                                                       o[0] = (i0[0] + i0[4] + i1[0] + i1[4] + i2[0] + i2[4] + i3[0] + i3[4] + 4) >> 3;
+                                                       o[1] = (i0[1] + i0[5] + i1[1] + i1[5] + i2[1] + i2[5] + i3[1] + i3[5] + 4) >> 3;
+                                                       o[2] = (i0[2] + i0[6] + i1[2] + i1[6] + i2[2] + i2[6] + i3[2] + i3[6] + 4) >> 3;
+                                                       o[3] = (i0[3] + i0[7] + i1[3] + i1[7] + i2[3] + i2[7] + i3[3] + i3[7] + 4) >> 3;
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // average 3D mipmap with parent width == 1
+                                               for (x = 0;x < w;x++)
+                                               {
+                                                       o[0] = (i0[0] + i1[0] + i2[0] + i3[0] + 2) >> 2;
+                                                       o[1] = (i0[1] + i1[1] + i2[1] + i3[1] + 2) >> 2;
+                                                       o[2] = (i0[2] + i1[2] + i2[2] + i3[2] + 2) >> 2;
+                                                       o[3] = (i0[3] + i1[3] + i2[3] + i3[3] + 2) >> 2;
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (texture->mipmap[i-1][2] > 1)
+                                       {
+                                               // average 2D texture (common case)
+                                               for (x = 0;x < w;x++)
+                                               {
+                                                       o[0] = (i0[0] + i0[4] + i1[0] + i1[4] + 2) >> 2;
+                                                       o[1] = (i0[1] + i0[5] + i1[1] + i1[5] + 2) >> 2;
+                                                       o[2] = (i0[2] + i0[6] + i1[2] + i1[6] + 2) >> 2;
+                                                       o[3] = (i0[3] + i0[7] + i1[3] + i1[7] + 2) >> 2;
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // 2D texture with parent width == 1
+                                               for (x = 0;x < w;x++)
+                                               {
+                                                       o[0] = (i0[0] + i1[0] + 1) >> 1;
+                                                       o[1] = (i0[1] + i1[1] + 1) >> 1;
+                                                       o[2] = (i0[2] + i1[2] + 1) >> 1;
+                                                       o[3] = (i0[3] + i1[3] + 1) >> 1;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+void DPSOFTRAST_Texture_UpdatePartial(int index, int mip, const unsigned char *pixels, int blockx, int blocky, int blockwidth, int blockheight)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return;
+
+       // FIXME IMPLEMENT
+
+       dpsoftrast.errorstring = "DPSOFTRAST_Texture_UpdatePartial: Not implemented.";
+}
+void DPSOFTRAST_Texture_UpdateFull(int index, const unsigned char *pixels)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return;
+
+       memcpy(texture->bytes, pixels, texture->mipmap[0][1]);
+       DPSOFTRAST_Texture_CalculateMipmaps(index);
+}
+int DPSOFTRAST_Texture_GetWidth(int index, int mip)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return 0;
+       return texture->width;
+}
+int DPSOFTRAST_Texture_GetHeight(int index, int mip)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return 0;
+       return texture->height;
+}
+int DPSOFTRAST_Texture_GetDepth(int index, int mip)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return 0;
+       return texture->depth;
+}
+unsigned char *DPSOFTRAST_Texture_GetPixelPointer(int index, int mip)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return 0;
+       return texture->bytes;
+}
+void DPSOFTRAST_Texture_Filter(int index, DPSOFTRAST_TEXTURE_FILTER filter)
+{
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return;
+       if (!(texture->flags & DPSOFTRAST_TEXTURE_FLAG_MIPMAP) && filter > DPSOFTRAST_TEXTURE_FILTER_LINEAR)
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_Texture_Filter: requested filter mode requires mipmaps";
+               return;
+       }
+       texture->filter = filter;
+}
+
+void DPSOFTRAST_SetRenderTargets(int width, int height, unsigned int *depthpixels, unsigned int *colorpixels0, unsigned int *colorpixels1, unsigned int *colorpixels2, unsigned int *colorpixels3)
+{
+       dpsoftrast.fb_width = width;
+       dpsoftrast.fb_height = height;
+       dpsoftrast.fb_depthpixels = depthpixels;
+       dpsoftrast.fb_colorpixels[0] = colorpixels0;
+       dpsoftrast.fb_colorpixels[1] = colorpixels1;
+       dpsoftrast.fb_colorpixels[2] = colorpixels2;
+       dpsoftrast.fb_colorpixels[3] = colorpixels3;
+}
+void DPSOFTRAST_Viewport(int x, int y, int width, int height)
+{
+       dpsoftrast.user.viewport[0] = x;
+       dpsoftrast.user.viewport[1] = y;
+       dpsoftrast.user.viewport[2] = width;
+       dpsoftrast.user.viewport[3] = height;
+       dpsoftrast.validate |= DPSOFTRAST_VALIDATE_FB;
+}
+void DPSOFTRAST_ClearColor(float r, float g, float b, float a)
+{
+       int i, x1, y1, x2, y2, w, h, x, y;
+       unsigned int *p;
+       unsigned int c;
+       DPSOFTRAST_Validate(DPSOFTRAST_VALIDATE_FB);
+       x1 = dpsoftrast.fb_clearscissor[0];
+       y1 = dpsoftrast.fb_clearscissor[1];
+       x2 = dpsoftrast.fb_clearscissor[2];
+       y2 = dpsoftrast.fb_clearscissor[1] + dpsoftrast.fb_clearscissor[3];
+       w = x2 - x1;
+       h = y2 - y1;
+       if (w < 1 || h < 1)
+               return;
+       // FIXME: honor dpsoftrast.fb_colormask?
+       c = DPSOFTRAST_BGRA8_FROM_RGBA32F(r,g,b,a);
+       for (i = 0;i < 4;i++)
+       {
+               if (!dpsoftrast.fb_colorpixels[i])
+                       continue;
+               for (y = y1;y < y2;y++)
+               {
+                       p = dpsoftrast.fb_colorpixels[i] + y * dpsoftrast.fb_width;
+                       for (x = x1;x < x2;x++)
+                               p[x] = c;
+               }
+       }
+}
+void DPSOFTRAST_ClearDepth(float d)
+{
+       int x1, y1, x2, y2, w, h, x, y;
+       unsigned int *p;
+       unsigned int c;
+       DPSOFTRAST_Validate(DPSOFTRAST_VALIDATE_FB);
+       x1 = dpsoftrast.fb_clearscissor[0];
+       y1 = dpsoftrast.fb_clearscissor[1];
+       x2 = dpsoftrast.fb_clearscissor[2];
+       y2 = dpsoftrast.fb_clearscissor[1] + dpsoftrast.fb_clearscissor[3];
+       w = x2 - x1;
+       h = y2 - y1;
+       if (w < 1 || h < 1)
+               return;
+       c = DPSOFTRAST_DEPTH32_FROM_DEPTH32F(d);
+       for (y = y1;y < y2;y++)
+       {
+               p = dpsoftrast.fb_depthpixels + y * dpsoftrast.fb_width;
+               for (x = x1;x < x2;x++)
+                       p[x] = c;
+       }
+}
+void DPSOFTRAST_ColorMask(int r, int g, int b, int a)
+{
+       dpsoftrast.user.colormask[0] = r != 0;
+       dpsoftrast.user.colormask[1] = g != 0;
+       dpsoftrast.user.colormask[2] = b != 0;
+       dpsoftrast.user.colormask[3] = a != 0;
+       dpsoftrast.fb_colormask = ((-dpsoftrast.user.colormask[0]) & 0x00FF0000) | ((-dpsoftrast.user.colormask[1]) & 0x0000FF00) | ((-dpsoftrast.user.colormask[2]) & 0x000000FF) | ((-dpsoftrast.user.colormask[3]) & 0xFF000000);
+}
+void DPSOFTRAST_DepthTest(int enable)
+{
+       dpsoftrast.user.depthtest = enable;
+       dpsoftrast.validate |= DPSOFTRAST_VALIDATE_DEPTHFUNC;
+}
+void DPSOFTRAST_ScissorTest(int enable)
+{
+       dpsoftrast.user.scissortest = enable;
+       dpsoftrast.validate |= DPSOFTRAST_VALIDATE_FB;
+}
+void DPSOFTRAST_Scissor(float x, float y, float width, float height)
+{
+       dpsoftrast.user.scissor[0] = x;
+       dpsoftrast.user.scissor[1] = y;
+       dpsoftrast.user.scissor[2] = width;
+       dpsoftrast.user.scissor[3] = height;
+       dpsoftrast.validate |= DPSOFTRAST_VALIDATE_FB;
+}
+
+void DPSOFTRAST_BlendFunc(int smodulate, int dmodulate)
+{
+       // FIXME: validate
+       dpsoftrast.user.blendfunc[0] = smodulate;
+       dpsoftrast.user.blendfunc[1] = dmodulate;
+       dpsoftrast.validate |= DPSOFTRAST_VALIDATE_BLENDFUNC;
+}
+void DPSOFTRAST_BlendSubtract(int enable)
+{
+       dpsoftrast.user.blendsubtract = enable != 0;
+       dpsoftrast.validate |= DPSOFTRAST_VALIDATE_BLENDFUNC;
+}
+void DPSOFTRAST_DepthMask(int enable)
+{
+       dpsoftrast.user.depthmask = enable;
+}
+void DPSOFTRAST_DepthFunc(int comparemode)
+{
+       // FIXME: validate
+       dpsoftrast.user.depthfunc = comparemode;
+}
+void DPSOFTRAST_DepthRange(float range0, float range1)
+{
+       dpsoftrast.user.depthrange[0] = range0;
+       dpsoftrast.user.depthrange[1] = range1;
+}
+void DPSOFTRAST_PolygonOffset(float alongnormal, float intoview)
+{
+       dpsoftrast.user.polygonoffset[0] = alongnormal;
+       dpsoftrast.user.polygonoffset[1] = intoview;
+}
+void DPSOFTRAST_CullFace(int mode)
+{
+       // FIXME: validate
+       dpsoftrast.user.cullface = mode;
+}
+void DPSOFTRAST_AlphaTest(float enable)
+{
+       dpsoftrast.user.alphatest = enable;
+}
+void DPSOFTRAST_AlphaFunc(int alphafunc, float alphavalue)
+{
+       // FIXME: validate
+       dpsoftrast.user.alphafunc = alphafunc;
+       dpsoftrast.user.alphavalue = alphavalue;
+}
+void DPSOFTRAST_Color4f(float r, float g, float b, float a)
+{
+       dpsoftrast.user.color[0] = r;
+       dpsoftrast.user.color[1] = g;
+       dpsoftrast.user.color[2] = b;
+       dpsoftrast.user.color[3] = a;
+}
+void DPSOFTRAST_GetPixelsBGRA(int blockx, int blocky, int blockwidth, int blockheight, unsigned char *outpixels)
+{
+       int outstride = blockwidth * 4;
+       int instride = dpsoftrast.fb_width * 4;
+       int bx1 = blockx;
+       int by1 = blocky;
+       int bx2 = blockx + blockwidth;
+       int by2 = blocky + blockheight;
+       int bw;
+       int bh;
+       int x;
+       int y;
+       unsigned char *inpixels;
+       unsigned char *b;
+       unsigned char *o;
+       if (bx1 < 0) bx1 = 0;
+       if (by1 < 0) by1 = 0;
+       if (bx2 > dpsoftrast.fb_width) bx2 = dpsoftrast.fb_width;
+       if (by2 > dpsoftrast.fb_height) by2 = dpsoftrast.fb_height;
+       bw = bx2 - bx1;
+       bh = by2 - by1;
+       inpixels = (unsigned char *)dpsoftrast.fb_colorpixels[0];
+       if (dpsoftrast.bigendian)
+       {
+               for (y = by1;y < by2;y++)
+               {
+                       b = (unsigned char *)inpixels + (dpsoftrast.fb_height - 1 - y) * instride + 4 * bx1;
+                       o = (unsigned char *)outpixels + (y - by1) * outstride;
+                       for (x = bx1;x < bx2;x++)
+                       {
+                               o[0] = b[3];
+                               o[1] = b[2];
+                               o[2] = b[1];
+                               o[3] = b[0];
+                               o += 4;
+                               b += 4;
+                       }
+               }
+       }
+       else
+       {
+               for (y = by1;y < by2;y++)
+               {
+                       b = (unsigned char *)inpixels + (dpsoftrast.fb_height - 1 - y) * instride + 4 * bx1;
+                       o = (unsigned char *)outpixels + (y - by1) * outstride;
+                       memcpy(o, b, bw*4);
+               }
+       }
+
+}
+void DPSOFTRAST_CopyRectangleToTexture(int index, int mip, int tx, int ty, int sx, int sy, int width, int height)
+{
+       int tx1 = tx;
+       int ty1 = ty;
+       int tx2 = tx + width;
+       int ty2 = ty + height;
+       int sx1 = sx;
+       int sy1 = sy;
+       int sx2 = sx + width;
+       int sy2 = sy + height;
+       int swidth;
+       int sheight;
+       int twidth;
+       int theight;
+       int sw;
+       int sh;
+       int tw;
+       int th;
+       int y;
+       unsigned int *spixels;
+       unsigned int *tpixels;
+       DPSOFTRAST_Texture *texture;
+       texture = DPSOFTRAST_Texture_GetByIndex(index);if (!texture) return;
+       if (mip < 0 || mip >= texture->mipmaps) return;
+       spixels = dpsoftrast.fb_colorpixels[0];
+       swidth = dpsoftrast.fb_width;
+       sheight = dpsoftrast.fb_height;
+       tpixels = (unsigned int *)(texture->bytes + texture->mipmap[mip][0]);
+       twidth = texture->mipmap[mip][2];
+       theight = texture->mipmap[mip][3];
+       if (tx1 < 0) tx1 = 0;
+       if (ty1 < 0) ty1 = 0;
+       if (tx2 > twidth) tx2 = twidth;
+       if (ty2 > theight) ty2 = theight;
+       if (sx1 < 0) sx1 = 0;
+       if (sy1 < 0) sy1 = 0;
+       if (sx2 > swidth) sx2 = swidth;
+       if (sy2 > sheight) sy2 = sheight;
+       tw = tx2 - tx1;
+       th = ty2 - ty1;
+       sw = sx2 - sx1;
+       sh = sy2 - sy1;
+       if (tw > sw) tw = sw;
+       if (th > sh) th = sh;
+       if (tw < 1 || th < 1)
+               return;
+       for (y = 0;y < th;y++)
+               memcpy(tpixels + ((ty1 + y) * twidth + tx1), spixels + ((sy1 + y) * swidth + sx1), tw*4);
+       if (texture->mipmaps > 1)
+               DPSOFTRAST_Texture_CalculateMipmaps(index);
+}
+void DPSOFTRAST_SetTexture(int unitnum, int index)
+{
+       DPSOFTRAST_Texture *texture;
+       if (unitnum < 0 || unitnum >= DPSOFTRAST_MAXTEXTUREUNITS)
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_SetTexture: invalid unit number";
+               return;
+       }
+       texture = DPSOFTRAST_Texture_GetByIndex(index);
+       if (index && !texture)
+       {
+               dpsoftrast.errorstring = "DPSOFTRAST_SetTexture: invalid texture handle";
+               return;
+       }
+       dpsoftrast.texbound[unitnum] = texture;
+}
+
+void DPSOFTRAST_SetVertexPointer(const float *vertex3f, size_t stride)
+{
+       dpsoftrast.pointer_vertex3f = vertex3f;
+       dpsoftrast.stride_vertex = stride;
+}
+void DPSOFTRAST_SetColorPointer(const float *color4f, size_t stride)
+{
+       dpsoftrast.pointer_color4f = color4f;
+       dpsoftrast.pointer_color4ub = NULL;
+       dpsoftrast.stride_color = stride;
+}
+void DPSOFTRAST_SetColorPointer4ub(const unsigned char *color4ub, size_t stride)
+{
+       dpsoftrast.pointer_color4f = NULL;
+       dpsoftrast.pointer_color4ub = color4ub;
+       dpsoftrast.stride_color = stride;
+}
+void DPSOFTRAST_SetTexCoordPointer(int unitnum, int numcomponents, size_t stride, const float *texcoordf)
+{
+       dpsoftrast.pointer_texcoordf[unitnum] = texcoordf;
+       dpsoftrast.components_texcoord[unitnum] = numcomponents;
+       dpsoftrast.stride_texcoord[unitnum] = stride;
+}
+
+void DPSOFTRAST_SetShader(unsigned int mode, unsigned int permutation)
+{
+       dpsoftrast.shader_mode = mode;
+       dpsoftrast.shader_permutation = permutation;
+}
+void DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM index, float v0, float v1, float v2, float v3)
+{
+       dpsoftrast.uniform4f[index*4+0] = v0;
+       dpsoftrast.uniform4f[index*4+1] = v1;
+       dpsoftrast.uniform4f[index*4+2] = v2;
+       dpsoftrast.uniform4f[index*4+3] = v3;
+}
+void DPSOFTRAST_Uniform4fvARB(DPSOFTRAST_UNIFORM index, const float *v)
+{
+       dpsoftrast.uniform4f[index*4+0] = v[0];
+       dpsoftrast.uniform4f[index*4+1] = v[1];
+       dpsoftrast.uniform4f[index*4+2] = v[2];
+       dpsoftrast.uniform4f[index*4+3] = v[3];
+}
+void DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM index, int arraysize, int transpose, const float *v)
+{
+       dpsoftrast.uniform4f[index*4+0] = v[0];
+       dpsoftrast.uniform4f[index*4+1] = v[1];
+       dpsoftrast.uniform4f[index*4+2] = v[2];
+       dpsoftrast.uniform4f[index*4+3] = v[3];
+       dpsoftrast.uniform4f[index*4+4] = v[4];
+       dpsoftrast.uniform4f[index*4+5] = v[5];
+       dpsoftrast.uniform4f[index*4+6] = v[6];
+       dpsoftrast.uniform4f[index*4+7] = v[7];
+       dpsoftrast.uniform4f[index*4+8] = v[8];
+       dpsoftrast.uniform4f[index*4+9] = v[9];
+       dpsoftrast.uniform4f[index*4+10] = v[10];
+       dpsoftrast.uniform4f[index*4+11] = v[11];
+       dpsoftrast.uniform4f[index*4+12] = v[12];
+       dpsoftrast.uniform4f[index*4+13] = v[13];
+       dpsoftrast.uniform4f[index*4+14] = v[14];
+       dpsoftrast.uniform4f[index*4+15] = v[15];
+}
+void DPSOFTRAST_Uniform1iARB(DPSOFTRAST_UNIFORM index, int i0)
+{
+       dpsoftrast.uniform1i[index] = i0;
+}
+
+void DPSOFTRAST_Draw_LoadVertices(int firstvertex, int numvertices, bool needcolors, int numtexcoords)
+{
+       int i;
+       int j;
+       int stride;
+       const float *v;
+       float *p;
+       float *data;
+       const unsigned char *b;
+       dpsoftrast.draw.numvertices = numvertices;
+       if (dpsoftrast.draw.maxvertices < dpsoftrast.draw.numvertices)
+       {
+               if (dpsoftrast.draw.maxvertices < 4096)
+                       dpsoftrast.draw.maxvertices = 4096;
+               while (dpsoftrast.draw.maxvertices < dpsoftrast.draw.numvertices)
+                       dpsoftrast.draw.maxvertices *= 2;
+               if (dpsoftrast.draw.in_array4f[0])
+                       free(dpsoftrast.draw.in_array4f[0]);
+               data = calloc(1, dpsoftrast.draw.maxvertices * sizeof(float[4])*(DPSOFTRAST_ARRAY_TOTAL*2 + 1));
+               for (i = 0;i < DPSOFTRAST_ARRAY_TOTAL;i++, data += dpsoftrast.draw.maxvertices * 4)
+                       dpsoftrast.draw.in_array4f[i] = data;
+               for (i = 0;i < DPSOFTRAST_ARRAY_TOTAL;i++, data += dpsoftrast.draw.maxvertices * 4)
+                       dpsoftrast.draw.post_array4f[i] = data;
+               dpsoftrast.draw.screencoord4f = data;
+               data += dpsoftrast.draw.maxvertices * 4;
+       }
+       stride = dpsoftrast.stride_vertex;
+       v = (const float *)((unsigned char *)dpsoftrast.pointer_vertex3f + firstvertex * stride);
+       p = dpsoftrast.draw.in_array4f[0];
+       for (i = 0;i < numvertices;i++)
+       {
+               p[0] = v[0];
+               p[1] = v[1];
+               p[2] = v[2];
+               p[3] = 1.0f;
+               p += 4;
+               v = (const float *)((const unsigned char *)v + stride);
+       }
+       if (needcolors)
+       {
+               if (dpsoftrast.pointer_color4f)
+               {
+                       stride = dpsoftrast.stride_color;
+                       v = (const float *)((const unsigned char *)dpsoftrast.pointer_color4f + firstvertex * stride);
+                       p = dpsoftrast.draw.in_array4f[1];
+                       for (i = 0;i < numvertices;i++)
+                       {
+                               p[0] = v[0];
+                               p[1] = v[1];
+                               p[2] = v[2];
+                               p[3] = v[3];
+                               p += 4;
+                               v = (const float *)((const unsigned char *)v + stride);
+                       }
+               }
+               else if (dpsoftrast.pointer_color4ub)
+               {
+                       stride = dpsoftrast.stride_color;
+                       b = (const unsigned char *)((const unsigned char *)dpsoftrast.pointer_color4ub + firstvertex * stride);
+                       p = dpsoftrast.draw.in_array4f[1];
+                       for (i = 0;i < numvertices;i++)
+                       {
+                               p[0] = b[0] * (1.0f / 255.0f);
+                               p[1] = b[1] * (1.0f / 255.0f);
+                               p[2] = b[2] * (1.0f / 255.0f);
+                               p[3] = b[3] * (1.0f / 255.0f);
+                               p += 4;
+                               b = (const unsigned char *)((const unsigned char *)b + stride);
+                       }
+               }
+               else
+               {
+                       v = dpsoftrast.user.color;
+                       p = dpsoftrast.draw.in_array4f[1];
+                       for (i = 0;i < numvertices;i++)
+                       {
+                               p[0] = v[0];
+                               p[1] = v[1];
+                               p[2] = v[2];
+                               p[3] = v[3];
+                               p += 4;
+                       }
+               }
+       }
+       for (j = 0;j < numtexcoords;j++)
+       {
+               if (dpsoftrast.pointer_texcoordf[j])
+               {
+                       stride = dpsoftrast.stride_texcoord[j];
+                       v = (const float *)((const unsigned char *)dpsoftrast.pointer_texcoordf[j] + firstvertex * stride);
+                       p = dpsoftrast.draw.in_array4f[j+2];
+                       switch(dpsoftrast.components_texcoord[j])
+                       {
+                       case 2:
+                               for (i = 0;i < numvertices;i++)
+                               {
+                                       p[0] = v[0];
+                                       p[1] = v[1];
+                                       p[2] = 0.0f;
+                                       p[3] = 1.0f;
+                                       p += 4;
+                                       v = (const float *)((const unsigned char *)v + stride);
+                               }
+                               break;
+                       case 3:
+                               for (i = 0;i < numvertices;i++)
+                               {
+                                       p[0] = v[0];
+                                       p[1] = v[1];
+                                       p[2] = v[2];
+                                       p[3] = 1.0f;
+                                       p += 4;
+                                       v = (const float *)((const unsigned char *)v + stride);
+                               }
+                               break;
+                       case 4:
+                               for (i = 0;i < numvertices;i++)
+                               {
+                                       p[0] = v[0];
+                                       p[1] = v[1];
+                                       p[2] = v[2];
+                                       p[3] = v[3];
+                                       p += 4;
+                                       v = (const float *)((const unsigned char *)v + stride);
+                               }
+                               break;
+                       }
+               }
+       }
+}
+
+void DPSOFTRAST_Array_Transform(float *out4f, const float *in4f, int numitems, const float *inmatrix16f)
+{
+       // TODO: SIMD
+       float matrix[4][4];
+       int i;
+       memcpy(matrix, inmatrix16f, sizeof(float[16]));
+       for (i = 0;i < numitems;i++, out4f += 4, in4f += 4)
+       {
+               out4f[0] = in4f[0] * matrix[0][0] + in4f[1] * matrix[1][0] + in4f[2] * matrix[2][0] + in4f[3] * matrix[3][0];
+               out4f[1] = in4f[0] * matrix[0][1] + in4f[1] * matrix[1][1] + in4f[2] * matrix[2][1] + in4f[3] * matrix[3][1];
+               out4f[2] = in4f[0] * matrix[0][2] + in4f[1] * matrix[1][2] + in4f[2] * matrix[2][2] + in4f[3] * matrix[3][2];
+               out4f[3] = in4f[0] * matrix[0][3] + in4f[1] * matrix[1][3] + in4f[2] * matrix[2][3] + in4f[3] * matrix[3][3];
+       }
+}
+
+void DPSOFTRAST_Array_Copy(float *out4f, const float *in4f, int numitems)
+{
+       memcpy(out4f, in4f, numitems * sizeof(float[4]));
+}
+
+void DPSOFTRAST_Draw_ProjectVertices(float *out4f, const float *in4f, int numitems)
+{
+       // NOTE: this is used both as a whole mesh transform function and a
+       // per-triangle transform function (for clipped triangles), accordingly
+       // it should not crash on divide by 0 but the result of divide by 0 is
+       // unimportant...
+       // TODO: SIMD
+       int i;
+       float w;
+       float viewportcenter[4];
+       float viewportscale[4];
+       viewportscale[0] = dpsoftrast.fb_viewportscale[0];
+       viewportscale[1] = dpsoftrast.fb_viewportscale[1];
+       viewportscale[2] = 0.5f;
+       viewportscale[3] = 0.0f;
+       viewportcenter[0] = dpsoftrast.fb_viewportcenter[0];
+       viewportcenter[1] = dpsoftrast.fb_viewportcenter[1];
+       viewportcenter[2] = 0.5f;
+       viewportcenter[3] = 0.0f;
+       for (i = 0;i < numitems;i++)
+       {
+               if (!in4f[3])
+               {
+                       out4f[0] = 0.0f;
+                       out4f[1] = 0.0f;
+                       out4f[2] = 0.0f;
+                       out4f[3] = 0.0f;
+                       continue;
+               }
+               w = 1.0f / in4f[3];
+               out4f[0] = viewportcenter[0] + viewportscale[0] * in4f[0] * w;
+               out4f[1] = viewportcenter[1] + viewportscale[1] * in4f[1] * w;
+               out4f[2] = viewportcenter[2] + viewportscale[2] * in4f[2] * w;
+               out4f[3] = viewportcenter[3] + viewportscale[3] * in4f[3] * w;
+               out4f[3] = w;
+               in4f += 4;
+               out4f += 4;
+       }
+}
+
+void DPSOFTRAST_Draw_DebugEdgePoints(const float *screen0, const float *screen1)
+{
+       int i;
+       int x;
+       int y;
+       int w = dpsoftrast.fb_width;
+       int bounds[4];
+       float v0[2], v1[2];
+       unsigned int *pixels = dpsoftrast.fb_colorpixels[0];
+       //const float *c4f;
+       bounds[0] = dpsoftrast.fb_viewportscissor[0];
+       bounds[1] = dpsoftrast.fb_viewportscissor[1];
+       bounds[2] = dpsoftrast.fb_viewportscissor[0] + dpsoftrast.fb_viewportscissor[2];
+       bounds[3] = dpsoftrast.fb_viewportscissor[1] + dpsoftrast.fb_viewportscissor[3];
+       v0[0] = screen0[0];
+       v0[1] = screen0[1];
+       v1[0] = screen1[0];
+       v1[1] = screen1[1];
+       for (i = 0;i <= 128;i++)
+       {
+               // check nearclip
+               //if (dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][i*4+3] != 1.0f)
+               //      continue;
+               x = (int)(v0[0] + (v1[0] - v0[0]) * (i/128.0f));
+               y = (int)(v0[1] + (v1[1] - v0[1]) * (i/128.0f));
+               if (x < bounds[0] || y < bounds[1] || x >= bounds[2] || y >= bounds[3])
+                       continue;
+               //c4f = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_COLOR] + element0*4;
+               //pixels[y*w+x] = DPSOFTRAST_BGRA8_FROM_RGBA32F(c4f[0], c4f[1], c4f[2], c4f[3]);
+               pixels[y*w+x] = 0xFFFFFFFF;
+       }
+}
+
+void DPSOFTRAST_Draw_Span_Begin(const DPSOFTRAST_State_Draw_Span *span, float *zf)
+{
+       int x;
+       int startx = span->startx;
+       int endx = span->endx;
+       float w = span->data[0][DPSOFTRAST_ARRAY_TOTAL][3];
+       float wslope = span->data[1][DPSOFTRAST_ARRAY_TOTAL][3];
+       // TODO: optimize by approximating every 8 pixels?
+       for (x = startx;x < endx;x++)
+               zf[x] = 1.0f / (w + wslope * x);
+}
+
+void DPSOFTRAST_Draw_Span_Finish(const DPSOFTRAST_State_Draw_Span *span, const float *in4f)
+{
+       int x;
+       int startx = span->startx;
+       int endx = span->endx;
+       int d[4];
+       float a, b;
+       unsigned char *pixelmask = span->pixelmask;
+       unsigned char *pixel = (unsigned char *)dpsoftrast.fb_colorpixels[0];
+       if (!pixel)
+               return;
+       pixel += span->start * 4;
+       // handle alphatest now (this affects depth writes too)
+       if (dpsoftrast.user.alphatest)
+               for (x = startx;x < endx;x++)
+                       if (in4f[x*4+3] < 0.5f)
+                               pixelmask[x] = false;
+       // FIXME: this does not handle bigendian
+       switch(dpsoftrast.fb_blendmode)
+       {
+       case DPSOFTRAST_BLENDMODE_OPAQUE:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       d[0] = (int)(in4f[x*4+2]*255.0f);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)(in4f[x*4+1]*255.0f);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)(in4f[x*4+0]*255.0f);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)(in4f[x*4+3]*255.0f);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_ALPHA:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       a = in4f[x*4+3] * 255.0f;
+                       b = 1.0f - in4f[x*4+3];
+                       d[0] = (int)(in4f[x*4+2]*a+pixel[x*4+0]*b);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)(in4f[x*4+1]*a+pixel[x*4+1]*b);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)(in4f[x*4+0]*a+pixel[x*4+2]*b);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)(in4f[x*4+3]*a+pixel[x*4+3]*b);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_ADDALPHA:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       a = in4f[x*4+3] * 255.0f;
+                       d[0] = (int)(in4f[x*4+2]*a+pixel[x*4+0]);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)(in4f[x*4+1]*a+pixel[x*4+1]);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)(in4f[x*4+0]*a+pixel[x*4+2]);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)(in4f[x*4+3]*a+pixel[x*4+3]);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_ADD:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       d[0] = (int)(in4f[x*4+2]*255.0f+pixel[x*4+0]);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)(in4f[x*4+1]*255.0f+pixel[x*4+1]);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)(in4f[x*4+0]*255.0f+pixel[x*4+2]);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)(in4f[x*4+3]*255.0f+pixel[x*4+3]);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_INVMOD:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       d[0] = (int)((1.0f-in4f[x*4+2])*pixel[x*4+0]);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)((1.0f-in4f[x*4+1])*pixel[x*4+1]);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)((1.0f-in4f[x*4+0])*pixel[x*4+2]);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)((1.0f-in4f[x*4+3])*pixel[x*4+3]);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_MUL:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       d[0] = (int)(in4f[x*4+2]*pixel[x*4+0]);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)(in4f[x*4+1]*pixel[x*4+1]);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)(in4f[x*4+0]*pixel[x*4+2]);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)(in4f[x*4+3]*pixel[x*4+3]);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_MUL2:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       d[0] = (int)(in4f[x*4+2]*pixel[x*4+0]*2.0f);if (d[0] > 255) d[0] = 255;
+                       d[1] = (int)(in4f[x*4+1]*pixel[x*4+1]*2.0f);if (d[1] > 255) d[1] = 255;
+                       d[2] = (int)(in4f[x*4+0]*pixel[x*4+2]*2.0f);if (d[2] > 255) d[2] = 255;
+                       d[3] = (int)(in4f[x*4+3]*pixel[x*4+3]*2.0f);if (d[3] > 255) d[3] = 255;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       case DPSOFTRAST_BLENDMODE_SUBALPHA:
+               for (x = startx;x < endx;x++)
+               {
+                       if (!pixelmask[x])
+                               continue;
+                       a = in4f[x*4+3] * -255.0f;
+                       d[0] = (int)(in4f[x*4+2]*a+pixel[x*4+0]);if (d[0] > 255) d[0] = 255;if (d[0] < 0) d[0] = 0;
+                       d[1] = (int)(in4f[x*4+1]*a+pixel[x*4+1]);if (d[1] > 255) d[1] = 255;if (d[1] < 0) d[1] = 0;
+                       d[2] = (int)(in4f[x*4+0]*a+pixel[x*4+2]);if (d[2] > 255) d[2] = 255;if (d[2] < 0) d[2] = 0;
+                       d[3] = (int)(in4f[x*4+3]*a+pixel[x*4+3]);if (d[3] > 255) d[3] = 255;if (d[3] < 0) d[3] = 0;
+                       pixel[x*4+0] = d[0];
+                       pixel[x*4+1] = d[1];
+                       pixel[x*4+2] = d[2];
+                       pixel[x*4+3] = d[3];
+               }
+               break;
+       }
+}
+
+void DPSOFTRAST_Draw_Span_Texture2DVarying(const DPSOFTRAST_State_Draw_Span *span, float *out4f, int texunitindex, int arrayindex, const float *zf)
+{
+       int x;
+       int startx = span->startx;
+       int endx = span->endx;
+       int flags;
+       float c[4];
+       float data[4];
+       float slope[4];
+       float z;
+       float tc[2];
+       float frac[2];
+       float ifrac[2];
+       float lerp[4];
+       float tcscale[2];
+       int tci[2];
+       int tci1[2];
+       int tcimin[2];
+       int tcimax[2];
+       int tciwrapmask[2];
+       int tciwidth;
+       int filter;
+       unsigned char *pixelbase;
+       unsigned char *pixel[4];
+       DPSOFTRAST_Texture *texture = dpsoftrast.texbound[texunitindex];
+       // if no texture is bound, just fill it with white
+       if (!texture)
+       {
+               for (x = startx;x < endx;x++)
+               {
+                       out4f[x*4+0] = 1.0f;
+                       out4f[x*4+1] = 1.0f;
+                       out4f[x*4+2] = 1.0f;
+                       out4f[x*4+3] = 1.0f;
+               }
+               return;
+       }
+       // if texture is 1 pixel, just fill it with that color
+       if (texture->width * texture->height * texture->depth * texture->sides == 1)
+       {
+               c[0] = texture->bytes[2] * (1.0f/255.0f);
+               c[1] = texture->bytes[1] * (1.0f/255.0f);
+               c[2] = texture->bytes[0] * (1.0f/255.0f);
+               c[3] = texture->bytes[3] * (1.0f/255.0f);
+               for (x = startx;x < endx;x++)
+               {
+                       out4f[x*4+0] = c[0];
+                       out4f[x*4+1] = c[1];
+                       out4f[x*4+2] = c[2];
+                       out4f[x*4+3] = c[3];
+               }
+               return;
+       }
+       filter = dpsoftrast.texbound[texunitindex]->filter & DPSOFTRAST_TEXTURE_FILTER_LINEAR;
+       data[0] = span->data[0][arrayindex][0];
+       data[1] = span->data[0][arrayindex][1];
+       data[2] = span->data[0][arrayindex][2];
+       data[3] = span->data[0][arrayindex][3];
+       slope[0] = span->data[1][arrayindex][0];
+       slope[1] = span->data[1][arrayindex][1];
+       slope[2] = span->data[1][arrayindex][2];
+       slope[3] = span->data[1][arrayindex][3];
+       flags = dpsoftrast.texbound[texunitindex]->flags;
+       pixelbase = (unsigned char *)dpsoftrast.texbound[texunitindex]->bytes;
+       tcscale[0] = dpsoftrast.texbound[texunitindex]->width;
+       tcscale[1] = dpsoftrast.texbound[texunitindex]->height;
+       tciwidth = dpsoftrast.texbound[texunitindex]->width;
+       tcimin[0] = dpsoftrast.texbound[texunitindex]->clampmin[0];
+       tcimin[1] = dpsoftrast.texbound[texunitindex]->clampmin[1];
+       tcimax[0] = dpsoftrast.texbound[texunitindex]->clampmax[0];
+       tcimax[1] = dpsoftrast.texbound[texunitindex]->clampmax[1];
+       tciwrapmask[0] = dpsoftrast.texbound[texunitindex]->wrapmask[0];
+       tciwrapmask[1] = dpsoftrast.texbound[texunitindex]->wrapmask[1];
+       if (filter)
+       {
+               if (flags & DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE)
+               {
+                       for (x = startx;x < endx;x++)
+                       {
+                               z = zf[x];
+                               tc[0] = (data[0] + slope[0]*x) * z * tcscale[0];
+                               tc[1] = (data[1] + slope[1]*x) * z * tcscale[1];
+                               tci[0] = (int)floor(tc[0]);
+                               tci[1] = (int)floor(tc[1]);
+                               tci1[0] = tci[0] + 1;
+                               tci1[1] = tci[1] + 1;
+                               frac[0] = tc[0] - tci[0];ifrac[0] = 1.0f - frac[0];
+                               frac[1] = tc[1] - tci[1];ifrac[1] = 1.0f - frac[1];
+                               lerp[0] = ifrac[0]*ifrac[1];
+                               lerp[1] =  frac[0]*ifrac[1];
+                               lerp[2] = ifrac[0]* frac[1];
+                               lerp[3] =  frac[0]* frac[1];
+                               tci[0] = tci[0] >= tcimin[0] ? (tci[0] <= tcimax[0] ? tci[0] : tcimax[0]) : tcimin[0];
+                               tci[1] = tci[1] >= tcimin[1] ? (tci[1] <= tcimax[1] ? tci[1] : tcimax[1]) : tcimin[1];
+                               tci1[0] = tci1[0] >= tcimin[0] ? (tci1[0] <= tcimax[0] ? tci1[0] : tcimax[0]) : tcimin[0];
+                               tci1[1] = tci1[1] >= tcimin[1] ? (tci1[1] <= tcimax[1] ? tci1[1] : tcimax[1]) : tcimin[1];
+                               pixel[0] = pixelbase + 4 * (tci[1]*tciwidth+tci[0]);
+                               pixel[1] = pixelbase + 4 * (tci[1]*tciwidth+tci1[0]);
+                               pixel[2] = pixelbase + 4 * (tci1[1]*tciwidth+tci[0]);
+                               pixel[3] = pixelbase + 4 * (tci1[1]*tciwidth+tci1[0]);
+                               c[0] = (pixel[0][2]*lerp[0]+pixel[1][2]*lerp[1]+pixel[2][2]*lerp[2]+pixel[3][2]*lerp[3]) * (1.0f / 255.0f);
+                               c[1] = (pixel[0][1]*lerp[0]+pixel[1][1]*lerp[1]+pixel[2][1]*lerp[2]+pixel[3][1]*lerp[3]) * (1.0f / 255.0f);
+                               c[2] = (pixel[0][0]*lerp[0]+pixel[1][0]*lerp[1]+pixel[2][0]*lerp[2]+pixel[3][0]*lerp[3]) * (1.0f / 255.0f);
+                               c[3] = (pixel[0][3]*lerp[0]+pixel[1][3]*lerp[1]+pixel[2][3]*lerp[2]+pixel[3][3]*lerp[3]) * (1.0f / 255.0f);
+                               out4f[x*4+0] = c[0];
+                               out4f[x*4+1] = c[1];
+                               out4f[x*4+2] = c[2];
+                               out4f[x*4+3] = c[3];
+                       }
+               }
+               else
+               {
+                       for (x = startx;x < endx;x++)
+                       {
+                               z = zf[x];
+                               tc[0] = (data[0] + slope[0]*x) * z * tcscale[0];
+                               tc[1] = (data[1] + slope[1]*x) * z * tcscale[1];
+                               tci[0] = (int)floor(tc[0]);
+                               tci[1] = (int)floor(tc[1]);
+                               tci1[0] = tci[0] + 1;
+                               tci1[1] = tci[1] + 1;
+                               frac[0] = tc[0] - tci[0];ifrac[0] = 1.0f - frac[0];
+                               frac[1] = tc[1] - tci[1];ifrac[1] = 1.0f - frac[1];
+                               lerp[0] = ifrac[0]*ifrac[1];
+                               lerp[1] =  frac[0]*ifrac[1];
+                               lerp[2] = ifrac[0]* frac[1];
+                               lerp[3] =  frac[0]* frac[1];
+                               tci[0] &= tciwrapmask[0];
+                               tci[1] &= tciwrapmask[1];
+                               tci1[0] &= tciwrapmask[0];
+                               tci1[1] &= tciwrapmask[1];
+                               pixel[0] = pixelbase + 4 * (tci[1]*tciwidth+tci[0]);
+                               pixel[1] = pixelbase + 4 * (tci[1]*tciwidth+tci1[0]);
+                               pixel[2] = pixelbase + 4 * (tci1[1]*tciwidth+tci[0]);
+                               pixel[3] = pixelbase + 4 * (tci1[1]*tciwidth+tci1[0]);
+                               c[0] = (pixel[0][2]*lerp[0]+pixel[1][2]*lerp[1]+pixel[2][2]*lerp[2]+pixel[3][2]*lerp[3]) * (1.0f / 255.0f);
+                               c[1] = (pixel[0][1]*lerp[0]+pixel[1][1]*lerp[1]+pixel[2][1]*lerp[2]+pixel[3][1]*lerp[3]) * (1.0f / 255.0f);
+                               c[2] = (pixel[0][0]*lerp[0]+pixel[1][0]*lerp[1]+pixel[2][0]*lerp[2]+pixel[3][0]*lerp[3]) * (1.0f / 255.0f);
+                               c[3] = (pixel[0][3]*lerp[0]+pixel[1][3]*lerp[1]+pixel[2][3]*lerp[2]+pixel[3][3]*lerp[3]) * (1.0f / 255.0f);
+                               out4f[x*4+0] = c[0];
+                               out4f[x*4+1] = c[1];
+                               out4f[x*4+2] = c[2];
+                               out4f[x*4+3] = c[3];
+                       }
+               }
+       }
+       else
+       {
+               if (flags & DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE)
+               {
+                       for (x = startx;x < endx;x++)
+                       {
+                               z = zf[x];
+                               tc[0] = (data[0] + slope[0]*x) * z * tcscale[0];
+                               tc[1] = (data[1] + slope[1]*x) * z * tcscale[1];
+                               tci[0] = (int)floor(tc[0]);
+                               tci[1] = (int)floor(tc[1]);
+                               tci[0] = tci[0] >= tcimin[0] ? (tci[0] <= tcimax[0] ? tci[0] : tcimax[0]) : tcimin[0];
+                               tci[1] = tci[1] >= tcimin[1] ? (tci[1] <= tcimax[1] ? tci[1] : tcimax[1]) : tcimin[1];
+                               pixel[0] = pixelbase + 4 * (tci[1]*tciwidth+tci[0]);
+                               c[0] = pixel[0][2] * (1.0f / 255.0f);
+                               c[1] = pixel[0][1] * (1.0f / 255.0f);
+                               c[2] = pixel[0][0] * (1.0f / 255.0f);
+                               c[3] = pixel[0][3] * (1.0f / 255.0f);
+                               out4f[x*4+0] = c[0];
+                               out4f[x*4+1] = c[1];
+                               out4f[x*4+2] = c[2];
+                               out4f[x*4+3] = c[3];
+                       }
+               }
+               else
+               {
+                       for (x = startx;x < endx;x++)
+                       {
+                               z = zf[x];
+                               tc[0] = (data[0] + slope[0]*x) * z * tcscale[0];
+                               tc[1] = (data[1] + slope[1]*x) * z * tcscale[1];
+                               tci[0] = (int)floor(tc[0]);
+                               tci[1] = (int)floor(tc[1]);
+                               tci[0] &= tciwrapmask[0];
+                               tci[1] &= tciwrapmask[1];
+                               pixel[0] = pixelbase + 4 * (tci[1]*tciwidth+tci[0]);
+                               c[0] = pixel[0][2] * (1.0f / 255.0f);
+                               c[1] = pixel[0][1] * (1.0f / 255.0f);
+                               c[2] = pixel[0][0] * (1.0f / 255.0f);
+                               c[3] = pixel[0][3] * (1.0f / 255.0f);
+                               out4f[x*4+0] = c[0];
+                               out4f[x*4+1] = c[1];
+                               out4f[x*4+2] = c[2];
+                               out4f[x*4+3] = c[3];
+                       }
+               }
+       }
+}
+
+void DPSOFTRAST_Draw_Span_MultiplyVarying(const DPSOFTRAST_State_Draw_Span *span, float *out4f, const float *in4f, int arrayindex, const float *zf)
+{
+       int x;
+       int startx = span->startx;
+       int endx = span->endx;
+       float c[4];
+       float data[4];
+       float slope[4];
+       float z;
+       data[0] = span->data[0][arrayindex][0];
+       data[1] = span->data[0][arrayindex][1];
+       data[2] = span->data[0][arrayindex][2];
+       data[3] = span->data[0][arrayindex][3];
+       slope[0] = span->data[1][arrayindex][0];
+       slope[1] = span->data[1][arrayindex][1];
+       slope[2] = span->data[1][arrayindex][2];
+       slope[3] = span->data[1][arrayindex][3];
+       for (x = startx;x < endx;x++)
+       {
+               z = zf[x];
+               c[0] = (data[0] + slope[0]*x) * z;
+               c[1] = (data[1] + slope[1]*x) * z;
+               c[2] = (data[2] + slope[2]*x) * z;
+               c[3] = (data[3] + slope[3]*x) * z;
+               out4f[x*4+0] = in4f[x*4+0] * c[0];
+               out4f[x*4+1] = in4f[x*4+1] * c[1];
+               out4f[x*4+2] = in4f[x*4+2] * c[2];
+               out4f[x*4+3] = in4f[x*4+3] * c[3];
+       }
+}
+
+void DPSOFTRAST_Draw_Span_AddBloom(const DPSOFTRAST_State_Draw_Span *span, float *out4f, const float *ina4f, const float *inb4f, const float *subcolor)
+{
+       int x, startx = span->startx, endx = span->endx;
+       float c[4], localcolor[4];
+       localcolor[0] = subcolor[0];
+       localcolor[1] = subcolor[1];
+       localcolor[2] = subcolor[2];
+       localcolor[3] = subcolor[3];
+       for (x = startx;x < endx;x++)
+       {
+               c[0] = inb4f[x*4+0] - localcolor[0];if (c[0] < 0.0f) c[0] = 0.0f;
+               c[1] = inb4f[x*4+1] - localcolor[1];if (c[1] < 0.0f) c[1] = 0.0f;
+               c[2] = inb4f[x*4+2] - localcolor[2];if (c[2] < 0.0f) c[2] = 0.0f;
+               c[3] = inb4f[x*4+3] - localcolor[3];if (c[3] < 0.0f) c[3] = 0.0f;
+               out4f[x*4+0] = ina4f[x*4+0] + c[0];
+               out4f[x*4+1] = ina4f[x*4+1] + c[1];
+               out4f[x*4+2] = ina4f[x*4+2] + c[2];
+               out4f[x*4+3] = ina4f[x*4+3] + c[3];
+       }
+}
+
+void DPSOFTRAST_Draw_Span_MixUniformColor(const DPSOFTRAST_State_Draw_Span *span, float *out4f, const float *in4f, const float *color)
+{
+       int x, startx = span->startx, endx = span->endx;
+       float localcolor[4], ilerp, lerp;
+       localcolor[0] = color[0];
+       localcolor[1] = color[1];
+       localcolor[2] = color[2];
+       localcolor[3] = color[3];
+       ilerp = 1.0f - localcolor[3];
+       lerp = localcolor[3];
+       for (x = startx;x < endx;x++)
+       {
+               out4f[x*4+0] = in4f[x*4+0] * ilerp + localcolor[0] * lerp;
+               out4f[x*4+1] = in4f[x*4+1] * ilerp + localcolor[1] * lerp;
+               out4f[x*4+2] = in4f[x*4+2] * ilerp + localcolor[2] * lerp;
+               out4f[x*4+3] = in4f[x*4+3] * ilerp + localcolor[3] * lerp;
+       }
+}
+
+void DPSOFTRAST_Draw_Span_Lightmap(const DPSOFTRAST_State_Draw_Span *span, float *out4f, const float *diffuse, const float *lightmap)
+{
+       int x, startx = span->startx, endx = span->endx;
+       float Color_Ambient[4], Color_Diffuse[4];
+       Color_Ambient[0] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+0];
+       Color_Ambient[1] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+1];
+       Color_Ambient[2] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+2];
+       Color_Ambient[3] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Alpha*4+0];
+       Color_Diffuse[0] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Diffuse*4+0];
+       Color_Diffuse[1] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Diffuse*4+1];
+       Color_Diffuse[2] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Diffuse*4+2];
+       Color_Diffuse[3] = 0.0f;
+       for (x = startx;x < endx;x++)
+       {
+               out4f[x*4+0] = diffuse[x*4+0] * (Color_Ambient[0] + lightmap[x*4+0] * Color_Diffuse[0]);
+               out4f[x*4+1] = diffuse[x*4+1] * (Color_Ambient[1] + lightmap[x*4+1] * Color_Diffuse[1]);
+               out4f[x*4+2] = diffuse[x*4+2] * (Color_Ambient[2] + lightmap[x*4+2] * Color_Diffuse[2]);
+               out4f[x*4+3] = diffuse[x*4+3] * (Color_Ambient[3] + lightmap[x*4+3] * Color_Diffuse[3]);
+       }
+}
+
+void DPSOFTRAST_Draw_Span_VertexColor(const DPSOFTRAST_State_Draw_Span *span, float *out4f, const float *diffuse, const float *zf)
+{
+       int x, startx = span->startx, endx = span->endx;
+       float Color_Ambient[4], Color_Diffuse[4];
+       float c[4];
+       float data[4];
+       float slope[4];
+       float z;
+       int arrayindex = DPSOFTRAST_ARRAY_COLOR;
+       data[0] = span->data[0][arrayindex][0];
+       data[1] = span->data[0][arrayindex][1];
+       data[2] = span->data[0][arrayindex][2];
+       data[3] = span->data[0][arrayindex][3];
+       slope[0] = span->data[1][arrayindex][0];
+       slope[1] = span->data[1][arrayindex][1];
+       slope[2] = span->data[1][arrayindex][2];
+       slope[3] = span->data[1][arrayindex][3];
+       Color_Ambient[0] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+0];
+       Color_Ambient[1] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+1];
+       Color_Ambient[2] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+2];
+       Color_Ambient[3] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Alpha*4+0];
+       Color_Diffuse[0] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Diffuse*4+0];
+       Color_Diffuse[1] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Diffuse*4+1];
+       Color_Diffuse[2] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Diffuse*4+2];
+       Color_Diffuse[3] = 0.0f;
+       for (x = startx;x < endx;x++)
+       {
+               z = zf[x];
+               c[0] = (data[0] + slope[0]*x) * z;
+               c[1] = (data[1] + slope[1]*x) * z;
+               c[2] = (data[2] + slope[2]*x) * z;
+               c[3] = (data[3] + slope[3]*x) * z;
+               out4f[x*4+0] = diffuse[x*4+0] * (Color_Ambient[0] + c[0] * Color_Diffuse[0]);
+               out4f[x*4+1] = diffuse[x*4+1] * (Color_Ambient[1] + c[1] * Color_Diffuse[1]);
+               out4f[x*4+2] = diffuse[x*4+2] * (Color_Ambient[2] + c[2] * Color_Diffuse[2]);
+               out4f[x*4+3] = diffuse[x*4+3] * (Color_Ambient[3] + c[3] * Color_Diffuse[3]);
+       }
+}
+
+void DPSOFTRAST_Draw_Span_FlatColor(const DPSOFTRAST_State_Draw_Span *span, float *out4f, const float *diffuse)
+{
+       int x, startx = span->startx, endx = span->endx;
+       float Color_Ambient[4];
+       Color_Ambient[0] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+0];
+       Color_Ambient[1] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+1];
+       Color_Ambient[2] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Color_Ambient*4+2];
+       Color_Ambient[3] = dpsoftrast.uniform4f[DPSOFTRAST_UNIFORM_Alpha*4+0];
+       for (x = startx;x < endx;x++)
+       {
+               out4f[x*4+0] = diffuse[x*4+0] * Color_Ambient[0];
+               out4f[x*4+1] = diffuse[x*4+1] * Color_Ambient[1];
+               out4f[x*4+2] = diffuse[x*4+2] * Color_Ambient[2];
+               out4f[x*4+3] = diffuse[x*4+3] * Color_Ambient[3];
+       }
+}
+
+void DPSOFTRAST_Draw_VertexShader(void)
+{
+       DPSOFTRAST_Array_Transform(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_POSITION], dpsoftrast.draw.numvertices, dpsoftrast.uniform4f + 4*DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1);
+       switch(dpsoftrast.shader_mode)
+       {
+       case SHADERMODE_GENERIC: ///< (particles/HUD/etc) vertex color: optionally multiplied by one texture
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_COLOR], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_COLOR], dpsoftrast.draw.numvertices);
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.numvertices);
+               break;
+       case SHADERMODE_POSTPROCESS: ///< postprocessing shader (r_glsl_postprocess)
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.numvertices);
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD1], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD1], dpsoftrast.draw.numvertices);
+               break;
+       case SHADERMODE_DEPTH_OR_SHADOW: ///< (depthfirst/shadows) vertex shader only
+               break;
+       case SHADERMODE_FLATCOLOR: ///< (lightmap) modulate texture by uniform color (q1bsp: q3bsp)
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.numvertices);
+               break;
+       case SHADERMODE_VERTEXCOLOR: ///< (lightmap) modulate texture by vertex colors (q3bsp)
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_COLOR], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_COLOR], dpsoftrast.draw.numvertices);
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.numvertices);
+               break;
+       case SHADERMODE_LIGHTMAP: ///< (lightmap) modulate texture by lightmap texture (q1bsp: q3bsp)
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD0], dpsoftrast.draw.numvertices);
+               DPSOFTRAST_Array_Copy(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_TEXCOORD4], dpsoftrast.draw.in_array4f[DPSOFTRAST_ARRAY_TEXCOORD4], dpsoftrast.draw.numvertices);
+               break;
+       case SHADERMODE_FAKELIGHT: ///< (fakelight) modulate texture by "fake" lighting (no lightmaps: no nothing)
+               break;
+       case SHADERMODE_LIGHTDIRECTIONMAP_MODELSPACE: ///< (lightmap) use directional pixel shading from texture containing modelspace light directions (q3bsp deluxemap)
+               break;
+       case SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE: ///< (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap)
+               break;
+       case SHADERMODE_LIGHTDIRECTION: ///< (lightmap) use directional pixel shading from fixed light direction (q3bsp)
+               break;
+       case SHADERMODE_LIGHTSOURCE: ///< (lightsource) use directional pixel shading from light source (rtlight)
+               break;
+       case SHADERMODE_REFRACTION: ///< refract background (the material is rendered normally after this pass)
+               break;
+       case SHADERMODE_WATER: ///< refract background and reflection (the material is rendered normally after this pass)
+               break;
+       case SHADERMODE_SHOWDEPTH: ///< (debugging) renders depth as color
+               break;
+       case SHADERMODE_DEFERREDGEOMETRY: ///< (deferred) render material properties to screenspace geometry buffers
+               break;
+       case SHADERMODE_DEFERREDLIGHTSOURCE: ///< (deferred) use directional pixel shading from light source (rtlight) on screenspace geometry buffers
+               break;
+       case SHADERMODE_COUNT:
+               break;
+       }
+}
+
+void DPSOFTRAST_Draw_PixelShaderSpan(const DPSOFTRAST_State_Draw_Span *span)
+{
+       float buffer_z[DPSOFTRAST_DRAW_MAXSPANLENGTH];
+       float buffer_texture_color[DPSOFTRAST_DRAW_MAXSPANLENGTH*4];
+       float buffer_texture_lightmap[DPSOFTRAST_DRAW_MAXSPANLENGTH*4];
+       float buffer_FragColor[DPSOFTRAST_DRAW_MAXSPANLENGTH*4];
+       switch(dpsoftrast.shader_mode)
+       {
+       case SHADERMODE_GENERIC: ///< (particles/HUD/etc) vertex color: optionally multiplied by one texture
+               DPSOFTRAST_Draw_Span_Begin(span, buffer_z);
+               DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_texture_color, GL20TU_FIRST, 2, buffer_z);
+               DPSOFTRAST_Draw_Span_MultiplyVarying(span, buffer_FragColor, buffer_texture_color, 1, buffer_z);
+               DPSOFTRAST_Draw_Span_Finish(span, buffer_FragColor);
+               break;
+       case SHADERMODE_POSTPROCESS: ///< postprocessing shader (r_glsl_postprocess)
+               // TODO: optimize!!  at the very least there is no reason to use texture sampling on the frame texture
+               DPSOFTRAST_Draw_Span_Begin(span, buffer_z);
+               DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_FragColor, GL20TU_FIRST, 2, buffer_z);
+               if (dpsoftrast.shader_permutation & SHADERPERMUTATION_BLOOM)
+               {
+                       DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_texture_color, GL20TU_SECOND, 3, buffer_z);
+                       DPSOFTRAST_Draw_Span_AddBloom(span, buffer_FragColor, buffer_FragColor, buffer_texture_color, dpsoftrast.uniform4f + DPSOFTRAST_UNIFORM_BloomColorSubtract * 4);
+               }
+               DPSOFTRAST_Draw_Span_MixUniformColor(span, buffer_FragColor, buffer_FragColor, dpsoftrast.uniform4f + DPSOFTRAST_UNIFORM_ViewTintColor * 4);
+               if (dpsoftrast.shader_permutation & SHADERPERMUTATION_SATURATION)
+               {
+                       // TODO: implement saturation
+               }
+               if (dpsoftrast.shader_permutation & SHADERPERMUTATION_GAMMARAMPS)
+               {
+                       // TODO: implement gammaramps
+               }
+               DPSOFTRAST_Draw_Span_Finish(span, buffer_FragColor);
+               break;
+       case SHADERMODE_DEPTH_OR_SHADOW: ///< (depthfirst/shadows) vertex shader only
+               break;
+       case SHADERMODE_FLATCOLOR: ///< (lightmap) modulate texture by uniform color (q1bsp: q3bsp)
+               DPSOFTRAST_Draw_Span_Begin(span, buffer_z);
+               DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_texture_color, GL20TU_COLOR, 2, buffer_z);
+               DPSOFTRAST_Draw_Span_FlatColor(span, buffer_FragColor, buffer_texture_color);
+               DPSOFTRAST_Draw_Span_Finish(span, buffer_FragColor);
+               break;
+       case SHADERMODE_VERTEXCOLOR: ///< (lightmap) modulate texture by vertex colors (q3bsp)
+               DPSOFTRAST_Draw_Span_Begin(span, buffer_z);
+               DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_texture_color, GL20TU_COLOR, 2, buffer_z);
+               DPSOFTRAST_Draw_Span_VertexColor(span, buffer_FragColor, buffer_texture_color, buffer_z);
+               DPSOFTRAST_Draw_Span_Finish(span, buffer_FragColor);
+               break;
+       case SHADERMODE_LIGHTMAP: ///< (lightmap) modulate texture by lightmap texture (q1bsp: q3bsp)
+               DPSOFTRAST_Draw_Span_Begin(span, buffer_z);
+               DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_texture_color, GL20TU_COLOR, 2, buffer_z);
+               DPSOFTRAST_Draw_Span_Texture2DVarying(span, buffer_texture_lightmap, GL20TU_LIGHTMAP, 6, buffer_z);
+               DPSOFTRAST_Draw_Span_Lightmap(span, buffer_FragColor, buffer_texture_color, buffer_texture_lightmap);
+               DPSOFTRAST_Draw_Span_Finish(span, buffer_FragColor);
+               break;
+       case SHADERMODE_FAKELIGHT: ///< (fakelight) modulate texture by "fake" lighting (no lightmaps: no nothing)
+               break;
+       case SHADERMODE_LIGHTDIRECTIONMAP_MODELSPACE: ///< (lightmap) use directional pixel shading from texture containing modelspace light directions (q3bsp deluxemap)
+               break;
+       case SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE: ///< (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap)
+               break;
+       case SHADERMODE_LIGHTDIRECTION: ///< (lightmap) use directional pixel shading from fixed light direction (q3bsp)
+               break;
+       case SHADERMODE_LIGHTSOURCE: ///< (lightsource) use directional pixel shading from light source (rtlight)
+               break;
+       case SHADERMODE_REFRACTION: ///< refract background (the material is rendered normally after this pass)
+               break;
+       case SHADERMODE_WATER: ///< refract background and reflection (the material is rendered normally after this pass)
+               break;
+       case SHADERMODE_SHOWDEPTH: ///< (debugging) renders depth as color
+               break;
+       case SHADERMODE_DEFERREDGEOMETRY: ///< (deferred) render material properties to screenspace geometry buffers
+               break;
+       case SHADERMODE_DEFERREDLIGHTSOURCE: ///< (deferred) use directional pixel shading from light source (rtlight) on screenspace geometry buffers
+               break;
+       case SHADERMODE_COUNT:
+               break;
+       }
+}
+
+void DPSOFTRAST_Draw_ProcessSpans(void)
+{
+       int i;
+       int x;
+       int startx;
+       int endx;
+       int numspans = dpsoftrast.draw.numspans;
+//     unsigned int c;
+//     unsigned int *colorpixel;
+       unsigned int *depthpixel;
+       float w;
+       float wslope;
+       int depth;
+       int depthslope;
+       unsigned int d;
+       DPSOFTRAST_State_Draw_Span *span = dpsoftrast.draw.spanqueue;
+       unsigned char pixelmask[DPSOFTRAST_DRAW_MAXSPANLENGTH];
+       for (i = 0;i < numspans;i++, span++)
+       {
+               w = span->data[0][DPSOFTRAST_ARRAY_TOTAL][3];
+               wslope = span->data[1][DPSOFTRAST_ARRAY_TOTAL][3];
+               if (dpsoftrast.user.depthtest && dpsoftrast.fb_depthpixels)
+               {
+                       depth = (int)(w*DPSOFTRAST_DEPTHSCALE);
+                       depthslope = (int)(wslope*DPSOFTRAST_DEPTHSCALE);
+                       depthpixel = dpsoftrast.fb_depthpixels + span->start;
+                       switch(dpsoftrast.fb_depthfunc)
+                       {
+                       default:
+                       case GL_ALWAYS:  for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = true; break;
+                       case GL_LESS:    for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = depthpixel[x] < d; break;
+                       case GL_LEQUAL:  for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = depthpixel[x] <= d; break;
+                       case GL_EQUAL:   for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = depthpixel[x] == d; break;
+                       case GL_GEQUAL:  for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = depthpixel[x] >= d; break;
+                       case GL_GREATER: for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = depthpixel[x] > d; break;
+                       case GL_NEVER:   for (x = 0, d = depth;x < span->length;x++, d += depthslope) pixelmask[x] = false; break;
+                       }
+                       //colorpixel = dpsoftrast.fb_colorpixels[0] + span->start;
+                       //for (x = 0;x < span->length;x++)
+                       //      colorpixel[x] = (depthpixel[x] & 0xFF000000) ? (0x00FF0000) : (depthpixel[x] & 0x00FF0000);
+                       // if there is no color buffer, skip pixel shader
+                       startx = 0;
+                       endx = span->length;
+                       while (startx < endx && !pixelmask[startx])
+                               startx++;
+                       while (endx > startx && !pixelmask[endx-1])
+                               endx--;
+                       if (startx >= endx)
+                               continue; // no pixels to fill
+                       span->pixelmask = pixelmask;
+                       span->startx = startx;
+                       span->endx = endx;
+                       // run pixel shader if appropriate
+                       // do this before running depthmask code, to allow the pixelshader
+                       // to clear pixelmask values for alpha testing
+                       if (dpsoftrast.fb_colorpixels[0] && dpsoftrast.fb_colormask)
+                               DPSOFTRAST_Draw_PixelShaderSpan(span);
+                       if (dpsoftrast.user.depthmask)
+                               for (x = 0, d = depth;x < span->length;x++, d += depthslope)
+                                       if (pixelmask[x])
+                                               depthpixel[x] = d;
+               }
+               else
+               {
+                       // no depth testing means we're just dealing with color...
+                       // if there is no color buffer, skip pixel shader
+                       if (dpsoftrast.fb_colorpixels[0] && dpsoftrast.fb_colormask)
+                       {
+                               memset(pixelmask, 1, span->length);
+                               span->pixelmask = pixelmask;
+                               span->startx = 0;
+                               span->endx = span->length;
+                               DPSOFTRAST_Draw_PixelShaderSpan(span);
+                       }
+               }
+       }
+}
+
+void DPSOFTRAST_Draw_ProcessTriangles(int firstvertex, int numvertices, int numtriangles, const int *element3i, const unsigned short *element3s, int numarrays)
+{
+       int cullface = dpsoftrast.user.cullface;
+       int width = dpsoftrast.fb_width;
+       int height = dpsoftrast.fb_height;
+       int i;
+       int j;
+       int k;
+       int y;
+       int e[3];
+       int screenx[4];
+       int screeny[4];
+       int screenyless[4];
+       int numpoints;
+       int clipflags;
+       int edge0p;
+       int edge0n;
+       int edge1p;
+       int edge1n;
+       int extent[4];
+       int startx;
+       int endx;
+       float startxf;
+       float endxf;
+       float edge0ylerp;
+       float edge0yilerp;
+       float edge1ylerp;
+       float edge1yilerp;
+       float edge0xf;
+       float edge1xf;
+       float spanilength;
+       float startxlerp;
+       float yc;
+       float w;
+       float frac;
+       float ifrac;
+       float trianglearea2;
+       float triangleedge[2][4];
+       float trianglenormal[4];
+       float clipdist[4];
+       float clipped[DPSOFTRAST_ARRAY_TOTAL][4][4];
+       float screen[4][4];
+       float proj[DPSOFTRAST_ARRAY_TOTAL][4][4];
+       DPSOFTRAST_State_Draw_Span *span;
+       DPSOFTRAST_State_Draw_Span *oldspan;
+       for (i = 0;i < numtriangles;i++)
+       {
+               // generate the 3 edges of this triangle
+               // generate spans for the triangle - switch based on left split or right split classification of triangle
+               if (element3i)
+               {
+                       e[0] = element3i[i*3+0] - firstvertex;
+                       e[1] = element3i[i*3+1] - firstvertex;
+                       e[2] = element3i[i*3+2] - firstvertex;
+               }
+               else if (element3s)
+               {
+                       e[0] = element3s[i*3+0] - firstvertex;
+                       e[1] = element3s[i*3+1] - firstvertex;
+                       e[2] = element3s[i*3+2] - firstvertex;
+               }
+               else
+               {
+                       e[0] = i*3+0;
+                       e[1] = i*3+1;
+                       e[2] = i*3+2;
+               }
+               triangleedge[0][0] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[0]*4+0] - dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+0];
+               triangleedge[0][1] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[0]*4+1] - dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+1];
+               triangleedge[0][2] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[0]*4+2] - dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+2];
+               triangleedge[1][0] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[2]*4+0] - dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+0];
+               triangleedge[1][1] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[2]*4+1] - dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+1];
+               triangleedge[1][2] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[2]*4+2] - dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+2];
+               trianglenormal[0] = triangleedge[0][1] * triangleedge[1][2] - triangleedge[0][2] * triangleedge[1][1];
+               trianglenormal[1] = triangleedge[0][2] * triangleedge[1][0] - triangleedge[0][0] * triangleedge[1][2];
+               trianglenormal[2] = triangleedge[0][0] * triangleedge[1][1] - triangleedge[0][1] * triangleedge[1][0];
+               trianglearea2 = trianglenormal[0] * trianglenormal[0] + trianglenormal[1] * trianglenormal[1] + trianglenormal[2] * trianglenormal[2];
+               // skip degenerate triangles, nothing good can come from them...
+               if (trianglearea2 == 0.0f)
+                       continue;
+               // apply current cullface mode (this culls many triangles)
+               switch(cullface)
+               {
+               case GL_BACK:
+                       if (trianglenormal[2] < 0)
+                               continue;
+                       break;
+               case GL_FRONT:
+                       if (trianglenormal[2] > 0)
+                               continue;
+                       break;
+               }
+               // calculate distance from nearplane
+               clipdist[0] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[0]*4+2] + 1.0f;
+               clipdist[1] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[1]*4+2] + 1.0f;
+               clipdist[2] = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][e[2]*4+2] + 1.0f;
+               clipflags = 0;
+               if (clipdist[0] < 0.0f)
+                       clipflags |= 1;
+               if (clipdist[1] < 0.0f)
+                       clipflags |= 2;
+               if (clipdist[2] < 0.0f)
+                       clipflags |= 4;
+               // clip triangle if necessary
+               switch(clipflags)
+               {
+               case 0: /*000*/
+                       // triangle is entirely in front of nearplane
+
+                       // macros for clipping vertices
+#define CLIPPEDVERTEXLERP(k,p1,p2) \
+                       frac = clipdist[p1] / (clipdist[p1] - clipdist[p2]);\
+                       ifrac = 1.0f - frac;\
+                       for (j = 0;j < numarrays;j++)\
+                       {\
+                               clipped[j][k][0] = dpsoftrast.draw.post_array4f[j][e[p1]*4+0]*ifrac+dpsoftrast.draw.post_array4f[j][e[p2]*4+0]*frac;\
+                               clipped[j][k][1] = dpsoftrast.draw.post_array4f[j][e[p1]*4+1]*ifrac+dpsoftrast.draw.post_array4f[j][e[p2]*4+1]*frac;\
+                               clipped[j][k][2] = dpsoftrast.draw.post_array4f[j][e[p1]*4+2]*ifrac+dpsoftrast.draw.post_array4f[j][e[p2]*4+2]*frac;\
+                               clipped[j][k][3] = dpsoftrast.draw.post_array4f[j][e[p1]*4+3]*ifrac+dpsoftrast.draw.post_array4f[j][e[p2]*4+3]*frac;\
+                       }\
+                       DPSOFTRAST_Draw_ProjectVertices(screen[k], clipped[DPSOFTRAST_ARRAY_POSITION][k], 1)
+#define CLIPPEDVERTEXCOPY(k,p1) \
+                       for (j = 0;j < numarrays;j++)\
+                       {\
+                               clipped[j][k][0] = dpsoftrast.draw.post_array4f[j][e[p1]*4+0];\
+                               clipped[j][k][1] = dpsoftrast.draw.post_array4f[j][e[p1]*4+1];\
+                               clipped[j][k][2] = dpsoftrast.draw.post_array4f[j][e[p1]*4+2];\
+                               clipped[j][k][3] = dpsoftrast.draw.post_array4f[j][e[p1]*4+3];\
+                       }\
+                       screen[k][0] = dpsoftrast.draw.screencoord4f[e[p1]*4+0];\
+                       screen[k][1] = dpsoftrast.draw.screencoord4f[e[p1]*4+1];\
+                       screen[k][2] = dpsoftrast.draw.screencoord4f[e[p1]*4+2];\
+                       screen[k][3] = dpsoftrast.draw.screencoord4f[e[p1]*4+3];
+
+                       CLIPPEDVERTEXCOPY(0,0);
+                       CLIPPEDVERTEXCOPY(1,1);
+                       CLIPPEDVERTEXCOPY(2,2);
+                       numpoints = 3;
+                       break;
+               case 1: /*100*/
+                       CLIPPEDVERTEXLERP(0,0,1);
+                       CLIPPEDVERTEXCOPY(1,1);
+                       CLIPPEDVERTEXCOPY(2,2);
+                       CLIPPEDVERTEXLERP(3,2,0);
+                       numpoints = 4;
+                       break;
+               case 2: /*010*/
+                       CLIPPEDVERTEXCOPY(0,0);
+                       CLIPPEDVERTEXLERP(1,0,1);
+                       CLIPPEDVERTEXLERP(2,1,2);
+                       CLIPPEDVERTEXCOPY(3,2);
+                       numpoints = 4;
+                       break;
+               case 3: /*110*/
+                       CLIPPEDVERTEXLERP(0,1,2);
+                       CLIPPEDVERTEXCOPY(1,2);
+                       CLIPPEDVERTEXLERP(2,2,0);
+                       numpoints = 3;
+                       break;
+               case 4: /*001*/
+                       CLIPPEDVERTEXCOPY(0,0);
+                       CLIPPEDVERTEXCOPY(1,1);
+                       CLIPPEDVERTEXLERP(2,1,2);
+                       CLIPPEDVERTEXLERP(3,2,0);
+                       numpoints = 4;
+                       break;
+               case 5: /*101*/
+                       CLIPPEDVERTEXLERP(0,0,1);
+                       CLIPPEDVERTEXCOPY(1,1);
+                       CLIPPEDVERTEXLERP(2,1,2);
+                       numpoints = 3;
+                       break;
+               case 6: /*011*/
+                       CLIPPEDVERTEXCOPY(0,0);
+                       CLIPPEDVERTEXLERP(1,0,1);
+                       CLIPPEDVERTEXLERP(2,2,0);
+                       numpoints = 3;
+                       break;
+               case 7: /*111*/
+                       // triangle is entirely behind nearplane
+                       continue;
+               }
+               // calculate integer y coords for triangle points
+               screenx[0] = (int)(screen[0][0]);
+               screeny[0] = (int)(screen[0][1]);
+               screenx[1] = (int)(screen[1][0]);
+               screeny[1] = (int)(screen[1][1]);
+               screenx[2] = (int)(screen[2][0]);
+               screeny[2] = (int)(screen[2][1]);
+               screenx[3] = (int)(screen[3][0]);
+               screeny[3] = (int)(screen[3][1]);
+               // figure out the extents (bounding box) of the triangle
+               extent[0] = screenx[0];
+               extent[1] = screeny[0];
+               extent[2] = screenx[0];
+               extent[3] = screeny[0];
+               for (j = 1;j < numpoints;j++)
+               {
+                       if (extent[0] > screenx[j]) extent[0] = screenx[j];
+                       if (extent[1] > screeny[j]) extent[1] = screeny[j];
+                       if (extent[2] < screenx[j]) extent[2] = screenx[j];
+                       if (extent[3] < screeny[j]) extent[3] = screeny[j];
+               }
+               //extent[0]--;
+               //extent[1]--;
+               extent[2]++;
+               extent[3]++;
+               if (extent[0] < 0)
+                       extent[0] = 0;
+               if (extent[1] < 0)
+                       extent[1] = 0;
+               if (extent[2] > width)
+                       extent[2] = width;
+               if (extent[3] > height)
+                       extent[3] = height;
+               // skip offscreen triangles
+               if (extent[2] <= extent[0] || extent[3] <= extent[1])
+                       continue;
+               // okay, this triangle is going to produce spans, we'd better project
+               // the interpolants now (this is what gives perspective texturing),
+               // this consists of simply multiplying all arrays by the W coord
+               // (which is basically 1/Z), which will be undone per-pixel
+               // (multiplying by Z again) to get the perspective-correct array
+               // values
+               for (j = 0;j < numarrays;j++)
+               {
+                       for (k = 0;k < numpoints;k++)
+                       {
+                               w = screen[k][3];
+                               proj[j][k][0] = clipped[j][k][0] * w;
+                               proj[j][k][1] = clipped[j][k][1] * w;
+                               proj[j][k][2] = clipped[j][k][2] * w;
+                               proj[j][k][3] = clipped[j][k][3] * w;
+                       }
+               }
+               // iterate potential spans
+               // TODO: optimize?  if we figured out the edge order beforehand, this
+               //       could do loops over the edges in the proper order rather than
+               //       selecting them for each span
+               // TODO: optimize?  the edges could have data slopes calculated
+               // TODO: optimize?  the data slopes could be calculated as a plane
+               //       (2D slopes) to avoid any interpolation along edges at all
+               for (y = extent[1];y < extent[3];y++)
+               {
+                       // get center of pixel y
+                       yc = y;
+                       // do the compares all at once
+                       screenyless[0] = y <= screeny[0];
+                       screenyless[1] = y <= screeny[1];
+                       screenyless[2] = y <= screeny[2];
+                       screenyless[3] = y <= screeny[3];
+                       if (numpoints == 4)
+                       {
+                               switch(screenyless[0] + screenyless[1] * 2 + screenyless[2] * 4 + screenyless[3] * 8)
+                               {
+                               case  0: /*0000*/ continue;
+                               case  1: /*1000*/ edge0p = 3;edge0n = 0;edge1p = 0;edge1n = 1;break;
+                               case  2: /*0100*/ edge0p = 0;edge0n = 1;edge1p = 1;edge1n = 2;break;
+                               case  3: /*1100*/ edge0p = 3;edge0n = 0;edge1p = 1;edge1n = 2;break;
+                               case  4: /*0010*/ edge0p = 1;edge0n = 2;edge1p = 2;edge1n = 3;break;
+                               case  5: /*1010*/ edge0p = 1;edge0n = 2;edge1p = 2;edge1n = 3;break; // concave - nonsense
+                               case  6: /*0110*/ edge0p = 0;edge0n = 1;edge1p = 2;edge1n = 3;break;
+                               case  7: /*1110*/ edge0p = 3;edge0n = 0;edge1p = 2;edge1n = 3;break;
+                               case  8: /*0001*/ edge0p = 2;edge0n = 3;edge1p = 3;edge1n = 0;break;
+                               case  9: /*1001*/ edge0p = 2;edge0n = 3;edge1p = 0;edge1n = 1;break;
+                               case 10: /*0101*/ edge0p = 2;edge0n = 3;edge1p = 1;edge1n = 2;break; // concave - nonsense
+                               case 11: /*1101*/ edge0p = 2;edge0n = 3;edge1p = 1;edge1n = 2;break;
+                               case 12: /*0011*/ edge0p = 1;edge0n = 2;edge1p = 3;edge1n = 0;break;
+                               case 13: /*1011*/ edge0p = 1;edge0n = 2;edge1p = 0;edge1n = 1;break;
+                               case 14: /*0111*/ edge0p = 0;edge0n = 1;edge1p = 3;edge1n = 0;break;
+                               case 15: /*1111*/ continue;
+                               }
+                       }
+                       else
+                       {
+                               switch(screenyless[0] + screenyless[1] * 2 + screenyless[2] * 4)
+                               {
+                               case 0: /*000*/ continue;
+                               case 1: /*100*/ edge0p = 2;edge0n = 0;edge1p = 0;edge1n = 1;break;
+                               case 2: /*010*/ edge0p = 0;edge0n = 1;edge1p = 1;edge1n = 2;break;
+                               case 3: /*110*/ edge0p = 2;edge0n = 0;edge1p = 1;edge1n = 2;break;
+                               case 4: /*001*/ edge0p = 1;edge0n = 2;edge1p = 2;edge1n = 0;break;
+                               case 5: /*101*/ edge0p = 1;edge0n = 2;edge1p = 0;edge1n = 1;break;
+                               case 6: /*011*/ edge0p = 0;edge0n = 1;edge1p = 2;edge1n = 0;break;
+                               case 7: /*111*/ continue;
+                               }
+                       }
+#if 0
+               {
+                       int foundedges = 0;
+                       int cedge0p = 0;
+                       int cedge0n = 0;
+                       int cedge1p = 0;
+                       int cedge1n = 0;
+                       for (j = 0, k = numpoints-1;j < numpoints;k = j, j++)
+                       {
+                               if (screenyless[k] && !screenyless[j])
+                               {
+                                       cedge1p = k;
+                                       cedge1n = j;
+                                       foundedges |= 1;
+                               }
+                               else if (screenyless[j] && !screenyless[k])
+                               {
+                                       cedge0p = k;
+                                       cedge0n = j;
+                                       foundedges |= 2;
+                               }
+                       }
+                       if (foundedges != 3)
+                               continue;
+                       if (cedge0p != edge0p || cedge0n != edge0n || cedge1p != edge1p || cedge1n != edge1n)
+                       {
+                               if (numpoints == 4)
+                                       printf("case %i%i%i%i is broken %i %i %i %i != %i %i %i %i\n", screenyless[0], screenyless[1], screenyless[2], screenyless[3], cedge0p, cedge0n, cedge1p, cedge1n, edge0p, edge0n, edge1p, edge1n);
+                               else
+                                       printf("case %i%i%i is broken %i %i %i %i != %i %i %i %i\n", screenyless[0], screenyless[1], screenyless[2], cedge0p, cedge0n, cedge1p, cedge1n, edge0p, edge0n, edge1p, edge1n);
+                       }
+               }
+#endif
+                       edge0ylerp = (yc - screen[edge0p][1]) / (screen[edge0n][1] - screen[edge0p][1]);
+                       edge1ylerp = (yc - screen[edge1p][1]) / (screen[edge1n][1] - screen[edge1p][1]);
+                       if (edge0ylerp < 0 || edge0ylerp > 1 || edge1ylerp < 0 || edge1ylerp > 1)
+                               continue;
+                       edge0yilerp = 1.0f - edge0ylerp;
+                       edge1yilerp = 1.0f - edge1ylerp;
+                       edge0xf = screen[edge0p][0] * edge0yilerp + screen[edge0n][0] * edge0ylerp;
+                       edge1xf = screen[edge1p][0] * edge1yilerp + screen[edge1n][0] * edge1ylerp;
+                       if (edge0xf < edge1xf)
+                       {
+                               startxf = edge0xf;
+                               endxf = edge1xf;
+                       }
+                       else
+                       {
+                               startxf = edge1xf;
+                               endxf = edge0xf;
+                       }
+                       startx = (int)ceil(startxf);
+                       endx = (int)ceil(endxf);
+                       if (startx < 0)
+                               startx = 0;
+                       if (endx > width)
+                               endx = width;
+                       if (startx >= endx)
+                               continue;
+                       if (startxf > startx || endxf < endx-1) { printf("%s:%i X wrong (%i to %i is outside %f to %f)\n", __FILE__, __LINE__, startx, endx, startxf, endxf); }
+                       spanilength = 1.0f / (endxf - startxf);
+                       startxlerp = startx - startxf;
+                       span = &dpsoftrast.draw.spanqueue[dpsoftrast.draw.numspans++];
+                       span->start = y * width + startx;
+                       span->length = endx - startx;
+                       j = DPSOFTRAST_ARRAY_TOTAL;
+                       if (edge0xf < edge1xf)
+                       {
+                               span->data[0][j][0] = screen[edge0p][0] * edge0yilerp + screen[edge0n][0] * edge0ylerp;
+                               span->data[0][j][1] = screen[edge0p][1] * edge0yilerp + screen[edge0n][1] * edge0ylerp;
+                               span->data[0][j][2] = screen[edge0p][2] * edge0yilerp + screen[edge0n][2] * edge0ylerp;
+                               span->data[0][j][3] = screen[edge0p][3] * edge0yilerp + screen[edge0n][3] * edge0ylerp;
+                               span->data[1][j][0] = screen[edge1p][0] * edge1yilerp + screen[edge1n][0] * edge1ylerp;
+                               span->data[1][j][1] = screen[edge1p][1] * edge1yilerp + screen[edge1n][1] * edge1ylerp;
+                               span->data[1][j][2] = screen[edge1p][2] * edge1yilerp + screen[edge1n][2] * edge1ylerp;
+                               span->data[1][j][3] = screen[edge1p][3] * edge1yilerp + screen[edge1n][3] * edge1ylerp;
+                               for (j = 0;j < numarrays;j++)
+                               {
+                                       span->data[0][j][0] = proj[j][edge0p][0] * edge0yilerp + proj[j][edge0n][0] * edge0ylerp;
+                                       span->data[0][j][1] = proj[j][edge0p][1] * edge0yilerp + proj[j][edge0n][1] * edge0ylerp;
+                                       span->data[0][j][2] = proj[j][edge0p][2] * edge0yilerp + proj[j][edge0n][2] * edge0ylerp;
+                                       span->data[0][j][3] = proj[j][edge0p][3] * edge0yilerp + proj[j][edge0n][3] * edge0ylerp;
+                                       span->data[1][j][0] = proj[j][edge1p][0] * edge1yilerp + proj[j][edge1n][0] * edge1ylerp;
+                                       span->data[1][j][1] = proj[j][edge1p][1] * edge1yilerp + proj[j][edge1n][1] * edge1ylerp;
+                                       span->data[1][j][2] = proj[j][edge1p][2] * edge1yilerp + proj[j][edge1n][2] * edge1ylerp;
+                                       span->data[1][j][3] = proj[j][edge1p][3] * edge1yilerp + proj[j][edge1n][3] * edge1ylerp;
+                               }
+                       }
+                       else
+                       {
+                               span->data[0][j][0] = screen[edge1p][0] * edge1yilerp + screen[edge1n][0] * edge1ylerp;
+                               span->data[0][j][1] = screen[edge1p][1] * edge1yilerp + screen[edge1n][1] * edge1ylerp;
+                               span->data[0][j][2] = screen[edge1p][2] * edge1yilerp + screen[edge1n][2] * edge1ylerp;
+                               span->data[0][j][3] = screen[edge1p][3] * edge1yilerp + screen[edge1n][3] * edge1ylerp;
+                               span->data[1][j][0] = screen[edge0p][0] * edge0yilerp + screen[edge0n][0] * edge0ylerp;
+                               span->data[1][j][1] = screen[edge0p][1] * edge0yilerp + screen[edge0n][1] * edge0ylerp;
+                               span->data[1][j][2] = screen[edge0p][2] * edge0yilerp + screen[edge0n][2] * edge0ylerp;
+                               span->data[1][j][3] = screen[edge0p][3] * edge0yilerp + screen[edge0n][3] * edge0ylerp;
+                               for (j = 0;j < numarrays;j++)
+                               {
+                                       span->data[0][j][0] = proj[j][edge1p][0] * edge1yilerp + proj[j][edge1n][0] * edge1ylerp;
+                                       span->data[0][j][1] = proj[j][edge1p][1] * edge1yilerp + proj[j][edge1n][1] * edge1ylerp;
+                                       span->data[0][j][2] = proj[j][edge1p][2] * edge1yilerp + proj[j][edge1n][2] * edge1ylerp;
+                                       span->data[0][j][3] = proj[j][edge1p][3] * edge1yilerp + proj[j][edge1n][3] * edge1ylerp;
+                                       span->data[1][j][0] = proj[j][edge0p][0] * edge0yilerp + proj[j][edge0n][0] * edge0ylerp;
+                                       span->data[1][j][1] = proj[j][edge0p][1] * edge0yilerp + proj[j][edge0n][1] * edge0ylerp;
+                                       span->data[1][j][2] = proj[j][edge0p][2] * edge0yilerp + proj[j][edge0n][2] * edge0ylerp;
+                                       span->data[1][j][3] = proj[j][edge0p][3] * edge0yilerp + proj[j][edge0n][3] * edge0ylerp;
+                               }
+                       }
+                       // change data[1][n][] to be a data slope
+                       j = DPSOFTRAST_ARRAY_TOTAL;
+                       span->data[1][j][0] = (span->data[1][j][0] - span->data[0][j][0]) * spanilength;
+                       span->data[1][j][1] = (span->data[1][j][1] - span->data[0][j][1]) * spanilength;
+                       span->data[1][j][2] = (span->data[1][j][2] - span->data[0][j][2]) * spanilength;
+                       span->data[1][j][3] = (span->data[1][j][3] - span->data[0][j][3]) * spanilength;
+                       for (j = 0;j < numarrays;j++)
+                       {
+                               span->data[1][j][0] = (span->data[1][j][0] - span->data[0][j][0]) * spanilength;
+                               span->data[1][j][1] = (span->data[1][j][1] - span->data[0][j][1]) * spanilength;
+                               span->data[1][j][2] = (span->data[1][j][2] - span->data[0][j][2]) * spanilength;
+                               span->data[1][j][3] = (span->data[1][j][3] - span->data[0][j][3]) * spanilength;
+                       }
+                       // adjust the data[0][n][] to be correct for the pixel centers
+                       // this also handles horizontal clipping where a major part of the
+                       // span may be off the left side of the screen
+                       j = DPSOFTRAST_ARRAY_TOTAL;
+                       span->data[0][j][0] += span->data[1][j][0] * startxlerp;
+                       span->data[0][j][1] += span->data[1][j][1] * startxlerp;
+                       span->data[0][j][2] += span->data[1][j][2] * startxlerp;
+                       span->data[0][j][3] += span->data[1][j][3] * startxlerp;
+                       for (j = 0;j < numarrays;j++)
+                       {
+                               span->data[0][j][0] += span->data[1][j][0] * startxlerp;
+                               span->data[0][j][1] += span->data[1][j][1] * startxlerp;
+                               span->data[0][j][2] += span->data[1][j][2] * startxlerp;
+                               span->data[0][j][3] += span->data[1][j][3] * startxlerp;
+                       }
+                       // to keep the shader routines from needing more than a small
+                       // buffer for pixel intermediate data, we split long spans...
+                       while (span->length > DPSOFTRAST_DRAW_MAXSPANLENGTH)
+                       {
+                               span->length = DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               if (dpsoftrast.draw.numspans >= DPSOFTRAST_DRAW_MAXSPANQUEUE)
+                               {
+                                       DPSOFTRAST_Draw_ProcessSpans();
+                                       dpsoftrast.draw.numspans = 0;
+                               }
+                               oldspan = span;
+                               span = &dpsoftrast.draw.spanqueue[dpsoftrast.draw.numspans++];
+                               *span = *oldspan;
+                               startx += DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               span->start = y * width + startx;
+                               span->length = endx - startx;
+                               j = DPSOFTRAST_ARRAY_TOTAL;
+                               span->data[0][j][0] += span->data[1][j][0] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               span->data[0][j][1] += span->data[1][j][1] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               span->data[0][j][2] += span->data[1][j][2] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               span->data[0][j][3] += span->data[1][j][3] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               for (j = 0;j < numarrays;j++)
+                               {
+                                       span->data[0][j][0] += span->data[1][j][0] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                                       span->data[0][j][1] += span->data[1][j][1] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                                       span->data[0][j][2] += span->data[1][j][2] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                                       span->data[0][j][3] += span->data[1][j][3] * DPSOFTRAST_DRAW_MAXSPANLENGTH;
+                               }
+                       }
+                       // after all that, we have a span suitable for the pixel shader...
+                       if (dpsoftrast.draw.numspans >= DPSOFTRAST_DRAW_MAXSPANQUEUE)
+                       {
+                               DPSOFTRAST_Draw_ProcessSpans();
+                               dpsoftrast.draw.numspans = 0;
+                       }
+               }
+               // draw outlines over triangle for debugging
+       //      for (j = 0, k = numpoints-1;j < numpoints;k = j, j++)
+       //              DPSOFTRAST_Draw_DebugEdgePoints(screen[k], screen[j]);
+       }
+       if (dpsoftrast.draw.numspans)
+       {
+               DPSOFTRAST_Draw_ProcessSpans();
+               dpsoftrast.draw.numspans = 0;
+       }
+}
+
+void DPSOFTRAST_Draw_DebugPoints(void)
+{
+       int i;
+       int x;
+       int y;
+       int numvertices = dpsoftrast.draw.numvertices;
+       int w = dpsoftrast.fb_width;
+       int bounds[4];
+       unsigned int *pixels = dpsoftrast.fb_colorpixels[0];
+       const float *c4f;
+       bounds[0] = dpsoftrast.fb_viewportscissor[0];
+       bounds[1] = dpsoftrast.fb_viewportscissor[1];
+       bounds[2] = dpsoftrast.fb_viewportscissor[0] + dpsoftrast.fb_viewportscissor[2];
+       bounds[3] = dpsoftrast.fb_viewportscissor[1] + dpsoftrast.fb_viewportscissor[3];
+       for (i = 0;i < numvertices;i++)
+       {
+               // check nearclip
+               //if (dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][i*4+3] != 1.0f)
+               //      continue;
+               x = (int)(dpsoftrast.draw.screencoord4f[i*4+0]);
+               y = (int)(dpsoftrast.draw.screencoord4f[i*4+1]);
+               //x = (int)(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][i*4+0] + 0.5f);
+               //y = (int)(dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][i*4+1] + 0.5f);
+               //x = (int)((dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][i*4+0] + 1.0f) * dpsoftrast.fb_width * 0.5f + 0.5f);
+               //y = (int)((dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION][i*4+1] + 1.0f) * dpsoftrast.fb_height * 0.5f + 0.5f);
+               if (x < bounds[0] || y < bounds[1] || x >= bounds[2] || y >= bounds[3])
+                       continue;
+               c4f = dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_COLOR] + i*4;
+               pixels[y*w+x] = DPSOFTRAST_BGRA8_FROM_RGBA32F(c4f[0], c4f[1], c4f[2], c4f[3]);
+       }
+}
+
+void DPSOFTRAST_DrawTriangles(int firstvertex, int numvertices, int numtriangles, const int *element3i, const unsigned short *element3s)
+{
+       DPSOFTRAST_Validate(DPSOFTRAST_VALIDATE_DRAW);
+       DPSOFTRAST_Draw_LoadVertices(firstvertex, numvertices, true, 1);
+       DPSOFTRAST_Draw_VertexShader();
+       DPSOFTRAST_Draw_ProjectVertices(dpsoftrast.draw.screencoord4f, dpsoftrast.draw.post_array4f[DPSOFTRAST_ARRAY_POSITION], numvertices);
+       DPSOFTRAST_Draw_ProcessTriangles(firstvertex, numvertices, numtriangles, element3i, element3s, 3);
+}
+
+void DPSOFTRAST_Init(int width, int height, unsigned int *colorpixels, unsigned int *depthpixels)
+{
+       union
+       {
+               int i;
+               unsigned char b[4];
+       }
+       u;
+       u.i = 1;
+       memset(&dpsoftrast, 0, sizeof(dpsoftrast));
+       dpsoftrast.bigendian = u.b[3];
+       dpsoftrast.fb_width = width;
+       dpsoftrast.fb_height = height;
+       dpsoftrast.fb_depthpixels = depthpixels;
+       dpsoftrast.fb_colorpixels[0] = colorpixels;
+       dpsoftrast.fb_colorpixels[1] = NULL;
+       dpsoftrast.fb_colorpixels[1] = NULL;
+       dpsoftrast.fb_colorpixels[1] = NULL;
+       dpsoftrast.texture_firstfree = 1;
+       dpsoftrast.texture_end = 1;
+       dpsoftrast.texture_max = 0;
+       dpsoftrast.user.colormask[0] = 1;
+       dpsoftrast.user.colormask[1] = 1;
+       dpsoftrast.user.colormask[2] = 1;
+       dpsoftrast.user.colormask[3] = 1;
+       dpsoftrast.user.blendfunc[0] = GL_ONE;
+       dpsoftrast.user.blendfunc[1] = GL_ZERO;
+       dpsoftrast.user.depthmask = true;
+       dpsoftrast.user.depthtest = true;
+       dpsoftrast.user.depthfunc = GL_LEQUAL;
+       dpsoftrast.user.scissortest = false;
+       dpsoftrast.user.cullface = GL_BACK;
+       dpsoftrast.user.alphatest = false;
+       dpsoftrast.user.alphafunc = GL_GREATER;
+       dpsoftrast.user.alphavalue = 0.5f;
+       dpsoftrast.user.scissor[0] = 0;
+       dpsoftrast.user.scissor[1] = 0;
+       dpsoftrast.user.scissor[2] = dpsoftrast.fb_width;
+       dpsoftrast.user.scissor[3] = dpsoftrast.fb_height;
+       dpsoftrast.user.viewport[0] = 0;
+       dpsoftrast.user.viewport[1] = 0;
+       dpsoftrast.user.viewport[2] = dpsoftrast.fb_width;
+       dpsoftrast.user.viewport[3] = dpsoftrast.fb_height;
+       dpsoftrast.user.depthrange[0] = 0;
+       dpsoftrast.user.depthrange[1] = 1;
+       dpsoftrast.user.polygonoffset[0] = 0;
+       dpsoftrast.user.polygonoffset[1] = 0;
+       dpsoftrast.user.color[0] = 1;
+       dpsoftrast.user.color[1] = 1;
+       dpsoftrast.user.color[2] = 1;
+       dpsoftrast.user.color[3] = 1;
+       dpsoftrast.validate = -1;
+       DPSOFTRAST_Validate(-1);
+       dpsoftrast.validate = 0;
+}
+
+void DPSOFTRAST_Shutdown(void)
+{
+       int i;
+       for (i = 0;i < dpsoftrast.texture_end;i++)
+               if (dpsoftrast.texture[i].bytes)
+                       free(dpsoftrast.texture[i].bytes);
+       if (dpsoftrast.texture)
+               free(dpsoftrast.texture);
+       memset(&dpsoftrast, 0, sizeof(dpsoftrast));
+}
diff --git a/dpsoftrast.h b/dpsoftrast.h
new file mode 100644 (file)
index 0000000..ea62611
--- /dev/null
@@ -0,0 +1,304 @@
+
+#ifndef DPSOFTRAST_H
+#define DPSOFTRAST_H
+
+#include <stdlib.h>
+
+#define DPSOFTRAST_MAXMIPMAPS 16
+#define DPSOFTRAST_TEXTURE_MAXSIZE (1<<(DPSOFTRAST_MAXMIPMAPS - 1))
+#define DPSOFTRAST_MAXTEXTUREUNITS 32
+#define DPSOFTRAST_MAXTEXCOORDARRAYS 8
+
+// type of pixels in texture (some of these are converted to BGRA8 on update)
+#define DPSOFTRAST_TEXTURE_FORMAT_BGRA8 0
+#define DPSOFTRAST_TEXTURE_FORMAT_DEPTH 1
+#define DPSOFTRAST_TEXTURE_FORMAT_RGBA8 2
+#define DPSOFTRAST_TEXTURE_FORMAT_ALPHA8 3
+#define DPSOFTRAST_TEXTURE_FORMAT_COMPAREMASK 3
+
+// modifier flags for texture (can not be changed after creation)
+#define DPSOFTRAST_TEXTURE_FLAG_MIPMAP 4
+#define DPSOFTRAST_TEXTURE_FLAG_CUBEMAP 8
+#define DPSOFTRAST_TEXTURE_FLAG_USEALPHA 16
+#define DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE 32
+
+typedef enum DPSOFTRAST_TEXTURE_FILTER_e
+{
+       DPSOFTRAST_TEXTURE_FILTER_NEAREST = 0,
+       DPSOFTRAST_TEXTURE_FILTER_LINEAR = 1,
+       DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE = 2,
+       DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE = 3,
+}
+DPSOFTRAST_TEXTURE_FILTER;
+
+void DPSOFTRAST_Init(int width, int height, unsigned int *colorpixels, unsigned int *depthpixels);
+void DPSOFTRAST_Shutdown(void);
+
+int DPSOFTRAST_Texture_New(int flags, int width, int height, int depth);
+void DPSOFTRAST_Texture_Free(int index);
+void DPSOFTRAST_Texture_UpdatePartial(int index, int mip, const unsigned char *pixels, int blockx, int blocky, int blockwidth, int blockheight);
+void DPSOFTRAST_Texture_UpdateFull(int index, const unsigned char *pixels);
+int DPSOFTRAST_Texture_GetWidth(int index, int mip);
+int DPSOFTRAST_Texture_GetHeight(int index, int mip);
+int DPSOFTRAST_Texture_GetDepth(int index, int mip);
+unsigned char *DPSOFTRAST_Texture_GetPixelPointer(int index, int mip);
+void DPSOFTRAST_Texture_Filter(int index, DPSOFTRAST_TEXTURE_FILTER filter);
+
+void DPSOFTRAST_SetRenderTargets(int width, int height, unsigned int *depthpixels, unsigned int *colorpixels0, unsigned int *colorpixels1, unsigned int *colorpixels2, unsigned int *colorpixels3);
+void DPSOFTRAST_Viewport(int x, int y, int width, int height);
+void DPSOFTRAST_ClearColor(float r, float g, float b, float a);
+void DPSOFTRAST_ClearDepth(float d);
+void DPSOFTRAST_ColorMask(int r, int g, int b, int a);
+void DPSOFTRAST_DepthTest(int enable);
+void DPSOFTRAST_ScissorTest(int enable);
+void DPSOFTRAST_Scissor(float x, float y, float width, float height);
+
+void DPSOFTRAST_BlendFunc(int smodulate, int dmodulate);
+void DPSOFTRAST_BlendSubtract(int enable);
+void DPSOFTRAST_DepthMask(int enable);
+void DPSOFTRAST_DepthFunc(int comparemode);
+void DPSOFTRAST_DepthRange(float range0, float range1);
+void DPSOFTRAST_PolygonOffset(float alongnormal, float intoview);
+void DPSOFTRAST_CullFace(int mode);
+void DPSOFTRAST_AlphaTest(float enable);
+void DPSOFTRAST_AlphaFunc(int alphafunc, float alphavalue);
+void DPSOFTRAST_Color4f(float r, float g, float b, float a);
+void DPSOFTRAST_GetPixelsBGRA(int blockx, int blocky, int blockwidth, int blockheight, unsigned char *outpixels);
+void DPSOFTRAST_CopyRectangleToTexture(int index, int mip, int tx, int ty, int sx, int sy, int width, int height);
+void DPSOFTRAST_SetTexture(int unitnum, int index);
+
+void DPSOFTRAST_SetVertexPointer(const float *vertex3f, size_t stride);
+void DPSOFTRAST_SetColorPointer(const float *color4f, size_t stride);
+void DPSOFTRAST_SetColorPointer4ub(const unsigned char *color4ub, size_t stride);
+void DPSOFTRAST_SetTexCoordPointer(int unitnum, int numcomponents, size_t stride, const float *texcoordf);
+
+typedef enum gl20_texunit_e
+{
+       // postprocess shaders, and generic shaders:
+       GL20TU_FIRST = 0,
+       GL20TU_SECOND = 1,
+       GL20TU_GAMMARAMPS = 2,
+       // standard material properties
+       GL20TU_NORMAL = 0,
+       GL20TU_COLOR = 1,
+       GL20TU_GLOSS = 2,
+       GL20TU_GLOW = 3,
+       // material properties for a second material
+       GL20TU_SECONDARY_NORMAL = 4,
+       GL20TU_SECONDARY_COLOR = 5,
+       GL20TU_SECONDARY_GLOSS = 6,
+       GL20TU_SECONDARY_GLOW = 7,
+       // material properties for a colormapped material
+       // conflicts with secondary material
+       GL20TU_PANTS = 4,
+       GL20TU_SHIRT = 7,
+       // fog fade in the distance
+       GL20TU_FOGMASK = 8,
+       // compiled ambient lightmap and deluxemap
+       GL20TU_LIGHTMAP = 9,
+       GL20TU_DELUXEMAP = 10,
+       // refraction, used by water shaders
+       GL20TU_REFRACTION = 3,
+       // reflection, used by water shaders, also with normal material rendering
+       // conflicts with secondary material
+       GL20TU_REFLECTION = 7,
+       // rtlight attenuation (distance fade) and cubemap filter (projection texturing)
+       // conflicts with lightmap/deluxemap
+       GL20TU_ATTENUATION = 9,
+       GL20TU_CUBE = 10,
+       GL20TU_SHADOWMAP2D = 15,
+       GL20TU_CUBEPROJECTION = 12,
+       // rtlight prepass data (screenspace depth and normalmap)
+       GL20TU_SCREENDEPTH = 13,
+       GL20TU_SCREENNORMALMAP = 14,
+       // lightmap prepass data (screenspace diffuse and specular from lights)
+       GL20TU_SCREENDIFFUSE = 11,
+       GL20TU_SCREENSPECULAR = 12,
+       // fake reflections
+       GL20TU_REFLECTMASK = 5,
+       GL20TU_REFLECTCUBE = 6,
+       GL20TU_FOGHEIGHTTEXTURE = 14
+}
+gl20_texunit;
+
+// this enum selects which of the glslshadermodeinfo entries should be used
+typedef enum shadermode_e
+{
+       SHADERMODE_GENERIC, ///< (particles/HUD/etc) vertex color, optionally multiplied by one texture
+       SHADERMODE_POSTPROCESS, ///< postprocessing shader (r_glsl_postprocess)
+       SHADERMODE_DEPTH_OR_SHADOW, ///< (depthfirst/shadows) vertex shader only
+       SHADERMODE_FLATCOLOR, ///< (lightmap) modulate texture by uniform color (q1bsp, q3bsp)
+       SHADERMODE_VERTEXCOLOR, ///< (lightmap) modulate texture by vertex colors (q3bsp)
+       SHADERMODE_LIGHTMAP, ///< (lightmap) modulate texture by lightmap texture (q1bsp, q3bsp)
+       SHADERMODE_FAKELIGHT, ///< (fakelight) modulate texture by "fake" lighting (no lightmaps, no nothing)
+       SHADERMODE_LIGHTDIRECTIONMAP_MODELSPACE, ///< (lightmap) use directional pixel shading from texture containing modelspace light directions (q3bsp deluxemap)
+       SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE, ///< (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap)
+       SHADERMODE_LIGHTDIRECTION, ///< (lightmap) use directional pixel shading from fixed light direction (q3bsp)
+       SHADERMODE_LIGHTSOURCE, ///< (lightsource) use directional pixel shading from light source (rtlight)
+       SHADERMODE_REFRACTION, ///< refract background (the material is rendered normally after this pass)
+       SHADERMODE_WATER, ///< refract background and reflection (the material is rendered normally after this pass)
+       SHADERMODE_SHOWDEPTH, ///< (debugging) renders depth as color
+       SHADERMODE_DEFERREDGEOMETRY, ///< (deferred) render material properties to screenspace geometry buffers
+       SHADERMODE_DEFERREDLIGHTSOURCE, ///< (deferred) use directional pixel shading from light source (rtlight) on screenspace geometry buffers
+       SHADERMODE_COUNT
+}
+shadermode_t;
+
+typedef enum shaderpermutation_e
+{
+       SHADERPERMUTATION_DIFFUSE = 1<<0, ///< (lightsource) whether to use directional shading
+       SHADERPERMUTATION_VERTEXTEXTUREBLEND = 1<<1, ///< indicates this is a two-layer material blend based on vertex alpha (q3bsp)
+       SHADERPERMUTATION_VIEWTINT = 1<<2, ///< view tint (postprocessing only), use vertex colors (generic only)
+       SHADERPERMUTATION_COLORMAPPING = 1<<3, ///< indicates this is a colormapped skin
+       SHADERPERMUTATION_SATURATION = 1<<4, ///< saturation (postprocessing only)
+       SHADERPERMUTATION_FOGINSIDE = 1<<5, ///< tint the color by fog color or black if using additive blend mode
+       SHADERPERMUTATION_FOGOUTSIDE = 1<<6, ///< tint the color by fog color or black if using additive blend mode
+       SHADERPERMUTATION_FOGHEIGHTTEXTURE = 1<<7, ///< fog color and density determined by texture mapped on vertical axis
+       SHADERPERMUTATION_GAMMARAMPS = 1<<8, ///< gamma (postprocessing only)
+       SHADERPERMUTATION_CUBEFILTER = 1<<9, ///< (lightsource) use cubemap light filter
+       SHADERPERMUTATION_GLOW = 1<<10, ///< (lightmap) blend in an additive glow texture
+       SHADERPERMUTATION_BLOOM = 1<<11, ///< bloom (postprocessing only)
+       SHADERPERMUTATION_SPECULAR = 1<<12, ///< (lightsource or deluxemapping) render specular effects
+       SHADERPERMUTATION_POSTPROCESSING = 1<<13, ///< user defined postprocessing (postprocessing only)
+       SHADERPERMUTATION_REFLECTION = 1<<14, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
+       SHADERPERMUTATION_OFFSETMAPPING = 1<<15, ///< adjust texcoords to roughly simulate a displacement mapped surface
+       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<16, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+       SHADERPERMUTATION_SHADOWMAP2D = 1<<17, ///< (lightsource) use shadowmap texture as light filter
+       SHADERPERMUTATION_SHADOWMAPPCF = 1<<18, ///< (lightsource) use percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<19, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWSAMPLER = 1<<20, ///< (lightsource) use hardware shadowmap test
+       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<21, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
+       SHADERPERMUTATION_SHADOWMAPORTHO = 1<<22, //< (lightsource) use orthographic shadowmap projection
+       SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<23, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
+       SHADERPERMUTATION_ALPHAKILL = 1<<24, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5
+       SHADERPERMUTATION_REFLECTCUBE = 1<<25, ///< fake reflections using global cubemap (not HDRI light probe)
+       SHADERPERMUTATION_NORMALMAPSCROLLBLEND = 1<<26, // (water) counter-direction normalmaps scrolling
+       SHADERPERMUTATION_LIMIT = 1<<27, ///< size of permutations array
+       SHADERPERMUTATION_COUNT = 27 ///< size of shaderpermutationinfo array
+}
+shaderpermutation_t;
+
+typedef enum DPSOFTRAST_UNIFORM_e
+{
+       DPSOFTRAST_UNIFORM_Texture_First,
+       DPSOFTRAST_UNIFORM_Texture_Second,
+       DPSOFTRAST_UNIFORM_Texture_GammaRamps,
+       DPSOFTRAST_UNIFORM_Texture_Normal,
+       DPSOFTRAST_UNIFORM_Texture_Color,
+       DPSOFTRAST_UNIFORM_Texture_Gloss,
+       DPSOFTRAST_UNIFORM_Texture_Glow,
+       DPSOFTRAST_UNIFORM_Texture_SecondaryNormal,
+       DPSOFTRAST_UNIFORM_Texture_SecondaryColor,
+       DPSOFTRAST_UNIFORM_Texture_SecondaryGloss,
+       DPSOFTRAST_UNIFORM_Texture_SecondaryGlow,
+       DPSOFTRAST_UNIFORM_Texture_Pants,
+       DPSOFTRAST_UNIFORM_Texture_Shirt,
+       DPSOFTRAST_UNIFORM_Texture_FogHeightTexture,
+       DPSOFTRAST_UNIFORM_Texture_FogMask,
+       DPSOFTRAST_UNIFORM_Texture_Lightmap,
+       DPSOFTRAST_UNIFORM_Texture_Deluxemap,
+       DPSOFTRAST_UNIFORM_Texture_Attenuation,
+       DPSOFTRAST_UNIFORM_Texture_Cube,
+       DPSOFTRAST_UNIFORM_Texture_Refraction,
+       DPSOFTRAST_UNIFORM_Texture_Reflection,
+       DPSOFTRAST_UNIFORM_Texture_ShadowMap2D,
+       DPSOFTRAST_UNIFORM_Texture_CubeProjection,
+       DPSOFTRAST_UNIFORM_Texture_ScreenDepth,
+       DPSOFTRAST_UNIFORM_Texture_ScreenNormalMap,
+       DPSOFTRAST_UNIFORM_Texture_ScreenDiffuse,
+       DPSOFTRAST_UNIFORM_Texture_ScreenSpecular,
+       DPSOFTRAST_UNIFORM_Texture_ReflectMask,
+       DPSOFTRAST_UNIFORM_Texture_ReflectCube,
+       DPSOFTRAST_UNIFORM_Alpha,
+       DPSOFTRAST_UNIFORM_BloomBlur_Parameters,
+       DPSOFTRAST_UNIFORM_ClientTime,
+       DPSOFTRAST_UNIFORM_Color_Ambient,
+       DPSOFTRAST_UNIFORM_Color_Diffuse,
+       DPSOFTRAST_UNIFORM_Color_Specular,
+       DPSOFTRAST_UNIFORM_Color_Glow,
+       DPSOFTRAST_UNIFORM_Color_Pants,
+       DPSOFTRAST_UNIFORM_Color_Shirt,
+       DPSOFTRAST_UNIFORM_DeferredColor_Ambient,
+       DPSOFTRAST_UNIFORM_DeferredColor_Diffuse,
+       DPSOFTRAST_UNIFORM_DeferredColor_Specular,
+       DPSOFTRAST_UNIFORM_DeferredMod_Diffuse,
+       DPSOFTRAST_UNIFORM_DeferredMod_Specular,
+       DPSOFTRAST_UNIFORM_DistortScaleRefractReflect,
+       DPSOFTRAST_UNIFORM_EyePosition,
+       DPSOFTRAST_UNIFORM_FogColor,
+       DPSOFTRAST_UNIFORM_FogHeightFade,
+       DPSOFTRAST_UNIFORM_FogPlane,
+       DPSOFTRAST_UNIFORM_FogPlaneViewDist,
+       DPSOFTRAST_UNIFORM_FogRangeRecip,
+       DPSOFTRAST_UNIFORM_LightColor,
+       DPSOFTRAST_UNIFORM_LightDir,
+       DPSOFTRAST_UNIFORM_LightPosition,
+       DPSOFTRAST_UNIFORM_OffsetMapping_Scale,
+       DPSOFTRAST_UNIFORM_PixelSize,
+       DPSOFTRAST_UNIFORM_ReflectColor,
+       DPSOFTRAST_UNIFORM_ReflectFactor,
+       DPSOFTRAST_UNIFORM_ReflectOffset,
+       DPSOFTRAST_UNIFORM_RefractColor,
+       DPSOFTRAST_UNIFORM_Saturation,
+       DPSOFTRAST_UNIFORM_ScreenCenterRefractReflect,
+       DPSOFTRAST_UNIFORM_ScreenScaleRefractReflect,
+       DPSOFTRAST_UNIFORM_ScreenToDepth,
+       DPSOFTRAST_UNIFORM_ShadowMap_Parameters,
+       DPSOFTRAST_UNIFORM_ShadowMap_TextureScale,
+       DPSOFTRAST_UNIFORM_SpecularPower,
+       DPSOFTRAST_UNIFORM_UserVec1,
+       DPSOFTRAST_UNIFORM_UserVec2,
+       DPSOFTRAST_UNIFORM_UserVec3,
+       DPSOFTRAST_UNIFORM_UserVec4,
+       DPSOFTRAST_UNIFORM_ViewTintColor,
+       DPSOFTRAST_UNIFORM_ViewToLightM1,
+       DPSOFTRAST_UNIFORM_ViewToLightM2,
+       DPSOFTRAST_UNIFORM_ViewToLightM3,
+       DPSOFTRAST_UNIFORM_ViewToLightM4,
+       DPSOFTRAST_UNIFORM_ModelToLightM1,
+       DPSOFTRAST_UNIFORM_ModelToLightM2,
+       DPSOFTRAST_UNIFORM_ModelToLightM3,
+       DPSOFTRAST_UNIFORM_ModelToLightM4,
+       DPSOFTRAST_UNIFORM_TexMatrixM1,
+       DPSOFTRAST_UNIFORM_TexMatrixM2,
+       DPSOFTRAST_UNIFORM_TexMatrixM3,
+       DPSOFTRAST_UNIFORM_TexMatrixM4,
+       DPSOFTRAST_UNIFORM_BackgroundTexMatrixM1,
+       DPSOFTRAST_UNIFORM_BackgroundTexMatrixM2,
+       DPSOFTRAST_UNIFORM_BackgroundTexMatrixM3,
+       DPSOFTRAST_UNIFORM_BackgroundTexMatrixM4,
+       DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1,
+       DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM2,
+       DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM3,
+       DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM4,
+       DPSOFTRAST_UNIFORM_ModelViewMatrixM1,
+       DPSOFTRAST_UNIFORM_ModelViewMatrixM2,
+       DPSOFTRAST_UNIFORM_ModelViewMatrixM3,
+       DPSOFTRAST_UNIFORM_ModelViewMatrixM4,
+       DPSOFTRAST_UNIFORM_PixelToScreenTexCoord,
+       DPSOFTRAST_UNIFORM_ModelToReflectCubeM1,
+       DPSOFTRAST_UNIFORM_ModelToReflectCubeM2,
+       DPSOFTRAST_UNIFORM_ModelToReflectCubeM3,
+       DPSOFTRAST_UNIFORM_ModelToReflectCubeM4,
+       DPSOFTRAST_UNIFORM_ShadowMapMatrixM1,
+       DPSOFTRAST_UNIFORM_ShadowMapMatrixM2,
+       DPSOFTRAST_UNIFORM_ShadowMapMatrixM3,
+       DPSOFTRAST_UNIFORM_ShadowMapMatrixM4,
+       DPSOFTRAST_UNIFORM_BloomColorSubtract,
+       DPSOFTRAST_UNIFORM_NormalmapScrollBlend,
+       DPSOFTRAST_UNIFORM_TOTAL
+}
+DPSOFTRAST_UNIFORM;
+
+void DPSOFTRAST_SetShader(unsigned int mode, unsigned int permutation);
+#define DPSOFTRAST_Uniform1fARB(index, v0) DPSOFTRAST_Uniform4fARB(index, v0, 0, 0, 0)
+#define DPSOFTRAST_Uniform2fARB(index, v0, v1) DPSOFTRAST_Uniform4fARB(index, v0, v1, 0, 0)
+#define DPSOFTRAST_Uniform3fARB(index, v0, v1, v2) DPSOFTRAST_Uniform4fARB(index, v0, v1, v2, 0)
+void DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM index, float v0, float v1, float v2, float v3);
+void DPSOFTRAST_Uniform4fvARB(DPSOFTRAST_UNIFORM index, const float *v);
+void DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM index, int arraysize, int transpose, const float *v);
+void DPSOFTRAST_Uniform1iARB(DPSOFTRAST_UNIFORM index, int i0);
+
+void DPSOFTRAST_DrawTriangles(int firstvertex, int numvertices, int numtriangles, const int *element3i, const unsigned short *element3s);
+
+#endif // DPSOFTRAST_H
index f2161b1..c81b3ed 100644 (file)
@@ -1,6 +1,7 @@
 
 #include "quakedef.h"
 #include "cl_collision.h"
+#include "dpsoftrast.h"
 #ifdef SUPPORTD3D
 #include <d3d9.h>
 extern LPDIRECT3DDEVICE9 vid_d3d9dev;
@@ -289,6 +290,12 @@ static void R_Mesh_SetUseVBO(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               gl_state.usevbo_staticvertex = false;
+               gl_state.usevbo_staticindex = false;
+               gl_state.usevbo_dynamicvertex = false;
+               gl_state.usevbo_dynamicindex = false;
+               break;
        }
 }
 
@@ -326,6 +333,8 @@ static void gl_backend_start(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -352,6 +361,8 @@ static void gl_backend_shutdown(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 
        if (gl_state.preparevertices_tempdata)
@@ -396,6 +407,8 @@ static void gl_backend_devicelost(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
        endindex = Mem_ExpandableArray_IndexRange(&gl_state.meshbufferarray);
        for (i = 0;i < endindex;i++)
@@ -428,6 +441,8 @@ static void gl_backend_devicelost(void)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -453,6 +468,8 @@ static void gl_backend_devicerestored(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -664,6 +681,7 @@ qboolean R_ScissorForBBox(const float *mins, const float *maxs, int *scissor)
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_CGGL:
+       case RENDERPATH_SOFT:
                break;
        }
 
@@ -746,6 +764,7 @@ void R_Viewport_InitOrtho(r_viewport_t *v, const matrix4x4_t *cameramatrix, int
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_CGGL:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
@@ -995,6 +1014,7 @@ void R_Viewport_InitRectSideView(r_viewport_t *v, const matrix4x4_t *cameramatri
        case RENDERPATH_CGGL:
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_D3D9:
                m[5] *= -1;
@@ -1062,6 +1082,9 @@ void R_SetViewport(const r_viewport_t *v)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_Viewport(v->x, v->y, v->width, v->height);
+               break;
        }
 
        // force an update of the derived matrices
@@ -1117,6 +1140,8 @@ int R_Mesh_CreateFramebufferObject(rtexture_t *depthtexture, rtexture_t *colorte
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                return 1;
+       case RENDERPATH_SOFT:
+               return 1;
        }
        return 0;
 }
@@ -1136,6 +1161,8 @@ void R_Mesh_DestroyFramebufferObject(int fbo)
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -1200,6 +1227,9 @@ void R_Mesh_ResetRenderTargets(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_SetRenderTargets(vid.width, vid.height, vid.softdepthpixels, vid.softpixels, NULL, NULL, NULL);
+               break;
        }
 }
 
@@ -1259,6 +1289,21 @@ void R_Mesh_SetRenderTargets(int fbo, rtexture_t *depthtexture, rtexture_t *colo
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (fbo)
+               {
+                       int width, height;
+                       unsigned int *pointers[5];
+                       memset(pointers, 0, sizeof(pointers));
+                       for (i = 0;i < 5;i++)
+                               pointers[i] = textures[i] ? (unsigned int *)DPSOFTRAST_Texture_GetPixelPointer(textures[i]->texnum, 0) : NULL;
+                       width = DPSOFTRAST_Texture_GetWidth(textures[0] ? textures[0]->texnum : textures[4]->texnum, 0);
+                       height = DPSOFTRAST_Texture_GetHeight(textures[0] ? textures[0]->texnum : textures[4]->texnum, 0);
+                       DPSOFTRAST_SetRenderTargets(width, height, pointers[4], pointers[0], pointers[1], pointers[2], pointers[3]);
+               }
+               else
+                       DPSOFTRAST_SetRenderTargets(vid.width, vid.height, vid.softdepthpixels, vid.softpixels, NULL, NULL, NULL);
+               break;
        }
 }
 
@@ -1426,7 +1471,7 @@ static void GL_Backend_ResetState(void)
 
                if (vid.support.ext_framebuffer_object)
                {
-                       qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+                       //qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
                        qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
                }
 
@@ -1468,6 +1513,17 @@ static void GL_Backend_ResetState(void)
                }
                CHECKGLERROR
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_ColorMask(1,1,1,1);
+               DPSOFTRAST_AlphaTest(gl_state.alphatest);
+               DPSOFTRAST_BlendFunc(gl_state.blendfunc1, gl_state.blendfunc2);
+               DPSOFTRAST_CullFace(gl_state.cullface);
+               DPSOFTRAST_DepthFunc(gl_state.depthfunc);
+               DPSOFTRAST_DepthMask(gl_state.depthmask);
+               DPSOFTRAST_PolygonOffset(gl_state.polygonoffset[0], gl_state.polygonoffset[1]);
+               DPSOFTRAST_SetRenderTargets(vid.width, vid.height, vid.softdepthpixels, vid.softpixels, NULL, NULL, NULL);
+               DPSOFTRAST_Viewport(0, 0, vid.width, vid.height);
+               break;
        }
 }
 
@@ -1493,6 +1549,8 @@ void GL_ActiveTexture(unsigned int num)
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -1519,6 +1577,8 @@ void GL_ClientActiveTexture(unsigned int num)
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -1592,6 +1652,9 @@ void GL_BlendFunc(int blendfunc1, int blendfunc2)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_BlendFunc(gl_state.blendfunc1, gl_state.blendfunc2);
+                       break;
                }
        }
 }
@@ -1621,6 +1684,9 @@ void GL_DepthMask(int state)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_DepthMask(gl_state.depthmask);
+                       break;
                }
        }
 }
@@ -1657,6 +1723,9 @@ void GL_DepthTest(int state)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_DepthTest(gl_state.depthtest);
+                       break;
                }
        }
 }
@@ -1686,6 +1755,9 @@ void GL_DepthFunc(int state)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_DepthFunc(gl_state.depthfunc);
+                       break;
                }
        }
 }
@@ -1724,6 +1796,9 @@ void GL_DepthRange(float nearfrac, float farfrac)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_DepthRange(gl_state.depthrange[0], gl_state.depthrange[1]);
+                       break;
                }
        }
 }
@@ -1788,6 +1863,9 @@ void R_SetStencilSeparate(qboolean enable, int writemask, int frontfail, int fro
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 }
 
@@ -1837,6 +1915,9 @@ void R_SetStencil(qboolean enable, int writemask, int fail, int zfail, int zpass
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 }
 
@@ -1866,6 +1947,9 @@ void GL_PolygonOffset(float planeoffset, float depthoffset)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_PolygonOffset(gl_state.polygonoffset[0], gl_state.polygonoffset[1]);
+                       break;
                }
        }
 }
@@ -1900,6 +1984,9 @@ void GL_SetMirrorState(qboolean state)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_CullFace(gl_state.cullface);
+                       break;
                }
        }
 }
@@ -1973,6 +2060,9 @@ void GL_CullFace(int state)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_CullFace(gl_state.cullface);
+               break;
        }
 }
 
@@ -2008,6 +2098,9 @@ void GL_AlphaTest(int state)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_AlphaTest(gl_state.alphatest);
+                       break;
                }
        }
 }
@@ -2039,6 +2132,9 @@ void GL_AlphaFunc(int state, float value)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_AlphaFunc(gl_state.alphafunc, gl_state.alphafuncvalue);
+                       break;
                }
        }
 }
@@ -2070,6 +2166,9 @@ void GL_ColorMask(int r, int g, int b, int a)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_ColorMask(r, g, b, a);
+                       break;
                }
        }
 }
@@ -2097,6 +2196,9 @@ void GL_Color(float cr, float cg, float cb, float ca)
                case RENDERPATH_D3D11:
                        // no equivalent in D3D
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_Color4f(cr, cg, cb, ca);
+                       break;
                }
        }
 }
@@ -2131,6 +2233,9 @@ void GL_Scissor (int x, int y, int width, int height)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_Scissor(x, y, width, height);
+               break;
        }
 }
 
@@ -2163,6 +2268,9 @@ void GL_ScissorTest(int state)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_ScissorTest(gl_state.scissortest);
+                       break;
                }
        }
 }
@@ -2210,6 +2318,12 @@ void GL_Clear(int mask, const float *colorvalue, float depthvalue, int stencilva
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (mask & GL_COLOR_BUFFER_BIT)
+                       DPSOFTRAST_ClearColor(colorvalue[0], colorvalue[1], colorvalue[2], colorvalue[3]);
+               if (mask & GL_DEPTH_BUFFER_BIT)
+                       DPSOFTRAST_ClearDepth(depthvalue);
+               break;
        }
 }
 
@@ -2266,6 +2380,9 @@ void GL_ReadPixelsBGRA(int x, int y, int width, int height, unsigned char *outpi
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_GetPixelsBGRA(x, y, width, height, outpixels);
+               break;
        }
 }
 
@@ -2426,6 +2543,8 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
        // upload a dynamic index buffer if needed
        if (element3s)
@@ -2738,11 +2857,11 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                        else
                        {
                                if (element3s)
-                                       IDirect3DDevice9_DrawIndexedPrimitiveUP(vid_d3d9dev, D3DPT_TRIANGLELIST, firstvertex, numvertices, numtriangles, element3s + firsttriangle*3, D3DFMT_INDEX16, gl_state.d3dvertexdata, gl_state.d3dvertexsize);
+                                       IDirect3DDevice9_DrawIndexedPrimitiveUP(vid_d3d9dev, D3DPT_TRIANGLELIST, firstvertex, numvertices, numtriangles, element3s, D3DFMT_INDEX16, gl_state.d3dvertexdata, gl_state.d3dvertexsize);
                                else if (element3i)
-                                       IDirect3DDevice9_DrawIndexedPrimitiveUP(vid_d3d9dev, D3DPT_TRIANGLELIST, firstvertex, numvertices, numtriangles, element3i + firsttriangle*3, D3DFMT_INDEX32, gl_state.d3dvertexdata, gl_state.d3dvertexsize);
+                                       IDirect3DDevice9_DrawIndexedPrimitiveUP(vid_d3d9dev, D3DPT_TRIANGLELIST, firstvertex, numvertices, numtriangles, element3i, D3DFMT_INDEX32, gl_state.d3dvertexdata, gl_state.d3dvertexsize);
                                else
-                                       IDirect3DDevice9_DrawPrimitiveUP(vid_d3d9dev, D3DPT_TRIANGLELIST, numvertices, (void *) ((byte *) gl_state.d3dvertexdata + (numvertices * gl_state.d3dvertexsize)), gl_state.d3dvertexsize);
+                                       IDirect3DDevice9_DrawPrimitiveUP(vid_d3d9dev, D3DPT_TRIANGLELIST, numvertices, (void *)gl_state.d3dvertexdata, gl_state.d3dvertexsize);
                        }
 #endif
                        break;
@@ -2752,6 +2871,9 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_DrawTriangles(firstvertex, numvertices, numtriangles, element3i, element3s);
+                       break;
                }
        }
 }
@@ -2866,6 +2988,8 @@ void R_Mesh_UpdateMeshBuffer(r_meshbuffer_t *buffer, const void *data, size_t si
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -2901,6 +3025,8 @@ void R_Mesh_DestroyMeshBuffer(r_meshbuffer_t *buffer)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
        Mem_ExpandableArray_FreeRecord(&gl_state.meshbufferarray, (void *)buffer);
 }
@@ -3085,6 +3211,9 @@ void R_Mesh_CopyToTexture(rtexture_t *tex, int tx, int ty, int sx, int sy, int w
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_CopyRectangleToTexture(tex->texnum, 0, tx, ty, sx, sy, width, height);
+               break;
        }
 }
 
@@ -3244,6 +3373,19 @@ void R_Mesh_TexBind(unsigned int unitnum, rtexture_t *tex)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (!tex)
+               {
+                       tex = r_texture_white;
+                       // not initialized enough yet...
+                       if (!tex)
+                               return;
+               }
+               if (unit->texture == tex)
+                       return;
+               unit->texture = tex;
+               DPSOFTRAST_SetTexture(unitnum, R_GetTexture(tex));
+               break;
        }
 }
 
@@ -3291,6 +3433,8 @@ void R_Mesh_TexMatrix(unsigned int unitnum, const matrix4x4_t *matrix)
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -3375,6 +3519,8 @@ void R_Mesh_TexCombine(unsigned int unitnum, int combinergb, int combinealpha, i
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -3490,6 +3636,8 @@ void R_Mesh_ResetTextureState(void)
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -3555,6 +3703,8 @@ static void R_Mesh_InitVertexDeclarations(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 #endif
 }
@@ -3660,6 +3810,15 @@ void R_Mesh_PrepareVertices_Vertex3f(int numvertices, const float *vertex3f, con
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_SetVertexPointer(vertex3f, sizeof(float[3]));
+               DPSOFTRAST_SetColorPointer(NULL, 0);
+               DPSOFTRAST_SetTexCoordPointer(0, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(1, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(2, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(3, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(4, 2, sizeof(float[2]), NULL);
+               break;
        }
 }
 
@@ -3725,6 +3884,15 @@ void R_Mesh_PrepareVertices_Generic_Arrays(int numvertices, const float *vertex3
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_SetVertexPointer(vertex3f, sizeof(float[3]));
+               DPSOFTRAST_SetColorPointer(color4f, sizeof(float[4]));
+               DPSOFTRAST_SetTexCoordPointer(0, 2, sizeof(float[2]), texcoord2f);
+               DPSOFTRAST_SetTexCoordPointer(1, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(2, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(3, 2, sizeof(float[2]), NULL);
+               DPSOFTRAST_SetTexCoordPointer(4, 2, sizeof(float[2]), NULL);
+               return;
        }
 
        // no quick path for this case, convert to vertex structs
@@ -3841,6 +4009,15 @@ void R_Mesh_PrepareVertices_Generic(int numvertices, const r_vertexgeneric_t *ve
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_SetVertexPointer(vertex->vertex3f, sizeof(*vertex));
+               DPSOFTRAST_SetColorPointer4ub(vertex->color4ub, sizeof(*vertex));
+               DPSOFTRAST_SetTexCoordPointer(0, 2, sizeof(*vertex), vertex->texcoord2f);
+               DPSOFTRAST_SetTexCoordPointer(1, 2, sizeof(*vertex), NULL);
+               DPSOFTRAST_SetTexCoordPointer(2, 2, sizeof(*vertex), NULL);
+               DPSOFTRAST_SetTexCoordPointer(3, 2, sizeof(*vertex), NULL);
+               DPSOFTRAST_SetTexCoordPointer(4, 2, sizeof(*vertex), NULL);
+               break;
        }
 }
 
@@ -3906,6 +4083,15 @@ void R_Mesh_PrepareVertices_Mesh_Arrays(int numvertices, const float *vertex3f,
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_SetVertexPointer(vertex3f, sizeof(float[3]));
+               DPSOFTRAST_SetColorPointer(color4f, sizeof(float[4]));
+               DPSOFTRAST_SetTexCoordPointer(0, 2, sizeof(float[2]), texcoordtexture2f);
+               DPSOFTRAST_SetTexCoordPointer(1, 3, sizeof(float[3]), svector3f);
+               DPSOFTRAST_SetTexCoordPointer(2, 3, sizeof(float[3]), tvector3f);
+               DPSOFTRAST_SetTexCoordPointer(3, 3, sizeof(float[3]), normal3f);
+               DPSOFTRAST_SetTexCoordPointer(4, 2, sizeof(float[2]), texcoordlightmap2f);
+               return;
        }
 
        vertex = R_Mesh_PrepareVertices_Mesh_Lock(numvertices);
@@ -4033,5 +4219,14 @@ void R_Mesh_PrepareVertices_Mesh(int numvertices, const r_vertexmesh_t *vertex,
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_SetVertexPointer(vertex->vertex3f, sizeof(*vertex));
+               DPSOFTRAST_SetColorPointer4ub(vertex->color4ub, sizeof(*vertex));
+               DPSOFTRAST_SetTexCoordPointer(0, 2, sizeof(*vertex), vertex->texcoordtexture2f);
+               DPSOFTRAST_SetTexCoordPointer(1, 3, sizeof(*vertex), vertex->svector3f);
+               DPSOFTRAST_SetTexCoordPointer(2, 3, sizeof(*vertex), vertex->tvector3f);
+               DPSOFTRAST_SetTexCoordPointer(3, 3, sizeof(*vertex), vertex->normal3f);
+               DPSOFTRAST_SetTexCoordPointer(4, 2, sizeof(*vertex), vertex->texcoordlightmap2f);
+               break;
        }
 }
index 04d3eb8..d0b132d 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -1938,6 +1938,9 @@ void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 }
 
@@ -1977,6 +1980,9 @@ void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, f
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 }
 
@@ -2019,6 +2025,9 @@ void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 }
 
@@ -2074,6 +2083,8 @@ void R_DrawGamma(void)
                if (vid_usinghwgamma)
                        return;
                break;
+       case RENDERPATH_SOFT:
+               return;
        }
        // all the blends ignore depth
 //     R_Mesh_ResetTextureState();
index 247f865..05cee8e 100644 (file)
@@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "ft2.h"
 #include "csprogs.h"
 #include "cl_video.h"
+#include "dpsoftrast.h"
 
 #ifdef SUPPORTD3D
 #include <d3d9.h>
@@ -3342,40 +3343,6 @@ typedef struct shadermodeinfo_s
 }
 shadermodeinfo_t;
 
-typedef enum shaderpermutation_e
-{
-       SHADERPERMUTATION_DIFFUSE = 1<<0, ///< (lightsource) whether to use directional shading
-       SHADERPERMUTATION_VERTEXTEXTUREBLEND = 1<<1, ///< indicates this is a two-layer material blend based on vertex alpha (q3bsp)
-       SHADERPERMUTATION_VIEWTINT = 1<<2, ///< view tint (postprocessing only), use vertex colors (generic only)
-       SHADERPERMUTATION_COLORMAPPING = 1<<3, ///< indicates this is a colormapped skin
-       SHADERPERMUTATION_SATURATION = 1<<4, ///< saturation (postprocessing only)
-       SHADERPERMUTATION_FOGINSIDE = 1<<5, ///< tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_FOGOUTSIDE = 1<<6, ///< tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_FOGHEIGHTTEXTURE = 1<<7, ///< fog color and density determined by texture mapped on vertical axis
-       SHADERPERMUTATION_GAMMARAMPS = 1<<8, ///< gamma (postprocessing only)
-       SHADERPERMUTATION_CUBEFILTER = 1<<9, ///< (lightsource) use cubemap light filter
-       SHADERPERMUTATION_GLOW = 1<<10, ///< (lightmap) blend in an additive glow texture
-       SHADERPERMUTATION_BLOOM = 1<<11, ///< bloom (postprocessing only)
-       SHADERPERMUTATION_SPECULAR = 1<<12, ///< (lightsource or deluxemapping) render specular effects
-       SHADERPERMUTATION_POSTPROCESSING = 1<<13, ///< user defined postprocessing (postprocessing only)
-       SHADERPERMUTATION_REFLECTION = 1<<14, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-       SHADERPERMUTATION_OFFSETMAPPING = 1<<15, ///< adjust texcoords to roughly simulate a displacement mapped surface
-       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<16, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-       SHADERPERMUTATION_SHADOWMAP2D = 1<<17, ///< (lightsource) use shadowmap texture as light filter
-       SHADERPERMUTATION_SHADOWMAPPCF = 1<<18, ///< (lightsource) use percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<19, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWSAMPLER = 1<<20, ///< (lightsource) use hardware shadowmap test
-       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<21, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
-       SHADERPERMUTATION_SHADOWMAPORTHO = 1<<22, //< (lightsource) use orthographic shadowmap projection
-       SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<23, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
-       SHADERPERMUTATION_ALPHAKILL = 1<<24, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5
-       SHADERPERMUTATION_REFLECTCUBE = 1<<25, ///< fake reflections using global cubemap (not HDRI light probe)
-       SHADERPERMUTATION_NORMALMAPSCROLLBLEND = 1<<26, // (water) counter-direction normalmaps scrolling
-       SHADERPERMUTATION_LIMIT = 1<<27, ///< size of permutations array
-       SHADERPERMUTATION_COUNT = 27 ///< size of shaderpermutationinfo array
-}
-shaderpermutation_t;
-
 // NOTE: MUST MATCH ORDER OF SHADERPERMUTATION_* DEFINES!
 shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
 {
@@ -3408,29 +3375,6 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"},
 };
 
-// this enum selects which of the glslshadermodeinfo entries should be used
-typedef enum shadermode_e
-{
-       SHADERMODE_GENERIC, ///< (particles/HUD/etc) vertex color, optionally multiplied by one texture
-       SHADERMODE_POSTPROCESS, ///< postprocessing shader (r_glsl_postprocess)
-       SHADERMODE_DEPTH_OR_SHADOW, ///< (depthfirst/shadows) vertex shader only
-       SHADERMODE_FLATCOLOR, ///< (lightmap) modulate texture by uniform color (q1bsp, q3bsp)
-       SHADERMODE_VERTEXCOLOR, ///< (lightmap) modulate texture by vertex colors (q3bsp)
-       SHADERMODE_LIGHTMAP, ///< (lightmap) modulate texture by lightmap texture (q1bsp, q3bsp)
-       SHADERMODE_FAKELIGHT, ///< (fakelight) modulate texture by "fake" lighting (no lightmaps, no nothing)
-       SHADERMODE_LIGHTDIRECTIONMAP_MODELSPACE, ///< (lightmap) use directional pixel shading from texture containing modelspace light directions (q3bsp deluxemap)
-       SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE, ///< (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap)
-       SHADERMODE_LIGHTDIRECTION, ///< (lightmap) use directional pixel shading from fixed light direction (q3bsp)
-       SHADERMODE_LIGHTSOURCE, ///< (lightsource) use directional pixel shading from light source (rtlight)
-       SHADERMODE_REFRACTION, ///< refract background (the material is rendered normally after this pass)
-       SHADERMODE_WATER, ///< refract background and reflection (the material is rendered normally after this pass)
-       SHADERMODE_SHOWDEPTH, ///< (debugging) renders depth as color
-       SHADERMODE_DEFERREDGEOMETRY, ///< (deferred) render material properties to screenspace geometry buffers
-       SHADERMODE_DEFERREDLIGHTSOURCE, ///< (deferred) use directional pixel shading from light source (rtlight) on screenspace geometry buffers
-       SHADERMODE_COUNT
-}
-shadermode_t;
-
 // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS!
 shadermodeinfo_t glslshadermodeinfo[SHADERMODE_COUNT] =
 {
@@ -4990,6 +4934,14 @@ void R_SetupShader_SetPermutationHLSL(unsigned int mode, unsigned int permutatio
 }
 #endif
 
+void R_SetupShader_SetPermutationSoft(unsigned int mode, unsigned int permutation)
+{
+       DPSOFTRAST_SetShader(mode, permutation);
+       DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f);
+       DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ModelViewMatrixM1, 1, false, gl_modelview16f);
+       DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_ClientTime, cl.time);
+}
+
 void R_GLSL_Restart_f(void)
 {
        unsigned int i, limit;
@@ -5079,6 +5031,8 @@ void R_GLSL_Restart_f(void)
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -5185,6 +5139,11 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
        case RENDERPATH_GL11:
                R_Mesh_TexBind(0, first );
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationSoft(SHADERMODE_GENERIC, SHADERPERMUTATION_VIEWTINT | (first ? SHADERPERMUTATION_DIFFUSE : 0) | (second ? SHADERPERMUTATION_SPECULAR : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
+               R_Mesh_TexBind(GL20TU_FIRST , first );
+               R_Mesh_TexBind(GL20TU_SECOND, second);
+               break;
        }
 }
 
@@ -5218,6 +5177,9 @@ void R_SetupShader_DepthOrShadow(void)
        case RENDERPATH_GL11:
                R_Mesh_TexBind(0, 0);
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationSoft(SHADERMODE_DEPTH_OR_SHADOW, 0);
+               break;
        }
 }
 
@@ -5248,6 +5210,9 @@ void R_SetupShader_ShowDepth(void)
                break;
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationSoft(SHADERMODE_SHOWDEPTH, 0);
+               break;
        }
 }
 
@@ -5739,7 +5704,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                }
                else
                {
-                       if (mode == SHADERMODE_LIGHTDIRECTION)                                   
+                       if (mode == SHADERMODE_LIGHTDIRECTION)
                        {
                                hlslVSSetParameter3f(D3DVSREGISTER_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
                        }
@@ -6195,6 +6160,138 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_ARRAY_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+               R_Mesh_PrepareVertices_Mesh_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchsvector3f, rsurface.batchtvector3f, rsurface.batchnormal3f, rsurface.batchlightmapcolor4f, rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordlightmap2f);
+               R_SetupShader_SetPermutationSoft(mode, permutation);
+               {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ModelToReflectCubeM1, 1, false, m16f);}
+               if (mode == SHADERMODE_LIGHTSOURCE)
+               {
+                       {Matrix4x4_ToArrayFloatGL(&rsurface.entitytolight, m16f);DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ModelToLightM1, 1, false, m16f);}
+                       DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);
+                       DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+                       DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Ambient, colormod[0] * ambientscale, colormod[1] * ambientscale, colormod[2] * ambientscale);
+                       DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Diffuse, colormod[0] * diffusescale, colormod[1] * diffusescale, colormod[2] * diffusescale);
+                       DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Specular, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale);
+       
+                       // additive passes are only darkened by fog, not tinted
+                       DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_FogColor, 0, 0, 0);
+                       DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               }
+               else
+               {
+                       if (mode == SHADERMODE_FLATCOLOR)
+                       {
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Ambient, colormod[0], colormod[1], colormod[2]);
+                       }
+                       else if (mode == SHADERMODE_LIGHTDIRECTION)
+                       {
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Ambient, (r_refdef.scene.ambient + rsurface.modellight_ambient[0] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[0], (r_refdef.scene.ambient + rsurface.modellight_ambient[1] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[1], (r_refdef.scene.ambient + rsurface.modellight_ambient[2] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[2]);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Diffuse, r_refdef.lightmapintensity * colormod[0], r_refdef.lightmapintensity * colormod[1], r_refdef.lightmapintensity * colormod[2]);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_DeferredMod_Diffuse, colormod[0] * r_shadow_deferred_8bitrange.value, colormod[1] * r_shadow_deferred_8bitrange.value, colormod[2] * r_shadow_deferred_8bitrange.value);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_LightColor, rsurface.modellight_diffuse[0] * r_refdef.scene.rtlightstylevalue[0], rsurface.modellight_diffuse[1] * r_refdef.scene.rtlightstylevalue[0], rsurface.modellight_diffuse[2] * r_refdef.scene.rtlightstylevalue[0]);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
+                       }
+                       else
+                       {
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Ambient, r_refdef.scene.ambient * colormod[0], r_refdef.scene.ambient * colormod[1], r_refdef.scene.ambient * colormod[2]);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Diffuse, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2]);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_DeferredMod_Diffuse, colormod[0] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[1] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[2] * diffusescale * r_shadow_deferred_8bitrange.value);
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                       }
+                       // additive passes are only darkened by fog, not tinted
+                       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ADD)
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_FogColor, 0, 0, 0);
+                       else
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
+                       DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_ReflectOffset, rsurface.texture->reflectmin);
+                       DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
+               }
+               {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_TexMatrixM1, 1, false, m16f);}
+               {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_BackgroundTexMatrixM1, 1, false, m16f);}
+               {Matrix4x4_ToArrayFloatGL(&r_shadow_shadowmapmatrix, m16f);DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ShadowMapMatrixM1, 1, false, m16f);}
+               DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+
+               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
+               DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
+               if (DPSOFTRAST_UNIFORM_Color_Pants >= 0)
+               {
+                       if (rsurface.texture->pantstexture)
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
+                       else
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Pants, 0, 0, 0);
+               }
+               if (DPSOFTRAST_UNIFORM_Color_Shirt >= 0)
+               {
+                       if (rsurface.texture->shirttexture)
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Shirt, rsurface.colormap_shirtcolor[0], rsurface.colormap_shirtcolor[1], rsurface.colormap_shirtcolor[2]);
+                       else
+                               DPSOFTRAST_Uniform3fARB(DPSOFTRAST_UNIFORM_Color_Shirt, 0, 0, 0);
+               }
+               DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
+               DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_FogPlaneViewDist, rsurface.fogplaneviewdist);
+               DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_FogRangeRecip, rsurface.fograngerecip);
+               DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_FogHeightFade, rsurface.fogheightfade);
+               DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale);
+               DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+
+       //      R_Mesh_TexBind(GL20TU_FIRST             , r_texture_white                                     );
+       //      R_Mesh_TexBind(GL20TU_SECOND            , r_texture_white                                     );
+       //      R_Mesh_TexBind(GL20TU_GAMMARAMPS        , r_texture_gammaramps                                );
+               R_Mesh_TexBind(GL20TU_NORMAL            , rsurface.texture->nmaptexture                       );
+               R_Mesh_TexBind(GL20TU_COLOR             , rsurface.texture->basetexture                       );
+               R_Mesh_TexBind(GL20TU_GLOSS             , rsurface.texture->glosstexture                      );
+               R_Mesh_TexBind(GL20TU_GLOW              , rsurface.texture->glowtexture                       );
+               R_Mesh_TexBind(GL20TU_SECONDARY_NORMAL  , rsurface.texture->backgroundnmaptexture             );
+               R_Mesh_TexBind(GL20TU_SECONDARY_COLOR   , rsurface.texture->backgroundbasetexture             );
+               R_Mesh_TexBind(GL20TU_SECONDARY_GLOSS   , rsurface.texture->backgroundglosstexture            );
+               R_Mesh_TexBind(GL20TU_SECONDARY_GLOW    , rsurface.texture->backgroundglowtexture             );
+               R_Mesh_TexBind(GL20TU_PANTS             , rsurface.texture->pantstexture                      );
+               R_Mesh_TexBind(GL20TU_SHIRT             , rsurface.texture->shirttexture                      );
+               R_Mesh_TexBind(GL20TU_REFLECTMASK       , rsurface.texture->reflectmasktexture                );
+               R_Mesh_TexBind(GL20TU_REFLECTCUBE       , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);
+               R_Mesh_TexBind(GL20TU_FOGHEIGHTTEXTURE  , r_texture_fogheighttexture                          );
+               R_Mesh_TexBind(GL20TU_FOGMASK           , r_texture_fogattenuation                            );
+               R_Mesh_TexBind(GL20TU_LIGHTMAP          , rsurface.lightmaptexture ? rsurface.lightmaptexture : r_texture_white);
+               R_Mesh_TexBind(GL20TU_DELUXEMAP         , rsurface.deluxemaptexture ? rsurface.deluxemaptexture : r_texture_blanknormalmap);
+               R_Mesh_TexBind(GL20TU_ATTENUATION       , r_shadow_attenuationgradienttexture                 );
+               if (rsurfacepass == RSURFPASS_BACKGROUND)
+               {
+                       if(DPSOFTRAST_UNIFORM_Texture_Refraction >= 0) R_Mesh_TexBind(GL20TU_REFRACTION        , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
+                       else if(DPSOFTRAST_UNIFORM_Texture_First >= 0) R_Mesh_TexBind(GL20TU_FIRST             , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
+                       if(DPSOFTRAST_UNIFORM_Texture_Reflection >= 0) R_Mesh_TexBind(GL20TU_REFLECTION        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+               }
+               else
+               {
+                       if (permutation & SHADERPERMUTATION_REFLECTION        ) R_Mesh_TexBind(GL20TU_REFLECTION        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+               }
+//             R_Mesh_TexBind(GL20TU_SCREENDEPTH       , r_shadow_prepassgeometrydepthtexture                );
+//             R_Mesh_TexBind(GL20TU_SCREENNORMALMAP   , r_shadow_prepassgeometrynormalmaptexture            );
+               R_Mesh_TexBind(GL20TU_SCREENDIFFUSE     , r_shadow_prepasslightingdiffusetexture              );
+               R_Mesh_TexBind(GL20TU_SCREENSPECULAR    , r_shadow_prepasslightingspeculartexture             );
+               if (rsurface.rtlight || (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW)))
+               {
+                       R_Mesh_TexBind(GL20TU_SHADOWMAP2D, r_shadow_shadowmap2dtexture                         );
+                       if (rsurface.rtlight)
+                       {
+                               R_Mesh_TexBind(GL20TU_CUBE              , rsurface.rtlight->currentcubemap                    );
+                               R_Mesh_TexBind(GL20TU_CUBEPROJECTION    , r_shadow_shadowmapvsdcttexture                      );
+                       }
+               }
+               break;
        }
 }
 
@@ -6317,6 +6414,26 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationGLSL(mode, permutation);
+               DPSOFTRAST_Uniform3fARB(       DPSOFTRAST_UNIFORM_LightPosition            , viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);
+               DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ViewToLightM1            , 1, false, viewtolight16f);
+               DPSOFTRAST_Uniform3fARB(       DPSOFTRAST_UNIFORM_DeferredColor_Ambient    , lightcolorbase[0] * ambientscale  * range, lightcolorbase[1] * ambientscale  * range, lightcolorbase[2] * ambientscale  * range);
+               DPSOFTRAST_Uniform3fARB(       DPSOFTRAST_UNIFORM_DeferredColor_Diffuse    , lightcolorbase[0] * diffusescale  * range, lightcolorbase[1] * diffusescale  * range, lightcolorbase[2] * diffusescale  * range);
+               DPSOFTRAST_Uniform3fARB(       DPSOFTRAST_UNIFORM_DeferredColor_Specular   , lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
+               DPSOFTRAST_Uniform2fARB(       DPSOFTRAST_UNIFORM_ShadowMap_TextureScale   , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               DPSOFTRAST_Uniform4fARB(       DPSOFTRAST_UNIFORM_ShadowMap_Parameters     , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+               DPSOFTRAST_Uniform1fARB(       DPSOFTRAST_UNIFORM_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               DPSOFTRAST_Uniform2fARB(       DPSOFTRAST_UNIFORM_ScreenToDepth            , r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+
+               R_Mesh_TexBind(GL20TU_ATTENUATION        , r_shadow_attenuationgradienttexture                 );
+               R_Mesh_TexBind(GL20TU_SCREENDEPTH        , r_shadow_prepassgeometrydepthtexture                );
+               R_Mesh_TexBind(GL20TU_SCREENNORMALMAP    , r_shadow_prepassgeometrynormalmaptexture            );
+               R_Mesh_TexBind(GL20TU_CUBE               , rsurface.rtlight->currentcubemap                    );
+               R_Mesh_TexBind(GL20TU_SHADOWMAP2D        , r_shadow_shadowmap2dtexture                         );
+               R_Mesh_TexBind(GL20TU_CUBEPROJECTION     , r_shadow_shadowmapvsdcttexture                      );
+               break;
        }
 }
 
@@ -7160,6 +7277,7 @@ void gl_main_start(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                Cvar_SetValueQuick(&r_textureunits, vid.texunits);
                Cvar_SetValueQuick(&gl_combine, 1);
                Cvar_SetValueQuick(&r_glsl, 1);
@@ -7260,6 +7378,8 @@ void gl_main_shutdown(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 
        r_numqueries = 0;
@@ -7881,6 +8001,8 @@ void R_AnimCache_CacheVisibleEntities(void)
        case RENDERPATH_GL11:
                wanttangents = false;
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 
        if (r_shownormals.integer)
@@ -8200,6 +8322,7 @@ static void R_View_SetFrustum(const int *scissor)
                        case RENDERPATH_D3D9:
                        case RENDERPATH_D3D10:
                        case RENDERPATH_D3D11:
+                       case RENDERPATH_SOFT:
                                // non-flipped y coordinates
                                fny = -1.0 + 2.0 * (vid.height - scissor[1] - scissor[3] - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
                                fpy = -1.0 + 2.0 * (vid.height - scissor[1]              - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
@@ -8470,6 +8593,10 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
                case RENDERPATH_GL11:
                        qglLoadMatrixf(gl_modelview16f);CHECKGLERROR
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f);
+                       DPSOFTRAST_UniformMatrix4fvARB(DPSOFTRAST_UNIFORM_ModelViewMatrixM1, 1, false, gl_modelview16f);
+                       break;
                }
        }
 }
@@ -8507,6 +8634,7 @@ void R_ResetViewRendering2D(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                break;
        }
        GL_CullFace(GL_NONE);
@@ -8542,6 +8670,7 @@ void R_ResetViewRendering3D(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                break;
        }
        GL_CullFace(r_refdef.view.cullface_back);
@@ -8582,6 +8711,7 @@ static void R_Water_StartFrame(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
@@ -8947,6 +9077,7 @@ void R_Bloom_StartFrame(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
@@ -9047,6 +9178,7 @@ void R_Bloom_StartFrame(void)
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_CGGL:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
@@ -9089,6 +9221,7 @@ void R_Bloom_CopyBloomTexture(float colorscale)
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_CGGL:
+       case RENDERPATH_SOFT:
                R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
                break;
        case RENDERPATH_D3D9:
@@ -9266,6 +9399,7 @@ static void R_BlendView(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                permutation =
                          (r_bloomstate.texture_bloom ? SHADERPERMUTATION_BLOOM : 0)
                        | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VIEWTINT : 0)
@@ -9319,6 +9453,7 @@ static void R_BlendView(void)
                                        case RENDERPATH_GL13:
                                        case RENDERPATH_GL20:
                                        case RENDERPATH_CGGL:
+                                       case RENDERPATH_SOFT:
                                                R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
                                                break;
                                        case RENDERPATH_D3D9:
@@ -9440,6 +9575,22 @@ static void R_BlendView(void)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
+                       R_SetupShader_SetPermutationSoft(SHADERMODE_POSTPROCESS, permutation);
+                       R_Mesh_TexBind(GL20TU_FIRST     , r_bloomstate.texture_screen);
+                       R_Mesh_TexBind(GL20TU_SECOND    , r_bloomstate.texture_bloom );
+                       R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps       );
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_ViewTintColor     , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                       DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_PixelSize         , 1.0/r_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_UserVec4          , uservecs[3][0], uservecs[3][1], uservecs[3][2], uservecs[3][3]);
+                       DPSOFTRAST_Uniform1fARB(DPSOFTRAST_UNIFORM_Saturation        , r_glsl_saturation.value);
+                       DPSOFTRAST_Uniform2fARB(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+                       DPSOFTRAST_Uniform4fARB(DPSOFTRAST_UNIFORM_BloomColorSubtract   , r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 0.0f);
+                       break;
                default:
                        break;
                }
@@ -9593,6 +9744,7 @@ void R_UpdateVariables(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                if(v_glslgamma.integer && !vid_gammatables_trivial)
                {
                        if(!r_texture_gammaramps || vid_gammatables_serial != r_texture_gammaramps_serial)
@@ -13032,6 +13184,7 @@ static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, const msurface
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
                break;
        case RENDERPATH_GL13:
@@ -13060,6 +13213,7 @@ static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
                break;
        case RENDERPATH_GL13:
@@ -13097,6 +13251,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
                        RSurf_ActiveModelEntity(ent, true, true, false);
                        break;
                case RENDERPATH_GL13:
@@ -14131,6 +14286,9 @@ void R_DrawDebugModel(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                return;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               return;
        }
 
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
@@ -14421,6 +14579,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
                        RSurf_ActiveModelEntity(ent, model->wantnormals, model->wanttangents, false);
                        break;
                case RENDERPATH_GL13:
@@ -14438,6 +14597,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
                        RSurf_ActiveModelEntity(ent, true, true, false);
                        break;
                case RENDERPATH_GL13:
index 61d8117..259fd7a 100644 (file)
@@ -8,6 +8,7 @@ extern LPDIRECT3DDEVICE9 vid_d3d9dev;
 #include "jpeg.h"
 #include "image_png.h"
 #include "intoverflow.h"
+#include "dpsoftrast.h"
 
 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
@@ -38,6 +39,8 @@ cvar_t r_texture_dds_swdecode = {0, "r_texture_dds_swdecode", "0", "0: don't sof
 qboolean       gl_filter_force = false;
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
 int            gl_filter_mag = GL_LINEAR;
+DPSOFTRAST_TEXTURE_FILTER dpsoftrast_filter_mipmap = DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE;
+DPSOFTRAST_TEXTURE_FILTER dpsoftrast_filter_nomipmap = DPSOFTRAST_TEXTURE_FILTER_NEAREST;
 
 #ifdef SUPPORTD3D
 int d3d_filter_flatmin = D3DTEXF_LINEAR;
@@ -325,6 +328,10 @@ void R_FreeTexture(rtexture_t *rt)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (glt->texnum)
+                       DPSOFTRAST_Texture_Free(glt->texnum);
+               break;
        }
 
        if (glt->inputtexels)
@@ -372,17 +379,18 @@ typedef struct glmode_s
 {
        const char *name;
        int minification, magnification;
+       DPSOFTRAST_TEXTURE_FILTER dpsoftrastfilter_mipmap, dpsoftrastfilter_nomipmap;
 }
 glmode_t;
 
 static glmode_t modes[6] =
 {
-       {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
-       {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
-       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
-       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
-       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
-       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
+       {"GL_NEAREST", GL_NEAREST, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR", GL_LINEAR, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR},
+       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_LINEAR},
+       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_LINEAR}
 };
 
 #ifdef SUPPORTD3D
@@ -439,6 +447,9 @@ static void GL_TextureMode_f (void)
        gl_filter_mag = modes[i].magnification;
        gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
 
+       dpsoftrast_filter_mipmap = modes[i].dpsoftrastfilter_mipmap;
+       dpsoftrast_filter_nomipmap = modes[i].dpsoftrastfilter_nomipmap;
+
        switch(vid.renderpath)
        {
        case RENDERPATH_GL11:
@@ -515,6 +526,13 @@ static void GL_TextureMode_f (void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               // change all the existing texture objects
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
+                               if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+                                       DPSOFTRAST_Texture_Filter(glt->texnum, (glt->flags & TEXF_MIPMAP) ? dpsoftrast_filter_mipmap : dpsoftrast_filter_nomipmap);
+               break;
        }
 }
 
@@ -565,6 +583,7 @@ static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, i
        case RENDERPATH_CGGL:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_D3D9:
 #if 0
@@ -695,6 +714,8 @@ static void r_textures_start(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 
        texturemempool = Mem_AllocPool("texture management", 0, NULL);
@@ -767,6 +788,8 @@ static void r_textures_devicelost(void)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -821,6 +844,8 @@ static void r_textures_devicerestored(void)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -917,6 +942,7 @@ void R_Textures_Frame (void)
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
                        break;
                }
        }
@@ -1086,6 +1112,9 @@ static void R_UploadPartialTexture(gltexture_t *glt, const unsigned char *data,
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_Texture_UpdatePartial(glt->texnum, 0, data, fragx, fragy, fragwidth, fragheight);
+               break;
        }
 }
 
@@ -1386,6 +1415,56 @@ static void R_UploadFullTexture(gltexture_t *glt, const unsigned char *data)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               switch(glt->texturetype)
+               {
+               case GLTEXTURETYPE_2D:
+                       DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               case GLTEXTURETYPE_3D:
+                       DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               case GLTEXTURETYPE_CUBEMAP:
+                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                       {
+                               unsigned char *combinedbuffer = Mem_Alloc(tempmempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+                               // convert and upload each side in turn,
+                               // from a continuous block of input texels
+                               // copy the results into combinedbuffer
+                               texturebuffer = (unsigned char *)prevbuffer;
+                               for (i = 0;i < 6;i++)
+                               {
+                                       prevbuffer = texturebuffer;
+                                       texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
+                                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                                       {
+                                               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       // picmip/max_size
+                                       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       memcpy(combinedbuffer + i*glt->tilewidth*glt->tileheight*glt->tiledepth*glt->bytesperpixel, prevbuffer, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->bytesperpixel);
+                               }
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, combinedbuffer);
+                               Mem_Free(combinedbuffer);
+                       }
+                       else
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               }
+               if (glt->flags & TEXF_FORCELINEAR)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_LINEAR);
+               else if (glt->flags & TEXF_FORCENEAREST)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_NEAREST);
+               else if (glt->flags & TEXF_MIPMAP)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_mipmap);
+               else
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_nomipmap);
+               break;
        }
 }
 
@@ -1590,6 +1669,26 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               {
+                       int tflags = 0;
+                       switch(textype)
+                       {
+                       case TEXTYPE_PALETTE: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_RGBA: tflags = DPSOFTRAST_TEXTURE_FORMAT_RGBA8;break;
+                       case TEXTYPE_BGRA: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_COLORBUFFER: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_SHADOWMAP: tflags = DPSOFTRAST_TEXTURE_FORMAT_DEPTH;break;
+                       case TEXTYPE_ALPHA: tflags = DPSOFTRAST_TEXTURE_FORMAT_ALPHA8;break;
+                       default: Sys_Error("R_LoadTexture: unsupported texture type %i when picking DPSOFTRAST_TEXTURE_FLAGS", (int)textype);
+                       }
+                       if (glt->miplevels > 1) tflags |= DPSOFTRAST_TEXTURE_FLAG_MIPMAP;
+                       if (flags & TEXF_ALPHA) tflags |= DPSOFTRAST_TEXTURE_FLAG_USEALPHA;
+                       if (glt->sides == 6) tflags |= DPSOFTRAST_TEXTURE_FLAG_CUBEMAP;
+                       if (glt->flags & TEXF_CLAMP) tflags |= DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE;
+                       glt->texnum = DPSOFTRAST_Texture_New(tflags, glt->tilewidth, glt->tileheight, glt->tiledepth);
+               }
+               break;
        }
 
        R_UploadFullTexture(glt, data);
@@ -2174,6 +2273,9 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               glt->texnum = DPSOFTRAST_Texture_New(((glt->flags & TEXF_CLAMP) ? DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE : 0) | (dds_miplevels > 1 ? DPSOFTRAST_TEXTURE_FLAG_MIPMAP : 0), glt->tilewidth, glt->tileheight, glt->tiledepth);
+               break;
        }
 
        // upload the texture
@@ -2219,6 +2321,14 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       if (bytesperblock)
+                               Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       else
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, mippixels);
+                       // DPSOFTRAST calculates its own mipmaps
+                       mip = dds_miplevels;
+                       break;
                }
                mippixels += mipsize;
                if (mipwidth <= 1 && mipheight <= 1)
@@ -2287,6 +2397,16 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (glt->flags & TEXF_FORCELINEAR)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_LINEAR);
+               else if (glt->flags & TEXF_FORCENEAREST)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_NEAREST);
+               else if (glt->flags & TEXF_MIPMAP)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_mipmap);
+               else
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_nomipmap);
+               break;
        }
 
        Mem_Free(dds);
@@ -2314,7 +2434,7 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in
                Host_Error("R_UpdateTexture: no texture supplied");
        if (!glt->texnum && !glt->d3dtexture)
        {
-               Con_Printf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet", (void *)glt, glt->identifier, (void *)glt->pool);
+               Con_DPrintf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet\n", (void *)glt, glt->identifier, (void *)glt->pool);
                return;
        }
        // update part of the texture
index a85df80..a566347 100644 (file)
@@ -120,6 +120,7 @@ OBJ_COMMON= \
        csprogs.o \
        curves.o \
        cvar.o \
+       dpsoftrast.o \
        dpvsimpledecode.o \
        filematch.o \
        fractalnoise.o \
index 6a110b6..1f09603 100644 (file)
@@ -139,6 +139,7 @@ demonstrated by the game Doom3.
 #include "cl_collision.h"
 #include "portals.h"
 #include "image.h"
+#include "dpsoftrast.h"
 
 #ifdef SUPPORTD3D
 #include <d3d9.h>
@@ -427,6 +428,7 @@ void R_Shadow_SetShadowMode(void)
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
                        r_shadow_shadowmapsampler = false;
                        r_shadow_shadowmappcf = 1;
                        r_shadow_shadowmode = R_SHADOW_SHADOWMODE_SHADOWMAP2D;
@@ -1897,6 +1899,7 @@ void R_Shadow_RenderMode_Begin(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
                break;
        case RENDERPATH_GL13:
@@ -2101,6 +2104,7 @@ init_done:
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_CGGL:
+       case RENDERPATH_SOFT:
                GL_CullFace(r_refdef.view.cullface_back);
                // OpenGL lets us scissor larger than the viewport, so go ahead and clear all views at once
                if ((clear & ((2 << side) - 1)) == (1 << side)) // only clear if the side is the first in the mask
@@ -2116,6 +2120,8 @@ init_done:
                GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
                break;
        case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
                Vector4Set(clearcolor, 1,1,1,1);
                // completely different meaning than in OpenGL path
                r_shadow_shadowmap_parameters[1] = 0;
@@ -2138,14 +2144,6 @@ init_done:
                                GL_Clear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT, clearcolor, 1.0f, 0);
                }
                break;
-       case RENDERPATH_D3D10:
-       case RENDERPATH_D3D11:
-               // D3D considers it an error to use a scissor larger than the viewport...  clear just this view
-               GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
-               GL_ColorMask(0,0,0,0);
-               if (clear)
-                       GL_Clear(GL_DEPTH_BUFFER_BIT, NULL, 1.0f, 0);
-               break;
        }
 }
 
@@ -2692,6 +2690,9 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_BlendSubtract(true);
+                       break;
                }
        }
        RSurf_SetupDepthAndCulling();
@@ -2735,6 +2736,9 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_BlendSubtract(false);
+                       break;
                }
        }
 }
@@ -3885,6 +3889,7 @@ void R_Shadow_PrepareLights(void)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                if (!r_shadow_deferred.integer || r_shadow_shadowmode == R_SHADOW_SHADOWMODE_STENCIL || !vid.support.ext_framebuffer_object || vid.maxdrawbuffers < 2)
                {
                        r_shadow_usingdeferredprepass = false;
@@ -4277,6 +4282,7 @@ void R_DrawModelShadowMaps(void)
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_CGGL:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
@@ -4478,6 +4484,9 @@ void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
                }
        }
        rtlight->corona_visibility = bound(0, (zdist - 32) / 32, 1);
@@ -4512,6 +4521,9 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
                }
                //Con_Printf("%i of %i pixels\n", (int)visiblepixels, (int)allpixels);
                if (visiblepixels < 1 || allpixels < 1)
@@ -4552,6 +4564,9 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                        case RENDERPATH_D3D11:
                                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                                break;
+                       case RENDERPATH_SOFT:
+                               DPSOFTRAST_BlendSubtract(true);
+                               break;
                        }
                }
                R_CalcSprite_Vertex3f(vertex3f, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale);
@@ -4578,6 +4593,9 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                        case RENDERPATH_D3D11:
                                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                                break;
+                       case RENDERPATH_SOFT:
+                               DPSOFTRAST_BlendSubtract(false);
+                               break;
                        }
                }
        }
@@ -4645,6 +4663,9 @@ void R_Shadow_DrawCoronas(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
        for (lightindex = 0;lightindex < range;lightindex++)
        {
index 9b6ba08..8ae5224 100644 (file)
--- a/render.h
+++ b/render.h
@@ -442,55 +442,6 @@ typedef enum rsurfacepass_e
 }
 rsurfacepass_t;
 
-typedef enum gl20_texunit_e
-{
-       // postprocess shaders, and generic shaders:
-       GL20TU_FIRST = 0,
-       GL20TU_SECOND = 1,
-       GL20TU_GAMMARAMPS = 2,
-       // standard material properties
-       GL20TU_NORMAL = 0,
-       GL20TU_COLOR = 1,
-       GL20TU_GLOSS = 2,
-       GL20TU_GLOW = 3,
-       // material properties for a second material
-       GL20TU_SECONDARY_NORMAL = 4,
-       GL20TU_SECONDARY_COLOR = 5,
-       GL20TU_SECONDARY_GLOSS = 6,
-       GL20TU_SECONDARY_GLOW = 7,
-       // material properties for a colormapped material
-       // conflicts with secondary material
-       GL20TU_PANTS = 4,
-       GL20TU_SHIRT = 7,
-       // fog fade in the distance
-       GL20TU_FOGMASK = 8,
-       // compiled ambient lightmap and deluxemap
-       GL20TU_LIGHTMAP = 9,
-       GL20TU_DELUXEMAP = 10,
-       // refraction, used by water shaders
-       GL20TU_REFRACTION = 3,
-       // reflection, used by water shaders, also with normal material rendering
-       // conflicts with secondary material
-       GL20TU_REFLECTION = 7,
-       // rtlight attenuation (distance fade) and cubemap filter (projection texturing)
-       // conflicts with lightmap/deluxemap
-       GL20TU_ATTENUATION = 9,
-       GL20TU_CUBE = 10,
-       GL20TU_SHADOWMAP2D = 15,
-       GL20TU_CUBEPROJECTION = 12,
-       // rtlight prepass data (screenspace depth and normalmap)
-       GL20TU_SCREENDEPTH = 13,
-       GL20TU_SCREENNORMALMAP = 14,
-       // lightmap prepass data (screenspace diffuse and specular from lights)
-       GL20TU_SCREENDIFFUSE = 11,
-       GL20TU_SCREENSPECULAR = 12,
-       // fake reflections
-       GL20TU_REFLECTMASK = 5,
-       GL20TU_REFLECTCUBE = 6,
-       GL20TU_FOGHEIGHTTEXTURE = 14
-}
-gl20_texunit;
-
 void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale);
 void R_SetupShader_DepthOrShadow(void);
 void R_SetupShader_ShowDepth(void);
diff --git a/vid.h b/vid.h
index 8927577..569a633 100644 (file)
--- a/vid.h
+++ b/vid.h
@@ -36,7 +36,8 @@ typedef enum renderpath_e
        RENDERPATH_CGGL,
        RENDERPATH_D3D9,
        RENDERPATH_D3D10,
-       RENDERPATH_D3D11
+       RENDERPATH_D3D11,
+       RENDERPATH_SOFT
 }
 renderpath_t;
 
@@ -118,6 +119,13 @@ typedef struct viddef_s
        unsigned int maxdrawbuffers;
 
        viddef_support_t support;
+
+       // in RENDERPATH_SOFT this is a 32bpp native-endian ARGB framebuffer
+       // (native-endian ARGB meaning that in little endian it is BGRA bytes,
+       //  in big endian it is ARGB byte order, the format is converted during
+       //  blit to the window)
+       unsigned int *softpixels;
+       unsigned int *softdepthpixels;
 } viddef_t;
 
 // global video state
index ba869c2..31e9cd9 100644 (file)
--- a/vid_sdl.c
+++ b/vid_sdl.c
@@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 #include "image.h"
+#include "dpsoftrast.h"
 
 #ifdef MACOSX
 #include <Carbon/Carbon.h>
@@ -63,6 +64,7 @@ int cl_available = true;
 
 qboolean vid_supportrefreshrate = false;
 
+cvar_t vid_soft = {CVAR_SAVE, "vid_soft", "0", "enables use of the DarkPlaces Software Rasterizer rather than OpenGL or Direct3D"};
 cvar_t joy_detected = {CVAR_READONLY, "joy_detected", "0", "number of joysticks detected by engine"};
 cvar_t joy_enable = {CVAR_SAVE, "joy_enable", "0", "enables joystick support"};
 cvar_t joy_index = {0, "joy_index", "0", "selects which joystick to use if you have multiple"};
@@ -98,6 +100,7 @@ static int win_half_height = 50;
 static int video_bpp, video_flags;
 
 static SDL_Surface *screen;
+static SDL_Surface *vid_softsurface;
 
 // joystick axes state
 #define MAX_JOYSTICK_AXES      16
@@ -606,6 +609,16 @@ void Sys_SendKeyEvents( void )
                                        vid.width = event.resize.w;
                                        vid.height = event.resize.h;
                                        SDL_SetVideoMode(vid.width, vid.height, video_bpp, video_flags);
+                                       if (vid_softsurface)
+                                       {
+                                               SDL_FreeSurface(vid_softsurface);
+                                               vid_softsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, vid.width, vid.height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
+                                               vid.softpixels = vid_softsurface->pixels;
+                                               SDL_SetAlpha(vid_softsurface, 0, 255);
+                                               if (vid.softdepthpixels)
+                                                       free(vid.softdepthpixels);
+                                               vid.softdepthpixels = calloc(1, vid.width * vid.height * 4);
+                                       }
 #ifdef SDL_R_RESTART
                                        // better not call R_Modules_Restart from here directly, as this may wreak havoc...
                                        // so, let's better queue it for next frame
@@ -657,6 +670,7 @@ void VID_Init (void)
 #ifdef MACOSX
        Cvar_RegisterVariable(&apple_mouse_noaccel);
 #endif
+       Cvar_RegisterVariable(&vid_soft);
        Cvar_RegisterVariable(&joy_detected);
        Cvar_RegisterVariable(&joy_enable);
        Cvar_RegisterVariable(&joy_index);
@@ -961,7 +975,7 @@ static void VID_OutputVersion(void)
                                        version->major, version->minor, version->patch );
 }
 
-qboolean VID_InitMode(viddef_mode_t *mode)
+qboolean VID_InitModeGL(viddef_mode_t *mode)
 {
        int i;
        static int notfirstvideomode = false;
@@ -1056,6 +1070,9 @@ qboolean VID_InitMode(viddef_mode_t *mode)
                return false;
        }
 
+       vid_softsurface = NULL;
+       vid.softpixels = NULL;
+
        // set window title
        VID_SetCaption();
        // set up an event filter to ask confirmation on close button in WIN32
@@ -1096,11 +1113,172 @@ qboolean VID_InitMode(viddef_mode_t *mode)
        return true;
 }
 
+extern cvar_t gl_info_extensions;
+extern cvar_t gl_info_vendor;
+extern cvar_t gl_info_renderer;
+extern cvar_t gl_info_version;
+extern cvar_t gl_info_platform;
+extern cvar_t gl_info_driver;
+
+qboolean VID_InitModeSoft(viddef_mode_t *mode)
+{
+       int i;
+       int flags = SDL_HWSURFACE;
+
+       win_half_width = mode->width>>1;
+       win_half_height = mode->height>>1;
+
+       if(vid_resizable.integer)
+               flags |= SDL_RESIZABLE;
+
+       VID_OutputVersion();
+
+       vid_isfullscreen = false;
+       if (mode->fullscreen) {
+               flags |= SDL_FULLSCREEN;
+               vid_isfullscreen = true;
+       }
+
+       video_bpp = mode->bitsperpixel;
+       video_flags = flags;
+       VID_SetIcon_Pre();
+       screen = SDL_SetVideoMode(mode->width, mode->height, mode->bitsperpixel, flags);
+       VID_SetIcon_Post();
+
+       if (screen == NULL)
+       {
+               Con_Printf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
+               VID_Shutdown();
+               return false;
+       }
+
+       // create a framebuffer using our specific color format, we let the SDL blit function convert it in VID_Finish
+       vid_softsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, mode->width, mode->height, 32, 0x00FF0000, 0x0000FF00, 0x00000000FF, 0xFF000000);
+       if (vid_softsurface == NULL)
+       {
+               Con_Printf("Failed to setup software rasterizer framebuffer %ix%ix32bpp: %s\n", mode->width, mode->height, SDL_GetError());
+               VID_Shutdown();
+               return false;
+       }
+       SDL_SetAlpha(vid_softsurface, 0, 255);
+
+       vid.softpixels = vid_softsurface->pixels;
+       vid.softdepthpixels = calloc(1, mode->width * mode->height * 4);
+       DPSOFTRAST_Init(mode->width, mode->height, vid_softsurface->pixels, vid.softdepthpixels);
+
+       // set window title
+       VID_SetCaption();
+       // set up an event filter to ask confirmation on close button in WIN32
+       SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
+       // init keyboard
+       SDL_EnableUNICODE( SDL_ENABLE );
+       // enable key repeat since everyone expects it
+       SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+
+       gl_platform = "SDLSoft";
+       gl_platformextensions = "";
+
+       gl_renderer = "DarkPlaces-Soft";
+       gl_vendor = "Forest Hale";
+       gl_version = "0.0";
+       gl_extensions = "";
+
+       // clear the extension flags
+       memset(&vid.support, 0, sizeof(vid.support));
+       Cvar_SetQuick(&gl_info_extensions, "");
+
+       vid.forcevbo = false;
+       vid.support.arb_depth_texture = true;
+       vid.support.arb_draw_buffers = true;
+       vid.support.arb_occlusion_query = true;
+       vid.support.arb_shadow = true;
+       vid.support.arb_texture_compression = true;
+       vid.support.arb_texture_cube_map = true;
+       vid.support.arb_texture_non_power_of_two = false;
+       vid.support.arb_vertex_buffer_object = true;
+       vid.support.ext_blend_subtract = true;
+       vid.support.ext_draw_range_elements = true;
+       vid.support.ext_framebuffer_object = true;
+       vid.support.ext_texture_3d = true;
+       vid.support.ext_texture_compression_s3tc = true;
+       vid.support.ext_texture_filter_anisotropic = true;
+       vid.support.ati_separate_stencil = true;
+
+       vid.maxtexturesize_2d = 16384;
+       vid.maxtexturesize_3d = 512;
+       vid.maxtexturesize_cubemap = 16384;
+       vid.texunits = 4;
+       vid.teximageunits = 32;
+       vid.texarrayunits = 8;
+       vid.max_anisotropy = 1;
+       vid.maxdrawbuffers = 4;
+
+       vid.texunits = bound(4, vid.texunits, MAX_TEXTUREUNITS);
+       vid.teximageunits = bound(16, vid.teximageunits, MAX_TEXTUREUNITS);
+       vid.texarrayunits = bound(8, vid.texarrayunits, MAX_TEXTUREUNITS);
+       Con_DPrintf("Using DarkPlaces Software Rasterizer rendering path\n");
+       vid.renderpath = RENDERPATH_SOFT;
+       vid.useinterleavedarrays = false;
+
+       Cvar_SetQuick(&gl_info_vendor, gl_vendor);
+       Cvar_SetQuick(&gl_info_renderer, gl_renderer);
+       Cvar_SetQuick(&gl_info_version, gl_version);
+       Cvar_SetQuick(&gl_info_platform, gl_platform ? gl_platform : "");
+       Cvar_SetQuick(&gl_info_driver, gl_driver);
+
+       // LordHavoc: report supported extensions
+       Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
+
+       // clear to black (loading plaque will be seen over this)
+       GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
+
+       vid_numjoysticks = SDL_NumJoysticks();
+       vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
+       Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
+       Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
+       memset(vid_joysticks, 0, sizeof(vid_joysticks));
+       for (i = 0;i < vid_numjoysticks;i++)
+       {
+               SDL_Joystick *joy;
+               joy = vid_joysticks[i] = SDL_JoystickOpen(i);
+               if (!joy)
+               {
+                       Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
+                       continue;
+               }
+               Con_Printf("joystick #%i: opened \"%s\" with %i axes, %i buttons, %i balls\n", i, SDL_JoystickName(i), (int)SDL_JoystickNumAxes(joy), (int)SDL_JoystickNumButtons(joy), (int)SDL_JoystickNumBalls(joy));
+       }
+
+       vid_hidden = false;
+       vid_activewindow = false;
+       vid_usingmouse = false;
+       vid_usinghidecursor = false;
+
+       SDL_WM_GrabInput(SDL_GRAB_OFF);
+       return true;
+}
+
+qboolean VID_InitMode(viddef_mode_t *mode)
+{
+       if (vid_soft.integer)
+               return VID_InitModeSoft(mode);
+       else
+               return VID_InitModeGL(mode);
+}
+
 void VID_Shutdown (void)
 {
        VID_SetMouse(false, false, false);
        VID_RestoreSystemGamma();
 
+       if (vid_softsurface)
+               SDL_FreeSurface(vid_softsurface);
+       vid_softsurface = NULL;
+       vid.softpixels = NULL;
+       if (vid.softdepthpixels)
+               free(vid.softdepthpixels);
+       vid.softdepthpixels = NULL;
+
        SDL_QuitSubSystem(SDL_INIT_VIDEO);
 
        gl_driver[0] = 0;
@@ -1137,12 +1315,28 @@ void VID_Finish (void)
 
        if (!vid_hidden)
        {
-               CHECKGLERROR
-               if (r_speeds.integer == 2 || gl_finish.integer)
+               switch(vid.renderpath)
                {
-                       qglFinish();CHECKGLERROR
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       CHECKGLERROR
+                       if (r_speeds.integer == 2 || gl_finish.integer)
+                       {
+                               qglFinish();CHECKGLERROR
+                       }
+                       SDL_GL_SwapBuffers();
+                       break;
+               case RENDERPATH_SOFT:
+                       SDL_BlitSurface(vid_softsurface, NULL, screen, NULL);
+                       SDL_Flip(screen);
+                       break;
+               case RENDERPATH_D3D9:
+               case RENDERPATH_D3D10:
+               case RENDERPATH_D3D11:
+                       break;
                }
-               SDL_GL_SwapBuffers();
        }
 }
 
index eae470f..164c528 100644 (file)
@@ -1063,6 +1063,7 @@ void VID_UpdateGamma(qboolean force, int rampsize)
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                if (v_glslgamma.integer)
                        wantgamma = 0;
                break;
index 35bbf85..9129377 100644 (file)
--- a/vid_wgl.c
+++ b/vid_wgl.c
@@ -1546,8 +1546,6 @@ qboolean VID_InitModeDX(viddef_mode_t *mode, int version)
        memset(&vid.support, 0, sizeof(vid.support));
        Cvar_SetQuick(&gl_info_extensions, "");
 
-       CHECKGLERROR
-
        vid.forcevbo = false;
        vid.support.arb_depth_texture = true;
        vid.support.arb_draw_buffers = vid_d3d9caps.NumSimultaneousRTs > 1;