Index: code/renderer/tr_image.c =================================================================== --- code/renderer/tr_image.c (revision 933) +++ code/renderer/tr_image.c (working copy) @@ -34,7 +34,24 @@ #define JPEG_INTERNALS #include "../jpeg-6/jpeglib.h" +/** + * Headers for cell shading + * @author Jordi Prats Catala + * @author Guillermo Miranda Alamo + */ +/* +byte getImageR(byte *targa_rgba, int x, int y, int columns, int rows); +byte getImageG(byte *targa_rgba, int x, int y, int columns, int rows); +byte getImageB(byte *targa_rgba, int x, int y, int columns, int rows); +byte getImageA(byte *targa_rgba, int x, int y, int columns, int rows); +void setImageR(byte *targa_rgba, int x, int y, int columns, int rows, byte value); +void setImageG(byte *targa_rgba, int x, int y, int columns, int rows, byte value); +void setImageB(byte *targa_rgba, int x, int y, int columns, int rows, byte value); +void setImageA(byte *targa_rgba, int x, int y, int columns, int rows, byte value); +*/ +//void kuwahara(int columns, int rows, byte *targa_rgba); + static void LoadBMP( const char *name, byte **pic, int *width, int *height ); static void LoadTGA( const char *name, byte **pic, int *width, int *height ); static void LoadJPG( const char *name, byte **pic, int *width, int *height ); @@ -799,7 +816,643 @@ return image; } +/**************************** +RGB GET/SET +****************************/ +//RED +static byte getImageR(byte *targa_rgba, int x, int y, int columns, int rows) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + if(rows<=y) + y=y%rows; + if(columns<=x) + x=x%columns; + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + + return *pixbuf; +} + +static void setImageR(byte *targa_rgba, int x, int y, int columns, int rows, byte value) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + + *pixbuf=value; +} +//GREEN +static byte getImageG(byte *targa_rgba, int x, int y, int columns, int rows) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + if(rows<=y) + y=y%rows; + if(columns<=x) + x=x%columns; + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + + pixbuf++; + return *pixbuf; +} + +static void setImageG(byte *targa_rgba, int x, int y, int columns, int rows, byte value) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + pixbuf++; + *pixbuf=value; +} +//BLUE +static byte getImageB(byte *targa_rgba, int x, int y, int columns, int rows) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + if(rows<=y) + y=y%rows; + if(columns<=x) + x=x%columns; + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + pixbuf+=2; + return *pixbuf; +} + +static void setImageB(byte *targa_rgba, int x, int y, int columns, int rows, byte value) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + pixbuf+=2; + *pixbuf=value; +} +//ALPHA +static byte getImageA(byte *targa_rgba, int x, int y, int columns, int rows) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + pixbuf+=3; + return *pixbuf; +} + +static void setImageA(byte *targa_rgba, int x, int y, int columns, int rows, byte value) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4; + + pixbuf+=(x*4); + pixbuf+=3; + *pixbuf=value; +} + +//RGB +static void getImageRGB(byte *targa_rgba, int x, int y, int columns, int rows, vec3_t rgb) +{ + byte *pixbuf; + + x*=((x<0)?-1:1); + y*=((y<0)?-1:1); + //if(rows<=y) + y=y%rows; + //if(columns<=x) + x=x%columns; + //x*=((x<0)?-1:1); + //y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4 + x*4; + + rgb[0]=*pixbuf; + rgb[1]=*(pixbuf+1); + rgb[2]=*(pixbuf+2); +} + +static void setImageRGB(byte *targa_rgba, int x, int y, int columns, int rows, vec3_t rgb) +{ + byte *pixbuf; + + //x*=((x<0)?-1:1); + //y*=((y<0)?-1:1); + + pixbuf = targa_rgba + y*columns*4 + (x*4); + + *pixbuf=(byte)(rgb[0]); + *(pixbuf+1)=(byte)(rgb[1]); + *(pixbuf+2)=(byte)(rgb[2]); +} + +/**************************** +NO BRAINER'S BLUR +****************************/ +static void blur(int columns, int rows, byte *targa_rgba) +{ + int row, column; + float sum; + + + for(row=0; row0){ + rMean=((float)rMean/(float)pixels); + gMean=((float)gMean/(float)pixels); + bMean=((float)bMean/(float)pixels); + } + else{ + return; + } + + for(row=0;row0){ + rMean=rMean/pixels; + gMean=gMean/pixels; + bMean=bMean/pixels; + } + else{ + return; + } + + + for(row=0;rowmax) max=value; + } + } + + mv[0]/=count; + mv[1]/=count; + mv[2]/=count; + mv[3]= (max-min)/3.0f; +} + + +static void rgb_kuwahara(int x, int y, int columns, int rows, byte *targa_rgba, vec4_t bmv) +{ + vec4_t mv; + bmv[0]=bmv[1]=bmv[2]=bmv[3]=255; + + mean_variance(x-KWH_RADIUS, y-KWH_RADIUS, x, y, columns, rows, targa_rgba, mv); + if( mv[3] < bmv[3] ) + { + Vector4Copy(mv,bmv); + } + + mean_variance(x, y-KWH_RADIUS, x+KWH_RADIUS, y, columns, rows, targa_rgba, mv); + if( mv[3] < bmv[3] ) + { + Vector4Copy(mv,bmv); + } + + mean_variance(x, y, x+KWH_RADIUS, y+KWH_RADIUS, columns, rows, targa_rgba, mv); + if( mv[3] < bmv[3] ) + { + Vector4Copy(mv,bmv); + } + + mean_variance(x-KWH_RADIUS, y, x, y+KWH_RADIUS, columns, rows, targa_rgba, mv); + if( mv[3] < bmv[3] ) + { + Vector4Copy(mv,bmv); + } +} + +static void kuwahara(int columns, int rows, byte *targa_rgba){ + int row, column; + vec4_t rgbv; + + for(row=0;row=0) break; + // Sum pixels values + r1=getImageR(targa_rgba,column+u,row+v,columns,rows); + g1=getImageG(targa_rgba,column+u,row+v,columns,rows); + b1=getImageB(targa_rgba,column+u,row+v,columns,rows); + + r2=getImageR(targa_rgba,column-u,row-v,columns,rows); + g2=getImageG(targa_rgba,column-u,row-v,columns,rows); + b2=getImageB(targa_rgba,column-u,row-v,columns,rows); + + if ( deltaE(r,g,b,r1,g1,b1) < deltaE(r,g,b,r2,g2,b2)) + { + sumR += r1; + sumG += g1; + sumB += b1; + } + else + { + sumR += r2; + sumG += g2; + sumB += b2; + } + count++; + } + } + + r=(byte)((int)(2*sumR+r)/(int)(2*count+1)); + g=(byte)((int)(2*sumG+g)/(int)(2*count+1)); + b=(byte)((int)(2*sumB+b)/(int)(2*count+1)); + + setImageR(targa_rgba,column,row,columns,rows,r); + setImageG(targa_rgba,column,row,columns,rows,g); + setImageB(targa_rgba,column,row,columns,rows,b); + } + } +} + + + /* ========================================================= @@ -1968,6 +2621,50 @@ } else if ( !Q_stricmp( name+len-4, ".jpg" ) ) { LoadJPG( name, pic, width, height ); } + + switch(r_celshadalgo->integer) + { + case 1: + whiteTextureOne(*width,*height,*pic); + break; + case 2: + whiteTextureTwo(*width,*height,*pic); + break; + case 10: + kuwahara(*width,*height,*pic); + break; + case 11: + blur(*width,*height,*pic); + kuwahara(*width,*height,*pic); + break; + case 12: + kuwahara(*width,*height,*pic); + blur(*width,*height,*pic); + break; + case 13: + blur(*width,*height,*pic); + kuwahara(*width,*height,*pic); + blur(*width,*height,*pic); + break; + case 20: + snn(*width,*height,*pic); + break; + case 21: + blur(*width,*height,*pic); + snn(*width,*height,*pic); + break; + case 22: + snn(*width,*height,*pic); + blur(*width,*height,*pic); + break; + case 23: + blur(*width,*height,*pic); + snn(*width,*height,*pic); + blur(*width,*height,*pic); + break; + default: + break; + } } Index: code/renderer/tr_init.c =================================================================== --- code/renderer/tr_init.c (revision 933) +++ code/renderer/tr_init.c (working copy) @@ -111,6 +111,10 @@ cvar_t *r_roundImagesDown; cvar_t *r_colorMipLevels; cvar_t *r_picmip; +// Next one added for cell shading algorithm selection +cvar_t *r_celshadalgo; +//. next one for enable/disable cel bordering all together. +cvar_t *r_celoutline; cvar_t *r_showtris; cvar_t *r_showsky; cvar_t *r_shownormals; @@ -1110,6 +1114,10 @@ r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT); r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT); r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT); + // for cell shading algorithm selection + r_celshadalgo = ri.Cvar_Get ("r_celshadalgo", "1", CVAR_LATCH); + // cel outline option + r_celoutline = ri.Cvar_Get("r_celoutline","1", CVAR_ARCHIVE); r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT); r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT); r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT); Index: code/renderer/tr_local.h =================================================================== --- code/renderer/tr_local.h (revision 933) +++ code/renderer/tr_local.h (working copy) @@ -1063,6 +1063,8 @@ extern cvar_t *r_uiFullScreen; // ui is running fullscreen extern cvar_t *r_logFile; // number of frames to emit GL logs +extern cvar_t *r_celshadalgo; // Cell shading, chooses method: 0 = disabled, 1 = kuwahara, 2 = whiteTexture +extern cvar_t *r_celoutline; //. cel outline. 1 on, 0 off. (maybe other options later) extern cvar_t *r_showtris; // enables wireframe rendering of the world extern cvar_t *r_showsky; // forces sky in front of all surfaces extern cvar_t *r_shownormals; // draws wireframe normals Index: code/renderer/tr_shade.c =================================================================== --- code/renderer/tr_shade.c (revision 933) +++ code/renderer/tr_shade.c (working copy) @@ -201,6 +201,86 @@ } +//R_DRAWCEL +static void R_DrawCel( int numIndexes, const glIndex_t *indexes ) { + int primitives; + + if( + //. ignore the 2d projection. do i smell the HUD? + (backEnd.projection2D == qtrue) || + //. ignore general entitites that are sprites. SEE NOTE #3. + (backEnd.currentEntity->e.reType == RT_SPRITE) || + //. ignore these liquids. why? ever see liquid with tris on the surface? exactly. SEE NOTE #4. + (tess.shader->contentFlags & (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_FOG)) || + //. ignore things that are two sided, meaning mostly things that have transparency. SEE NOTE #1. + (tess.shader->cullType == CT_TWO_SIDED) + + ) { + return; + } + + primitives = r_primitives->integer; + + // default is to use triangles if compiled vertex arrays are present + if ( primitives == 0 ) { + if ( qglLockArraysEXT ) { + primitives = 2; + } else { + primitives = 1; + } + } + + //. correction for mirrors. SEE NOTE #2. + if(backEnd.viewParms.isMirror == qtrue) { qglCullFace (GL_FRONT); } + else { qglCullFace (GL_BACK); } + + qglEnable (GL_BLEND); + qglBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA); + qglColor3f (0.0f,0.0f,0.0f); + qglLineWidth( (float) r_celoutline->integer ); + + if(primitives == 2) { + qglDrawElements( GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexes ); + } else if(primitives == 1) { + R_DrawStripElements( numIndexes, indexes, qglArrayElement ); + } else if(primitives == 3) { + R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete ); + } + + //. correction for mirrors. SEE NOTE #2. + if(backEnd.viewParms.isMirror == qtrue) { qglCullFace (GL_BACK); } + else { qglCullFace (GL_FRONT); } + + qglDisable (GL_BLEND); + + return; + +/* Notes + +1. this is going to be a pain in the arse. it fixes things like light `beams` from being cel'd but it +also will ignore any other shader set with no culling. this usually is everything that is translucent. +but this is a good hack to clean up the screen untill something more selective comes along. or who knows +group desision might actually be that this is liked. if so i take back calling it a `hack`, lol. + = bob. + +2. mirrors display correctly because the normals of the displayed are inverted of normal space. so to +continue to have them display correctly, we must invert them inversely from a normal inversion. + = bob. + +3. this turns off a lot of space hogging sprite cel outlines. picture if you will five people in a small +room all shooting rockets. each smoke puff gets a big black square around it, each explosion gets a big +black square around it, and now nobody can see eachother because everyones screen is solid black. + = bob. + +4. ignoring liquids means you will not get black tris lines all over the top of your liquid. i put this in +after seeing the lava on q3dm7 and water on q3ctf2 that had black lines all over the top, making the +liquids look solid instead of... liquid. + = bob. + +*/ +} + + /* ============================================================= @@ -245,6 +325,33 @@ GL_Bind( bundle->image[ index ] ); } +//DRAWCEL +static void DrawCel (shaderCommands_t *input) { + + GL_Bind( tr.whiteImage ); + qglColor3f (1,1,1); + + GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); + + qglDisableClientState (GL_COLOR_ARRAY); + qglDisableClientState (GL_TEXTURE_COORD_ARRAY); + + qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD + + if (qglLockArraysEXT) { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } + + R_DrawCel( input->numIndexes, input->indexes ); + + if (qglUnlockArraysEXT) { + qglUnlockArraysEXT(); + GLimp_LogComment( "glUnlockArraysEXT\n" ); + } + +} + /* ================ DrawTris @@ -1140,6 +1247,12 @@ qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); } + //. show me cel outlines. + //. there has to be a better place to put this. + if(r_celoutline->integer > 0) { + DrawCel(&tess); + } + // // if there is only a single pass then we can enable color // and texture arrays before we compile, otherwise we need Index: code/renderer/tr_shader.c =================================================================== --- code/renderer/tr_shader.c (revision 933) +++ code/renderer/tr_shader.c (working copy) @@ -2744,7 +2744,17 @@ */ qhandle_t RE_RegisterShaderNoMip( const char *name ) { shader_t *sh; + // Remember previous value + int old_r_celshadalgo; + /* + * This will prevent sprites, like buttons, go through + * cel shading filters, like kuwahara. + * @author gmiranda + */ + old_r_celshadalgo = r_celshadalgo->integer; + r_celshadalgo->integer=0; + if ( strlen( name ) >= MAX_QPATH ) { Com_Printf( "Shader name exceeds MAX_QPATH\n" ); return 0; @@ -2752,6 +2762,9 @@ sh = R_FindShader( name, LIGHTMAP_2D, qfalse ); + // Restore value + r_celshadalgo->integer=old_r_celshadalgo; + // we want to return 0 if the shader failed to // load for some reason, but R_FindShader should // still keep a name allocated for it, so if