X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=gl_rmain.c;h=7f998d21a7db8d087192f88cf27bf53144a91d7e;hb=f268c57a296c53f3bdd239911cc5569ac08ae70f;hp=5aed6a0554efb3242d0ed620d2e0496d45d91cb7;hpb=15c45f623a8fdd9117f6eebebb0b335467de40ea;p=xonotic%2Fdarkplaces.git diff --git a/gl_rmain.c b/gl_rmain.c index 5aed6a05..7f998d21 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -115,6 +115,7 @@ cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset", cvar_t r_polygonoffset_decals_factor = {0, "r_polygonoffset_decals_factor", "0", "biases depth values of decals to prevent z-fighting artifacts"}; cvar_t r_polygonoffset_decals_offset = {0, "r_polygonoffset_decals_offset", "-14", "biases depth values of decals to prevent z-fighting artifacts"}; cvar_t r_fog_exp2 = {0, "r_fog_exp2", "0", "uses GL_EXP2 fog (as in Nehahra) rather than realistic GL_EXP fog"}; +cvar_t r_fog_clear = {0, "r_fog_clear", "1", "clears renderbuffer with fog color before render starts"}; cvar_t r_drawfog = {CVAR_SAVE, "r_drawfog", "1", "allows one to disable fog rendering"}; cvar_t r_transparentdepthmasking = {CVAR_SAVE, "r_transparentdepthmasking", "0", "enables depth writes on transparent meshes whose materially is normally opaque, this prevents seeing the inside of a transparent mesh"}; @@ -140,9 +141,20 @@ cvar_t r_textureunits = {0, "r_textureunits", "32", "number of texture units to static cvar_t gl_combine = {CVAR_READONLY, "gl_combine", "1", "indicates whether the OpenGL 1.3 rendering path is active"}; static cvar_t r_glsl = {CVAR_READONLY, "r_glsl", "1", "indicates whether the OpenGL 2.0 rendering path is active"}; +cvar_t r_viewfbo = {CVAR_SAVE, "r_viewfbo", "0", "enables use of an 8bit (1) or 16bit (2) or 32bit (3) per component float framebuffer render, which may be at a different resolution than the video mode"}; +cvar_t r_viewscale = {CVAR_SAVE, "r_viewscale", "1", "scaling factor for resolution of the fbo rendering method, must be > 0, can be above 1 for a costly antialiasing behavior, typical values are 0.5 for 1/4th as many pixels rendered, or 1 for normal rendering"}; +cvar_t r_viewscale_fpsscaling = {CVAR_SAVE, "r_viewscale_fpsscaling", "0", "change resolution based on framerate"}; +cvar_t r_viewscale_fpsscaling_min = {CVAR_SAVE, "r_viewscale_fpsscaling_min", "0.0625", "worst acceptable quality"}; +cvar_t r_viewscale_fpsscaling_multiply = {CVAR_SAVE, "r_viewscale_fpsscaling_multiply", "5", "adjust quality up or down by the frametime difference from 1.0/target, multiplied by this factor"}; +cvar_t r_viewscale_fpsscaling_stepsize = {CVAR_SAVE, "r_viewscale_fpsscaling_stepsize", "0.01", "smallest adjustment to hit the target framerate (this value prevents minute oscillations)"}; +cvar_t r_viewscale_fpsscaling_stepmax = {CVAR_SAVE, "r_viewscale_fpsscaling_stepmax", "1.00", "largest adjustment to hit the target framerate (this value prevents wild overshooting of the estimate)"}; +cvar_t r_viewscale_fpsscaling_target = {CVAR_SAVE, "r_viewscale_fpsscaling_target", "70", "desired framerate"}; + cvar_t r_glsl_deluxemapping = {CVAR_SAVE, "r_glsl_deluxemapping", "1", "use per pixel lighting on deluxemap-compiled q3bsp maps (or a value of 2 forces deluxemap shading even without deluxemaps)"}; cvar_t r_glsl_offsetmapping = {CVAR_SAVE, "r_glsl_offsetmapping", "0", "offset mapping effect (also known as parallax mapping or virtual displacement mapping)"}; +cvar_t r_glsl_offsetmapping_steps = {CVAR_SAVE, "r_glsl_offsetmapping_steps", "2", "offset mapping steps (note: too high values may be not supported by your GPU)"}; cvar_t r_glsl_offsetmapping_reliefmapping = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"}; +cvar_t r_glsl_offsetmapping_reliefmapping_steps = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping_steps", "10", "relief mapping steps (note: too high values may be not supported by your GPU)"}; cvar_t r_glsl_offsetmapping_scale = {CVAR_SAVE, "r_glsl_offsetmapping_scale", "0.04", "how deep the offset mapping effect is"}; cvar_t r_glsl_postprocess = {CVAR_SAVE, "r_glsl_postprocess", "0", "use a GLSL postprocessing shader"}; cvar_t r_glsl_postprocess_uservec1 = {CVAR_SAVE, "r_glsl_postprocess_uservec1", "0 0 0 0", "a 4-component vector to pass as uservec1 to the postprocessing shader (only useful if default.glsl has been customized)"}; @@ -204,6 +216,8 @@ cvar_t r_overheadsprites_scaley = {CVAR_SAVE, "r_overheadsprites_scaley", "1", " cvar_t r_glsl_saturation = {CVAR_SAVE, "r_glsl_saturation", "1", "saturation multiplier (only working in glsl!)"}; cvar_t r_glsl_saturation_redcompensate = {CVAR_SAVE, "r_glsl_saturation_redcompensate", "0", "a 'vampire sight' addition to desaturation effect, does compensation for red color, r_glsl_restart is required"}; +cvar_t r_glsl_vertextextureblend_usebothalphas = {CVAR_SAVE, "r_glsl_vertextextureblend_usebothalphas", "0", "use both alpha layers on vertex blended surfaces, each alpha layer sets amount of 'blend leak' on another layer."}; + cvar_t r_framedatasize = {CVAR_SAVE, "r_framedatasize", "0.5", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"}; extern cvar_t v_glslgamma; @@ -217,6 +231,13 @@ static struct r_bloomstate_s int bloomwidth, bloomheight; + textype_t texturetype; + int viewfbo; // used to check if r_viewfbo cvar has changed + + int fbo_framebuffer; // non-zero if r_viewfbo is enabled and working + rtexture_t *texture_framebuffercolor; // non-NULL if fbo_screen is non-zero + rtexture_t *texture_framebufferdepth; // non-NULL if fbo_screen is non-zero + int screentexturewidth, screentextureheight; rtexture_t *texture_screen; /// \note also used for motion blur if enabled! @@ -633,6 +654,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] = {"#define USEFOGINSIDE\n", " foginside"}, {"#define USEFOGOUTSIDE\n", " fogoutside"}, {"#define USEFOGHEIGHTTEXTURE\n", " fogheighttexture"}, + {"#define USEFOGALPHAHACK\n", " fogalphahack"}, {"#define USEGAMMARAMPS\n", " gammaramps"}, {"#define USECUBEFILTER\n", " cubefilter"}, {"#define USEGLOW\n", " glow"}, @@ -653,6 +675,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] = {"#define USEREFLECTCUBE\n", " reflectcube"}, {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"}, {"#define USEBOUNCEGRID\n", " bouncegrid"}, + {"#define USEBOUNCEGRIDDIRECTIONAL\n", " bouncegriddirectional"}, }; // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS! @@ -794,7 +817,7 @@ typedef struct r_glsl_permutation_s int loc_LightColor; int loc_LightDir; int loc_LightPosition; - int loc_OffsetMapping_Scale; + int loc_OffsetMapping_ScaleSteps; int loc_PixelSize; int loc_ReflectColor; int loc_ReflectFactor; @@ -840,9 +863,10 @@ enum SHADERSTATICPARM_POSTPROCESS_USERVEC1 = 2, ///< postprocess uservec1 is enabled SHADERSTATICPARM_POSTPROCESS_USERVEC2 = 3, ///< postprocess uservec2 is enabled SHADERSTATICPARM_POSTPROCESS_USERVEC3 = 4, ///< postprocess uservec3 is enabled - SHADERSTATICPARM_POSTPROCESS_USERVEC4 = 5 ///< postprocess uservec4 is enabled + SHADERSTATICPARM_POSTPROCESS_USERVEC4 = 5, ///< postprocess uservec4 is enabled + SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS = 6 // use both alpha layers while blending materials, allows more advanced microblending }; -#define SHADERSTATICPARMS_COUNT 6 +#define SHADERSTATICPARMS_COUNT 7 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT]; static int shaderstaticparms_count = 0; @@ -858,6 +882,8 @@ qboolean R_CompileShader_CheckStaticParms(void) // detect all if (r_glsl_saturation_redcompensate.integer) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_SATURATION_REDCOMPENSATE); + if (r_glsl_vertextextureblend_usebothalphas.integer) + R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS); if (r_shadow_glossexact.integer) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_EXACTSPECULARMATH); if (r_glsl_postprocess.integer) @@ -890,6 +916,7 @@ void R_CompileShader_AddStaticParms(unsigned int mode, unsigned int permutation) R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC2, "USERVEC2"); R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC3, "USERVEC3"); R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC4, "USERVEC4"); + R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS, "USEBOTHALPHAS"); } /// information about each possible shader permutation @@ -979,6 +1006,17 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode strlcat(permutationname, modeinfo->vertexfilename, sizeof(permutationname)); + // if we can do #version 130, we should (this improves quality of offset/reliefmapping thanks to textureGrad) + if(vid.support.gl20shaders130) + { + vertstrings_list[vertstrings_count++] = "#version 130\n"; + geomstrings_list[geomstrings_count++] = "#version 130\n"; + fragstrings_list[fragstrings_count++] = "#version 130\n"; + vertstrings_list[vertstrings_count++] = "#define GLSL130\n"; + geomstrings_list[geomstrings_count++] = "#define GLSL130\n"; + fragstrings_list[fragstrings_count++] = "#define GLSL130\n"; + } + // the first pretext is which type of shader to compile as // (later these will all be bound together as a program object) vertstrings_list[vertstrings_count++] = "#define VERTEX_SHADER\n"; @@ -1096,7 +1134,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->loc_LightColor = qglGetUniformLocation(p->program, "LightColor"); p->loc_LightDir = qglGetUniformLocation(p->program, "LightDir"); p->loc_LightPosition = qglGetUniformLocation(p->program, "LightPosition"); - p->loc_OffsetMapping_Scale = qglGetUniformLocation(p->program, "OffsetMapping_Scale"); + p->loc_OffsetMapping_ScaleSteps = qglGetUniformLocation(p->program, "OffsetMapping_ScaleSteps"); p->loc_PixelSize = qglGetUniformLocation(p->program, "PixelSize"); p->loc_ReflectColor = qglGetUniformLocation(p->program, "ReflectColor"); p->loc_ReflectFactor = qglGetUniformLocation(p->program, "ReflectFactor"); @@ -1313,7 +1351,7 @@ typedef enum D3DPSREGISTER_e D3DPSREGISTER_LightColor = 21, D3DPSREGISTER_LightDir = 22, // unused D3DPSREGISTER_LightPosition = 23, - D3DPSREGISTER_OffsetMapping_Scale = 24, + D3DPSREGISTER_OffsetMapping_ScaleSteps = 24, D3DPSREGISTER_PixelSize = 25, D3DPSREGISTER_ReflectColor = 26, D3DPSREGISTER_ReflectFactor = 27, @@ -1750,7 +1788,7 @@ void R_SetupShader_SetPermutationHLSL(unsigned int mode, unsigned int permutatio void R_SetupShader_SetPermutationSoft(unsigned int mode, unsigned int permutation) { - DPSOFTRAST_SetShader(mode, permutation); + DPSOFTRAST_SetShader(mode, permutation, r_shadow_glossexact.integer); DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f); DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewMatrixM1, 1, false, gl_modelview16f); DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ClientTime, cl.time); @@ -1987,193 +2025,53 @@ extern rtexture_t *r_shadow_prepassgeometrynormalmaptexture; extern rtexture_t *r_shadow_prepassgeometrydepthcolortexture; extern rtexture_t *r_shadow_prepasslightingdiffusetexture; extern rtexture_t *r_shadow_prepasslightingspeculartexture; -static qboolean R_BlendFuncAllowsColormod(int src, int dst) + +#define BLENDFUNC_ALLOWS_COLORMOD 1 +#define BLENDFUNC_ALLOWS_FOG 2 +#define BLENDFUNC_ALLOWS_FOG_HACK0 4 +#define BLENDFUNC_ALLOWS_FOG_HACKALPHA 8 +#define BLENDFUNC_ALLOWS_ANYFOG (BLENDFUNC_ALLOWS_FOG | BLENDFUNC_ALLOWS_FOG_HACK0 | BLENDFUNC_ALLOWS_FOG_HACKALPHA) +static int R_BlendFuncFlags(int src, int dst) { + int r = 0; + // a blendfunc allows colormod if: // a) it can never keep the destination pixel invariant, or // b) it can keep the destination pixel invariant, and still can do so if colormodded // this is to prevent unintended side effects from colormod - // in formulas: - // IF there is a (s, sa) for which for all (d, da), - // s * src(s, d, sa, da) + d * dst(s, d, sa, da) == d - // THEN, for this (s, sa) and all (colormod, d, da): - // s*colormod * src(s*colormod, d, sa, da) + d * dst(s*colormod, d, sa, da) == d - // OBVIOUSLY, this means that - // s*colormod * src(s*colormod, d, sa, da) = 0 - // dst(s*colormod, d, sa, da) = 1 - - // note: not caring about GL_SRC_ALPHA_SATURATE and following here, these are unused in DP code - - // main condition to leave dst color invariant: - // s * src(s, d, sa, da) + d * dst(s, d, sa, da) == d - // src == GL_ZERO: - // s * 0 + d * dst(s, d, sa, da) == d - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is a problem for GL_SRC_COLOR only - // src == GL_ONE: - // s + d * dst(s, d, sa, da) == d - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - // src == GL_SRC_COLOR: - // s*s + d * dst(s, d, sa, da) == d - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - // src == GL_ONE_MINUS_SRC_COLOR: - // s*(1-s) + d * dst(s, d, sa, da) == d - // => s == 0 or s == 1 - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is a problem for GL_SRC_COLOR only - // src == GL_DST_COLOR - // s*d + d * dst(s, d, sa, da) == d - // => s == 1 - // => dst == GL_ZERO/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is always a problem - // or - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - // => BUT, we do not know s! We must assume it is problematic - // then... except in GL_ONE case, where we know all invariant - // cases are fine - // src == GL_ONE_MINUS_DST_COLOR - // s*(1-d) + d * dst(s, d, sa, da) == d - // => s == 0 (1-d is impossible to handle for our desired result) - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - // src == GL_SRC_ALPHA - // s*sa + d * dst(s, d, sa, da) == d - // => s == 0, or sa == 0 - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod breaks in the case GL_SRC_COLOR only - // src == GL_ONE_MINUS_SRC_ALPHA - // s*(1-sa) + d * dst(s, d, sa, da) == d - // => s == 0, or sa == 1 - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod breaks in the case GL_SRC_COLOR only - // src == GL_DST_ALPHA - // s*da + d * dst(s, d, sa, da) == d - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - - switch(src) - { - case GL_ZERO: - case GL_ONE_MINUS_SRC_COLOR: - case GL_SRC_ALPHA: - case GL_ONE_MINUS_SRC_ALPHA: - if(dst == GL_SRC_COLOR) - return false; - return true; - case GL_ONE: - case GL_SRC_COLOR: - case GL_ONE_MINUS_DST_COLOR: - case GL_DST_ALPHA: - case GL_ONE_MINUS_DST_ALPHA: - return true; - case GL_DST_COLOR: - if(dst == GL_ONE) - return true; - return false; - default: - return false; - } -} -static qboolean R_BlendFuncAllowsFog(int src, int dst) -{ // a blendfunc allows fog if: - // a) it can never keep the destination pixel invariant, or - // b) it can keep the destination pixel invariant, and still can do so if fogged - // this is to prevent unintended side effects from colormod - - // main condition to leave dst color invariant: - // s * src(s, d, sa, da) + d * dst(s, d, sa, da) == d - // src == GL_ZERO: - // s * 0 + d * dst(s, d, sa, da) == d - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog is a problem for GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR only - // src == GL_ONE: - // s + d * dst(s, d, sa, da) == d - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog is a problem for all of them, because we require s == 0 - // src == GL_SRC_COLOR: - // s*s + d * dst(s, d, sa, da) == d - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog is a problem for all of them, because we require s == 0 - // src == GL_ONE_MINUS_SRC_COLOR: - // s*(1-s) + d * dst(s, d, sa, da) == d - // => s == 0 or s == 1 - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog is a problem for all of them, because we require s == 0 or s == 1 - // src == GL_DST_COLOR - // s*d + d * dst(s, d, sa, da) == d - // => s == 1 - // => dst == GL_ZERO/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog is a problem for all of them, because we require s == 1 - // or - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - // => BUT, we do not know s! We must assume it is problematic - // then... except in GL_ONE case, where we know all invariant - // cases are fine - // => fog is a problem for all of them, because we require s == 0 or s == 1 - // src == GL_ONE_MINUS_DST_COLOR - // s*(1-d) + d * dst(s, d, sa, da) == d - // => s == 0 (1-d is impossible to handle for our desired result) - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod is never problematic for these - // => fog is a problem for all of them, because we require s == 0 - // src == GL_SRC_ALPHA - // s*sa + d * dst(s, d, sa, da) == d - // => s == 0, or sa == 0 - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog breaks in the case GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR only - // src == GL_ONE_MINUS_SRC_ALPHA - // s*(1-sa) + d * dst(s, d, sa, da) == d - // => s == 0, or sa == 1 - // => dst == GL_ONE/GL_SRC_COLOR/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => colormod breaks in the case GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR only - // src == GL_DST_ALPHA - // s*da + d * dst(s, d, sa, da) == d - // => s == 0 - // => dst == GL_ONE/GL_ONE_MINUS_SRC_COLOR/GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA - // => fog is a problem for all of them, because we require s == 0 - - switch(src) - { - case GL_ZERO: - case GL_SRC_ALPHA: - case GL_ONE_MINUS_SRC_ALPHA: - if(dst == GL_SRC_COLOR || dst == GL_ONE_MINUS_SRC_COLOR) - return false; - return true; - case GL_ONE_MINUS_SRC_COLOR: - case GL_ONE_MINUS_DST_COLOR: - if(dst == GL_ONE || dst == GL_SRC_COLOR || dst == GL_ONE_MINUS_SRC_COLOR || dst == GL_SRC_ALPHA || dst == GL_ONE_MINUS_SRC_ALPHA) - return false; - return true; - case GL_ONE: - case GL_SRC_COLOR: - case GL_DST_ALPHA: - if(dst == GL_ONE || dst == GL_ONE_MINUS_SRC_COLOR || dst == GL_SRC_ALPHA || dst == GL_ONE_MINUS_SRC_ALPHA) - return false; - return true; - case GL_DST_COLOR: - if(dst == GL_ZERO || dst == GL_ONE_MINUS_SRC_COLOR || dst == GL_SRC_ALPHA || dst == GL_ONE_MINUS_SRC_ALPHA) - return false; - return true; - case GL_ONE_MINUS_DST_ALPHA: - return true; - default: - return false; - } + // blend(fog(src), fog(dst)) == fog(blend(src, dst)) + // this is to prevent unintended side effects from fog + + // these checks are the output of fogeval.pl + + r |= BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_DST_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0; + if(src == GL_DST_ALPHA && dst == GL_ONE_MINUS_DST_ALPHA) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_ALPHA) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_COLOR) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_DST_COLOR && dst == GL_SRC_ALPHA) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_DST_COLOR && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_DST_COLOR && dst == GL_ZERO) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_ONE && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0; + if(src == GL_ONE && dst == GL_ONE_MINUS_SRC_ALPHA) r |= BLENDFUNC_ALLOWS_FOG_HACKALPHA; + if(src == GL_ONE && dst == GL_ZERO) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_ONE_MINUS_DST_ALPHA && dst == GL_DST_ALPHA) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_ONE_MINUS_DST_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0; + if(src == GL_ONE_MINUS_DST_COLOR && dst == GL_SRC_COLOR) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_ONE_MINUS_SRC_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0; + if(src == GL_ONE_MINUS_SRC_ALPHA && dst == GL_SRC_ALPHA) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_ONE_MINUS_SRC_ALPHA && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_ONE_MINUS_SRC_COLOR && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + if(src == GL_SRC_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0; + if(src == GL_SRC_ALPHA && dst == GL_ONE_MINUS_SRC_ALPHA) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_ZERO && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG; + if(src == GL_ZERO && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD; + + return r; } + void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane) { // select a permutation of the lighting shader appropriate to this @@ -2182,8 +2080,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, // fragment shader on features that are not being used unsigned int permutation = 0; unsigned int mode = 0; - qboolean allow_colormod; - qboolean allow_fog; + int blendfuncflags; static float dummy_colormod[3] = {1, 1, 1}; float *colormod = rsurface.colormod; float m16f[16]; @@ -2191,43 +2088,39 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, r_waterstate_waterplane_t *waterplane = (r_waterstate_waterplane_t *)surfacewaterplane; if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) permutation |= SHADERPERMUTATION_ALPHAKILL; + if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1]) + permutation |= SHADERPERMUTATION_NORMALMAPSCROLLBLEND; // todo: make generic if (rsurfacepass == RSURFPASS_BACKGROUND) { // distorted background if (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERSHADER) { mode = SHADERMODE_WATER; - if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1]) - permutation |= SHADERPERMUTATION_NORMALMAPSCROLLBLEND; if((r_wateralpha.value < 1) && (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERALPHA)) { // this is the right thing to do for wateralpha GL_BlendFunc(GL_ONE, GL_ZERO); - allow_colormod = R_BlendFuncAllowsColormod(GL_ONE, GL_ZERO); - allow_fog = R_BlendFuncAllowsFog(GL_ONE, GL_ZERO); + blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO); } else { // this is the right thing to do for entity alpha GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - allow_colormod = R_BlendFuncAllowsColormod(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - allow_fog = R_BlendFuncAllowsFog(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFRACTION) { mode = SHADERMODE_REFRACTION; GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - allow_colormod = R_BlendFuncAllowsColormod(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - allow_fog = R_BlendFuncAllowsFog(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { mode = SHADERMODE_GENERIC; permutation |= SHADERPERMUTATION_DIFFUSE; GL_BlendFunc(GL_ONE, GL_ZERO); - allow_colormod = R_BlendFuncAllowsColormod(GL_ONE, GL_ZERO); - allow_fog = R_BlendFuncAllowsFog(GL_ONE, GL_ZERO); + blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO); } } else if (rsurfacepass == RSURFPASS_DEFERREDGEOMETRY) @@ -2252,8 +2145,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND) permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND; GL_BlendFunc(GL_ONE, GL_ZERO); - allow_colormod = R_BlendFuncAllowsColormod(GL_ONE, GL_ZERO); - allow_fog = R_BlendFuncAllowsFog(GL_ONE, GL_ZERO); + blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO); } else if (rsurfacepass == RSURFPASS_RTLIGHT) { @@ -2302,8 +2194,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); - allow_colormod = R_BlendFuncAllowsColormod(GL_SRC_ALPHA, GL_ONE); - allow_fog = R_BlendFuncAllowsFog(GL_SRC_ALPHA, GL_ONE); + blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE); } else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT) { @@ -2348,8 +2239,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_fog = R_BlendFuncAllowsFog(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); + blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT_DIRECTIONAL) { @@ -2398,10 +2288,13 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; if (r_shadow_bouncegridtexture) + { permutation |= SHADERPERMUTATION_BOUNCEGRID; + if (r_shadow_bouncegriddirectional) + permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; + } GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_fog = R_BlendFuncAllowsFog(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); + blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) { @@ -2447,10 +2340,13 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; if (r_shadow_bouncegridtexture) + { permutation |= SHADERPERMUTATION_BOUNCEGRID; + if (r_shadow_bouncegriddirectional) + permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; + } GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_fog = R_BlendFuncAllowsFog(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); + blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } else { @@ -2532,17 +2428,20 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, mode = SHADERMODE_VERTEXCOLOR; } if (r_shadow_bouncegridtexture) + { permutation |= SHADERPERMUTATION_BOUNCEGRID; + if (r_shadow_bouncegriddirectional) + permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; + } GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); - allow_fog = R_BlendFuncAllowsFog(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); + blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } - if(!allow_colormod) + if(!(blendfuncflags & BLENDFUNC_ALLOWS_COLORMOD)) colormod = dummy_colormod; - if(rsurface.texture->currentmaterialflags & MATERIALFLAG_ADD) - allow_fog = allow_colormod; // we actually implement fog by colormodding with a color (f,f,f) for some f - if(!allow_fog) + if(!(blendfuncflags & BLENDFUNC_ALLOWS_ANYFOG)) permutation &= ~(SHADERPERMUTATION_FOGHEIGHTTEXTURE | SHADERPERMUTATION_FOGOUTSIDE | SHADERPERMUTATION_FOGINSIDE); + if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACKALPHA) + permutation |= SHADERPERMUTATION_FOGALPHAHACK; switch(vid.renderpath) { case RENDERPATH_D3D9: @@ -2606,7 +2505,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, hlslPSSetParameter3f(D3DPSREGISTER_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) + if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0) hlslPSSetParameter3f(D3DPSREGISTER_FogColor, 0, 0, 0); else hlslPSSetParameter3f(D3DPSREGISTER_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]); @@ -2638,7 +2537,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, hlslPSSetParameter1f(D3DPSREGISTER_FogPlaneViewDist, rsurface.fogplaneviewdist); hlslPSSetParameter1f(D3DPSREGISTER_FogRangeRecip, rsurface.fograngerecip); hlslPSSetParameter1f(D3DPSREGISTER_FogHeightFade, rsurface.fogheightfade); - hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value); + hlslPSSetParameter3f(D3DPSREGISTER_OffsetMapping_ScaleSteps, r_glsl_offsetmapping_scale.value, max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer), 1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer)); hlslPSSetParameter2f(D3DPSREGISTER_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]); hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height); @@ -2751,7 +2650,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, // additive passes are only darkened by fog, not tinted if (r_glsl_permutation->loc_FogColor >= 0) { - if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ADD) + if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0) qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0); else qglUniform3f(r_glsl_permutation->loc_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]); @@ -2793,11 +2692,11 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (r_glsl_permutation->loc_FogPlaneViewDist >= 0) qglUniform1f(r_glsl_permutation->loc_FogPlaneViewDist, rsurface.fogplaneviewdist); if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1f(r_glsl_permutation->loc_FogRangeRecip, rsurface.fograngerecip); if (r_glsl_permutation->loc_FogHeightFade >= 0) qglUniform1f(r_glsl_permutation->loc_FogHeightFade, rsurface.fogheightfade); - if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale); + if (r_glsl_permutation->loc_OffsetMapping_ScaleSteps >= 0) qglUniform3f(r_glsl_permutation->loc_OffsetMapping_ScaleSteps, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale, max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer), 1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer)); if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2f(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]); if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height); if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegridmatrix, &rsurface.matrix);Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BounceGridMatrix, 1, false, m16f);} - if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegridintensity); + if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegridintensity*r_refdef.view.colorscale); if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , r_texture_white ); if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , r_texture_white ); @@ -2891,7 +2790,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, DPSOFTRAST_Uniform3f(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) + if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0) DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, 0, 0, 0); else DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]); @@ -2932,7 +2831,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_FogPlaneViewDist, rsurface.fogplaneviewdist); DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_FogRangeRecip, rsurface.fograngerecip); DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_FogHeightFade, rsurface.fogheightfade); - DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale); + DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_OffsetMapping_ScaleSteps, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale, max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer), 1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer)); DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]); DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height); @@ -3005,7 +2904,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight) permutation |= SHADERPERMUTATION_CUBEFILTER; if (diffusescale > 0) permutation |= SHADERPERMUTATION_DIFFUSE; - if (specularscale > 0) + if (specularscale > 0 && r_shadow_gloss.integer > 0) permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE; if (r_shadow_usingshadowmap2d) { @@ -3941,6 +3840,7 @@ void gl_main_start(void) case RENDERPATH_D3D10: case RENDERPATH_D3D11: case RENDERPATH_SOFT: + case RENDERPATH_GLES2: Cvar_SetValueQuick(&r_textureunits, vid.texunits); Cvar_SetValueQuick(&gl_combine, 1); Cvar_SetValueQuick(&r_glsl, 1); @@ -3964,14 +3864,6 @@ void gl_main_start(void) r_loadgloss = false; r_loadfog = true; break; - case RENDERPATH_GLES2: - Cvar_SetValueQuick(&r_textureunits, 1); - Cvar_SetValueQuick(&gl_combine, 1); - Cvar_SetValueQuick(&r_glsl, 1); - r_loadnormalmap = true; - r_loadgloss = false; - r_loadfog = false; - break; } R_AnimCache_Free(); @@ -3984,6 +3876,9 @@ void gl_main_start(void) r_qwskincache = NULL; r_qwskincache_size = 0; + // due to caching of texture_t references, the collision cache must be reset + Collision_Cache_Reset(true); + // set up r_skinframe loading system for textures memset(&r_skinframe, 0, sizeof(r_skinframe)); r_skinframe.loadsequence = 1; @@ -4193,6 +4088,7 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_polygonoffset_decals_factor); Cvar_RegisterVariable(&r_polygonoffset_decals_offset); Cvar_RegisterVariable(&r_fog_exp2); + Cvar_RegisterVariable(&r_fog_clear); Cvar_RegisterVariable(&r_drawfog); Cvar_RegisterVariable(&r_transparentdepthmasking); Cvar_RegisterVariable(&r_texture_dds_load); @@ -4204,10 +4100,20 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_texture_convertsRGB_particles); Cvar_RegisterVariable(&r_textureunits); Cvar_RegisterVariable(&gl_combine); + Cvar_RegisterVariable(&r_viewfbo); + Cvar_RegisterVariable(&r_viewscale); + Cvar_RegisterVariable(&r_viewscale_fpsscaling); + Cvar_RegisterVariable(&r_viewscale_fpsscaling_min); + Cvar_RegisterVariable(&r_viewscale_fpsscaling_multiply); + Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepsize); + Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepmax); + Cvar_RegisterVariable(&r_viewscale_fpsscaling_target); Cvar_RegisterVariable(&r_glsl); Cvar_RegisterVariable(&r_glsl_deluxemapping); Cvar_RegisterVariable(&r_glsl_offsetmapping); + Cvar_RegisterVariable(&r_glsl_offsetmapping_steps); Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping); + Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping_steps); Cvar_RegisterVariable(&r_glsl_offsetmapping_scale); Cvar_RegisterVariable(&r_glsl_postprocess); Cvar_RegisterVariable(&r_glsl_postprocess_uservec1); @@ -4252,6 +4158,7 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_test); Cvar_RegisterVariable(&r_glsl_saturation); Cvar_RegisterVariable(&r_glsl_saturation_redcompensate); + Cvar_RegisterVariable(&r_glsl_vertextextureblend_usebothalphas); Cvar_RegisterVariable(&r_framedatasize); if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE) Cvar_SetValue("r_fullbrights", 0); @@ -4724,7 +4631,7 @@ static void R_View_UpdateEntityLighting (void) { if (ent->model->sprite.sprnum_type == SPR_OVERHEAD) // apply offset for overhead sprites org[2] = org[2] + r_overheadsprites_pushback.value; - R_CompleteLightPoint(ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal, org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT); + R_LightPoint(ent->modellight_ambient, org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT); } else R_CompleteLightPoint(ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal, org, LP_LIGHTMAP); @@ -5016,11 +4923,11 @@ 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); break; + case RENDERPATH_SOFT: case RENDERPATH_GL11: case RENDERPATH_GL13: case RENDERPATH_GL20: @@ -5218,10 +5125,29 @@ void R_View_Update(void) R_View_UpdateEntityLighting(); } +float viewscalefpsadjusted = 1.0f; + +void R_GetScaledViewSize(int width, int height, int *outwidth, int *outheight) +{ + float scale = r_viewscale.value * sqrt(viewscalefpsadjusted); + scale = bound(0.03125f, scale, 1.0f); + *outwidth = (int)ceil(width * scale); + *outheight = (int)ceil(height * scale); +} + +void R_Mesh_SetMainRenderTargets(void) +{ + if (r_bloomstate.fbo_framebuffer) + R_Mesh_SetRenderTargets(r_bloomstate.fbo_framebuffer, r_bloomstate.texture_framebufferdepth, r_bloomstate.texture_framebuffercolor, NULL, NULL, NULL); + else + R_Mesh_ResetRenderTargets(); +} + void R_SetupView(qboolean allowwaterclippingplane) { const float *customclipplane = NULL; float plane[4]; + int scaledwidth, scaledheight; if (r_refdef.view.useclipplane && allowwaterclippingplane) { // LordHavoc: couldn't figure out how to make this approach the @@ -5232,17 +5158,29 @@ void R_SetupView(qboolean allowwaterclippingplane) plane[0] = r_refdef.view.clipplane.normal[0]; plane[1] = r_refdef.view.clipplane.normal[1]; plane[2] = r_refdef.view.clipplane.normal[2]; - plane[3] = dist; - customclipplane = plane; + plane[3] = -dist; + if(vid.renderpath != RENDERPATH_SOFT) customclipplane = plane; } + R_GetScaledViewSize(r_refdef.view.width, r_refdef.view.height, &scaledwidth, &scaledheight); if (!r_refdef.view.useperspective) - R_Viewport_InitOrtho(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, -r_refdef.view.ortho_x, -r_refdef.view.ortho_y, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane); + R_Viewport_InitOrtho(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - scaledheight - r_refdef.view.y, scaledwidth, scaledheight, -r_refdef.view.ortho_x, -r_refdef.view.ortho_y, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane); else if (vid.stencil && r_useinfinitefarclip.integer) - R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane); + R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - scaledheight - r_refdef.view.y, scaledwidth, scaledheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane); else - R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane); + R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - scaledheight - r_refdef.view.y, scaledwidth, scaledheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane); + R_Mesh_SetMainRenderTargets(); R_SetViewport(&r_refdef.view.viewport); + if (r_refdef.view.useclipplane && allowwaterclippingplane && vid.renderpath == RENDERPATH_SOFT) + { + matrix4x4_t mvpmatrix, invmvpmatrix, invtransmvpmatrix; + float screenplane[4]; + Matrix4x4_Concat(&mvpmatrix, &r_refdef.view.viewport.projectmatrix, &r_refdef.view.viewport.viewmatrix); + Matrix4x4_Invert_Full(&invmvpmatrix, &mvpmatrix); + Matrix4x4_Transpose(&invtransmvpmatrix, &invmvpmatrix); + Matrix4x4_Transform4(&invtransmvpmatrix, plane, screenplane); + DPSOFTRAST_ClipPlane(screenplane[0], screenplane[1], screenplane[2], screenplane[3]); + } } void R_EntityMatrix(const matrix4x4_t *matrix) @@ -5294,6 +5232,7 @@ void R_ResetViewRendering2D(void) // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, 0, 0, 1, 1, -10, 100, NULL); + R_Mesh_ResetRenderTargets(); R_SetViewport(&viewport); GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height); GL_Color(1, 1, 1, 1); @@ -5736,6 +5675,7 @@ static void R_Water_ProcessPlanes(void) } } + DPSOFTRAST_ClipPlane(0, 0, 0, 1); r_waterstate.renderingscene = false; r_refdef.view = originalview; R_ResetViewRendering3D(); @@ -5753,6 +5693,27 @@ error: void R_Bloom_StartFrame(void) { int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight; + int viewwidth, viewheight; + textype_t textype; + + if (r_viewscale_fpsscaling.integer) + { + double actualframetime; + double targetframetime; + double adjust; + actualframetime = r_refdef.lastdrawscreentime; + targetframetime = (1.0 / r_viewscale_fpsscaling_target.value); + adjust = (targetframetime - actualframetime) * r_viewscale_fpsscaling_multiply.value; + adjust = bound(-r_viewscale_fpsscaling_stepmax.value, adjust, r_viewscale_fpsscaling_stepmax.value); + if (r_viewscale_fpsscaling_stepsize.value > 0) + adjust = (int)(adjust / r_viewscale_fpsscaling_stepsize.value) * r_viewscale_fpsscaling_stepsize.value; + viewscalefpsadjusted += adjust; + viewscalefpsadjusted = bound(r_viewscale_fpsscaling_min.value, viewscalefpsadjusted, 1.0f); + } + else + viewscalefpsadjusted = 1.0f; + + R_GetScaledViewSize(r_refdef.view.width, r_refdef.view.height, &viewwidth, &viewheight); switch(vid.renderpath) { @@ -5779,8 +5740,8 @@ void R_Bloom_StartFrame(void) // calculate desired texture sizes if (vid.support.arb_texture_non_power_of_two) { - screentexturewidth = r_refdef.view.width; - screentextureheight = r_refdef.view.height; + screentexturewidth = vid.width; + screentextureheight = vid.height; bloomtexturewidth = r_bloomstate.bloomwidth; bloomtextureheight = r_bloomstate.bloomheight; } @@ -5800,31 +5761,82 @@ void R_Bloom_StartFrame(void) Cvar_SetValueQuick(&r_damageblur, 0); } - if (!(r_glsl_postprocess.integer || (!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) || (v_glslgamma.integer && !vid_gammatables_trivial)) && !r_bloom.integer && !r_hdr.integer && (R_Stereo_Active() || (r_motionblur.value <= 0 && r_damageblur.value <= 0))) + if (!(r_glsl_postprocess.integer || (!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) || (v_glslgamma.integer && !vid_gammatables_trivial)) && !r_bloom.integer && !r_hdr.integer && (R_Stereo_Active() || (r_motionblur.value <= 0 && r_damageblur.value <= 0)) && r_viewfbo.integer < 1 && r_viewscale.value == 1.0f && !r_viewscale_fpsscaling.integer) screentexturewidth = screentextureheight = 0; if (!r_hdr.integer && !r_bloom.integer) bloomtexturewidth = bloomtextureheight = 0; + textype = TEXTYPE_COLORBUFFER; + switch (vid.renderpath) + { + case RENDERPATH_GL20: + case RENDERPATH_GLES2: + if (vid.support.ext_framebuffer_object) + { + if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F; + if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F; + } + break; + case RENDERPATH_D3D9: + case RENDERPATH_D3D10: + case RENDERPATH_D3D11: + case RENDERPATH_SOFT: + case RENDERPATH_GL13: + case RENDERPATH_GL11: + break; + } + // allocate textures as needed - if (r_bloomstate.screentexturewidth != screentexturewidth || r_bloomstate.screentextureheight != screentextureheight) + if (r_bloomstate.screentexturewidth != screentexturewidth + || r_bloomstate.screentextureheight != screentextureheight + || r_bloomstate.bloomtexturewidth != bloomtexturewidth + || r_bloomstate.bloomtextureheight != bloomtextureheight + || r_bloomstate.texturetype != textype + || r_bloomstate.viewfbo != r_viewfbo.integer) { + if (r_bloomstate.texture_bloom) + R_FreeTexture(r_bloomstate.texture_bloom); + r_bloomstate.texture_bloom = NULL; if (r_bloomstate.texture_screen) R_FreeTexture(r_bloomstate.texture_screen); r_bloomstate.texture_screen = NULL; + if (r_bloomstate.fbo_framebuffer) + R_Mesh_DestroyFramebufferObject(r_bloomstate.fbo_framebuffer); + r_bloomstate.fbo_framebuffer = 0; + if (r_bloomstate.texture_framebuffercolor) + R_FreeTexture(r_bloomstate.texture_framebuffercolor); + r_bloomstate.texture_framebuffercolor = NULL; + if (r_bloomstate.texture_framebufferdepth) + R_FreeTexture(r_bloomstate.texture_framebufferdepth); + r_bloomstate.texture_framebufferdepth = NULL; r_bloomstate.screentexturewidth = screentexturewidth; r_bloomstate.screentextureheight = screentextureheight; if (r_bloomstate.screentexturewidth && r_bloomstate.screentextureheight) - r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP, -1, NULL); - } - if (r_bloomstate.bloomtexturewidth != bloomtexturewidth || r_bloomstate.bloomtextureheight != bloomtextureheight) - { - if (r_bloomstate.texture_bloom) - R_FreeTexture(r_bloomstate.texture_bloom); - r_bloomstate.texture_bloom = NULL; + r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL); + if (r_viewfbo.integer >= 1 && vid.support.ext_framebuffer_object) + { + // FIXME: choose depth bits based on a cvar + r_bloomstate.texture_framebufferdepth = R_LoadTextureShadowMap2D(r_main_texturepool, "framebufferdepth", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, 24, false); + r_bloomstate.texture_framebuffercolor = R_LoadTexture2D(r_main_texturepool, "framebuffercolor", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL); + r_bloomstate.fbo_framebuffer = R_Mesh_CreateFramebufferObject(r_bloomstate.texture_framebufferdepth, r_bloomstate.texture_framebuffercolor, NULL, NULL, NULL); + R_Mesh_SetRenderTargets(r_bloomstate.fbo_framebuffer, r_bloomstate.texture_framebufferdepth, r_bloomstate.texture_framebuffercolor, NULL, NULL, NULL); + // render depth into one texture and normalmap into the other + if (qglDrawBuffer) + { + int status; + qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR + qglReadBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR + status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + Con_Printf("R_Bloom_StartFrame: glCheckFramebufferStatusEXT returned %i\n", status); + } + } r_bloomstate.bloomtexturewidth = bloomtexturewidth; r_bloomstate.bloomtextureheight = bloomtextureheight; if (r_bloomstate.bloomtexturewidth && r_bloomstate.bloomtextureheight) - r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL); + r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL); + r_bloomstate.viewfbo = r_viewfbo.integer; + r_bloomstate.texturetype = textype; } // when doing a reduced render (HDR) we want to use a smaller area @@ -5837,10 +5849,10 @@ void R_Bloom_StartFrame(void) // set up a texcoord array for the full resolution screen image // (we have to keep this around to copy back during final render) r_bloomstate.screentexcoord2f[0] = 0; - r_bloomstate.screentexcoord2f[1] = (float)r_refdef.view.height / (float)r_bloomstate.screentextureheight; - r_bloomstate.screentexcoord2f[2] = (float)r_refdef.view.width / (float)r_bloomstate.screentexturewidth; - r_bloomstate.screentexcoord2f[3] = (float)r_refdef.view.height / (float)r_bloomstate.screentextureheight; - r_bloomstate.screentexcoord2f[4] = (float)r_refdef.view.width / (float)r_bloomstate.screentexturewidth; + r_bloomstate.screentexcoord2f[1] = (float)viewheight / (float)r_bloomstate.screentextureheight; + r_bloomstate.screentexcoord2f[2] = (float)viewwidth / (float)r_bloomstate.screentexturewidth; + r_bloomstate.screentexcoord2f[3] = (float)viewheight / (float)r_bloomstate.screentextureheight; + r_bloomstate.screentexcoord2f[4] = (float)viewwidth / (float)r_bloomstate.screentexturewidth; r_bloomstate.screentexcoord2f[5] = 0; r_bloomstate.screentexcoord2f[6] = 0; r_bloomstate.screentexcoord2f[7] = 0; @@ -5880,13 +5892,16 @@ void R_Bloom_StartFrame(void) break; } - if (r_hdr.integer || r_bloom.integer) + if ((r_hdr.integer || r_bloom.integer) && r_bloomstate.bloomwidth) { r_bloomstate.enabled = true; - r_bloomstate.hdr = r_hdr.integer != 0; + r_bloomstate.hdr = r_hdr.integer != 0 && !r_bloomstate.fbo_framebuffer; } R_Viewport_InitOrtho(&r_bloomstate.viewport, &identitymatrix, r_refdef.view.x, vid.height - r_bloomstate.bloomheight - r_refdef.view.y, r_bloomstate.bloomwidth, r_bloomstate.bloomheight, 0, 0, 1, 1, -10, 100, NULL); + + if (r_bloomstate.fbo_framebuffer) + r_refdef.view.clear = true; } void R_Bloom_CopyBloomTexture(float colorscale) @@ -5895,6 +5910,7 @@ void R_Bloom_CopyBloomTexture(float colorscale) // scale down screen texture to the bloom texture size CHECKGLERROR + R_Mesh_SetMainRenderTargets(); R_SetViewport(&r_bloomstate.viewport); GL_BlendFunc(GL_ONE, GL_ZERO); GL_Color(colorscale, colorscale, colorscale, 1); @@ -5962,7 +5978,7 @@ void R_Bloom_MakeTexture(void) range = r_bloom_blur.integer * r_bloomstate.bloomwidth / 320; brighten = r_bloom_brighten.value; - if (r_hdr.integer) + if (r_bloomstate.hdr) brighten *= r_hdr_range.value; brighten = sqrt(brighten); if(range >= 1) @@ -6097,6 +6113,7 @@ static void R_BlendView(void) if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); } R_ResetViewRendering2D(); + R_Mesh_SetMainRenderTargets(); if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0)) { @@ -6262,7 +6279,7 @@ static void R_BlendView(void) break; } R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); - r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height; + r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height; break; case RENDERPATH_GL13: case RENDERPATH_GL11: @@ -6567,7 +6584,7 @@ void R_RenderView(void) r_refdef.view.clear = true; // this produces a bloom texture to be used in R_BlendView() later - if (r_hdr.integer && r_bloomstate.bloomwidth) + if (r_bloomstate.hdr) { R_HDR_RenderBloomTexture(); // we have to bump the texture frame again because r_refdef.view.colorscale is cached in the textures @@ -7623,7 +7640,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) blendfunc2 = GL_ZERO; } // don't colormod evilblend textures - if(!R_BlendFuncAllowsColormod(blendfunc1, blendfunc2)) + if(!R_BlendFuncFlags(blendfunc1, blendfunc2) & BLENDFUNC_ALLOWS_COLORMOD) VectorSet(t->lightmapcolor, 1, 1, 1); depthmask = !(t->currentmaterialflags & MATERIALFLAG_BLENDED); if (t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT) @@ -9316,6 +9333,12 @@ static void RSurf_DrawBatch_GL11_MakeFogColor(float r, float g, float b, float a float f; const float *v; float *c; + + // fake shading + rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4])); + rsurface.passcolor4f_vertexbuffer = 0; + rsurface.passcolor4f_bufferoffset = 0; + for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4) { f = 1 - RSurf_FogVertex(v); @@ -10407,11 +10430,23 @@ static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, flo vertex3f = rsurface.modelvertex3f; normal3f = rsurface.modelnormal3f; - for (cornerindex = 0;cornerindex < 3;cornerindex++) + if (normal3f) { - index = 3*e[cornerindex]; - VectorMA(vertex3f + index, cl_decals_bias.value, normal3f + index, v[cornerindex]); + for (cornerindex = 0;cornerindex < 3;cornerindex++) + { + index = 3*e[cornerindex]; + VectorMA(vertex3f + index, cl_decals_bias.value, normal3f + index, v[cornerindex]); + } } + else + { + for (cornerindex = 0;cornerindex < 3;cornerindex++) + { + index = 3*e[cornerindex]; + VectorCopy(vertex3f + index, v[cornerindex]); + } + } + // cull backfaces //TriangleNormal(v[0], v[1], v[2], normal); //if (DotProduct(normal, localnormal) < 0.0f)