X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=gl_rmain.c;h=349b78e58c41df61c960f293a3506414123f5f0c;hp=cb376db7e6075e6850839b512537ce8bde1c4441;hb=f5e4d031fd609e6a6ab36559e599211aa9ac51c3;hpb=44e8d40924a848e5dee64d310f60e39917bf061a diff --git a/gl_rmain.c b/gl_rmain.c index cb376db7..349b78e5 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -34,6 +34,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern LPDIRECT3DDEVICE9 vid_d3d9dev; #endif +#ifdef WIN32 +// Enable NVIDIA High Performance Graphics while using Integrated Graphics. +#ifdef __cplusplus +extern "C" { +#endif +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +#ifdef __cplusplus +} +#endif +#endif + mempool_t *r_main_mempool; rtexturepool_t *r_main_texturepool; @@ -44,14 +55,15 @@ static qboolean r_loadgloss; qboolean r_loadfog; static qboolean r_loaddds; static qboolean r_savedds; +static qboolean r_gpuskeletal; // // screen size info // r_refdef_t r_refdef; -cvar_t r_motionblur = {CVAR_SAVE, "r_motionblur", "0", "screen motionblur - value represents intensity, somewhere around 0.5 recommended"}; -cvar_t r_damageblur = {CVAR_SAVE, "r_damageblur", "0", "screen motionblur based on damage - value represents intensity, somewhere around 0.5 recommended"}; +cvar_t r_motionblur = {CVAR_SAVE, "r_motionblur", "0", "screen motionblur - value represents intensity, somewhere around 0.5 recommended - NOTE: bad performance on multi-gpu!"}; +cvar_t r_damageblur = {CVAR_SAVE, "r_damageblur", "0", "screen motionblur based on damage - value represents intensity, somewhere around 0.5 recommended - NOTE: bad performance on multi-gpu!"}; cvar_t r_motionblur_averaging = {CVAR_SAVE, "r_motionblur_averaging", "0.1", "sliding average reaction time for velocity (higher = slower adaption to change)"}; cvar_t r_motionblur_randomize = {CVAR_SAVE, "r_motionblur_randomize", "0.1", "randomizing coefficient to workaround ghosting"}; cvar_t r_motionblur_minblur = {CVAR_SAVE, "r_motionblur_minblur", "0.5", "factor of blur to apply at all times (always have this amount of blur no matter what the other factors are)"}; @@ -120,6 +132,7 @@ cvar_t r_shadows_drawafterrtlighting = {CVAR_SAVE, "r_shadows_drawafterrtlightin cvar_t r_shadows_castfrombmodels = {CVAR_SAVE, "r_shadows_castfrombmodels", "0", "do cast shadows from bmodels"}; cvar_t r_shadows_focus = {CVAR_SAVE, "r_shadows_focus", "0 0 0", "offset the shadowed area focus"}; cvar_t r_shadows_shadowmapscale = {CVAR_SAVE, "r_shadows_shadowmapscale", "1", "increases shadowmap quality (multiply global shadowmap precision) for fake shadows. Needs shadowmapping ON."}; +cvar_t r_shadows_shadowmapbias = {CVAR_SAVE, "r_shadows_shadowmapbias", "-1", "sets shadowmap bias for fake shadows. -1 sets the value of r_shadow_shadowmapping_bias. Needs shadowmapping ON."}; cvar_t r_q1bsp_skymasking = {0, "r_q1bsp_skymasking", "1", "allows sky polygons in quake1 maps to obscure other geometry"}; cvar_t r_polygonoffset_submodel_factor = {0, "r_polygonoffset_submodel_factor", "0", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"}; cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset", "14", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"}; @@ -132,6 +145,8 @@ cvar_t r_transparentdepthmasking = {CVAR_SAVE, "r_transparentdepthmasking", "0", cvar_t r_transparent_sortmindist = {CVAR_SAVE, "r_transparent_sortmindist", "0", "lower distance limit for transparent sorting"}; cvar_t r_transparent_sortmaxdist = {CVAR_SAVE, "r_transparent_sortmaxdist", "32768", "upper distance limit for transparent sorting"}; cvar_t r_transparent_sortarraysize = {CVAR_SAVE, "r_transparent_sortarraysize", "4096", "number of distance-sorting layers"}; +cvar_t r_celshading = {CVAR_SAVE, "r_celshading", "0", "cartoon-style light shading (OpenGL 2.x only)"}; // FIXME remove OpenGL 2.x only once implemented for DX9 +cvar_t r_celoutlines = {CVAR_SAVE, "r_celoutlines", "0", "cartoon-style outlines (requires r_shadow_deferred; OpenGL 2.x only)"}; // FIXME remove OpenGL 2.x only once implemented for DX9 cvar_t gl_fogenable = {0, "gl_fogenable", "0", "nehahra fog enable (for Nehahra compatibility only)"}; cvar_t gl_fogdensity = {0, "gl_fogdensity", "0.25", "nehahra fog density (recommend values below 0.1) (for Nehahra compatibility only)"}; @@ -159,6 +174,7 @@ cvar_t r_viewscale_fpsscaling_stepsize = {CVAR_SAVE, "r_viewscale_fpsscaling_ste 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_skeletal = {CVAR_SAVE, "r_glsl_skeletal", "1", "render skeletal models faster using a gpu-skinning technique"}; 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)"}; @@ -179,6 +195,7 @@ cvar_t r_glsl_postprocess_uservec3_enable = {CVAR_SAVE, "r_glsl_postprocess_user cvar_t r_glsl_postprocess_uservec4_enable = {CVAR_SAVE, "r_glsl_postprocess_uservec4_enable", "1", "enables postprocessing uservec4 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"}; cvar_t r_water = {CVAR_SAVE, "r_water", "0", "whether to use reflections and refraction on water surfaces (note: r_wateralpha must be set below 1)"}; +cvar_t r_water_cameraentitiesonly = {CVAR_SAVE, "r_water_cameraentitiesonly", "0", "whether to only show QC-defined reflections/refractions (typically used for camera- or portal-like effects)"}; cvar_t r_water_clippingplanebias = {CVAR_SAVE, "r_water_clippingplanebias", "1", "a rather technical setting which avoids black pixels around water edges"}; cvar_t r_water_resolutionmultiplier = {CVAR_SAVE, "r_water_resolutionmultiplier", "0.5", "multiplier for screen resolution when rendering refracted/reflected scenes, 1 is full quality, lower values are faster"}; cvar_t r_water_refractdistort = {CVAR_SAVE, "r_water_refractdistort", "0.01", "how much water refractions shimmer"}; @@ -201,6 +218,7 @@ cvar_t r_bloom_blur = {CVAR_SAVE, "r_bloom_blur", "4", "how large the glow is"}; cvar_t r_bloom_resolution = {CVAR_SAVE, "r_bloom_resolution", "320", "what resolution to perform the bloom effect at (independent of screen resolution)"}; cvar_t r_bloom_colorexponent = {CVAR_SAVE, "r_bloom_colorexponent", "1", "how exaggerated the glow is"}; cvar_t r_bloom_colorsubtract = {CVAR_SAVE, "r_bloom_colorsubtract", "0.125", "reduces bloom colors by a certain amount"}; +cvar_t r_bloom_scenebrightness = {CVAR_SAVE, "r_bloom_scenebrightness", "1", "global rendering brightness when bloom is enabled"}; cvar_t r_hdr_scenebrightness = {CVAR_SAVE, "r_hdr_scenebrightness", "1", "global rendering brightness"}; cvar_t r_hdr_glowintensity = {CVAR_SAVE, "r_hdr_glowintensity", "1", "how bright light emitting textures should appear"}; @@ -217,16 +235,28 @@ cvar_t r_smoothnormals_areaweighting = {0, "r_smoothnormals_areaweighting", "1", cvar_t developer_texturelogging = {0, "developer_texturelogging", "0", "produces a textures.log file containing names of skins and map textures the engine tried to load"}; -cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture (for level designers)"}; +cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture (for level designers), a value of 2 keeps normalmap shading"}; cvar_t r_test = {0, "r_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"}; +cvar_t r_batch_multidraw = {CVAR_SAVE, "r_batch_multidraw", "1", "issue multiple glDrawElements calls when rendering a batch of surfaces with the same texture (otherwise the index data is copied to make it one draw)"}; +cvar_t r_batch_multidraw_mintriangles = {CVAR_SAVE, "r_batch_multidraw_mintriangles", "0", "minimum number of triangles to activate multidraw path (copying small groups of triangles may be faster)"}; +cvar_t r_batch_debugdynamicvertexpath = {CVAR_SAVE, "r_batch_debugdynamicvertexpath", "0", "force the dynamic batching code path for debugging purposes"}; +cvar_t r_batch_dynamicbuffer = {CVAR_SAVE, "r_batch_dynamicbuffer", "0", "use vertex/index buffers for drawing dynamic and copytriangles batches"}; + 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_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, requires mod_q3shader_force_terrain_alphaflag on."}; 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)"}; +cvar_t r_buffermegs[R_BUFFERDATA_COUNT] = +{ + {CVAR_SAVE, "r_buffermegs_vertex", "4", "vertex buffer size for one frame"}, + {CVAR_SAVE, "r_buffermegs_index16", "1", "index buffer size for one frame (16bit indices)"}, + {CVAR_SAVE, "r_buffermegs_index32", "1", "index buffer size for one frame (32bit indices)"}, + {CVAR_SAVE, "r_buffermegs_uniform", "0.25", "uniform buffer size for one frame"}, +}; extern cvar_t v_glslgamma; extern cvar_t v_glslgamma_2d; @@ -238,6 +268,8 @@ r_framebufferstate_t r_fb; /// shadow volume bsp struct with automatically growing nodes buffer svbsp_t r_svbsp; +int r_uniformbufferalignment = 32; // dynamically updated to match GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT + rtexture_t *r_texture_blanknormalmap; rtexture_t *r_texture_white; rtexture_t *r_texture_grey128; @@ -593,16 +625,17 @@ static void R_BuildFogHeightTexture(void) //======================================================================================================================================================= -static const char *builtinshaderstring = +static const char *builtinshaderstrings[] = +{ #include "shader_glsl.h" -; +0 +}; -const char *builtinhlslshaderstring = +const char *builtinhlslshaderstrings[] = +{ #include "shader_hlsl.h" -; - -char *glslshaderstring = NULL; -char *hlslshaderstring = NULL; +0 +}; //======================================================================================================================================================= @@ -615,11 +648,14 @@ shaderpermutationinfo_t; typedef struct shadermodeinfo_s { - const char *vertexfilename; - const char *geometryfilename; - const char *fragmentfilename; + const char *sourcebasename; + const char *extension; + const char **builtinshaderstrings; const char *pretext; const char *name; + char *filename; + char *builtinstring; + int builtincrc; } shadermodeinfo_t; @@ -655,52 +691,54 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] = {"#define USEBOUNCEGRIDDIRECTIONAL\n", " bouncegriddirectional"}, // TODO make this a static parm {"#define USETRIPPY\n", " trippy"}, {"#define USEDEPTHRGB\n", " depthrgb"}, - {"#define USEALPHAGENVERTEX\n", "alphagenvertex"} + {"#define USEALPHAGENVERTEX\n", " alphagenvertex"}, + {"#define USESKELETAL\n", " skeletal"}, + {"#define USEOCCLUDE\n", " occlude"} }; // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS! -shadermodeinfo_t glslshadermodeinfo[SHADERMODE_COUNT] = -{ - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_GENERIC\n", " generic"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_POSTPROCESS\n", " postprocess"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_FLATCOLOR\n", " flatcolor"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_VERTEXCOLOR\n", " vertexcolor"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTMAP\n", " lightmap"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_FAKELIGHT\n", " fakelight"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTION\n", " lightdirection"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTSOURCE\n", " lightsource"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_REFRACTION\n", " refraction"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_WATER\n", " water"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_SHOWDEPTH\n", " showdepth"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_DEFERREDGEOMETRY\n", " deferredgeometry"}, - {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"}, -}; - -shadermodeinfo_t hlslshadermodeinfo[SHADERMODE_COUNT] = -{ - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_GENERIC\n", " generic"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_POSTPROCESS\n", " postprocess"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_FLATCOLOR\n", " flatcolor"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_VERTEXCOLOR\n", " vertexcolor"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTMAP\n", " lightmap"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_FAKELIGHT\n", " fakelight"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTION\n", " lightdirection"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTSOURCE\n", " lightsource"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_REFRACTION\n", " refraction"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_WATER\n", " water"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_SHOWDEPTH\n", " showdepth"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_DEFERREDGEOMETRY\n", " deferredgeometry"}, - {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"}, +shadermodeinfo_t shadermodeinfo[SHADERLANGUAGE_COUNT][SHADERMODE_COUNT] = +{ + // SHADERLANGUAGE_GLSL + { + {"combined", "glsl", builtinshaderstrings, "#define MODE_GENERIC\n", " generic"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_POSTPROCESS\n", " postprocess"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_FLATCOLOR\n", " flatcolor"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_VERTEXCOLOR\n", " vertexcolor"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTMAP\n", " lightmap"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_FAKELIGHT\n", " fakelight"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTION\n", " lightdirection"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTSOURCE\n", " lightsource"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_REFRACTION\n", " refraction"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_WATER\n", " water"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_DEFERREDGEOMETRY\n", " deferredgeometry"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"}, + }, + // SHADERLANGUAGE_HLSL + { + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_GENERIC\n", " generic"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_POSTPROCESS\n", " postprocess"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_FLATCOLOR\n", " flatcolor"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_VERTEXCOLOR\n", " vertexcolor"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTMAP\n", " lightmap"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_FAKELIGHT\n", " fakelight"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTDIRECTION\n", " lightdirection"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_LIGHTSOURCE\n", " lightsource"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_REFRACTION\n", " refraction"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_WATER\n", " water"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_DEFERREDGEOMETRY\n", " deferredgeometry"}, + {"combined", "hlsl", builtinhlslshaderstrings, "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"}, + }, }; struct r_glsl_permutation_s; @@ -814,6 +852,7 @@ typedef struct r_glsl_permutation_s int loc_ShadowMap_Parameters; int loc_ShadowMap_TextureScale; int loc_SpecularPower; + int loc_Skeletal_Transform12; int loc_UserVec1; int loc_UserVec2; int loc_UserVec3; @@ -832,6 +871,10 @@ typedef struct r_glsl_permutation_s int loc_NormalmapScrollBlend; int loc_BounceGridMatrix; int loc_BounceGridIntensity; + /// uniform block bindings + int ubibind_Skeletal_Transform12_UniformBlock; + /// uniform block indices + int ubiloc_Skeletal_Transform12_UniformBlock; } r_glsl_permutation_t; @@ -853,8 +896,11 @@ enum SHADERSTATICPARM_SHADOWMAPPCF_1 = 8, ///< PCF 1 SHADERSTATICPARM_SHADOWMAPPCF_2 = 9, ///< PCF 2 SHADERSTATICPARM_SHADOWSAMPLER = 10, ///< sampler + SHADERSTATICPARM_CELSHADING = 11, ///< celshading (alternative diffuse and specular math) + SHADERSTATICPARM_CELOUTLINES = 12, ///< celoutline (depth buffer analysis to produce outlines) + SHADERSTATICPARM_FXAA = 13 ///< fast approximate anti aliasing }; -#define SHADERSTATICPARMS_COUNT 11 +#define SHADERSTATICPARMS_COUNT 14 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT]; static int shaderstaticparms_count = 0; @@ -866,7 +912,7 @@ extern qboolean r_shadow_shadowmapsampler; extern int r_shadow_shadowmappcf; qboolean R_CompileShader_CheckStaticParms(void) { - static int r_compileshader_staticparms_save[1]; + static int r_compileshader_staticparms_save[(SHADERSTATICPARMS_COUNT + 0x1F) >> 5]; memcpy(r_compileshader_staticparms_save, r_compileshader_staticparms, sizeof(r_compileshader_staticparms)); memset(r_compileshader_staticparms, 0, sizeof(r_compileshader_staticparms)); @@ -888,6 +934,8 @@ qboolean R_CompileShader_CheckStaticParms(void) if (r_glsl_postprocess_uservec4_enable.integer) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC4); } + if (r_fxaa.integer) + R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_FXAA); if (r_glsl_offsetmapping_lod.integer && r_glsl_offsetmapping_lod_distance.integer > 0) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_OFFSETMAPPING_USELOD); @@ -897,6 +945,10 @@ qboolean R_CompileShader_CheckStaticParms(void) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_SHADOWMAPPCF_2); else if (r_shadow_shadowmappcf) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_SHADOWMAPPCF_1); + if (r_celshading.integer) + R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_CELSHADING); + if (r_celoutlines.integer) + R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_CELOUTLINES); return memcmp(r_compileshader_staticparms, r_compileshader_staticparms_save, sizeof(r_compileshader_staticparms)) != 0; } @@ -922,6 +974,9 @@ static void R_CompileShader_AddStaticParms(unsigned int mode, unsigned int permu R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_SHADOWMAPPCF_1, "USESHADOWMAPPCF 1"); R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_SHADOWMAPPCF_2, "USESHADOWMAPPCF 2"); R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_SHADOWSAMPLER, "USESHADOWSAMPLER"); + R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELSHADING, "USECELSHADING"); + R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELOUTLINES, "USECELOUTLINES"); + R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_FXAA, "USEFXAA"); } /// information about each possible shader permutation @@ -956,41 +1011,72 @@ static r_glsl_permutation_t *R_GLSL_FindPermutation(unsigned int mode, unsigned return p; } -static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice) +static char *R_ShaderStrCat(const char **strings) { - char *shaderstring; - if (!filename || !filename[0]) - return NULL; - if (!strcmp(filename, "glsl/default.glsl")) + char *string, *s; + const char **p = strings; + const char *t; + size_t len = 0; + for (p = strings;(t = *p);p++) + len += strlen(t); + len++; + s = string = (char *)Mem_Alloc(r_main_mempool, len); + len = 0; + for (p = strings;(t = *p);p++) + { + len = strlen(t); + memcpy(s, t, len); + s += len; + } + *s = 0; + return string; +} + +static char *R_ShaderStrCat(const char **strings); +static void R_InitShaderModeInfo(void) +{ + int i, language; + shadermodeinfo_t *modeinfo; + // we have a bunch of things to compute that weren't calculated at engine compile time - all filenames should have a crc of the builtin strings to prevent accidental overrides (any customization must be updated to match engine) + for (language = 0; language < SHADERLANGUAGE_COUNT; language++) { - if (!glslshaderstring) + for (i = 0; i < SHADERMODE_COUNT; i++) { - glslshaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL); - if (glslshaderstring) - Con_DPrintf("Loading shaders from file %s...\n", filename); - else - glslshaderstring = (char *)builtinshaderstring; + char filename[MAX_QPATH]; + modeinfo = &shadermodeinfo[language][i]; + modeinfo->builtinstring = R_ShaderStrCat(modeinfo->builtinshaderstrings); + modeinfo->builtincrc = CRC_Block((const unsigned char *)modeinfo->builtinstring, strlen(modeinfo->builtinstring)); + dpsnprintf(filename, sizeof(filename), "%s/%s_crc%i.%s", modeinfo->extension, modeinfo->sourcebasename, modeinfo->builtincrc, modeinfo->extension); + modeinfo->filename = Mem_strdup(r_main_mempool, filename); } - shaderstring = (char *) Mem_Alloc(r_main_mempool, strlen(glslshaderstring) + 1); - memcpy(shaderstring, glslshaderstring, strlen(glslshaderstring) + 1); - return shaderstring; } - shaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL); +} + +static char *ShaderModeInfo_GetShaderText(shadermodeinfo_t *modeinfo, qboolean printfromdisknotice, qboolean builtinonly) +{ + char *shaderstring; + // if the mode has no filename we have to return the builtin string + if (builtinonly || !modeinfo->filename) + return Mem_strdup(r_main_mempool, modeinfo->builtinstring); + // note that FS_LoadFile appends a 0 byte to make it a valid string + shaderstring = (char *)FS_LoadFile(modeinfo->filename, r_main_mempool, false, NULL); if (shaderstring) { if (printfromdisknotice) - Con_DPrintf("from disk %s... ", filename); + Con_DPrintf("Loading shaders from file %s...\n", modeinfo->filename); return shaderstring; } - return shaderstring; + // fall back to builtinstring + return Mem_strdup(r_main_mempool, modeinfo->builtinstring); } static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode, unsigned int permutation) { int i; + int ubibind; int sampler; - shadermodeinfo_t *modeinfo = glslshadermodeinfo + mode; - char *vertexstring, *geometrystring, *fragmentstring; + shadermodeinfo_t *modeinfo = &shadermodeinfo[SHADERLANGUAGE_GLSL][mode]; + char *sourcestring; char permutationname[256]; int vertstrings_count = 0; int geomstrings_count = 0; @@ -1005,14 +1091,22 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->program = 0; permutationname[0] = 0; - vertexstring = R_GLSL_GetText(modeinfo->vertexfilename, true); - geometrystring = R_GLSL_GetText(modeinfo->geometryfilename, false); - fragmentstring = R_GLSL_GetText(modeinfo->fragmentfilename, false); + sourcestring = ShaderModeInfo_GetShaderText(modeinfo, true, false); - strlcat(permutationname, modeinfo->vertexfilename, sizeof(permutationname)); + strlcat(permutationname, modeinfo->filename, sizeof(permutationname)); + // we need 140 for r_glsl_skeletal (GL_ARB_uniform_buffer_object) + if(vid.support.glshaderversion >= 140) + { + vertstrings_list[vertstrings_count++] = "#version 140\n"; + geomstrings_list[geomstrings_count++] = "#version 140\n"; + fragstrings_list[fragstrings_count++] = "#version 140\n"; + vertstrings_list[vertstrings_count++] = "#define GLSL140\n"; + geomstrings_list[geomstrings_count++] = "#define GLSL140\n"; + fragstrings_list[fragstrings_count++] = "#define GLSL140\n"; + } // if we can do #version 130, we should (this improves quality of offset/reliefmapping thanks to textureGrad) - if(vid.support.gl20shaders130) + else if(vid.support.glshaderversion >= 130) { vertstrings_list[vertstrings_count++] = "#version 130\n"; geomstrings_list[geomstrings_count++] = "#version 130\n"; @@ -1063,17 +1157,9 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode fragstrings_count += shaderstaticparms_count; // now append the shader text itself - vertstrings_list[vertstrings_count++] = vertexstring; - geomstrings_list[geomstrings_count++] = geometrystring; - fragstrings_list[fragstrings_count++] = fragmentstring; - - // if any sources were NULL, clear the respective list - if (!vertexstring) - vertstrings_count = 0; - if (!geometrystring) - geomstrings_count = 0; - if (!fragmentstring) - fragstrings_count = 0; + vertstrings_list[vertstrings_count++] = sourcestring; + geomstrings_list[geomstrings_count++] = sourcestring; + fragstrings_list[fragstrings_count++] = sourcestring; // compile the shader program if (vertstrings_count + geomstrings_count + fragstrings_count) @@ -1085,6 +1171,26 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode // look up all the uniform variable names we care about, so we don't // have to look them up every time we set them +#if 0 + // debugging aid + { + GLint activeuniformindex = 0; + GLint numactiveuniforms = 0; + char uniformname[128]; + GLsizei uniformnamelength = 0; + GLint uniformsize = 0; + GLenum uniformtype = 0; + memset(uniformname, 0, sizeof(uniformname)); + qglGetProgramiv(p->program, GL_ACTIVE_UNIFORMS, &numactiveuniforms); + Con_Printf("Shader has %i uniforms\n", numactiveuniforms); + for (activeuniformindex = 0;activeuniformindex < numactiveuniforms;activeuniformindex++) + { + qglGetActiveUniform(p->program, activeuniformindex, sizeof(uniformname) - 1, &uniformnamelength, &uniformsize, &uniformtype, uniformname); + Con_Printf("Uniform %i name \"%s\" size %i type %i\n", (int)activeuniformindex, uniformname, (int)uniformsize, (int)uniformtype); + } + } +#endif + p->loc_Texture_First = qglGetUniformLocation(p->program, "Texture_First"); p->loc_Texture_Second = qglGetUniformLocation(p->program, "Texture_Second"); p->loc_Texture_GammaRamps = qglGetUniformLocation(p->program, "Texture_GammaRamps"); @@ -1201,6 +1307,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->tex_Texture_ReflectMask = -1; p->tex_Texture_ReflectCube = -1; p->tex_Texture_BounceGrid = -1; + // bind the texture samplers in use sampler = 0; if (p->loc_Texture_First >= 0) {p->tex_Texture_First = sampler;qglUniform1i(p->loc_Texture_First , sampler);sampler++;} if (p->loc_Texture_Second >= 0) {p->tex_Texture_Second = sampler;qglUniform1i(p->loc_Texture_Second , sampler);sampler++;} @@ -1231,6 +1338,21 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode if (p->loc_Texture_ReflectMask >= 0) {p->tex_Texture_ReflectMask = sampler;qglUniform1i(p->loc_Texture_ReflectMask , sampler);sampler++;} if (p->loc_Texture_ReflectCube >= 0) {p->tex_Texture_ReflectCube = sampler;qglUniform1i(p->loc_Texture_ReflectCube , sampler);sampler++;} if (p->loc_Texture_BounceGrid >= 0) {p->tex_Texture_BounceGrid = sampler;qglUniform1i(p->loc_Texture_BounceGrid , sampler);sampler++;} + // get the uniform block indices so we can bind them +#ifndef USE_GLES2 /* FIXME: GLES3 only */ + if (vid.support.arb_uniform_buffer_object) + p->ubiloc_Skeletal_Transform12_UniformBlock = qglGetUniformBlockIndex(p->program, "Skeletal_Transform12_UniformBlock"); + else +#endif + p->ubiloc_Skeletal_Transform12_UniformBlock = -1; + // clear the uniform block bindings + p->ubibind_Skeletal_Transform12_UniformBlock = -1; + // bind the uniform blocks in use + ubibind = 0; +#ifndef USE_GLES2 /* FIXME: GLES3 only */ + if (p->ubiloc_Skeletal_Transform12_UniformBlock >= 0) {p->ubibind_Skeletal_Transform12_UniformBlock = ubibind;qglUniformBlockBinding(p->program, p->ubiloc_Skeletal_Transform12_UniformBlock, ubibind);ubibind++;} +#endif + // we're done compiling and setting up the shader, at least until it is used CHECKGLERROR Con_DPrintf("^5GLSL shader %s compiled (%i textures).\n", permutationname, sampler); } @@ -1238,12 +1360,8 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode Con_Printf("^1GLSL shader %s failed! some features may not work properly.\n", permutationname); // free the strings - if (vertexstring) - Mem_Free(vertexstring); - if (geometrystring) - Mem_Free(geometrystring); - if (fragmentstring) - Mem_Free(fragmentstring); + if (sourcestring) + Mem_Free(sourcestring); } static void R_SetupShader_SetPermutationGLSL(unsigned int mode, unsigned int permutation) @@ -1255,7 +1373,10 @@ static void R_SetupShader_SetPermutationGLSL(unsigned int mode, unsigned int per if (!r_glsl_permutation->program) { if (!r_glsl_permutation->compiled) + { + Con_DPrintf("Compiling shader mode %u permutation %u\n", mode, permutation); R_GLSL_CompilePermutation(perm, mode, permutation); + } if (!r_glsl_permutation->program) { // remove features until we find a valid permutation @@ -1275,7 +1396,7 @@ static void R_SetupShader_SetPermutationGLSL(unsigned int mode, unsigned int per } if (i >= SHADERPERMUTATION_COUNT) { - //Con_Printf("Could not find a working OpenGL 2.0 shader for permutation %s %s\n", shadermodeinfo[mode].vertexfilename, shadermodeinfo[mode].pretext); + //Con_Printf("Could not find a working OpenGL 2.0 shader for permutation %s %s\n", shadermodeinfo[mode].filename, shadermodeinfo[mode].pretext); r_glsl_permutation = R_GLSL_FindPermutation(mode, permutation); qglUseProgram(0);CHECKGLERROR return; // no bit left to clear, entire mode is broken @@ -1288,6 +1409,7 @@ static void R_SetupShader_SetPermutationGLSL(unsigned int mode, unsigned int per if (r_glsl_permutation->loc_ModelViewProjectionMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewProjectionMatrix, 1, false, gl_modelviewprojection16f); if (r_glsl_permutation->loc_ModelViewMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewMatrix, 1, false, gl_modelview16f); if (r_glsl_permutation->loc_ClientTime >= 0) qglUniform1f(r_glsl_permutation->loc_ClientTime, cl.time); + CHECKGLERROR } #ifdef SUPPORTD3D @@ -1416,35 +1538,6 @@ static r_hlsl_permutation_t *R_HLSL_FindPermutation(unsigned int mode, unsigned return p; } -static char *R_HLSL_GetText(const char *filename, qboolean printfromdisknotice) -{ - char *shaderstring; - if (!filename || !filename[0]) - return NULL; - if (!strcmp(filename, "hlsl/default.hlsl")) - { - if (!hlslshaderstring) - { - hlslshaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL); - if (hlslshaderstring) - Con_DPrintf("Loading shaders from file %s...\n", filename); - else - hlslshaderstring = (char *)builtinhlslshaderstring; - } - shaderstring = (char *) Mem_Alloc(r_main_mempool, strlen(hlslshaderstring) + 1); - memcpy(shaderstring, hlslshaderstring, strlen(hlslshaderstring) + 1); - return shaderstring; - } - shaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL); - if (shaderstring) - { - if (printfromdisknotice) - Con_DPrintf("from disk %s... ", filename); - return shaderstring; - } - return shaderstring; -} - #include //#include //#include @@ -1513,6 +1606,18 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c {"D3DXCompileShader", (void **) &qD3DXCompileShader}, {NULL, NULL} }; + // LordHavoc: the June 2010 SDK lacks these macros to make ID3DXBuffer usable in C, and to make it work in both C and C++ the macros are needed... +#ifndef ID3DXBuffer_GetBufferPointer +#if !defined(__cplusplus) || defined(CINTERFACE) +#define ID3DXBuffer_GetBufferPointer(p) (p)->lpVtbl->GetBufferPointer(p) +#define ID3DXBuffer_GetBufferSize(p) (p)->lpVtbl->GetBufferSize(p) +#define ID3DXBuffer_Release(p) (p)->lpVtbl->Release(p) +#else +#define ID3DXBuffer_GetBufferPointer(p) (p)->GetBufferPointer() +#define ID3DXBuffer_GetBufferSize(p) (p)->GetBufferSize() +#define ID3DXBuffer_Release(p) (p)->Release() +#endif +#endif if (Sys_LoadLibrary(dllnames_d3dx9, &d3dx9_dll, d3dx9_dllfuncs)) { DWORD shaderflags = 0; @@ -1524,50 +1629,46 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c { if (debugshader) { -// vsresult = qD3DXPreprocessShader(vertstring, strlen(vertstring), NULL, NULL, &vsbuffer, &vslog); -// FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_vs.fx", cachename), vsbuffer->GetBufferPointer(), vsbuffer->GetBufferSize()); FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_vs.fx", cachename), vertstring, strlen(vertstring)); vsresult = qD3DXCompileShaderFromFileA(va(vabuf, sizeof(vabuf), "%s/%s_vs.fx", fs_gamedir, cachename), NULL, NULL, "main", vsversion, shaderflags, &vsbuffer, &vslog, &vsconstanttable); } else - vsresult = qD3DXCompileShader(vertstring, strlen(vertstring), NULL, NULL, "main", vsversion, shaderflags, &vsbuffer, &vslog, &vsconstanttable); + vsresult = qD3DXCompileShader(vertstring, (unsigned int)strlen(vertstring), NULL, NULL, "main", vsversion, shaderflags, &vsbuffer, &vslog, &vsconstanttable); if (vsbuffer) { - vsbinsize = vsbuffer->GetBufferSize(); + vsbinsize = ID3DXBuffer_GetBufferSize(vsbuffer); vsbin = (DWORD *)Mem_Alloc(tempmempool, vsbinsize); - memcpy(vsbin, vsbuffer->GetBufferPointer(), vsbinsize); - vsbuffer->Release(); + memcpy(vsbin, ID3DXBuffer_GetBufferPointer(vsbuffer), vsbinsize); + ID3DXBuffer_Release(vsbuffer); } if (vslog) { - strlcpy(temp, (const char *)vslog->GetBufferPointer(), min(sizeof(temp), vslog->GetBufferSize())); + strlcpy(temp, (const char *)ID3DXBuffer_GetBufferPointer(vslog), min(sizeof(temp), ID3DXBuffer_GetBufferSize(vslog))); Con_DPrintf("HLSL vertex shader compile output for %s follows:\n%s\n", cachename, temp); - vslog->Release(); + ID3DXBuffer_Release(vslog); } } if (fragstring && fragstring[0]) { if (debugshader) { -// psresult = qD3DXPreprocessShader(fragstring, strlen(fragstring), NULL, NULL, &psbuffer, &pslog); -// FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_ps.fx", cachename), psbuffer->GetBufferPointer(), psbuffer->GetBufferSize()); FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_ps.fx", cachename), fragstring, strlen(fragstring)); psresult = qD3DXCompileShaderFromFileA(va(vabuf, sizeof(vabuf), "%s/%s_ps.fx", fs_gamedir, cachename), NULL, NULL, "main", psversion, shaderflags, &psbuffer, &pslog, &psconstanttable); } else - psresult = qD3DXCompileShader(fragstring, strlen(fragstring), NULL, NULL, "main", psversion, shaderflags, &psbuffer, &pslog, &psconstanttable); + psresult = qD3DXCompileShader(fragstring, (unsigned int)strlen(fragstring), NULL, NULL, "main", psversion, shaderflags, &psbuffer, &pslog, &psconstanttable); if (psbuffer) { - psbinsize = psbuffer->GetBufferSize(); + psbinsize = ID3DXBuffer_GetBufferSize(psbuffer); psbin = (DWORD *)Mem_Alloc(tempmempool, psbinsize); - memcpy(psbin, psbuffer->GetBufferPointer(), psbinsize); - psbuffer->Release(); + memcpy(psbin, ID3DXBuffer_GetBufferPointer(psbuffer), psbinsize); + ID3DXBuffer_Release(psbuffer); } if (pslog) { - strlcpy(temp, (const char *)pslog->GetBufferPointer(), min(sizeof(temp), pslog->GetBufferSize())); + strlcpy(temp, (const char *)ID3DXBuffer_GetBufferPointer(pslog), min(sizeof(temp), ID3DXBuffer_GetBufferSize(pslog))); Con_DPrintf("HLSL pixel shader compile output for %s follows:\n%s\n", cachename, temp); - pslog->Release(); + ID3DXBuffer_Release(pslog); } } Sys_UnloadLibrary(&d3dx9_dll); @@ -1592,12 +1693,12 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode, unsigned int permutation) { int i; - shadermodeinfo_t *modeinfo = hlslshadermodeinfo + mode; + shadermodeinfo_t *modeinfo = &shadermodeinfo[SHADERLANGUAGE_HLSL][mode]; int vertstring_length = 0; int geomstring_length = 0; int fragstring_length = 0; char *t; - char *vertexstring, *geometrystring, *fragmentstring; + char *sourcestring; char *vertstring, *geomstring, *fragstring; char permutationname[256]; char cachename[256]; @@ -1616,11 +1717,9 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode permutationname[0] = 0; cachename[0] = 0; - vertexstring = R_HLSL_GetText(modeinfo->vertexfilename, true); - geometrystring = R_HLSL_GetText(modeinfo->geometryfilename, false); - fragmentstring = R_HLSL_GetText(modeinfo->fragmentfilename, false); + sourcestring = ShaderModeInfo_GetShaderText(modeinfo, true, false); - strlcat(permutationname, modeinfo->vertexfilename, sizeof(permutationname)); + strlcat(permutationname, modeinfo->filename, sizeof(permutationname)); strlcat(cachename, "hlsl/", sizeof(cachename)); // define HLSL so that the shader can tell apart the HLSL compiler and the Cg compiler @@ -1679,37 +1778,29 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode cachename[i] = '_'; // now append the shader text itself - vertstrings_list[vertstrings_count++] = vertexstring; - geomstrings_list[geomstrings_count++] = geometrystring; - fragstrings_list[fragstrings_count++] = fragmentstring; - - // if any sources were NULL, clear the respective list - if (!vertexstring) - vertstrings_count = 0; - if (!geometrystring) - geomstrings_count = 0; - if (!fragmentstring) - fragstrings_count = 0; + vertstrings_list[vertstrings_count++] = sourcestring; + geomstrings_list[geomstrings_count++] = sourcestring; + fragstrings_list[fragstrings_count++] = sourcestring; vertstring_length = 0; for (i = 0;i < vertstrings_count;i++) - vertstring_length += strlen(vertstrings_list[i]); + vertstring_length += (int)strlen(vertstrings_list[i]); vertstring = t = (char *)Mem_Alloc(tempmempool, vertstring_length + 1); - for (i = 0;i < vertstrings_count;t += strlen(vertstrings_list[i]), i++) + for (i = 0;i < vertstrings_count;t += (int)strlen(vertstrings_list[i]), i++) memcpy(t, vertstrings_list[i], strlen(vertstrings_list[i])); geomstring_length = 0; for (i = 0;i < geomstrings_count;i++) - geomstring_length += strlen(geomstrings_list[i]); + geomstring_length += (int)strlen(geomstrings_list[i]); geomstring = t = (char *)Mem_Alloc(tempmempool, geomstring_length + 1); - for (i = 0;i < geomstrings_count;t += strlen(geomstrings_list[i]), i++) + for (i = 0;i < geomstrings_count;t += (int)strlen(geomstrings_list[i]), i++) memcpy(t, geomstrings_list[i], strlen(geomstrings_list[i])); fragstring_length = 0; for (i = 0;i < fragstrings_count;i++) - fragstring_length += strlen(fragstrings_list[i]); + fragstring_length += (int)strlen(fragstrings_list[i]); fragstring = t = (char *)Mem_Alloc(tempmempool, fragstring_length + 1); - for (i = 0;i < fragstrings_count;t += strlen(fragstrings_list[i]), i++) + for (i = 0;i < fragstrings_count;t += (int)strlen(fragstrings_list[i]), i++) memcpy(t, fragstrings_list[i], strlen(fragstrings_list[i])); // try to load the cached shader, or generate one @@ -1727,12 +1818,8 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode Mem_Free(geomstring); if (fragstring) Mem_Free(fragstring); - if (vertexstring) - Mem_Free(vertexstring); - if (geometrystring) - Mem_Free(geometrystring); - if (fragmentstring) - Mem_Free(fragmentstring); + if (sourcestring) + Mem_Free(sourcestring); } static inline void hlslVSSetParameter16f(D3DVSREGISTER_t r, const float *a) {IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, a, 4);} @@ -1778,7 +1865,7 @@ void R_SetupShader_SetPermutationHLSL(unsigned int mode, unsigned int permutatio } if (i >= SHADERPERMUTATION_COUNT) { - //Con_Printf("Could not find a working HLSL shader for permutation %s %s\n", shadermodeinfo[mode].vertexfilename, shadermodeinfo[mode].pretext); + //Con_Printf("Could not find a working HLSL shader for permutation %s %s\n", shadermodeinfo[mode].filename, shadermodeinfo[mode].pretext); r_hlsl_permutation = R_HLSL_FindPermutation(mode, permutation); return; // no bit left to clear, entire mode is broken } @@ -1804,12 +1891,6 @@ static void R_SetupShader_SetPermutationSoft(unsigned int mode, unsigned int per void R_GLSL_Restart_f(void) { unsigned int i, limit; - if (glslshaderstring && glslshaderstring != builtinshaderstring) - Mem_Free(glslshaderstring); - glslshaderstring = NULL; - if (hlslshaderstring && hlslshaderstring != builtinhlslshaderstring) - Mem_Free(hlslshaderstring); - hlslshaderstring = NULL; switch(vid.renderpath) { case RENDERPATH_D3D9: @@ -1817,7 +1898,7 @@ void R_GLSL_Restart_f(void) { r_hlsl_permutation_t *p; r_hlsl_permutation = NULL; - limit = Mem_ExpandableArray_IndexRange(&r_hlsl_permutationarray); + limit = (unsigned int)Mem_ExpandableArray_IndexRange(&r_hlsl_permutationarray); for (i = 0;i < limit;i++) { if ((p = (r_hlsl_permutation_t*)Mem_ExpandableArray_RecordAtIndex(&r_hlsl_permutationarray, i))) @@ -1844,7 +1925,7 @@ void R_GLSL_Restart_f(void) { r_glsl_permutation_t *p; r_glsl_permutation = NULL; - limit = Mem_ExpandableArray_IndexRange(&r_glsl_permutationarray); + limit = (unsigned int)Mem_ExpandableArray_IndexRange(&r_glsl_permutationarray); for (i = 0;i < limit;i++) { if ((p = (r_glsl_permutation_t*)Mem_ExpandableArray_RecordAtIndex(&r_glsl_permutationarray, i))) @@ -1867,42 +1948,43 @@ void R_GLSL_Restart_f(void) static void R_GLSL_DumpShader_f(void) { - int i; + int i, language, mode, dupe; + char *text; + shadermodeinfo_t *modeinfo; qfile_t *file; - file = FS_OpenRealFile("glsl/default.glsl", "w", false); - if (file) + for (language = 0;language < SHADERLANGUAGE_COUNT;language++) { - FS_Print(file, "/* The engine may define the following macros:\n"); - FS_Print(file, "#define VERTEX_SHADER\n#define GEOMETRY_SHADER\n#define FRAGMENT_SHADER\n"); - for (i = 0;i < SHADERMODE_COUNT;i++) - FS_Print(file, glslshadermodeinfo[i].pretext); - for (i = 0;i < SHADERPERMUTATION_COUNT;i++) - FS_Print(file, shaderpermutationinfo[i].pretext); - FS_Print(file, "*/\n"); - FS_Print(file, builtinshaderstring); - FS_Close(file); - Con_Printf("glsl/default.glsl written\n"); - } - else - Con_Printf("failed to write to glsl/default.glsl\n"); - - file = FS_OpenRealFile("hlsl/default.hlsl", "w", false); - if (file) - { - FS_Print(file, "/* The engine may define the following macros:\n"); - FS_Print(file, "#define VERTEX_SHADER\n#define GEOMETRY_SHADER\n#define FRAGMENT_SHADER\n"); - for (i = 0;i < SHADERMODE_COUNT;i++) - FS_Print(file, hlslshadermodeinfo[i].pretext); - for (i = 0;i < SHADERPERMUTATION_COUNT;i++) - FS_Print(file, shaderpermutationinfo[i].pretext); - FS_Print(file, "*/\n"); - FS_Print(file, builtinhlslshaderstring); - FS_Close(file); - Con_Printf("hlsl/default.hlsl written\n"); + modeinfo = shadermodeinfo[language]; + for (mode = 0;mode < SHADERMODE_COUNT;mode++) + { + // don't dump the same file multiple times (most or all shaders come from the same file) + for (dupe = mode - 1;dupe >= 0;dupe--) + if (!strcmp(modeinfo[mode].filename, modeinfo[dupe].filename)) + break; + if (dupe >= 0) + continue; + text = modeinfo[mode].builtinstring; + if (!text) + continue; + file = FS_OpenRealFile(modeinfo[mode].filename, "w", false); + if (file) + { + FS_Print(file, "/* The engine may define the following macros:\n"); + FS_Print(file, "#define VERTEX_SHADER\n#define GEOMETRY_SHADER\n#define FRAGMENT_SHADER\n"); + for (i = 0;i < SHADERMODE_COUNT;i++) + FS_Print(file, modeinfo[i].pretext); + for (i = 0;i < SHADERPERMUTATION_COUNT;i++) + FS_Print(file, shaderpermutationinfo[i].pretext); + FS_Print(file, "*/\n"); + FS_Print(file, text); + FS_Close(file); + Con_Printf("%s written\n", modeinfo[mode].filename); + } + else + Con_Printf("failed to write to %s\n", modeinfo[mode].filename); + } } - else - Con_Printf("failed to write to hlsl/default.hlsl\n"); } void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale, qboolean usegamma, qboolean notrippy, qboolean suppresstexalpha) @@ -1949,8 +2031,10 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod case RENDERPATH_GL20: case RENDERPATH_GLES2: R_SetupShader_SetPermutationGLSL(SHADERMODE_GENERIC, permutation); - R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , first ); - R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second, second); + if (r_glsl_permutation->tex_Texture_First >= 0) + R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , first ); + if (r_glsl_permutation->tex_Texture_Second >= 0) + R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second, second); if (r_glsl_permutation->tex_Texture_GammaRamps >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps); break; @@ -1958,12 +2042,18 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod case RENDERPATH_GLES1: R_Mesh_TexBind(0, first ); R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1); + R_Mesh_TexMatrix(0, NULL); R_Mesh_TexBind(1, second); if (second) + { R_Mesh_TexCombine(1, texturemode, texturemode, rgbscale, 1); + R_Mesh_TexMatrix(1, NULL); + } break; case RENDERPATH_GL11: R_Mesh_TexBind(0, first ); + R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1); + R_Mesh_TexMatrix(0, NULL); break; case RENDERPATH_SOFT: R_SetupShader_SetPermutationSoft(SHADERMODE_GENERIC, permutation); @@ -1978,13 +2068,16 @@ void R_SetupShader_Generic_NoTexture(qboolean usegamma, qboolean notrippy) R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, usegamma, notrippy, false); } -void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb) +void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb, qboolean skeletal) { unsigned int permutation = 0; if (r_trippy.integer && !notrippy) permutation |= SHADERPERMUTATION_TRIPPY; if (depthrgb) permutation |= SHADERPERMUTATION_DEPTHRGB; + if (skeletal) + permutation |= SHADERPERMUTATION_SKELETAL; + if (vid.allowalphatocoverage) GL_AlphaToCoverage(false); switch (vid.renderpath) @@ -2003,6 +2096,9 @@ void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb) case RENDERPATH_GL20: case RENDERPATH_GLES2: R_SetupShader_SetPermutationGLSL(SHADERMODE_DEPTH_OR_SHADOW, permutation); +#ifndef USE_GLES2 /* FIXME: GLES3 only */ + if (r_glsl_permutation->ubiloc_Skeletal_Transform12_UniformBlock >= 0 && rsurface.batchskeletaltransform3x4buffer) qglBindBufferRange(GL_UNIFORM_BUFFER, r_glsl_permutation->ubibind_Skeletal_Transform12_UniformBlock, rsurface.batchskeletaltransform3x4buffer->bufferobject, rsurface.batchskeletaltransform3x4offset, rsurface.batchskeletaltransform3x4size); +#endif break; case RENDERPATH_GL13: case RENDERPATH_GLES1: @@ -2018,55 +2114,21 @@ void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb) } } -void R_SetupShader_ShowDepth(qboolean notrippy) -{ - int permutation = 0; - if (r_trippy.integer && !notrippy) - permutation |= SHADERPERMUTATION_TRIPPY; - if (vid.allowalphatocoverage) - GL_AlphaToCoverage(false); - switch (vid.renderpath) - { - case RENDERPATH_D3D9: -#ifdef SUPPORTHLSL - R_SetupShader_SetPermutationHLSL(SHADERMODE_SHOWDEPTH, permutation); -#endif - break; - case RENDERPATH_D3D10: - Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); - break; - case RENDERPATH_D3D11: - Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); - break; - case RENDERPATH_GL20: - case RENDERPATH_GLES2: - R_SetupShader_SetPermutationGLSL(SHADERMODE_SHOWDEPTH, permutation); - break; - case RENDERPATH_GL13: - case RENDERPATH_GLES1: - break; - case RENDERPATH_GL11: - break; - case RENDERPATH_SOFT: - R_SetupShader_SetPermutationSoft(SHADERMODE_SHOWDEPTH, permutation); - break; - } -} - extern qboolean r_shadow_usingdeferredprepass; extern rtexture_t *r_shadow_attenuationgradienttexture; extern rtexture_t *r_shadow_attenuation2dtexture; extern rtexture_t *r_shadow_attenuation3dtexture; extern qboolean r_shadow_usingshadowmap2d; extern qboolean r_shadow_usingshadowmaportho; -extern float r_shadow_shadowmap_texturescale[2]; -extern float r_shadow_shadowmap_parameters[4]; +extern float r_shadow_modelshadowmap_texturescale[4]; +extern float r_shadow_modelshadowmap_parameters[4]; +extern float r_shadow_lightshadowmap_texturescale[4]; +extern float r_shadow_lightshadowmap_parameters[4]; extern qboolean r_shadow_shadowmapvsdct; extern rtexture_t *r_shadow_shadowmap2ddepthbuffer; extern rtexture_t *r_shadow_shadowmap2ddepthtexture; extern rtexture_t *r_shadow_shadowmapvsdcttexture; extern matrix4x4_t r_shadow_shadowmapmatrix; -extern int r_shadow_shadowmaplod; // changes for each light based on distance extern int r_shadow_prepass_width; extern int r_shadow_prepass_height; extern rtexture_t *r_shadow_prepassgeometrydepthbuffer; @@ -2138,6 +2200,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_TRIPPY; if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) permutation |= SHADERPERMUTATION_ALPHAKILL; + if (rsurface.texture->currentmaterialflags & MATERIALFLAG_OCCLUDE) + permutation |= SHADERPERMUTATION_OCCLUDE; if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1]) permutation |= SHADERPERMUTATION_NORMALMAPSCROLLBLEND; // todo: make generic if (rsurfacepass == RSURFPASS_BACKGROUND) @@ -2172,9 +2236,9 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, else { mode = SHADERMODE_GENERIC; - permutation |= SHADERPERMUTATION_DIFFUSE; - GL_BlendFunc(GL_ONE, GL_ZERO); - blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO); + permutation |= SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_ALPHAKILL; + GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } if (vid.allowalphatocoverage) GL_AlphaToCoverage(false); @@ -2263,7 +2327,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, // unshaded geometry (fullbright or ambient model lighting) mode = SHADERMODE_FLATCOLOR; ambientscale = diffusescale = specularscale = 0; - if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) + if ((rsurface.texture->glowtexture || rsurface.texture->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) permutation |= SHADERPERMUTATION_GLOW; if (r_refdef.fogenabled) permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE); @@ -2313,7 +2377,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_ALPHAGEN_VERTEX; // directional model lighting mode = SHADERMODE_LIGHTDIRECTION; - if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) + if ((rsurface.texture->glowtexture || rsurface.texture->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) permutation |= SHADERPERMUTATION_GLOW; permutation |= SHADERPERMUTATION_DIFFUSE; if (specularscale > 0) @@ -2336,10 +2400,10 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP; if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; - if (r_shadow_bouncegridtexture && cl.csqc_vidvars.drawworld) + if (r_shadow_bouncegrid_state.texture && cl.csqc_vidvars.drawworld) { permutation |= SHADERPERMUTATION_BOUNCEGRID; - if (r_shadow_bouncegriddirectional) + if (r_shadow_bouncegrid_state.directional) permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; } GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); @@ -2374,7 +2438,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_ALPHAGEN_VERTEX; // ambient model lighting mode = SHADERMODE_LIGHTDIRECTION; - if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) + if ((rsurface.texture->glowtexture || rsurface.texture->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) permutation |= SHADERPERMUTATION_GLOW; if (r_refdef.fogenabled) permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE); @@ -2394,10 +2458,10 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP; if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; - if (r_shadow_bouncegridtexture && cl.csqc_vidvars.drawworld) + if (r_shadow_bouncegrid_state.texture && cl.csqc_vidvars.drawworld) { permutation |= SHADERPERMUTATION_BOUNCEGRID; - if (r_shadow_bouncegriddirectional) + if (r_shadow_bouncegrid_state.directional) permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; } GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); @@ -2431,7 +2495,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHAGEN_VERTEX) permutation |= SHADERPERMUTATION_ALPHAGEN_VERTEX; // lightmapped wall - if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) + if ((rsurface.texture->glowtexture || rsurface.texture->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) permutation |= SHADERPERMUTATION_GLOW; if (r_refdef.fogenabled) permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE); @@ -2491,10 +2555,10 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, // ordinary vertex coloring (q3bsp) mode = SHADERMODE_VERTEXCOLOR; } - if (r_shadow_bouncegridtexture && cl.csqc_vidvars.drawworld) + if (r_shadow_bouncegrid_state.texture && cl.csqc_vidvars.drawworld) { permutation |= SHADERPERMUTATION_BOUNCEGRID; - if (r_shadow_bouncegriddirectional) + if (r_shadow_bouncegrid_state.directional) permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; } GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); @@ -2521,8 +2585,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, { case RENDERPATH_D3D9: #ifdef SUPPORTD3D - RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist); - R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmeshbuffer); + RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0) | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist); + R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmesh_vertexbuffer, rsurface.batchvertexmesh_bufferoffset); R_SetupShader_SetPermutationHLSL(mode, permutation); Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);hlslPSSetParameter16f(D3DPSREGISTER_ModelToReflectCube, m16f); if (mode == SHADERMODE_LIGHTSOURCE) @@ -2595,8 +2659,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (mode == SHADERMODE_WATER) hlslPSSetParameter2f(D3DPSREGISTER_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]); } - hlslPSSetParameter2f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]); - hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]); + if (permutation & SHADERPERMUTATION_SHADOWMAPORTHO) + { + hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_modelshadowmap_texturescale[0], r_shadow_modelshadowmap_texturescale[1], r_shadow_modelshadowmap_texturescale[2], r_shadow_modelshadowmap_texturescale[3]); + hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_modelshadowmap_parameters[0], r_shadow_modelshadowmap_parameters[1], r_shadow_modelshadowmap_parameters[2], r_shadow_modelshadowmap_parameters[3]); + } + else + { + hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_lightshadowmap_texturescale[0], r_shadow_lightshadowmap_texturescale[1], r_shadow_lightshadowmap_texturescale[2], r_shadow_lightshadowmap_texturescale[3]); + hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_lightshadowmap_parameters[0], r_shadow_lightshadowmap_parameters[1], r_shadow_lightshadowmap_parameters[2], r_shadow_lightshadowmap_parameters[3]); + } hlslPSSetParameter3f(D3DPSREGISTER_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]); hlslPSSetParameter1f(D3DPSREGISTER_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1)); hlslPSSetParameter3f(D3DPSREGISTER_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]); @@ -2674,7 +2746,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, case RENDERPATH_GLES2: if (!vid.useinterleavedarrays) { - 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); + 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) | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist); R_Mesh_VertexPointer( 3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset); R_Mesh_ColorPointer( 4, GL_FLOAT, sizeof(float[4]), rsurface.batchlightmapcolor4f, rsurface.batchlightmapcolor4f_vertexbuffer, rsurface.batchlightmapcolor4f_bufferoffset); R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset); @@ -2682,13 +2754,22 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchtvector3f, rsurface.batchtvector3f_vertexbuffer, rsurface.batchtvector3f_bufferoffset); R_Mesh_TexCoordPointer(3, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchnormal3f, rsurface.batchnormal3f_vertexbuffer, rsurface.batchnormal3f_bufferoffset); R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset); + R_Mesh_TexCoordPointer(5, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0); + R_Mesh_TexCoordPointer(6, 4, GL_UNSIGNED_BYTE | 0x80000000, sizeof(unsigned char[4]), rsurface.batchskeletalindex4ub, rsurface.batchskeletalindex4ub_vertexbuffer, rsurface.batchskeletalindex4ub_bufferoffset); + R_Mesh_TexCoordPointer(7, 4, GL_UNSIGNED_BYTE, sizeof(unsigned char[4]), rsurface.batchskeletalweight4ub, rsurface.batchskeletalweight4ub_vertexbuffer, rsurface.batchskeletalweight4ub_bufferoffset); } else { - RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist); - R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmeshbuffer); + RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0) | (rsurface.entityskeletaltransform3x4 ? BATCHNEED_VERTEXMESH_SKELETAL : 0) | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist); + R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmesh_vertexbuffer, rsurface.batchvertexmesh_bufferoffset); } + // this has to be after RSurf_PrepareVerticesForBatch + if (rsurface.batchskeletaltransform3x4buffer) + permutation |= SHADERPERMUTATION_SKELETAL; R_SetupShader_SetPermutationGLSL(mode, permutation); +#ifndef USE_GLES2 /* FIXME: GLES3 only */ + if (r_glsl_permutation->ubiloc_Skeletal_Transform12_UniformBlock >= 0 && rsurface.batchskeletaltransform3x4buffer) qglBindBufferRange(GL_UNIFORM_BUFFER, r_glsl_permutation->ubibind_Skeletal_Transform12_UniformBlock, rsurface.batchskeletaltransform3x4buffer->bufferobject, rsurface.batchskeletaltransform3x4offset, rsurface.batchskeletaltransform3x4size); +#endif if (r_glsl_permutation->loc_ModelToReflectCube >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_ModelToReflectCube, 1, false, m16f);} if (mode == SHADERMODE_LIGHTSOURCE) { @@ -2749,8 +2830,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (r_glsl_permutation->loc_TexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_TexMatrix, 1, false, m16f);} if (r_glsl_permutation->loc_BackgroundTexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BackgroundTexMatrix, 1, false, m16f);} if (r_glsl_permutation->loc_ShadowMapMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&r_shadow_shadowmapmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_ShadowMapMatrix, 1, false, m16f);} - if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform2f(r_glsl_permutation->loc_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]); - if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]); + if (permutation & SHADERPERMUTATION_SHADOWMAPORTHO) + { + if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_TextureScale, r_shadow_modelshadowmap_texturescale[0], r_shadow_modelshadowmap_texturescale[1], r_shadow_modelshadowmap_texturescale[2], r_shadow_modelshadowmap_texturescale[3]); + if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_modelshadowmap_parameters[0], r_shadow_modelshadowmap_parameters[1], r_shadow_modelshadowmap_parameters[2], r_shadow_modelshadowmap_parameters[3]); + } + else + { + if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_TextureScale, r_shadow_lightshadowmap_texturescale[0], r_shadow_lightshadowmap_texturescale[1], r_shadow_lightshadowmap_texturescale[2], r_shadow_lightshadowmap_texturescale[3]); + if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_lightshadowmap_parameters[0], r_shadow_lightshadowmap_parameters[1], r_shadow_lightshadowmap_parameters[2], r_shadow_lightshadowmap_parameters[3]); + } if (r_glsl_permutation->loc_Color_Glow >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]); if (r_glsl_permutation->loc_Alpha >= 0) qglUniform1f(r_glsl_permutation->loc_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1)); @@ -2783,8 +2872,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (r_glsl_permutation->loc_OffsetMapping_Bias >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_Bias, rsurface.texture->offsetbias); 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*r_refdef.view.colorscale); + if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegrid_state.matrix, &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_bouncegrid_state.intensity*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 ); @@ -2828,7 +2917,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (r_glsl_permutation->tex_Texture_CubeProjection >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_CubeProjection , r_shadow_shadowmapvsdcttexture ); } } - if (r_glsl_permutation->tex_Texture_BounceGrid >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_BounceGrid, r_shadow_bouncegridtexture); + if (r_glsl_permutation->tex_Texture_BounceGrid >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_BounceGrid, r_shadow_bouncegrid_state.texture); CHECKGLERROR break; case RENDERPATH_GL11: @@ -2836,7 +2925,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, case RENDERPATH_GLES1: 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); + 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) | BATCHNEED_ALLOWMULTIDRAW, 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_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelToReflectCubeM1, 1, false, m16f);} @@ -2895,8 +2984,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_TexMatrixM1, 1, false, m16f);} {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_BackgroundTexMatrixM1, 1, false, m16f);} {Matrix4x4_ToArrayFloatGL(&r_shadow_shadowmapmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ShadowMapMatrixM1, 1, false, m16f);} - DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]); - DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]); + if (permutation & SHADERPERMUTATION_SHADOWMAPORTHO) + { + DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_TextureScale, r_shadow_modelshadowmap_texturescale[0], r_shadow_modelshadowmap_texturescale[1], r_shadow_modelshadowmap_texturescale[2], r_shadow_modelshadowmap_texturescale[3]); + DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_Parameters, r_shadow_modelshadowmap_parameters[0], r_shadow_modelshadowmap_parameters[1], r_shadow_modelshadowmap_parameters[2], r_shadow_modelshadowmap_parameters[3]); + } + else + { + DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_TextureScale, r_shadow_lightshadowmap_texturescale[0], r_shadow_lightshadowmap_texturescale[1], r_shadow_lightshadowmap_texturescale[2], r_shadow_lightshadowmap_texturescale[3]); + DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_Parameters, r_shadow_lightshadowmap_parameters[0], r_shadow_lightshadowmap_parameters[1], r_shadow_lightshadowmap_parameters[2], r_shadow_lightshadowmap_parameters[3]); + } DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]); DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1)); @@ -3012,7 +3109,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight) GL_AlphaToCoverage(false); Matrix4x4_Transform(&r_refdef.view.viewport.viewmatrix, rtlight->shadoworigin, viewlightorigin); Matrix4x4_Concat(&lighttoview, &r_refdef.view.viewport.viewmatrix, &rtlight->matrix_lighttoworld); - Matrix4x4_Invert_Simple(&viewtolight, &lighttoview); + Matrix4x4_Invert_Full(&viewtolight, &lighttoview); Matrix4x4_ToArrayFloatGL(&viewtolight, viewtolight16f); switch(vid.renderpath) { @@ -3024,8 +3121,8 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight) hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Ambient , lightcolorbase[0] * ambientscale , lightcolorbase[1] * ambientscale , lightcolorbase[2] * ambientscale ); hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Diffuse , lightcolorbase[0] * diffusescale , lightcolorbase[1] * diffusescale , lightcolorbase[2] * diffusescale ); hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Specular, lightcolorbase[0] * specularscale, lightcolorbase[1] * specularscale, lightcolorbase[2] * specularscale); - hlslPSSetParameter2f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]); - hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]); + hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_lightshadowmap_texturescale[0], r_shadow_lightshadowmap_texturescale[1], r_shadow_lightshadowmap_texturescale[2], r_shadow_lightshadowmap_texturescale[3]); + hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_lightshadowmap_parameters[0], r_shadow_lightshadowmap_parameters[1], r_shadow_lightshadowmap_parameters[2], r_shadow_lightshadowmap_parameters[3]); hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f); 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); @@ -3051,8 +3148,8 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight) if (r_glsl_permutation->loc_DeferredColor_Ambient >= 0) qglUniform3f( r_glsl_permutation->loc_DeferredColor_Ambient , lightcolorbase[0] * ambientscale , lightcolorbase[1] * ambientscale , lightcolorbase[2] * ambientscale ); if (r_glsl_permutation->loc_DeferredColor_Diffuse >= 0) qglUniform3f( r_glsl_permutation->loc_DeferredColor_Diffuse , lightcolorbase[0] * diffusescale , lightcolorbase[1] * diffusescale , lightcolorbase[2] * diffusescale ); if (r_glsl_permutation->loc_DeferredColor_Specular >= 0) qglUniform3f( r_glsl_permutation->loc_DeferredColor_Specular , lightcolorbase[0] * specularscale, lightcolorbase[1] * specularscale, lightcolorbase[2] * specularscale); - if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform2f( r_glsl_permutation->loc_ShadowMap_TextureScale , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]); - if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f( r_glsl_permutation->loc_ShadowMap_Parameters , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]); + if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform4f( r_glsl_permutation->loc_ShadowMap_TextureScale , r_shadow_lightshadowmap_texturescale[0], r_shadow_lightshadowmap_texturescale[1], r_shadow_lightshadowmap_texturescale[2], r_shadow_lightshadowmap_texturescale[3]); + if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f( r_glsl_permutation->loc_ShadowMap_Parameters , r_shadow_lightshadowmap_parameters[0], r_shadow_lightshadowmap_parameters[1], r_shadow_lightshadowmap_parameters[2], r_shadow_lightshadowmap_parameters[3]); if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f( r_glsl_permutation->loc_SpecularPower , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f); 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); @@ -3074,8 +3171,8 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight) DPSOFTRAST_Uniform3f( DPSOFTRAST_UNIFORM_DeferredColor_Ambient , lightcolorbase[0] * ambientscale , lightcolorbase[1] * ambientscale , lightcolorbase[2] * ambientscale ); DPSOFTRAST_Uniform3f( DPSOFTRAST_UNIFORM_DeferredColor_Diffuse , lightcolorbase[0] * diffusescale , lightcolorbase[1] * diffusescale , lightcolorbase[2] * diffusescale ); DPSOFTRAST_Uniform3f( DPSOFTRAST_UNIFORM_DeferredColor_Specular , lightcolorbase[0] * specularscale, lightcolorbase[1] * specularscale, lightcolorbase[2] * specularscale); - DPSOFTRAST_Uniform2f( DPSOFTRAST_UNIFORM_ShadowMap_TextureScale , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]); - DPSOFTRAST_Uniform4f( 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_Uniform4f( DPSOFTRAST_UNIFORM_ShadowMap_TextureScale , r_shadow_lightshadowmap_texturescale[0], r_shadow_lightshadowmap_texturescale[1], r_shadow_lightshadowmap_texturescale[2], r_shadow_lightshadowmap_texturescale[3]); + DPSOFTRAST_Uniform4f( DPSOFTRAST_UNIFORM_ShadowMap_Parameters , r_shadow_lightshadowmap_parameters[0], r_shadow_lightshadowmap_parameters[1], r_shadow_lightshadowmap_parameters[2], r_shadow_lightshadowmap_parameters[3]); DPSOFTRAST_Uniform1f( DPSOFTRAST_UNIFORM_SpecularPower , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f); 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); @@ -3093,7 +3190,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight) typedef struct { - int loadsequence; // incremented each level change + unsigned int loadsequence; // incremented each level change memexpandablearray_t array; skinframe_t *hash[SKINFRAME_HASH]; } @@ -3300,7 +3397,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole Image_StripImageExtension(name, basename, sizeof(basename)); // check for DDS texture file first - if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", basename), vid.sRGB3D, textureflags, &ddshasalpha, ddsavgcolor, miplevel))) + if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", basename), vid.sRGB3D, textureflags, &ddshasalpha, ddsavgcolor, miplevel, false))) { basepixels = loadimagepixelsbgra(name, complain, true, false, &miplevel); if (basepixels == NULL) @@ -3327,6 +3424,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole skinframe->fog = NULL; skinframe->reflect = NULL; skinframe->hasalpha = false; + // we could store the q2animname here too if (ddsbase) { @@ -3334,7 +3432,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole skinframe->hasalpha = ddshasalpha; VectorCopy(ddsavgcolor, skinframe->avgcolor); if (r_loadfog && skinframe->hasalpha) - skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_mask.dds", skinframe->basename), false, textureflags | TEXF_ALPHA, NULL, NULL, miplevel); + skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_mask.dds", skinframe->basename), false, textureflags | TEXF_ALPHA, NULL, NULL, miplevel, true); //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]); } else @@ -3381,13 +3479,13 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole { mymiplevel = savemiplevel; if (r_loadnormalmap) - skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_norm.dds", skinframe->basename), false, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), NULL, NULL, mymiplevel); - skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_glow.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel); + skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_norm.dds", skinframe->basename), false, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), NULL, NULL, mymiplevel, true); + skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_glow.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel, true); if (r_loadgloss) - skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_gloss.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel); - skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_pants.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel); - skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_shirt.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel); - skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_reflect.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel); + skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_gloss.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel, true); + skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_pants.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel, true); + skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_shirt.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel, true); + skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s_reflect.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel, true); } // _norm is the name used by tenebrae and has been adopted as standard @@ -3492,7 +3590,6 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, const unsigned char *skindata, int width, int height, qboolean sRGB) { int i; - unsigned char *temp1, *temp2; skinframe_t *skinframe; char vabuf[1024]; @@ -3501,7 +3598,7 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co // if already loaded just return it, otherwise make a new skinframe skinframe = R_SkinFrame_Find(name, textureflags, width, height, (textureflags & TEXF_FORCE_RELOAD) ? -1 : skindata ? CRC_Block(skindata, width*height*4) : 0, true); - if (skinframe && skinframe->base) + if (skinframe->base) return skinframe; textureflags &= ~TEXF_FORCE_RELOAD; @@ -3526,11 +3623,11 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co if (r_loadnormalmap && r_shadow_bumpscale_basetexture.value > 0) { - temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8); - temp2 = temp1 + width * height * 4; - Image_HeightmapToNormalmap_BGRA(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value); - skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, (textureflags | TEXF_ALPHA) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), -1, NULL); - Mem_Free(temp1); + unsigned char *a = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8); + unsigned char *b = a + width * height * 4; + Image_HeightmapToNormalmap_BGRA(skindata, b, width, height, false, r_shadow_bumpscale_basetexture.value); + skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_nmap", skinframe->basename), width, height, b, TEXTYPE_BGRA, (textureflags | TEXF_ALPHA) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), -1, NULL); + Mem_Free(a); } skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, sRGB ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags, -1, NULL); if (textureflags & TEXF_ALPHA) @@ -3571,9 +3668,9 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i // if already loaded just return it, otherwise make a new skinframe skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height) : 0, true); - if (skinframe && skinframe->base) + if (skinframe->base) return skinframe; - textureflags &= ~TEXF_FORCE_RELOAD; + //textureflags &= ~TEXF_FORCE_RELOAD; skinframe->stain = NULL; skinframe->merged = NULL; @@ -3605,6 +3702,9 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i featuresmask |= palette_featureflags[skindata[i]]; skinframe->hasalpha = false; + // fence textures + if (name[0] == '{') + skinframe->hasalpha = true; skinframe->qhascolormapping = loadpantsandshirt && (featuresmask & (PALETTEFEATURE_PANTS | PALETTEFEATURE_SHIRT)); skinframe->qgeneratenmap = r_shadow_bumpscale_basetexture.value > 0; skinframe->qgeneratemerged = true; @@ -3647,21 +3747,24 @@ static void R_SkinFrame_GenerateTexturesFromQPixels(skinframe_t *skinframe, qboo if (skinframe->qgeneratenmap) { - unsigned char *temp1, *temp2; + unsigned char *a, *b; skinframe->qgeneratenmap = false; - temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8); - temp2 = temp1 + width * height * 4; + a = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8); + b = a + width * height * 4; // use either a custom palette or the quake palette - Image_Copy8bitBGRA(skindata, temp1, width * height, palette_bgra_complete); - Image_HeightmapToNormalmap_BGRA(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value); - skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, (skinframe->textureflags | TEXF_ALPHA) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), -1, NULL); - Mem_Free(temp1); + Image_Copy8bitBGRA(skindata, a, width * height, palette_bgra_complete); + Image_HeightmapToNormalmap_BGRA(a, b, width, height, false, r_shadow_bumpscale_basetexture.value); + skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_nmap", skinframe->basename), width, height, b, TEXTYPE_BGRA, (skinframe->textureflags | TEXF_ALPHA) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), -1, NULL); + Mem_Free(a); } if (skinframe->qgenerateglow) { skinframe->qgenerateglow = false; - skinframe->glow = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_glow", skinframe->basename), width, height, skindata, vid.sRGB3D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_onlyfullbrights); // glow + if (skinframe->hasalpha) // fence textures + skinframe->glow = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_glow", skinframe->basename), width, height, skindata, vid.sRGB3D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, skinframe->textureflags | TEXF_ALPHA, -1, palette_bgra_onlyfullbrights_transparent); // glow + else + skinframe->glow = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "%s_glow", skinframe->basename), width, height, skindata, vid.sRGB3D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_onlyfullbrights); // glow } if (colormapped) @@ -3674,7 +3777,10 @@ static void R_SkinFrame_GenerateTexturesFromQPixels(skinframe_t *skinframe, qboo else { skinframe->qgeneratemerged = false; - skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, vid.sRGB3D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, skinframe->textureflags, -1, skinframe->glow ? palette_bgra_nofullbrights : palette_bgra_complete); + if (skinframe->hasalpha) // fence textures + skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, vid.sRGB3D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, skinframe->textureflags | TEXF_ALPHA, -1, skinframe->glow ? palette_bgra_nofullbrights_transparent : palette_bgra_transparent); + else + skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, vid.sRGB3D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, skinframe->textureflags, -1, skinframe->glow ? palette_bgra_nofullbrights : palette_bgra_complete); } if (!skinframe->qgeneratemerged && !skinframe->qgeneratebase) @@ -3695,7 +3801,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co // if already loaded just return it, otherwise make a new skinframe skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height) : 0, true); - if (skinframe && skinframe->base) + if (skinframe->base) return skinframe; textureflags &= ~TEXF_FORCE_RELOAD; @@ -3951,6 +4057,7 @@ static void gl_main_start(void) r_texture_fogheighttexture = NULL; r_texture_gammaramps = NULL; r_texture_numcubemaps = 0; + r_uniformbufferalignment = 32; r_loaddds = r_texture_dds_load.integer != 0; r_savedds = vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc && r_texture_dds_save.integer; @@ -3969,7 +4076,11 @@ static void gl_main_start(void) r_loadnormalmap = true; r_loadgloss = true; r_loadfog = false; - break; +#ifdef GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT + if (vid.support.arb_uniform_buffer_object) + qglGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &r_uniformbufferalignment); +#endif + break; case RENDERPATH_GL13: case RENDERPATH_GLES1: Cvar_SetValueQuick(&r_textureunits, vid.texunits); @@ -3991,6 +4102,7 @@ static void gl_main_start(void) R_AnimCache_Free(); R_FrameData_Reset(); + R_BufferData_Reset(); r_numqueries = 0; r_maxqueries = 0; @@ -4023,25 +4135,45 @@ static void gl_main_start(void) r_glsl_permutation = NULL; memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash)); Mem_ExpandableArray_NewArray(&r_glsl_permutationarray, r_main_mempool, sizeof(r_glsl_permutation_t), 256); - glslshaderstring = NULL; #ifdef SUPPORTD3D r_hlsl_permutation = NULL; memset(r_hlsl_permutationhash, 0, sizeof(r_hlsl_permutationhash)); Mem_ExpandableArray_NewArray(&r_hlsl_permutationarray, r_main_mempool, sizeof(r_hlsl_permutation_t), 256); #endif - hlslshaderstring = NULL; memset(&r_svbsp, 0, sizeof (r_svbsp)); memset(r_texture_cubemaps, 0, sizeof(r_texture_cubemaps)); r_texture_numcubemaps = 0; r_refdef.fogmasktable_density = 0; + +#ifdef __ANDROID__ + // For Steelstorm Android + // FIXME CACHE the program and reload + // FIXME see possible combinations for SS:BR android + Con_DPrintf("Compiling most used shaders for SS:BR android... START\n"); + R_SetupShader_SetPermutationGLSL(0, 12); + R_SetupShader_SetPermutationGLSL(0, 13); + R_SetupShader_SetPermutationGLSL(0, 8388621); + R_SetupShader_SetPermutationGLSL(3, 0); + R_SetupShader_SetPermutationGLSL(3, 2048); + R_SetupShader_SetPermutationGLSL(5, 0); + R_SetupShader_SetPermutationGLSL(5, 2); + R_SetupShader_SetPermutationGLSL(5, 2048); + R_SetupShader_SetPermutationGLSL(5, 8388608); + R_SetupShader_SetPermutationGLSL(11, 1); + R_SetupShader_SetPermutationGLSL(11, 2049); + R_SetupShader_SetPermutationGLSL(11, 8193); + R_SetupShader_SetPermutationGLSL(11, 10241); + Con_DPrintf("Compiling most used shaders for SS:BR android... END\n"); +#endif } static void gl_main_shutdown(void) { R_AnimCache_Free(); R_FrameData_Reset(); + R_BufferData_Reset(); R_Main_FreeViewCache(); @@ -4052,7 +4184,7 @@ static void gl_main_shutdown(void) case RENDERPATH_GL20: case RENDERPATH_GLES1: case RENDERPATH_GLES2: -#ifdef GL_SAMPLES_PASSED_ARB +#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2) if (r_maxqueries) qglDeleteQueriesARB(r_maxqueries, r_queries); #endif @@ -4103,13 +4235,11 @@ static void gl_main_shutdown(void) r_glsl_permutation = NULL; memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash)); Mem_ExpandableArray_FreeArray(&r_glsl_permutationarray); - glslshaderstring = NULL; #ifdef SUPPORTD3D r_hlsl_permutation = NULL; memset(r_hlsl_permutationhash, 0, sizeof(r_hlsl_permutationhash)); Mem_ExpandableArray_FreeArray(&r_hlsl_permutationarray); #endif - hlslshaderstring = NULL; } static void gl_main_newmap(void) @@ -4135,11 +4265,14 @@ static void gl_main_newmap(void) R_Main_FreeViewCache(); R_FrameData_Reset(); + R_BufferData_Reset(); } void GL_Main_Init(void) { + int i; r_main_mempool = Mem_AllocPool("Renderer", 0, NULL); + R_InitShaderModeInfo(); Cmd_AddCommand("r_glsl_restart", R_GLSL_Restart_f, "unloads GLSL shaders, they will then be reloaded as needed"); Cmd_AddCommand("r_glsl_dumpshader", R_GLSL_DumpShader_f, "dumps the engine internal default.glsl shader into glsl/default.glsl"); @@ -4219,6 +4352,7 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_shadows_throwdirection); Cvar_RegisterVariable(&r_shadows_focus); Cvar_RegisterVariable(&r_shadows_shadowmapscale); + Cvar_RegisterVariable(&r_shadows_shadowmapbias); Cvar_RegisterVariable(&r_q1bsp_skymasking); Cvar_RegisterVariable(&r_polygonoffset_submodel_factor); Cvar_RegisterVariable(&r_polygonoffset_submodel_offset); @@ -4263,8 +4397,11 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_glsl_postprocess_uservec2_enable); Cvar_RegisterVariable(&r_glsl_postprocess_uservec3_enable); Cvar_RegisterVariable(&r_glsl_postprocess_uservec4_enable); + Cvar_RegisterVariable(&r_celshading); + Cvar_RegisterVariable(&r_celoutlines); Cvar_RegisterVariable(&r_water); + Cvar_RegisterVariable(&r_water_cameraentitiesonly); Cvar_RegisterVariable(&r_water_resolutionmultiplier); Cvar_RegisterVariable(&r_water_clippingplanebias); Cvar_RegisterVariable(&r_water_refractdistort); @@ -4285,6 +4422,7 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_bloom_resolution); Cvar_RegisterVariable(&r_bloom_colorexponent); Cvar_RegisterVariable(&r_bloom_colorsubtract); + Cvar_RegisterVariable(&r_bloom_scenebrightness); Cvar_RegisterVariable(&r_hdr_scenebrightness); Cvar_RegisterVariable(&r_hdr_glowintensity); Cvar_RegisterVariable(&r_hdr_irisadaptation); @@ -4299,12 +4437,26 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&developer_texturelogging); Cvar_RegisterVariable(&gl_lightmaps); Cvar_RegisterVariable(&r_test); + Cvar_RegisterVariable(&r_batch_multidraw); + Cvar_RegisterVariable(&r_batch_multidraw_mintriangles); + Cvar_RegisterVariable(&r_batch_debugdynamicvertexpath); + Cvar_RegisterVariable(&r_glsl_skeletal); Cvar_RegisterVariable(&r_glsl_saturation); Cvar_RegisterVariable(&r_glsl_saturation_redcompensate); Cvar_RegisterVariable(&r_glsl_vertextextureblend_usebothalphas); Cvar_RegisterVariable(&r_framedatasize); + for (i = 0;i < R_BUFFERDATA_COUNT;i++) + Cvar_RegisterVariable(&r_buffermegs[i]); + Cvar_RegisterVariable(&r_batch_dynamicbuffer); if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE) Cvar_SetValue("r_fullbrights", 0); +#ifdef DP_MOBILETOUCH + // GLES devices have terrible depth precision in general, so... + Cvar_SetValueQuick(&r_nearclip, 4); + Cvar_SetValueQuick(&r_farclip_base, 4096); + Cvar_SetValueQuick(&r_farclip_world, 0); + Cvar_SetValueQuick(&r_useinfinitefarclip, 0); +#endif R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap, NULL, NULL); } @@ -4353,7 +4505,11 @@ void GL_Init (void) VID_CheckExtensions(); // LordHavoc: report supported extensions +#ifdef CONFIG_MENU Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions ); +#else + Con_DPrintf("\nQuakeC extensions for server and client: %s\n", vm_sv_extensions ); +#endif // clear to black (loading plaque will be seen over this) GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128); @@ -4368,9 +4524,6 @@ int R_CullBox(const vec3_t mins, const vec3_t maxs) return false; for (i = 0;i < r_refdef.view.numfrustumplanes;i++) { - // skip nearclip plane, it often culls portals when you are very close, and is almost never useful - if (i == 4) - continue; p = r_refdef.view.frustum + i; switch(p->signbits) { @@ -4488,12 +4641,12 @@ void R_FrameData_Reset(void) } } -static void R_FrameData_Resize(void) +static void R_FrameData_Resize(qboolean mustgrow) { size_t wantedsize; wantedsize = (size_t)(r_framedatasize.value * 1024*1024); wantedsize = bound(65536, wantedsize, 1000*1024*1024); - if (!r_framedata_mem || r_framedata_mem->wantedsize != wantedsize) + if (!r_framedata_mem || r_framedata_mem->wantedsize != wantedsize || mustgrow) { r_framedata_mem_t *newmem = (r_framedata_mem_t *)Mem_Alloc(r_main_mempool, wantedsize); newmem->wantedsize = wantedsize; @@ -4508,7 +4661,7 @@ static void R_FrameData_Resize(void) void R_FrameData_NewFrame(void) { - R_FrameData_Resize(); + R_FrameData_Resize(false); if (!r_framedata_mem) return; // if we ran out of space on the last frame, free the old memory now @@ -4527,6 +4680,7 @@ void R_FrameData_NewFrame(void) void *R_FrameData_Alloc(size_t size) { void *data; + float newvalue; // align to 16 byte boundary - the data pointer is already aligned, so we // only need to ensure the size of every allocation is also aligned @@ -4535,16 +4689,18 @@ void *R_FrameData_Alloc(size_t size) while (!r_framedata_mem || r_framedata_mem->current + size > r_framedata_mem->size) { // emergency - we ran out of space, allocate more memory - Cvar_SetValueQuick(&r_framedatasize, bound(0.25f, r_framedatasize.value * 2.0f, 128.0f)); - R_FrameData_Resize(); + newvalue = bound(0.25f, r_framedatasize.value * 2.0f, 256.0f); + // this might not be a growing it, but we'll allocate another buffer every time + Cvar_SetValueQuick(&r_framedatasize, newvalue); + R_FrameData_Resize(true); } data = r_framedata_mem->data + r_framedata_mem->current; r_framedata_mem->current += size; // count the usage for stats - r_refdef.stats.framedatacurrent = max(r_refdef.stats.framedatacurrent, (int)r_framedata_mem->current); - r_refdef.stats.framedatasize = max(r_refdef.stats.framedatasize, (int)r_framedata_mem->size); + r_refdef.stats[r_stat_framedatacurrent] = max(r_refdef.stats[r_stat_framedatacurrent], (int)r_framedata_mem->current); + r_refdef.stats[r_stat_framedatasize] = max(r_refdef.stats[r_stat_framedatasize], (int)r_framedata_mem->size); return (void *)data; } @@ -4573,6 +4729,160 @@ void R_FrameData_ReturnToMark(void) //================================================================================== +// avoid reusing the same buffer objects on consecutive frames +#define R_BUFFERDATA_CYCLE 3 + +typedef struct r_bufferdata_buffer_s +{ + struct r_bufferdata_buffer_s *purge; // older buffer to free on next frame + size_t size; // how much usable space + size_t current; // how much space in use + r_meshbuffer_t *buffer; // the buffer itself +} +r_bufferdata_buffer_t; + +static int r_bufferdata_cycle = 0; // incremented and wrapped each frame +static r_bufferdata_buffer_t *r_bufferdata_buffer[R_BUFFERDATA_CYCLE][R_BUFFERDATA_COUNT]; + +/// frees all dynamic buffers +void R_BufferData_Reset(void) +{ + int cycle, type; + r_bufferdata_buffer_t **p, *mem; + for (cycle = 0;cycle < R_BUFFERDATA_CYCLE;cycle++) + { + for (type = 0;type < R_BUFFERDATA_COUNT;type++) + { + // free all buffers + p = &r_bufferdata_buffer[cycle][type]; + while (*p) + { + mem = *p; + *p = (*p)->purge; + if (mem->buffer) + R_Mesh_DestroyMeshBuffer(mem->buffer); + Mem_Free(mem); + } + } + } +} + +// resize buffer as needed (this actually makes a new one, the old one will be recycled next frame) +static void R_BufferData_Resize(r_bufferdata_type_t type, qboolean mustgrow, size_t minsize) +{ + r_bufferdata_buffer_t *mem = r_bufferdata_buffer[r_bufferdata_cycle][type]; + size_t size; + float newvalue = r_buffermegs[type].value; + + // increase the cvar if we have to (but only if we already have a mem) + if (mustgrow && mem) + newvalue *= 2.0f; + newvalue = bound(0.25f, newvalue, 256.0f); + while (newvalue * 1024*1024 < minsize) + newvalue *= 2.0f; + + // clamp the cvar to valid range + newvalue = bound(0.25f, newvalue, 256.0f); + if (r_buffermegs[type].value != newvalue) + Cvar_SetValueQuick(&r_buffermegs[type], newvalue); + + // calculate size in bytes + size = (size_t)(newvalue * 1024*1024); + size = bound(131072, size, 256*1024*1024); + + // allocate a new buffer if the size is different (purge old one later) + // or if we were told we must grow the buffer + if (!mem || mem->size != size || mustgrow) + { + mem = (r_bufferdata_buffer_t *)Mem_Alloc(r_main_mempool, sizeof(*mem)); + mem->size = size; + mem->current = 0; + if (type == R_BUFFERDATA_VERTEX) + mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbuffervertex", false, false, true, false); + else if (type == R_BUFFERDATA_INDEX16) + mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbufferindex16", true, false, true, true); + else if (type == R_BUFFERDATA_INDEX32) + mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbufferindex32", true, false, true, false); + else if (type == R_BUFFERDATA_UNIFORM) + mem->buffer = R_Mesh_CreateMeshBuffer(NULL, mem->size, "dynamicbufferuniform", false, true, true, false); + mem->purge = r_bufferdata_buffer[r_bufferdata_cycle][type]; + r_bufferdata_buffer[r_bufferdata_cycle][type] = mem; + } +} + +void R_BufferData_NewFrame(void) +{ + int type; + r_bufferdata_buffer_t **p, *mem; + // cycle to the next frame's buffers + r_bufferdata_cycle = (r_bufferdata_cycle + 1) % R_BUFFERDATA_CYCLE; + // if we ran out of space on the last time we used these buffers, free the old memory now + for (type = 0;type < R_BUFFERDATA_COUNT;type++) + { + if (r_bufferdata_buffer[r_bufferdata_cycle][type]) + { + R_BufferData_Resize((r_bufferdata_type_t)type, false, 131072); + // free all but the head buffer, this is how we recycle obsolete + // buffers after they are no longer in use + p = &r_bufferdata_buffer[r_bufferdata_cycle][type]->purge; + while (*p) + { + mem = *p; + *p = (*p)->purge; + if (mem->buffer) + R_Mesh_DestroyMeshBuffer(mem->buffer); + Mem_Free(mem); + } + // reset the current offset + r_bufferdata_buffer[r_bufferdata_cycle][type]->current = 0; + } + } +} + +r_meshbuffer_t *R_BufferData_Store(size_t datasize, const void *data, r_bufferdata_type_t type, int *returnbufferoffset) +{ + r_bufferdata_buffer_t *mem; + int offset = 0; + int padsize; + + *returnbufferoffset = 0; + + // align size to a byte boundary appropriate for the buffer type, this + // makes all allocations have aligned start offsets + if (type == R_BUFFERDATA_UNIFORM) + padsize = (datasize + r_uniformbufferalignment - 1) & ~(r_uniformbufferalignment - 1); + else + padsize = (datasize + 15) & ~15; + + // if we ran out of space in this buffer we must allocate a new one + if (!r_bufferdata_buffer[r_bufferdata_cycle][type] || r_bufferdata_buffer[r_bufferdata_cycle][type]->current + padsize > r_bufferdata_buffer[r_bufferdata_cycle][type]->size) + R_BufferData_Resize(type, true, padsize); + + // if the resize did not give us enough memory, fail + if (!r_bufferdata_buffer[r_bufferdata_cycle][type] || r_bufferdata_buffer[r_bufferdata_cycle][type]->current + padsize > r_bufferdata_buffer[r_bufferdata_cycle][type]->size) + Sys_Error("R_BufferData_Store: failed to create a new buffer of sufficient size\n"); + + mem = r_bufferdata_buffer[r_bufferdata_cycle][type]; + offset = (int)mem->current; + mem->current += padsize; + + // upload the data to the buffer at the chosen offset + if (offset == 0) + R_Mesh_UpdateMeshBuffer(mem->buffer, NULL, mem->size, false, 0); + R_Mesh_UpdateMeshBuffer(mem->buffer, data, datasize, true, offset); + + // count the usage for stats + r_refdef.stats[r_stat_bufferdatacurrent_vertex + type] = max(r_refdef.stats[r_stat_bufferdatacurrent_vertex + type], (int)mem->current); + r_refdef.stats[r_stat_bufferdatasize_vertex + type] = max(r_refdef.stats[r_stat_bufferdatasize_vertex + type], (int)mem->size); + + // return the buffer offset + *returnbufferoffset = offset; + + return mem->buffer; +} + +//================================================================================== + // LordHavoc: animcache originally written by Echon, rewritten since then /** @@ -4593,12 +4903,24 @@ void R_AnimCache_ClearCache(void) { ent = r_refdef.scene.entities[i]; ent->animcache_vertex3f = NULL; + ent->animcache_vertex3f_vertexbuffer = NULL; + ent->animcache_vertex3f_bufferoffset = 0; ent->animcache_normal3f = NULL; + ent->animcache_normal3f_vertexbuffer = NULL; + ent->animcache_normal3f_bufferoffset = 0; ent->animcache_svector3f = NULL; + ent->animcache_svector3f_vertexbuffer = NULL; + ent->animcache_svector3f_bufferoffset = 0; ent->animcache_tvector3f = NULL; + ent->animcache_tvector3f_vertexbuffer = NULL; + ent->animcache_tvector3f_bufferoffset = 0; ent->animcache_vertexmesh = NULL; - ent->animcache_vertex3fbuffer = NULL; - ent->animcache_vertexmeshbuffer = NULL; + ent->animcache_vertexmesh_vertexbuffer = NULL; + ent->animcache_vertexmesh_bufferoffset = 0; + ent->animcache_skeletaltransform3x4 = NULL; + ent->animcache_skeletaltransform3x4buffer = NULL; + ent->animcache_skeletaltransform3x4offset = 0; + ent->animcache_skeletaltransform3x4size = 0; } } @@ -4612,10 +4934,13 @@ static void R_AnimCache_UpdateEntityMeshBuffers(entity_render_t *ent, int numver if (!ent->animcache_vertexmesh && ent->animcache_normal3f) ent->animcache_vertexmesh = (r_vertexmesh_t *)R_FrameData_Alloc(sizeof(r_vertexmesh_t)*numvertices); - // TODO: upload vertex3f buffer? + // TODO: upload vertexbuffer? if (ent->animcache_vertexmesh) { - memcpy(ent->animcache_vertexmesh, ent->model->surfmesh.vertexmesh, sizeof(r_vertexmesh_t)*numvertices); + r_refdef.stats[r_stat_animcache_vertexmesh_count] += 1; + r_refdef.stats[r_stat_animcache_vertexmesh_vertices] += numvertices; + r_refdef.stats[r_stat_animcache_vertexmesh_maxvertices] = max(r_refdef.stats[r_stat_animcache_vertexmesh_maxvertices], numvertices); + memcpy(ent->animcache_vertexmesh, ent->model->surfmesh.data_vertexmesh, sizeof(r_vertexmesh_t)*numvertices); for (i = 0;i < numvertices;i++) memcpy(ent->animcache_vertexmesh[i].vertex3f, ent->animcache_vertex3f + 3*i, sizeof(float[3])); if (ent->animcache_svector3f) @@ -4627,7 +4952,6 @@ static void R_AnimCache_UpdateEntityMeshBuffers(entity_render_t *ent, int numver if (ent->animcache_normal3f) for (i = 0;i < numvertices;i++) memcpy(ent->animcache_vertexmesh[i].normal3f, ent->animcache_normal3f + 3*i, sizeof(float[3])); - // TODO: upload vertexmeshbuffer? } } @@ -4635,37 +4959,65 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool { dp_model_t *model = ent->model; int numvertices; - // see if it's already cached this frame + + // see if this ent is worth caching + if (!model || !model->Draw || !model->AnimateVertices) + return false; + // nothing to cache if it contains no animations and has no skeleton + if (!model->surfmesh.isanimated && !(model->num_bones && ent->skeleton && ent->skeleton->relativetransforms)) + return false; + // see if it is already cached for gpuskeletal + if (ent->animcache_skeletaltransform3x4) + return false; + // see if it is already cached as a mesh if (ent->animcache_vertex3f) { - // add normals/tangents if needed (this only happens with multiple views, reflections, cameras, etc) + // check if we need to add normals or tangents + if (ent->animcache_normal3f) + wantnormals = false; + if (ent->animcache_svector3f) + wanttangents = false; + if (!wantnormals && !wanttangents) + return false; + } + + // check which kind of cache we need to generate + if (r_gpuskeletal && model->num_bones > 0 && model->surfmesh.data_skeletalindex4ub) + { + // cache the skeleton so the vertex shader can use it + r_refdef.stats[r_stat_animcache_skeletal_count] += 1; + r_refdef.stats[r_stat_animcache_skeletal_bones] += model->num_bones; + r_refdef.stats[r_stat_animcache_skeletal_maxbones] = max(r_refdef.stats[r_stat_animcache_skeletal_maxbones], model->num_bones); + ent->animcache_skeletaltransform3x4 = (float *)R_FrameData_Alloc(sizeof(float[3][4]) * model->num_bones); + Mod_Skeletal_BuildTransforms(model, ent->frameblend, ent->skeleton, NULL, ent->animcache_skeletaltransform3x4); + // note: this can fail if the buffer is at the grow limit + ent->animcache_skeletaltransform3x4size = sizeof(float[3][4]) * model->num_bones; + ent->animcache_skeletaltransform3x4buffer = R_BufferData_Store(ent->animcache_skeletaltransform3x4size, ent->animcache_skeletaltransform3x4, R_BUFFERDATA_UNIFORM, &ent->animcache_skeletaltransform3x4offset); + } + else if (ent->animcache_vertex3f) + { + // mesh was already cached but we may need to add normals/tangents + // (this only happens with multiple views, reflections, cameras, etc) if (wantnormals || wanttangents) { - if (ent->animcache_normal3f) - wantnormals = false; - if (ent->animcache_svector3f) - wanttangents = false; - if (wantnormals || wanttangents) + numvertices = model->surfmesh.num_vertices; + if (wantnormals) + ent->animcache_normal3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); + if (wanttangents) { - numvertices = model->surfmesh.num_vertices; - if (wantnormals) - ent->animcache_normal3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); - if (wanttangents) - { - ent->animcache_svector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); - ent->animcache_tvector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); - } - model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? ent->animcache_normal3f : NULL, wanttangents ? ent->animcache_svector3f : NULL, wanttangents ? ent->animcache_tvector3f : NULL); - R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices); + ent->animcache_svector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); + ent->animcache_tvector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); } + model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? ent->animcache_normal3f : NULL, wanttangents ? ent->animcache_svector3f : NULL, wanttangents ? ent->animcache_tvector3f : NULL); + R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices); + r_refdef.stats[r_stat_animcache_shade_count] += 1; + r_refdef.stats[r_stat_animcache_shade_vertices] += numvertices; + r_refdef.stats[r_stat_animcache_shade_maxvertices] = max(r_refdef.stats[r_stat_animcache_shade_maxvertices], numvertices); } } else { - // see if this ent is worth caching - if (!model || !model->Draw || !model->surfmesh.isanimated || !model->AnimateVertices) - return false; - // get some memory for this entity and generate mesh data + // generate mesh cache numvertices = model->surfmesh.num_vertices; ent->animcache_vertex3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices); if (wantnormals) @@ -4677,6 +5029,15 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool } model->AnimateVertices(model, ent->frameblend, ent->skeleton, ent->animcache_vertex3f, ent->animcache_normal3f, ent->animcache_svector3f, ent->animcache_tvector3f); R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices); + if (wantnormals || wanttangents) + { + r_refdef.stats[r_stat_animcache_shade_count] += 1; + r_refdef.stats[r_stat_animcache_shade_vertices] += numvertices; + r_refdef.stats[r_stat_animcache_shade_maxvertices] = max(r_refdef.stats[r_stat_animcache_shade_maxvertices], numvertices); + } + r_refdef.stats[r_stat_animcache_shape_count] += 1; + r_refdef.stats[r_stat_animcache_shape_vertices] += numvertices; + r_refdef.stats[r_stat_animcache_shape_maxvertices] = max(r_refdef.stats[r_stat_animcache_shape_maxvertices], numvertices); } return true; } @@ -4736,7 +5097,7 @@ static void R_View_UpdateEntityLighting (void) continue; // skip bsp models - if (ent->model && (ent->model == cl.worldmodel || ent->model->brush.parentmodel == cl.worldmodel)) + if (ent->model && ent->model == cl.worldmodel) { // TODO: use modellight for r_ambient settings on world? VectorSet(ent->modellight_ambient, 0, 0, 0); @@ -4880,18 +5241,20 @@ static void R_View_UpdateEntityVisible (void) int samples; entity_render_t *ent; - renderimask = r_refdef.envmap ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL) - : r_fb.water.hideplayer ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL) - : (chase_active.integer || r_fb.water.renderingscene) ? RENDER_VIEWMODEL - : RENDER_EXTERIORMODEL; + if (r_refdef.envmap || r_fb.water.hideplayer) + renderimask = RENDER_EXTERIORMODEL | RENDER_VIEWMODEL; + else if (chase_active.integer || r_fb.water.renderingscene) + renderimask = RENDER_VIEWMODEL; + else + renderimask = RENDER_EXTERIORMODEL; if (!r_drawviewmodel.integer) renderimask |= RENDER_VIEWMODEL; if (!r_drawexteriormodel.integer) renderimask |= RENDER_EXTERIORMODEL; + memset(r_refdef.viewcache.entityvisible, 0, r_refdef.scene.numentities); if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs) { // worldmodel can check visibility - memset(r_refdef.viewcache.entityvisible, 0, r_refdef.scene.numentities); for (i = 0;i < r_refdef.scene.numentities;i++) { ent = r_refdef.scene.entities[i]; @@ -4907,10 +5270,12 @@ static void R_View_UpdateEntityVisible (void) for (i = 0;i < r_refdef.scene.numentities;i++) { ent = r_refdef.scene.entities[i]; - r_refdef.viewcache.entityvisible[i] = !(ent->flags & renderimask) && ((ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)) || !R_CullBox(ent->mins, ent->maxs)); + if (!(ent->flags & renderimask)) + if (!R_CullBox(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE))) + r_refdef.viewcache.entityvisible[i] = true; } } - if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane && !r_trippy.integer) + if(r_cullentities_trace.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane && !r_trippy.integer) // sorry, this check doesn't work for portal/reflection/refraction renders as the view origin is not useful for culling { for (i = 0;i < r_refdef.scene.numentities;i++) @@ -4963,7 +5328,7 @@ static void R_DrawModels(void) if (!r_refdef.viewcache.entityvisible[i]) continue; ent = r_refdef.scene.entities[i]; - r_refdef.stats.entities++; + r_refdef.stats[r_stat_entities]++; /* if (ent->model && !strncmp(ent->model->name, "models/proto_", 13)) { @@ -5281,7 +5646,6 @@ static void R_View_UpdateWithScissor(const int *myscissor) R_View_WorldVisibility(r_refdef.view.useclipplane); R_View_UpdateEntityVisible(); R_View_UpdateEntityLighting(); - R_AnimCache_CacheVisibleEntities(); } static void R_View_Update(void) @@ -5291,7 +5655,6 @@ static void R_View_Update(void) R_View_WorldVisibility(r_refdef.view.useclipplane); R_View_UpdateEntityVisible(); R_View_UpdateEntityLighting(); - R_AnimCache_CacheVisibleEntities(); } float viewscalefpsadjusted = 1.0f; @@ -5375,7 +5738,9 @@ void R_EntityMatrix(const matrix4x4_t *matrix) case RENDERPATH_GL11: case RENDERPATH_GL13: case RENDERPATH_GLES1: +#ifndef USE_GLES2 qglLoadMatrixf(gl_modelview16f);CHECKGLERROR +#endif break; case RENDERPATH_SOFT: DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f); @@ -5489,7 +5854,7 @@ void R_RenderView_UpdateViewVectors(void) Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin); VectorNegate(r_refdef.view.left, r_refdef.view.right); // make an inverted copy of the view matrix for tracking sprites - Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix); + Matrix4x4_Invert_Full(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix); } void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture); @@ -5500,7 +5865,7 @@ static void R_Water_StartFrame(void) int i; int waterwidth, waterheight, texturewidth, textureheight, camerawidth, cameraheight; r_waterstate_waterplane_t *p; - qboolean usewaterfbo = (r_viewfbo.integer >= 1 || r_water_fbo.integer >= 1) && vid.support.ext_framebuffer_object && vid.samples < 2; + qboolean usewaterfbo = (r_viewfbo.integer >= 1 || r_water_fbo.integer >= 1) && vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two && vid.samples < 2; if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d) return; @@ -5537,10 +5902,10 @@ static void R_Water_StartFrame(void) } else { - for (texturewidth = 1;texturewidth < waterwidth ;texturewidth *= 2); - for (textureheight = 1;textureheight < waterheight;textureheight *= 2); - for (camerawidth = 1;camerawidth <= waterwidth; camerawidth *= 2); camerawidth /= 2; - for (cameraheight = 1;cameraheight <= waterheight;cameraheight *= 2); cameraheight /= 2; + for (texturewidth = 1;texturewidth < waterwidth ;texturewidth *= 2); + for (textureheight = 1;textureheight < waterheight;textureheight *= 2); + for (camerawidth = 1;camerawidth * 2 <= waterwidth ;camerawidth *= 2); + for (cameraheight = 1;cameraheight * 2 <= waterheight;cameraheight *= 2); } // allocate textures as needed @@ -5663,25 +6028,33 @@ void R_Water_AddWaterPlane(msurface_t *surface, int entno) } } planeindex = bestplaneindex; - p = r_fb.water.waterplanes + planeindex; // if this surface does not fit any known plane rendered this frame, add one - if ((planeindex < 0 || bestplanescore > 0.001f) && r_fb.water.numwaterplanes < r_fb.water.maxwaterplanes) + if (planeindex < 0 || bestplanescore > 0.001f) { - // store the new plane - planeindex = r_fb.water.numwaterplanes++; - p = r_fb.water.waterplanes + planeindex; - p->plane = plane; - // clear materialflags and pvs - p->materialflags = 0; - p->pvsvalid = false; - p->camera_entity = t->camera_entity; - VectorCopy(mins, p->mins); - VectorCopy(maxs, p->maxs); + if (r_fb.water.numwaterplanes < r_fb.water.maxwaterplanes) + { + // store the new plane + planeindex = r_fb.water.numwaterplanes++; + p = r_fb.water.waterplanes + planeindex; + p->plane = plane; + // clear materialflags and pvs + p->materialflags = 0; + p->pvsvalid = false; + p->camera_entity = t->camera_entity; + VectorCopy(mins, p->mins); + VectorCopy(maxs, p->maxs); + } + else + { + // We're totally screwed. + return; + } } else { // merge mins/maxs when we're adding this surface to the plane + p = r_fb.water.waterplanes + planeindex; p->mins[0] = min(p->mins[0], mins[0]); p->mins[1] = min(p->mins[1], mins[1]); p->mins[2] = min(p->mins[2], mins[2]); @@ -5694,7 +6067,7 @@ void R_Water_AddWaterPlane(msurface_t *surface, int entno) if(!(p->materialflags & MATERIALFLAG_CAMERA)) { // merge this surface's PVS into the waterplane - if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS + if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0) { r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid); @@ -5714,7 +6087,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t int planeindex, qualityreduction = 0, old_r_dynamic = 0, old_r_shadows = 0, old_r_worldrtlight = 0, old_r_dlight = 0, old_r_particles = 0, old_r_decals = 0; r_waterstate_waterplane_t *p; vec3_t visorigin; - qboolean usewaterfbo = (r_viewfbo.integer >= 1 || r_water_fbo.integer >= 1) && vid.support.ext_framebuffer_object && vid.samples < 2; + qboolean usewaterfbo = (r_viewfbo.integer >= 1 || r_water_fbo.integer >= 1) && vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two && vid.samples < 2; char vabuf[1024]; originalview = r_refdef.view; @@ -5746,6 +6119,8 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t // make sure enough textures are allocated for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++) { + if (r_water_cameraentitiesonly.value != 0 && !p->camera_entity) + continue; if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION)) { if (!p->texture_refraction) @@ -5801,6 +6176,8 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t r_fb.water.renderingscene = true; for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++) { + if (r_water_cameraentitiesonly.value != 0 && !p->camera_entity) + continue; if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)) { r_refdef.view = myview; @@ -5828,13 +6205,14 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t memset(r_refdef.viewcache.world_pvsbits, 0xFF, r_refdef.scene.worldmodel->brush.num_pvsclusterbytes); } - r_fb.water.hideplayer = r_water_hideplayer.integer >= 2; + r_fb.water.hideplayer = ((r_water_hideplayer.integer >= 2) && !chase_active.integer); R_ResetViewRendering3D(p->fbo_reflection, r_fb.water.depthtexture, p->texture_reflection); R_ClearScreen(r_refdef.fogenabled); if(r_water_scissormode.integer & 2) R_View_UpdateWithScissor(myscissor); else R_View_Update(); + R_AnimCache_CacheVisibleEntities(); if(r_water_scissormode.integer & 1) GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]); R_RenderScene(p->fbo_reflection, r_fb.water.depthtexture, p->texture_reflection); @@ -5856,7 +6234,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t continue; // FIXME the plane then still may get rendered but with broken texture, but it sure won't be visible } - r_fb.water.hideplayer = r_water_hideplayer.integer >= 1; + r_fb.water.hideplayer = ((r_water_hideplayer.integer >= 1) && !chase_active.integer); r_refdef.view.clipplane = p->plane; VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal); @@ -5883,6 +6261,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t R_View_UpdateWithScissor(myscissor); else R_View_Update(); + R_AnimCache_CacheVisibleEntities(); if(r_water_scissormode.integer & 1) GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]); R_RenderScene(p->fbo_refraction, r_fb.water.depthtexture, p->texture_refraction); @@ -5937,6 +6316,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t R_ResetViewRendering3D(p->fbo_camera, r_fb.water.depthtexture, p->texture_camera); R_ClearScreen(r_refdef.fogenabled); R_View_Update(); + R_AnimCache_CacheVisibleEntities(); R_RenderScene(p->fbo_camera, r_fb.water.depthtexture, p->texture_camera); if (!p->fbo_camera) @@ -5952,6 +6332,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t if (!r_fb.water.depthtexture) R_ClearScreen(r_refdef.fogenabled); R_View_Update(); + R_AnimCache_CacheVisibleEntities(); goto finish; error: r_refdef.view = originalview; @@ -5982,14 +6363,14 @@ static void R_Bloom_StartFrame(void) int i; int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight; int viewwidth, viewheight; - qboolean useviewfbo = r_viewfbo.integer >= 1 && vid.support.ext_framebuffer_object && vid.samples < 2; + qboolean useviewfbo = r_viewfbo.integer >= 1 && vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two && vid.samples < 2; textype_t textype = TEXTYPE_COLORBUFFER; switch (vid.renderpath) { case RENDERPATH_GL20: r_fb.usedepthtextures = r_usedepthtextures.integer != 0; - if (vid.support.ext_framebuffer_object) + if (vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two) { if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F; if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F; @@ -6045,7 +6426,7 @@ static void R_Bloom_StartFrame(void) // set bloomwidth and bloomheight to the bloom resolution that will be // used (often less than the screen resolution for faster rendering) - r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, vid.height); + r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, vid.width); r_fb.bloomheight = r_fb.bloomwidth * vid.height / vid.width; r_fb.bloomheight = bound(1, r_fb.bloomheight, vid.height); r_fb.bloomwidth = bound(1, r_fb.bloomwidth, (int)vid.maxtexturesize_2d); @@ -6074,7 +6455,7 @@ static 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)) + if (!((r_glsl_postprocess.integer || r_fxaa.integer) || (!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) || (v_glslgamma.integer && !vid_gammatables_trivial)) && !r_bloom.integer && (R_Stereo_Active() || (r_motionblur.value <= 0 && r_damageblur.value <= 0)) && !useviewfbo @@ -6151,7 +6532,7 @@ static void R_Bloom_StartFrame(void) } // bloom texture is a different resolution - r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.height); + r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.width); r_fb.bloomheight = r_fb.bloomwidth * r_refdef.view.height / r_refdef.view.width; r_fb.bloomheight = bound(1, r_fb.bloomheight, r_refdef.view.height); r_fb.bloomwidth = bound(1, r_fb.bloomwidth, r_fb.bloomtexturewidth); @@ -6168,6 +6549,14 @@ static void R_Bloom_StartFrame(void) r_fb.screentexcoord2f[6] = 0; r_fb.screentexcoord2f[7] = 0; + if(r_fb.fbo) + { + for (i = 1;i < 8;i += 2) + { + r_fb.screentexcoord2f[i] += 1 - (float)(viewheight + r_refdef.view.y) / (float)r_fb.screentextureheight; + } + } + // set up a texcoord array for the reduced resolution bloom image // (which will be additive blended over the screen image) r_fb.bloomtexcoord2f[0] = 0; @@ -6191,20 +6580,17 @@ static void R_Bloom_StartFrame(void) case RENDERPATH_D3D9: case RENDERPATH_D3D10: case RENDERPATH_D3D11: + for (i = 0;i < 4;i++) { - int i; - for (i = 0;i < 4;i++) - { - r_fb.screentexcoord2f[i*2+0] += 0.5f / (float)r_fb.screentexturewidth; - r_fb.screentexcoord2f[i*2+1] += 0.5f / (float)r_fb.screentextureheight; - r_fb.bloomtexcoord2f[i*2+0] += 0.5f / (float)r_fb.bloomtexturewidth; - r_fb.bloomtexcoord2f[i*2+1] += 0.5f / (float)r_fb.bloomtextureheight; - } + r_fb.screentexcoord2f[i*2+0] += 0.5f / (float)r_fb.screentexturewidth; + r_fb.screentexcoord2f[i*2+1] += 0.5f / (float)r_fb.screentextureheight; + r_fb.bloomtexcoord2f[i*2+0] += 0.5f / (float)r_fb.bloomtexturewidth; + r_fb.bloomtexcoord2f[i*2+1] += 0.5f / (float)r_fb.bloomtextureheight; } break; } - R_Viewport_InitOrtho(&r_fb.bloomviewport, &identitymatrix, r_refdef.view.x, (r_fb.bloomfbo[0] ? r_fb.bloomtextureheight : vid.height) - r_fb.bloomheight - r_refdef.view.y, r_fb.bloomwidth, r_fb.bloomheight, 0, 0, 1, 1, -10, 100, NULL); + R_Viewport_InitOrtho(&r_fb.bloomviewport, &identitymatrix, 0, 0, r_fb.bloomwidth, r_fb.bloomheight, 0, 0, 1, 1, -10, 100, NULL); if (r_fb.fbo) r_refdef.view.clear = true; @@ -6217,19 +6603,23 @@ static void R_Bloom_MakeTexture(void) rtexture_t *intex; float colorscale = r_bloom_colorscale.value; - r_refdef.stats.bloom++; - + r_refdef.stats[r_stat_bloom]++; + +#if 0 + // this copy is unnecessary since it happens in R_BlendView already if (!r_fb.fbo) { R_Mesh_CopyToTexture(r_fb.colortexture, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height); - r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height; + r_refdef.stats[r_stat_bloom_copypixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height; } +#endif // scale down screen texture to the bloom texture size CHECKGLERROR r_fb.bloomindex = 0; R_Mesh_SetRenderTargets(r_fb.bloomfbo[r_fb.bloomindex], NULL, r_fb.bloomtexture[r_fb.bloomindex], NULL, NULL, NULL); R_SetViewport(&r_fb.bloomviewport); + GL_DepthTest(false); GL_BlendFunc(GL_ONE, GL_ZERO); GL_Color(colorscale, colorscale, colorscale, 1); // D3D has upside down Y coords, the easiest way to flip this is to flip the screen vertices rather than the texcoords, so we just use a different array for that... @@ -6252,14 +6642,14 @@ static void R_Bloom_MakeTexture(void) // TODO: do boxfilter scale-down in shader? R_SetupShader_Generic(r_fb.colortexture, NULL, GL_MODULATE, 1, false, true, true); R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); - r_refdef.stats.bloom_drawpixels += r_fb.bloomwidth * r_fb.bloomheight; + r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight; // we now have a properly scaled bloom image if (!r_fb.bloomfbo[r_fb.bloomindex]) { // copy it into the bloom texture R_Mesh_CopyToTexture(r_fb.bloomtexture[r_fb.bloomindex], 0, 0, r_fb.bloomviewport.x, r_fb.bloomviewport.y, r_fb.bloomviewport.width, r_fb.bloomviewport.height); - r_refdef.stats.bloom_copypixels += r_fb.bloomviewport.width * r_fb.bloomviewport.height; + r_refdef.stats[r_stat_bloom_copypixels] += r_fb.bloomviewport.width * r_fb.bloomviewport.height; } // multiply bloom image by itself as many times as desired @@ -6285,13 +6675,13 @@ static void R_Bloom_MakeTexture(void) R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.bloomtexcoord2f); R_SetupShader_Generic(intex, NULL, GL_MODULATE, 1, false, true, false); R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); - r_refdef.stats.bloom_drawpixels += r_fb.bloomwidth * r_fb.bloomheight; + r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight; if (!r_fb.bloomfbo[r_fb.bloomindex]) { // copy the darkened image to a texture R_Mesh_CopyToTexture(r_fb.bloomtexture[r_fb.bloomindex], 0, 0, r_fb.bloomviewport.x, r_fb.bloomviewport.y, r_fb.bloomviewport.width, r_fb.bloomviewport.height); - r_refdef.stats.bloom_copypixels += r_fb.bloomviewport.width * r_fb.bloomviewport.height; + r_refdef.stats[r_stat_bloom_copypixels] += r_fb.bloomviewport.width * r_fb.bloomviewport.height; } } @@ -6318,14 +6708,14 @@ static void R_Bloom_MakeTexture(void) xoffset /= (float)r_fb.bloomtexturewidth; yoffset /= (float)r_fb.bloomtextureheight; // compute a texcoord array with the specified x and y offset - r_fb.offsettexcoord2f[0] = xoffset+0; - r_fb.offsettexcoord2f[1] = yoffset+(float)r_fb.bloomheight / (float)r_fb.bloomtextureheight; - r_fb.offsettexcoord2f[2] = xoffset+(float)r_fb.bloomwidth / (float)r_fb.bloomtexturewidth; - r_fb.offsettexcoord2f[3] = yoffset+(float)r_fb.bloomheight / (float)r_fb.bloomtextureheight; - r_fb.offsettexcoord2f[4] = xoffset+(float)r_fb.bloomwidth / (float)r_fb.bloomtexturewidth; - r_fb.offsettexcoord2f[5] = yoffset+0; - r_fb.offsettexcoord2f[6] = xoffset+0; - r_fb.offsettexcoord2f[7] = yoffset+0; + r_fb.offsettexcoord2f[0] = xoffset+r_fb.bloomtexcoord2f[0]; + r_fb.offsettexcoord2f[1] = yoffset+r_fb.bloomtexcoord2f[1]; + r_fb.offsettexcoord2f[2] = xoffset+r_fb.bloomtexcoord2f[2]; + r_fb.offsettexcoord2f[3] = yoffset+r_fb.bloomtexcoord2f[3]; + r_fb.offsettexcoord2f[4] = xoffset+r_fb.bloomtexcoord2f[4]; + r_fb.offsettexcoord2f[5] = yoffset+r_fb.bloomtexcoord2f[5]; + r_fb.offsettexcoord2f[6] = xoffset+r_fb.bloomtexcoord2f[6]; + r_fb.offsettexcoord2f[7] = yoffset+r_fb.bloomtexcoord2f[7]; // this r value looks like a 'dot' particle, fading sharply to // black at the edges // (probably not realistic but looks good enough) @@ -6337,7 +6727,7 @@ static void R_Bloom_MakeTexture(void) GL_Color(r, r, r, 1); R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.offsettexcoord2f); R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); - r_refdef.stats.bloom_drawpixels += r_fb.bloomwidth * r_fb.bloomheight; + r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight; GL_BlendFunc(GL_ONE, GL_ONE); } @@ -6345,7 +6735,7 @@ static void R_Bloom_MakeTexture(void) { // copy the vertically or horizontally blurred bloom view to a texture R_Mesh_CopyToTexture(r_fb.bloomtexture[r_fb.bloomindex], 0, 0, r_fb.bloomviewport.x, r_fb.bloomviewport.y, r_fb.bloomviewport.width, r_fb.bloomviewport.height); - r_refdef.stats.bloom_copypixels += r_fb.bloomviewport.width * r_fb.bloomviewport.height; + r_refdef.stats[r_stat_bloom_copypixels] += r_fb.bloomviewport.width * r_fb.bloomviewport.height; } } } @@ -6355,6 +6745,8 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext unsigned int permutation; float uservecs[4][4]; + R_EntityMatrix(&identitymatrix); + switch (vid.renderpath) { case RENDERPATH_GL20: @@ -6375,7 +6767,7 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext if (!r_fb.fbo) { R_Mesh_CopyToTexture(r_fb.colortexture, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height); - r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height; + r_refdef.stats[r_stat_bloom_copypixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height; } if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0) && r_fb.ghosttexture) @@ -6441,7 +6833,7 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext } R_SetupShader_Generic(r_fb.ghosttexture, NULL, GL_MODULATE, 1, false, true, true); 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[r_stat_bloom_drawpixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height; } // updates old view angles for next pass @@ -6449,7 +6841,7 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext // copy view into the ghost texture R_Mesh_CopyToTexture(r_fb.ghosttexture, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height); - r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height; + r_refdef.stats[r_stat_bloom_copypixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height; r_fb.ghosttexture_valid = true; } } @@ -6557,7 +6949,7 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext break; } R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); - r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height; + r_refdef.stats[r_stat_bloom_drawpixels] += r_refdef.view.width * r_refdef.view.height; break; case RENDERPATH_GL11: case RENDERPATH_GL13: @@ -6705,9 +7097,11 @@ void R_UpdateVariables(void) r_refdef.lightmapintensity = 0; } + r_gpuskeletal = false; switch(vid.renderpath) { case RENDERPATH_GL20: + r_gpuskeletal = vid.support.arb_uniform_buffer_object && r_glsl_skeletal.integer && !r_showsurfaces.integer; // FIXME add r_showsurfaces support to GLSL skeletal! case RENDERPATH_D3D9: case RENDERPATH_D3D10: case RENDERPATH_D3D11: @@ -6848,7 +7242,6 @@ void R_RenderView(void) R_SortEntities(); R_AnimCache_ClearCache(); - R_FrameData_NewFrame(); /* adjust for stereo display */ if(R_Stereo_Active()) @@ -6895,6 +7288,11 @@ void R_RenderView(void) R_Shadow_UpdateWorldLightSelection(); R_Bloom_StartFrame(); + + // apply bloom brightness offset + if(r_fb.bloomtexture[0]) + r_refdef.view.colorscale *= r_bloom_scenebrightness.value; + R_Water_StartFrame(); // now we probably have an fbo to render into @@ -6922,6 +7320,10 @@ void R_RenderView(void) if (r_timereport_active) R_TimeReport("visibility"); + R_AnimCache_CacheVisibleEntities(); + if (r_timereport_active) + R_TimeReport("animcache"); + R_Shadow_UpdateBounceGridTexture(); if (r_timereport_active && r_shadow_bouncegrid.integer) R_TimeReport("bouncegrid"); @@ -6976,6 +7378,7 @@ static void R_DrawEntityBBoxes(void); static void R_DrawModelDecals(void); extern cvar_t cl_decals_newsystem; extern qboolean r_shadow_usingdeferredprepass; +extern int r_shadow_shadowmapatlas_modelshadows_size; void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture) { qboolean shadowmapping = false; @@ -6983,7 +7386,7 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture) if (r_timereport_active) R_TimeReport("beginscene"); - r_refdef.stats.renders++; + r_refdef.stats[r_stat_renders]++; R_UpdateFog(); @@ -7027,18 +7430,25 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture) } } + R_Shadow_PrepareModelShadows(); R_Shadow_PrepareLights(fbo, depthtexture, colortexture); - if (r_shadows.integer > 0 && r_refdef.lightmapintensity > 0) - R_Shadow_PrepareModelShadows(); if (r_timereport_active) R_TimeReport("preparelights"); - if (R_Shadow_ShadowMappingEnabled()) - shadowmapping = true; + // render all the shadowmaps that will be used for this view + shadowmapping = R_Shadow_ShadowMappingEnabled(); + if (shadowmapping || r_shadow_shadowmapatlas_modelshadows_size) + { + R_Shadow_DrawShadowMaps(); + if (r_timereport_active) + R_TimeReport("shadowmaps"); + } + // render prepass deferred lighting if r_shadow_deferred is on, this produces light buffers that will be sampled in forward pass if (r_shadow_usingdeferredprepass) R_Shadow_DrawPrepass(); + // now we begin the forward pass of the view render if (r_depthfirst.integer >= 1 && cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawDepth) { r_refdef.scene.worldmodel->DrawDepth(r_refdef.scene.worldentity); @@ -7052,16 +7462,6 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture) R_TimeReport("modeldepth"); } - if (r_shadows.integer >= 2 && shadowmapping && r_refdef.lightmapintensity > 0) - { - R_ResetViewRendering3D(fbo, depthtexture, colortexture); - R_DrawModelShadowMaps(fbo, depthtexture, colortexture); - R_ResetViewRendering3D(fbo, depthtexture, colortexture); - // don't let sound skip if going slow - if (r_refdef.scene.extraupdate) - S_ExtraUpdate (); - } - if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->Draw) { r_refdef.scene.worldmodel->Draw(r_refdef.scene.worldentity); @@ -7084,7 +7484,7 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture) if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0) { R_ResetViewRendering3D(fbo, depthtexture, colortexture); - R_DrawModelShadows(fbo, depthtexture, colortexture); + R_Shadow_DrawModelShadows(); R_ResetViewRendering3D(fbo, depthtexture, colortexture); // don't let sound skip if going slow if (r_refdef.scene.extraupdate) @@ -7105,7 +7505,7 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture) if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0) { R_ResetViewRendering3D(fbo, depthtexture, colortexture); - R_DrawModelShadows(fbo, depthtexture, colortexture); + R_Shadow_DrawModelShadows(); R_ResetViewRendering3D(fbo, depthtexture, colortexture); // don't let sound skip if going slow if (r_refdef.scene.extraupdate) @@ -7323,7 +7723,7 @@ static void R_DrawEntityBBoxes(void) if(PRVM_serveredictedict(edict, viewmodelforclient) != 0) continue; VectorLerp(edict->priv.server->areamins, 0.5f, edict->priv.server->areamaxs, center); - R_MeshQueue_AddTransparent(MESHQUEUE_SORT_DISTANCE, center, R_DrawEntityBBoxes_Callback, (entity_render_t *)NULL, i, (rtlight_t *)NULL); + R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, center, R_DrawEntityBBoxes_Callback, (entity_render_t *)NULL, i, (rtlight_t *)NULL); } } @@ -7431,12 +7831,12 @@ void R_DrawNoModel(entity_render_t *ent) vec3_t org; Matrix4x4_OriginFromMatrix(&ent->matrix, org); if ((ent->flags & RENDER_ADDITIVE) || (ent->alpha < 1)) - R_MeshQueue_AddTransparent((ent->flags & RENDER_NODEPTHTEST) ? MESHQUEUE_SORT_HUD : MESHQUEUE_SORT_DISTANCE, org, R_DrawNoModel_TransparentCallback, ent, 0, rsurface.rtlight); + R_MeshQueue_AddTransparent((ent->flags & RENDER_NODEPTHTEST) ? TRANSPARENTSORT_HUD : TRANSPARENTSORT_DISTANCE, org, R_DrawNoModel_TransparentCallback, ent, 0, rsurface.rtlight); else R_DrawNoModel_TransparentCallback(ent, rsurface.rtlight, 0, NULL); } -void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, float width) +void R_CalcBeam_Vertex3f (float *vert, const float *org1, const float *org2, float width) { vec3_t right1, right2, diff, normal; @@ -7647,10 +8047,22 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms) static void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *tcmod, int currentmaterialflags) { int w, h, idx; - double f; - double offsetd[2]; + float shadertime; + float f; + float offsetd[2]; float tcmat[12]; matrix4x4_t matrix, temp; + // if shadertime exceeds about 9 hours (32768 seconds), just wrap it, + // it's better to have one huge fixup every 9 hours than gradual + // degradation over time which looks consistently bad after many hours. + // + // tcmod scroll in particular suffers from this degradation which can't be + // effectively worked around even with floor() tricks because we don't + // know if tcmod scroll is the last tcmod being applied, and for clampmap + // a workaround involving floor() would be incorrect anyway... + shadertime = rsurface.shadertime; + if (shadertime >= 32768.0f) + shadertime -= floor(rsurface.shadertime * (1.0f / 32768.0f)) * 32768.0f; switch(tcmod->tcmod) { case Q3TCMOD_COUNT: @@ -7666,16 +8078,17 @@ static void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcm Matrix4x4_CreateTranslate(&matrix, 0, 0, 0); break; case Q3TCMOD_ROTATE: - f = tcmod->parms[0] * rsurface.shadertime; Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0); - Matrix4x4_ConcatRotate(&matrix, (f / 360 - floor(f / 360)) * 360, 0, 0, 1); + Matrix4x4_ConcatRotate(&matrix, tcmod->parms[0] * rsurface.shadertime, 0, 0, 1); Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0); break; case Q3TCMOD_SCALE: Matrix4x4_CreateScale3(&matrix, tcmod->parms[0], tcmod->parms[1], 1); break; case Q3TCMOD_SCROLL: - // extra care is needed because of precision breakdown with large values of time + // this particular tcmod is a "bug for bug" compatible one with regards to + // Quake3, the wrapping is unnecessary with our shadetime fix but quake3 + // specifically did the wrapping and so we must mimic that... offsetd[0] = tcmod->parms[0] * rsurface.shadertime; offsetd[1] = tcmod->parms[1] * rsurface.shadertime; Matrix4x4_CreateTranslate(&matrix, offsetd[0] - floor(offsetd[0]), offsetd[1] - floor(offsetd[1]), 0); @@ -7739,15 +8152,15 @@ texture_t *R_GetCurrentTexture(texture_t *t) { int i; const entity_render_t *ent = rsurface.entity; - dp_model_t *model = ent->model; + dp_model_t *model = ent->model; // when calling this, ent must not be NULL q3shaderinfo_layer_tcmod_t *tcmod; - if (t->update_lastrenderframe == r_textureframe && t->update_lastrenderentity == (void *)ent) + if (t->update_lastrenderframe == r_textureframe && t->update_lastrenderentity == (void *)ent && !rsurface.forcecurrenttextureupdate) return t->currentframe; t->update_lastrenderframe = r_textureframe; t->update_lastrenderentity = (void *)ent; - if(ent && ent->entitynumber >= MAX_EDICTS && ent->entitynumber < 2 * MAX_EDICTS) + if(ent->entitynumber >= MAX_EDICTS && ent->entitynumber < 2 * MAX_EDICTS) t->camera_entity = ent->entitynumber; else t->camera_entity = 0; @@ -7771,7 +8184,9 @@ texture_t *R_GetCurrentTexture(texture_t *t) { // use an alternate animation if the entity's frame is not 0, // and only if the texture has an alternate animation - if (rsurface.ent_alttextures && t->anim_total[1]) + if (t->animated == 2) // q2bsp + t = t->anim_frames[0][ent->framegroupblend[0].frame % t->anim_total[0]]; + else if (rsurface.ent_alttextures && t->anim_total[1]) t = t->anim_frames[1][(t->anim_total[1] >= 2) ? ((int)(rsurface.shadertime * 5.0f) % t->anim_total[1]) : 0]; else t = t->anim_frames[0][(t->anim_total[0] >= 2) ? ((int)(rsurface.shadertime * 5.0f) % t->anim_total[0]) : 0]; @@ -7793,16 +8208,16 @@ texture_t *R_GetCurrentTexture(texture_t *t) if (strcmp(r_qwskincache[i].name, cl.scores[i].qw_skin)) R_LoadQWSkin(&r_qwskincache[i], cl.scores[i].qw_skin); t->currentskinframe = r_qwskincache[i].skinframe; - if (t->currentskinframe == NULL) - t->currentskinframe = t->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->skinframerate, t->numskinframes)]; + if (t->materialshaderpass && t->currentskinframe == NULL) + t->currentskinframe = t->materialshaderpass->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->materialshaderpass->framerate, t->materialshaderpass->numframes)]; } - else if (t->numskinframes >= 2) - t->currentskinframe = t->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->skinframerate, t->numskinframes)]; - if (t->backgroundnumskinframes >= 2) - t->backgroundcurrentskinframe = t->backgroundskinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->backgroundskinframerate, t->backgroundnumskinframes)]; + else if (t->materialshaderpass && t->materialshaderpass->numframes >= 2) + t->currentskinframe = t->materialshaderpass->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->materialshaderpass->framerate, t->materialshaderpass->numframes)]; + if (t->backgroundshaderpass && t->backgroundshaderpass->numframes >= 2) + t->backgroundcurrentskinframe = t->backgroundshaderpass->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->backgroundshaderpass->framerate, t->backgroundshaderpass->numframes)]; t->currentmaterialflags = t->basematerialflags; - t->currentalpha = rsurface.colormod[3]; + t->currentalpha = rsurface.colormod[3] * t->basealpha; if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer || r_trippy.integer)) t->currentalpha *= r_wateralpha.value; if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) @@ -7815,7 +8230,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) { // no modellight if using fakelight for the map } - else if (rsurface.modeltexcoordlightmap2f == NULL && !(t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)) + else if ((rsurface.modeltexcoordlightmap2f == NULL || (rsurface.ent_flags & (RENDER_DYNAMICMODELLIGHT | RENDER_CUSTOMIZEDMODELLIGHT))) && !(t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)) { // pick a model lighting mode if (VectorLength2(rsurface.modellight_diffuse) >= (1.0f / 256.0f)) @@ -7834,7 +8249,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) t->currentmaterialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE; if (rsurface.ent_flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL)) t->currentmaterialflags |= MATERIALFLAG_SHORTDEPTHRANGE; - if (t->backgroundnumskinframes) + if (t->backgroundshaderpass) t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND; if (t->currentmaterialflags & MATERIALFLAG_BLENDED) { @@ -7863,10 +8278,9 @@ texture_t *R_GetCurrentTexture(texture_t *t) Matrix4x4_CreateIdentity(&t->currentbackgroundtexmatrix); } - for (i = 0, tcmod = t->tcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++) - R_tcMod_ApplyToMatrix(&t->currenttexmatrix, tcmod, t->currentmaterialflags); - for (i = 0, tcmod = t->backgroundtcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++) - R_tcMod_ApplyToMatrix(&t->currentbackgroundtexmatrix, tcmod, t->currentmaterialflags); + if (t->materialshaderpass) + for (i = 0, tcmod = t->materialshaderpass->tcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++) + R_tcMod_ApplyToMatrix(&t->currenttexmatrix, tcmod, t->currentmaterialflags); t->colormapping = VectorLength2(rsurface.colormap_pantscolor) + VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f); if (t->currentskinframe->qpixels) @@ -7883,14 +8297,21 @@ texture_t *R_GetCurrentTexture(texture_t *t) t->glowtexture = t->currentskinframe->glow; t->fogtexture = t->currentskinframe->fog; t->reflectmasktexture = t->currentskinframe->reflect; - if (t->backgroundnumskinframes) + if (t->backgroundshaderpass) { + for (i = 0, tcmod = t->backgroundshaderpass->tcmods; i < Q3MAXTCMODS && tcmod->tcmod; i++, tcmod++) + R_tcMod_ApplyToMatrix(&t->currentbackgroundtexmatrix, tcmod, t->currentmaterialflags); t->backgroundbasetexture = (!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base; t->backgroundnmaptexture = t->backgroundcurrentskinframe->nmap; t->backgroundglosstexture = r_texture_black; t->backgroundglowtexture = t->backgroundcurrentskinframe->glow; if (!t->backgroundnmaptexture) t->backgroundnmaptexture = r_texture_blanknormalmap; + // make sure that if glow is going to be used, both textures are not NULL + if (!t->backgroundglowtexture && t->glowtexture) + t->backgroundglowtexture = r_texture_black; + if (!t->glowtexture && t->backgroundglowtexture) + t->glowtexture = r_texture_black; } else { @@ -7933,13 +8354,15 @@ texture_t *R_GetCurrentTexture(texture_t *t) t->basetexture = r_texture_grey128; t->pantstexture = r_texture_black; t->shirttexture = r_texture_black; - t->nmaptexture = r_texture_blanknormalmap; + if (gl_lightmaps.integer < 2) + t->nmaptexture = r_texture_blanknormalmap; t->glosstexture = r_texture_black; t->glowtexture = NULL; t->fogtexture = NULL; t->reflectmasktexture = NULL; t->backgroundbasetexture = NULL; - t->backgroundnmaptexture = r_texture_blanknormalmap; + if (gl_lightmaps.integer < 2) + t->backgroundnmaptexture = r_texture_blanknormalmap; t->backgroundglosstexture = r_texture_black; t->backgroundglowtexture = NULL; t->specularscale = 0; @@ -7974,7 +8397,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) blendfunc2 = GL_ZERO; } // don't colormod evilblend textures - if(!R_BlendFuncFlags(blendfunc1, blendfunc2) & BLENDFUNC_ALLOWS_COLORMOD) + if(!(R_BlendFuncFlags(blendfunc1, blendfunc2) & BLENDFUNC_ALLOWS_COLORMOD)) VectorSet(t->lightmapcolor, 1, 1, 1); depthmask = !(t->currentmaterialflags & MATERIALFLAG_BLENDED); if (t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT) @@ -8038,7 +8461,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) } } - return t->currentframe; + return t; } rsurfacestate_t rsurface; @@ -8079,6 +8502,11 @@ void RSurf_ActiveWorldEntity(void) rsurface.ent_alttextures = false; rsurface.basepolygonfactor = r_refdef.polygonfactor; rsurface.basepolygonoffset = r_refdef.polygonoffset; + rsurface.entityskeletaltransform3x4 = NULL; + rsurface.entityskeletaltransform3x4buffer = NULL; + rsurface.entityskeletaltransform3x4offset = 0; + rsurface.entityskeletaltransform3x4size = 0;; + rsurface.entityskeletalnumtransforms = 0; rsurface.modelvertex3f = model->surfmesh.data_vertex3f; rsurface.modelvertex3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer; rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f; @@ -8100,6 +8528,12 @@ void RSurf_ActiveWorldEntity(void) rsurface.modeltexcoordlightmap2f = model->surfmesh.data_texcoordlightmap2f; rsurface.modeltexcoordlightmap2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer; rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f; + rsurface.modelskeletalindex4ub = model->surfmesh.data_skeletalindex4ub; + rsurface.modelskeletalindex4ub_vertexbuffer = model->surfmesh.vbo_vertexbuffer; + rsurface.modelskeletalindex4ub_bufferoffset = model->surfmesh.vbooffset_skeletalindex4ub; + rsurface.modelskeletalweight4ub = model->surfmesh.data_skeletalweight4ub; + rsurface.modelskeletalweight4ub_vertexbuffer = model->surfmesh.vbo_vertexbuffer; + rsurface.modelskeletalweight4ub_bufferoffset = model->surfmesh.vbooffset_skeletalweight4ub; rsurface.modelelement3i = model->surfmesh.data_element3i; rsurface.modelelement3i_indexbuffer = model->surfmesh.data_element3i_indexbuffer; rsurface.modelelement3i_bufferoffset = model->surfmesh.data_element3i_bufferoffset; @@ -8110,9 +8544,9 @@ void RSurf_ActiveWorldEntity(void) rsurface.modelnumvertices = model->surfmesh.num_vertices; rsurface.modelnumtriangles = model->surfmesh.num_triangles; rsurface.modelsurfaces = model->data_surfaces; - rsurface.modelvertexmesh = model->surfmesh.vertexmesh; - rsurface.modelvertexmeshbuffer = model->surfmesh.vertexmeshbuffer; - rsurface.modelvertex3fbuffer = model->surfmesh.vertex3fbuffer; + rsurface.modelvertexmesh = model->surfmesh.data_vertexmesh; + rsurface.modelvertexmesh_vertexbuffer = model->surfmesh.vbo_vertexbuffer; + rsurface.modelvertexmesh_bufferoffset = model->surfmesh.vbooffset_vertex3f; rsurface.modelgeneratedvertex = false; rsurface.batchgeneratedvertex = false; rsurface.batchfirstvertex = 0; @@ -8140,9 +8574,15 @@ void RSurf_ActiveWorldEntity(void) rsurface.batchtexcoordlightmap2f = NULL; rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL; rsurface.batchtexcoordlightmap2f_bufferoffset = 0; + rsurface.batchskeletalindex4ub = NULL; + rsurface.batchskeletalindex4ub_vertexbuffer = NULL; + rsurface.batchskeletalindex4ub_bufferoffset = 0; + rsurface.batchskeletalweight4ub = NULL; + rsurface.batchskeletalweight4ub_vertexbuffer = NULL; + rsurface.batchskeletalweight4ub_bufferoffset = 0; rsurface.batchvertexmesh = NULL; - rsurface.batchvertexmeshbuffer = NULL; - rsurface.batchvertex3fbuffer = NULL; + rsurface.batchvertexmesh_vertexbuffer = NULL; + rsurface.batchvertexmesh_bufferoffset = 0; rsurface.batchelement3i = NULL; rsurface.batchelement3i_indexbuffer = NULL; rsurface.batchelement3i_bufferoffset = 0; @@ -8152,6 +8592,7 @@ void RSurf_ActiveWorldEntity(void) rsurface.passcolor4f = NULL; rsurface.passcolor4f_vertexbuffer = NULL; rsurface.passcolor4f_bufferoffset = 0; + rsurface.forcecurrenttextureupdate = false; } void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents, qboolean prepass) @@ -8194,63 +8635,129 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q rsurface.basepolygonfactor += r_polygonoffset_submodel_factor.value; rsurface.basepolygonoffset += r_polygonoffset_submodel_offset.value; } - if (model->surfmesh.isanimated && model->AnimateVertices) + // if the animcache code decided it should use the shader path, skip the deform step + rsurface.entityskeletaltransform3x4 = ent->animcache_skeletaltransform3x4; + rsurface.entityskeletaltransform3x4buffer = ent->animcache_skeletaltransform3x4buffer; + rsurface.entityskeletaltransform3x4offset = ent->animcache_skeletaltransform3x4offset; + rsurface.entityskeletaltransform3x4size = ent->animcache_skeletaltransform3x4size; + rsurface.entityskeletalnumtransforms = rsurface.entityskeletaltransform3x4 ? model->num_bones : 0; + if (model->surfmesh.isanimated && model->AnimateVertices && !rsurface.entityskeletaltransform3x4) { if (ent->animcache_vertex3f) { + r_refdef.stats[r_stat_batch_entitycache_count]++; + r_refdef.stats[r_stat_batch_entitycache_surfaces] += model->num_surfaces; + r_refdef.stats[r_stat_batch_entitycache_vertices] += model->surfmesh.num_vertices; + r_refdef.stats[r_stat_batch_entitycache_triangles] += model->surfmesh.num_triangles; rsurface.modelvertex3f = ent->animcache_vertex3f; + rsurface.modelvertex3f_vertexbuffer = ent->animcache_vertex3f_vertexbuffer; + rsurface.modelvertex3f_bufferoffset = ent->animcache_vertex3f_bufferoffset; rsurface.modelsvector3f = wanttangents ? ent->animcache_svector3f : NULL; + rsurface.modelsvector3f_vertexbuffer = wanttangents ? ent->animcache_svector3f_vertexbuffer : NULL; + rsurface.modelsvector3f_bufferoffset = wanttangents ? ent->animcache_svector3f_bufferoffset : 0; rsurface.modeltvector3f = wanttangents ? ent->animcache_tvector3f : NULL; + rsurface.modeltvector3f_vertexbuffer = wanttangents ? ent->animcache_tvector3f_vertexbuffer : NULL; + rsurface.modeltvector3f_bufferoffset = wanttangents ? ent->animcache_tvector3f_bufferoffset : 0; rsurface.modelnormal3f = wantnormals ? ent->animcache_normal3f : NULL; + rsurface.modelnormal3f_vertexbuffer = wantnormals ? ent->animcache_normal3f_vertexbuffer : NULL; + rsurface.modelnormal3f_bufferoffset = wantnormals ? ent->animcache_normal3f_bufferoffset : 0; rsurface.modelvertexmesh = ent->animcache_vertexmesh; - rsurface.modelvertexmeshbuffer = ent->animcache_vertexmeshbuffer; - rsurface.modelvertex3fbuffer = ent->animcache_vertex3fbuffer; + rsurface.modelvertexmesh_vertexbuffer = ent->animcache_vertexmesh_vertexbuffer; + rsurface.modelvertexmesh_bufferoffset = ent->animcache_vertexmesh_bufferoffset; } else if (wanttangents) { + r_refdef.stats[r_stat_batch_entityanimate_count]++; + r_refdef.stats[r_stat_batch_entityanimate_surfaces] += model->num_surfaces; + r_refdef.stats[r_stat_batch_entityanimate_vertices] += model->surfmesh.num_vertices; + r_refdef.stats[r_stat_batch_entityanimate_triangles] += model->surfmesh.num_triangles; rsurface.modelvertex3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); rsurface.modelsvector3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); rsurface.modeltvector3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); rsurface.modelnormal3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.modelvertex3f, rsurface.modelnormal3f, rsurface.modelsvector3f, rsurface.modeltvector3f); rsurface.modelvertexmesh = NULL; - rsurface.modelvertexmeshbuffer = NULL; - rsurface.modelvertex3fbuffer = NULL; + rsurface.modelvertexmesh_vertexbuffer = NULL; + rsurface.modelvertexmesh_bufferoffset = 0; + rsurface.modelvertex3f_vertexbuffer = NULL; + rsurface.modelvertex3f_bufferoffset = 0; + rsurface.modelvertex3f_vertexbuffer = 0; + rsurface.modelvertex3f_bufferoffset = 0; + rsurface.modelsvector3f_vertexbuffer = 0; + rsurface.modelsvector3f_bufferoffset = 0; + rsurface.modeltvector3f_vertexbuffer = 0; + rsurface.modeltvector3f_bufferoffset = 0; + rsurface.modelnormal3f_vertexbuffer = 0; + rsurface.modelnormal3f_bufferoffset = 0; } else if (wantnormals) { + r_refdef.stats[r_stat_batch_entityanimate_count]++; + r_refdef.stats[r_stat_batch_entityanimate_surfaces] += model->num_surfaces; + r_refdef.stats[r_stat_batch_entityanimate_vertices] += model->surfmesh.num_vertices; + r_refdef.stats[r_stat_batch_entityanimate_triangles] += model->surfmesh.num_triangles; rsurface.modelvertex3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); rsurface.modelsvector3f = NULL; rsurface.modeltvector3f = NULL; rsurface.modelnormal3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.modelvertex3f, rsurface.modelnormal3f, NULL, NULL); rsurface.modelvertexmesh = NULL; - rsurface.modelvertexmeshbuffer = NULL; - rsurface.modelvertex3fbuffer = NULL; + rsurface.modelvertexmesh_vertexbuffer = NULL; + rsurface.modelvertexmesh_bufferoffset = 0; + rsurface.modelvertex3f_vertexbuffer = NULL; + rsurface.modelvertex3f_bufferoffset = 0; + rsurface.modelvertex3f_vertexbuffer = 0; + rsurface.modelvertex3f_bufferoffset = 0; + rsurface.modelsvector3f_vertexbuffer = 0; + rsurface.modelsvector3f_bufferoffset = 0; + rsurface.modeltvector3f_vertexbuffer = 0; + rsurface.modeltvector3f_bufferoffset = 0; + rsurface.modelnormal3f_vertexbuffer = 0; + rsurface.modelnormal3f_bufferoffset = 0; } else { + r_refdef.stats[r_stat_batch_entityanimate_count]++; + r_refdef.stats[r_stat_batch_entityanimate_surfaces] += model->num_surfaces; + r_refdef.stats[r_stat_batch_entityanimate_vertices] += model->surfmesh.num_vertices; + r_refdef.stats[r_stat_batch_entityanimate_triangles] += model->surfmesh.num_triangles; rsurface.modelvertex3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3])); rsurface.modelsvector3f = NULL; rsurface.modeltvector3f = NULL; rsurface.modelnormal3f = NULL; model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.modelvertex3f, NULL, NULL, NULL); rsurface.modelvertexmesh = NULL; - rsurface.modelvertexmeshbuffer = NULL; - rsurface.modelvertex3fbuffer = NULL; - } - rsurface.modelvertex3f_vertexbuffer = 0; - rsurface.modelvertex3f_bufferoffset = 0; - rsurface.modelsvector3f_vertexbuffer = 0; - rsurface.modelsvector3f_bufferoffset = 0; - rsurface.modeltvector3f_vertexbuffer = 0; - rsurface.modeltvector3f_bufferoffset = 0; - rsurface.modelnormal3f_vertexbuffer = 0; - rsurface.modelnormal3f_bufferoffset = 0; + rsurface.modelvertexmesh_vertexbuffer = NULL; + rsurface.modelvertexmesh_bufferoffset = 0; + rsurface.modelvertex3f_vertexbuffer = NULL; + rsurface.modelvertex3f_bufferoffset = 0; + rsurface.modelvertex3f_vertexbuffer = 0; + rsurface.modelvertex3f_bufferoffset = 0; + rsurface.modelsvector3f_vertexbuffer = 0; + rsurface.modelsvector3f_bufferoffset = 0; + rsurface.modeltvector3f_vertexbuffer = 0; + rsurface.modeltvector3f_bufferoffset = 0; + rsurface.modelnormal3f_vertexbuffer = 0; + rsurface.modelnormal3f_bufferoffset = 0; + } rsurface.modelgeneratedvertex = true; } else { + if (rsurface.entityskeletaltransform3x4) + { + r_refdef.stats[r_stat_batch_entityskeletal_count]++; + r_refdef.stats[r_stat_batch_entityskeletal_surfaces] += model->num_surfaces; + r_refdef.stats[r_stat_batch_entityskeletal_vertices] += model->surfmesh.num_vertices; + r_refdef.stats[r_stat_batch_entityskeletal_triangles] += model->surfmesh.num_triangles; + } + else + { + r_refdef.stats[r_stat_batch_entitystatic_count]++; + r_refdef.stats[r_stat_batch_entitystatic_surfaces] += model->num_surfaces; + r_refdef.stats[r_stat_batch_entitystatic_vertices] += model->surfmesh.num_vertices; + r_refdef.stats[r_stat_batch_entitystatic_triangles] += model->surfmesh.num_triangles; + } rsurface.modelvertex3f = model->surfmesh.data_vertex3f; rsurface.modelvertex3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer; rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f; @@ -8263,9 +8770,9 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q rsurface.modelnormal3f = model->surfmesh.data_normal3f; rsurface.modelnormal3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer; rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f; - rsurface.modelvertexmesh = model->surfmesh.vertexmesh; - rsurface.modelvertexmeshbuffer = model->surfmesh.vertexmeshbuffer; - rsurface.modelvertex3fbuffer = model->surfmesh.vertex3fbuffer; + rsurface.modelvertexmesh = model->surfmesh.data_vertexmesh; + rsurface.modelvertexmesh_vertexbuffer = model->surfmesh.vbo_vertexbuffer; + rsurface.modelvertexmesh_bufferoffset = model->surfmesh.vbooffset_vertex3f; rsurface.modelgeneratedvertex = false; } rsurface.modellightmapcolor4f = model->surfmesh.data_lightmapcolor4f; @@ -8277,6 +8784,12 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q rsurface.modeltexcoordlightmap2f = model->surfmesh.data_texcoordlightmap2f; rsurface.modeltexcoordlightmap2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer; rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f; + rsurface.modelskeletalindex4ub = model->surfmesh.data_skeletalindex4ub; + rsurface.modelskeletalindex4ub_vertexbuffer = model->surfmesh.vbo_vertexbuffer; + rsurface.modelskeletalindex4ub_bufferoffset = model->surfmesh.vbooffset_skeletalindex4ub; + rsurface.modelskeletalweight4ub = model->surfmesh.data_skeletalweight4ub; + rsurface.modelskeletalweight4ub_vertexbuffer = model->surfmesh.vbo_vertexbuffer; + rsurface.modelskeletalweight4ub_bufferoffset = model->surfmesh.vbooffset_skeletalweight4ub; rsurface.modelelement3i = model->surfmesh.data_element3i; rsurface.modelelement3i_indexbuffer = model->surfmesh.data_element3i_indexbuffer; rsurface.modelelement3i_bufferoffset = model->surfmesh.data_element3i_bufferoffset; @@ -8313,9 +8826,15 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q rsurface.batchtexcoordlightmap2f = NULL; rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL; rsurface.batchtexcoordlightmap2f_bufferoffset = 0; + rsurface.batchskeletalindex4ub = NULL; + rsurface.batchskeletalindex4ub_vertexbuffer = NULL; + rsurface.batchskeletalindex4ub_bufferoffset = 0; + rsurface.batchskeletalweight4ub = NULL; + rsurface.batchskeletalweight4ub_vertexbuffer = NULL; + rsurface.batchskeletalweight4ub_bufferoffset = 0; rsurface.batchvertexmesh = NULL; - rsurface.batchvertexmeshbuffer = NULL; - rsurface.batchvertex3fbuffer = NULL; + rsurface.batchvertexmesh_vertexbuffer = NULL; + rsurface.batchvertexmesh_bufferoffset = 0; rsurface.batchelement3i = NULL; rsurface.batchelement3i_indexbuffer = NULL; rsurface.batchelement3i_bufferoffset = 0; @@ -8325,6 +8844,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q rsurface.passcolor4f = NULL; rsurface.passcolor4f_vertexbuffer = NULL; rsurface.passcolor4f_bufferoffset = 0; + rsurface.forcecurrenttextureupdate = false; } void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, int entflags, double shadertime, float r, float g, float b, float a, int numvertices, const float *vertex3f, const float *texcoord2f, const float *normal3f, const float *svector3f, const float *tvector3f, const float *color4f, int numtriangles, const int *element3i, const unsigned short *element3s, qboolean wantnormals, qboolean wanttangents) @@ -8360,6 +8880,15 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve rsurface.ent_alttextures = false; rsurface.basepolygonfactor = r_refdef.polygonfactor; rsurface.basepolygonoffset = r_refdef.polygonoffset; + rsurface.entityskeletaltransform3x4 = NULL; + rsurface.entityskeletaltransform3x4buffer = NULL; + rsurface.entityskeletaltransform3x4offset = 0; + rsurface.entityskeletaltransform3x4size = 0; + rsurface.entityskeletalnumtransforms = 0; + r_refdef.stats[r_stat_batch_entitycustom_count]++; + r_refdef.stats[r_stat_batch_entitycustom_surfaces] += 1; + r_refdef.stats[r_stat_batch_entitycustom_vertices] += rsurface.modelnumvertices; + r_refdef.stats[r_stat_batch_entitycustom_triangles] += rsurface.modelnumtriangles; if (wanttangents) { rsurface.modelvertex3f = (float *)vertex3f; @@ -8382,8 +8911,8 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve rsurface.modelnormal3f = NULL; } rsurface.modelvertexmesh = NULL; - rsurface.modelvertexmeshbuffer = NULL; - rsurface.modelvertex3fbuffer = NULL; + rsurface.modelvertexmesh_vertexbuffer = NULL; + rsurface.modelvertexmesh_bufferoffset = 0; rsurface.modelvertex3f_vertexbuffer = 0; rsurface.modelvertex3f_bufferoffset = 0; rsurface.modelsvector3f_vertexbuffer = 0; @@ -8402,6 +8931,12 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve rsurface.modeltexcoordlightmap2f = NULL; rsurface.modeltexcoordlightmap2f_vertexbuffer = 0; rsurface.modeltexcoordlightmap2f_bufferoffset = 0; + rsurface.modelskeletalindex4ub = NULL; + rsurface.modelskeletalindex4ub_vertexbuffer = NULL; + rsurface.modelskeletalindex4ub_bufferoffset = 0; + rsurface.modelskeletalweight4ub = NULL; + rsurface.modelskeletalweight4ub_vertexbuffer = NULL; + rsurface.modelskeletalweight4ub_bufferoffset = 0; rsurface.modelelement3i = (int *)element3i; rsurface.modelelement3i_indexbuffer = NULL; rsurface.modelelement3i_bufferoffset = 0; @@ -8436,9 +8971,15 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve rsurface.batchtexcoordlightmap2f = NULL; rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL; rsurface.batchtexcoordlightmap2f_bufferoffset = 0; + rsurface.batchskeletalindex4ub = NULL; + rsurface.batchskeletalindex4ub_vertexbuffer = NULL; + rsurface.batchskeletalindex4ub_bufferoffset = 0; + rsurface.batchskeletalweight4ub = NULL; + rsurface.batchskeletalweight4ub_vertexbuffer = NULL; + rsurface.batchskeletalweight4ub_bufferoffset = 0; rsurface.batchvertexmesh = NULL; - rsurface.batchvertexmeshbuffer = NULL; - rsurface.batchvertex3fbuffer = NULL; + rsurface.batchvertexmesh_vertexbuffer = NULL; + rsurface.batchvertexmesh_bufferoffset = 0; rsurface.batchelement3i = NULL; rsurface.batchelement3i_indexbuffer = NULL; rsurface.batchelement3i_bufferoffset = 0; @@ -8448,6 +8989,7 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve rsurface.passcolor4f = NULL; rsurface.passcolor4f_vertexbuffer = NULL; rsurface.passcolor4f_bufferoffset = 0; + rsurface.forcecurrenttextureupdate = true; if (rsurface.modelnumvertices && rsurface.modelelement3i) { @@ -8519,6 +9061,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const int surfacefirstvertex; int surfaceendvertex; int surfacenumvertices; + int batchnumsurfaces = texturenumsurfaces; int batchnumvertices; int batchnumtriangles; int needsupdate; @@ -8527,9 +9070,9 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const qboolean dynamicvertex; float amplitude; float animpos; - float scale; float center[3], forward[3], right[3], up[3], v[3], newforward[3], newright[3], newup[3]; float waveparms[4]; + unsigned char *ub; q3shaderinfo_deform_t *deform; const msurface_t *surface, *firstsurface; r_vertexmesh_t *vertexmesh; @@ -8559,6 +9102,13 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const batchnumtriangles += surfacenumtriangles; } + r_refdef.stats[r_stat_batch_batches]++; + if (gaps) + r_refdef.stats[r_stat_batch_withgaps]++; + r_refdef.stats[r_stat_batch_surfaces] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_vertices] += batchnumvertices; + r_refdef.stats[r_stat_batch_triangles] += batchnumtriangles; + // we now know the vertex range used, and if there are any gaps in it rsurface.batchfirstvertex = firstvertex; rsurface.batchnumvertices = endvertex - firstvertex; @@ -8572,11 +9122,30 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const // check if any dynamic vertex processing must occur dynamicvertex = false; + // a cvar to force the dynamic vertex path to be taken, for debugging + if (r_batch_debugdynamicvertexpath.integer) + { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_cvar] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_cvar] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_cvar] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_cvar] += batchnumtriangles; + } + dynamicvertex = true; + } + // if there is a chance of animated vertex colors, it's a dynamic batch if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo) { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_lightmapvertex] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_lightmapvertex] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_lightmapvertex] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_lightmapvertex] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_NOGAPS; needsupdate |= BATCHNEED_VERTEXMESH_VERTEXCOLOR; } @@ -8597,77 +9166,176 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const case Q3DEFORM_NONE: break; case Q3DEFORM_AUTOSPRITE: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_deformvertexes_autosprite] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_deformvertexes_autosprite] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_deformvertexes_autosprite] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_deformvertexes_autosprite] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_TEXCOORD; needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR; break; case Q3DEFORM_AUTOSPRITE2: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_deformvertexes_autosprite2] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_deformvertexes_autosprite2] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_deformvertexes_autosprite2] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_deformvertexes_autosprite2] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD; needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR; break; case Q3DEFORM_NORMAL: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_deformvertexes_normal] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_deformvertexes_normal] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_deformvertexes_normal] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_deformvertexes_normal] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD; needsupdate |= BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR; break; case Q3DEFORM_WAVE: if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms)) break; // if wavefunc is a nop, ignore this transform + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_deformvertexes_wave] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_deformvertexes_wave] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_deformvertexes_wave] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_deformvertexes_wave] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD; needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR; break; case Q3DEFORM_BULGE: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_deformvertexes_bulge] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_deformvertexes_bulge] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_deformvertexes_bulge] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_deformvertexes_bulge] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD; needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR; break; case Q3DEFORM_MOVE: if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms)) break; // if wavefunc is a nop, ignore this transform + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_deformvertexes_move] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_deformvertexes_move] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_deformvertexes_move] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_deformvertexes_move] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS; + batchneed |= BATCHNEED_ARRAY_VERTEX; needsupdate |= BATCHNEED_VERTEXMESH_VERTEX; break; } } - switch(rsurface.texture->tcgen.tcgen) + if (rsurface.texture->materialshaderpass) { - default: - case Q3TCGEN_TEXTURE: - break; - case Q3TCGEN_LIGHTMAP: - dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_LIGHTMAP | BATCHNEED_NOGAPS; - needsupdate |= BATCHNEED_VERTEXMESH_LIGHTMAP; - break; - case Q3TCGEN_VECTOR: - dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS; - needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD; - break; - case Q3TCGEN_ENVIRONMENT: - dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS; - needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD; - break; + switch (rsurface.texture->materialshaderpass->tcgen.tcgen) + { + default: + case Q3TCGEN_TEXTURE: + break; + case Q3TCGEN_LIGHTMAP: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_tcgen_lightmap] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_tcgen_lightmap] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_tcgen_lightmap] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_tcgen_lightmap] += batchnumtriangles; + } + dynamicvertex = true; + batchneed |= BATCHNEED_ARRAY_LIGHTMAP; + needsupdate |= BATCHNEED_VERTEXMESH_LIGHTMAP; + break; + case Q3TCGEN_VECTOR: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_tcgen_vector] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_tcgen_vector] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_tcgen_vector] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_tcgen_vector] += batchnumtriangles; + } + dynamicvertex = true; + batchneed |= BATCHNEED_ARRAY_VERTEX; + needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD; + break; + case Q3TCGEN_ENVIRONMENT: + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_tcgen_environment] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_tcgen_environment] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_tcgen_environment] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_tcgen_environment] += batchnumtriangles; + } + dynamicvertex = true; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL; + needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD; + break; + } + if (rsurface.texture->materialshaderpass->tcmods[0].tcmod == Q3TCMOD_TURBULENT) + { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_tcmod_turbulent] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_tcmod_turbulent] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_tcmod_turbulent] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_tcmod_turbulent] += batchnumtriangles; + } + dynamicvertex = true; + batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD; + needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD; + } } - if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT) + + if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))) { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_interleavedarrays] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_interleavedarrays] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_interleavedarrays] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_interleavedarrays] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS; - needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD; + needsupdate |= (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)); } - if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))) + // when the model data has no vertex buffer (dynamic mesh), we need to + // eliminate gaps + if (vid.useinterleavedarrays && !rsurface.modelvertexmesh_vertexbuffer) + batchneed |= BATCHNEED_NOGAPS; + + // the caller can specify BATCHNEED_NOGAPS to force a batch with + // firstvertex = 0 and endvertex = numvertices (no gaps, no firstvertex), + // we ensure this by treating the vertex batch as dynamic... + if ((batchneed & BATCHNEED_NOGAPS) && (gaps || firstvertex > 0)) { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_nogaps] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_nogaps] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_nogaps] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_nogaps] += batchnumtriangles; + } dynamicvertex = true; - batchneed |= BATCHNEED_NOGAPS; - needsupdate |= (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)); } - if (dynamicvertex || gaps || rsurface.batchfirstvertex) + if (dynamicvertex) { // when copying, we need to consider the regeneration of vertexmesh, any dependencies it may have must be set... if (batchneed & BATCHNEED_VERTEXMESH_VERTEX) batchneed |= BATCHNEED_ARRAY_VERTEX; @@ -8676,25 +9344,38 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const if (batchneed & BATCHNEED_VERTEXMESH_VERTEXCOLOR) batchneed |= BATCHNEED_ARRAY_VERTEXCOLOR; if (batchneed & BATCHNEED_VERTEXMESH_TEXCOORD) batchneed |= BATCHNEED_ARRAY_TEXCOORD; if (batchneed & BATCHNEED_VERTEXMESH_LIGHTMAP) batchneed |= BATCHNEED_ARRAY_LIGHTMAP; + if (batchneed & BATCHNEED_VERTEXMESH_SKELETAL) batchneed |= BATCHNEED_ARRAY_SKELETAL; } - // when the model data has no vertex buffer (dynamic mesh), we need to - // eliminate gaps - if (vid.useinterleavedarrays ? !rsurface.modelvertexmeshbuffer : !rsurface.modelvertex3f_vertexbuffer) - batchneed |= BATCHNEED_NOGAPS; - // if needsupdate, we have to do a dynamic vertex batch for sure if (needsupdate & batchneed) + { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_derived] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_derived] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_derived] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_derived] += batchnumtriangles; + } dynamicvertex = true; + } // see if we need to build vertexmesh from arrays if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))) + { + if (!dynamicvertex) + { + r_refdef.stats[r_stat_batch_dynamic_batches_because_interleavedarrays] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces_because_interleavedarrays] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices_because_interleavedarrays] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles_because_interleavedarrays] += batchnumtriangles; + } dynamicvertex = true; + } - // if gaps are unacceptable, and there are gaps, it's a dynamic batch... - // also some drivers strongly dislike firstvertex - if ((batchneed & BATCHNEED_NOGAPS) && (gaps || firstvertex)) - dynamicvertex = true; + // if we're going to have to apply the skeletal transform manually, we need to batch the skeletal data + if (dynamicvertex && rsurface.entityskeletaltransform3x4) + batchneed |= BATCHNEED_ARRAY_SKELETAL; rsurface.batchvertex3f = rsurface.modelvertex3f; rsurface.batchvertex3f_vertexbuffer = rsurface.modelvertex3f_vertexbuffer; @@ -8717,15 +9398,26 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const rsurface.batchtexcoordlightmap2f = rsurface.modeltexcoordlightmap2f; rsurface.batchtexcoordlightmap2f_vertexbuffer = rsurface.modeltexcoordlightmap2f_vertexbuffer; rsurface.batchtexcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset; - rsurface.batchvertex3fbuffer = rsurface.modelvertex3fbuffer; + rsurface.batchskeletalindex4ub = rsurface.modelskeletalindex4ub; + rsurface.batchskeletalindex4ub_vertexbuffer = rsurface.modelskeletalindex4ub_vertexbuffer; + rsurface.batchskeletalindex4ub_bufferoffset = rsurface.modelskeletalindex4ub_bufferoffset; + rsurface.batchskeletalweight4ub = rsurface.modelskeletalweight4ub; + rsurface.batchskeletalweight4ub_vertexbuffer = rsurface.modelskeletalweight4ub_vertexbuffer; + rsurface.batchskeletalweight4ub_bufferoffset = rsurface.modelskeletalweight4ub_bufferoffset; rsurface.batchvertexmesh = rsurface.modelvertexmesh; - rsurface.batchvertexmeshbuffer = rsurface.modelvertexmeshbuffer; + rsurface.batchvertexmesh_vertexbuffer = rsurface.modelvertexmesh_vertexbuffer; + rsurface.batchvertexmesh_bufferoffset = rsurface.modelvertexmesh_bufferoffset; rsurface.batchelement3i = rsurface.modelelement3i; rsurface.batchelement3i_indexbuffer = rsurface.modelelement3i_indexbuffer; rsurface.batchelement3i_bufferoffset = rsurface.modelelement3i_bufferoffset; rsurface.batchelement3s = rsurface.modelelement3s; rsurface.batchelement3s_indexbuffer = rsurface.modelelement3s_indexbuffer; rsurface.batchelement3s_bufferoffset = rsurface.modelelement3s_bufferoffset; + rsurface.batchskeletaltransform3x4 = rsurface.entityskeletaltransform3x4; + rsurface.batchskeletaltransform3x4buffer = rsurface.entityskeletaltransform3x4buffer; + rsurface.batchskeletaltransform3x4offset = rsurface.entityskeletaltransform3x4offset; + rsurface.batchskeletaltransform3x4size = rsurface.entityskeletaltransform3x4size; + rsurface.batchskeletalnumtransforms = rsurface.entityskeletalnumtransforms; // if any dynamic vertex processing has to occur in software, we copy the // entire surface list together before processing to rebase the vertices @@ -8735,10 +9427,15 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const // copy the surface list together to avoid wasting upload bandwidth on the // vertices in the gaps. // - // if gaps exist and we have a static vertex buffer, we still have to - // combine the index buffer ranges into one dynamic index buffer. + // if gaps exist and we have a static vertex buffer, we can choose whether + // to combine the index buffer ranges into one dynamic index buffer or + // simply issue multiple glDrawElements calls (BATCHNEED_ALLOWMULTIDRAW). // - // in all cases we end up with data that can be drawn in one call. + // in many cases the batch is reduced to one draw call. + + rsurface.batchmultidraw = false; + rsurface.batchmultidrawnumsurfaces = 0; + rsurface.batchmultidrawsurfacelist = NULL; if (!dynamicvertex) { @@ -8748,6 +9445,17 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const // otherwise use the original static buffer with an appropriate offset if (gaps) { + r_refdef.stats[r_stat_batch_copytriangles_batches] += 1; + r_refdef.stats[r_stat_batch_copytriangles_surfaces] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_copytriangles_vertices] += batchnumvertices; + r_refdef.stats[r_stat_batch_copytriangles_triangles] += batchnumtriangles; + if ((batchneed & BATCHNEED_ALLOWMULTIDRAW) && r_batch_multidraw.integer && batchnumtriangles >= r_batch_multidraw_mintriangles.integer) + { + rsurface.batchmultidraw = true; + rsurface.batchmultidrawnumsurfaces = texturenumsurfaces; + rsurface.batchmultidrawsurfacelist = texturesurfacelist; + return; + } // build a new triangle elements array for this batch rsurface.batchelement3i = (int *)R_FrameData_Alloc(batchnumtriangles * sizeof(int[3])); rsurface.batchfirsttriangle = 0; @@ -8771,6 +9479,21 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const for (i = 0;i < numtriangles*3;i++) rsurface.batchelement3s[i] = rsurface.batchelement3i[i]; } + // upload buffer data for the copytriangles batch + if (((r_batch_dynamicbuffer.integer || gl_vbo_dynamicindex.integer) && vid.support.arb_vertex_buffer_object && gl_vbo.integer) || vid.forcevbo) + { + if (rsurface.batchelement3s) + rsurface.batchelement3s_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(short[3]), rsurface.batchelement3s, R_BUFFERDATA_INDEX16, &rsurface.batchelement3s_bufferoffset); + else if (rsurface.batchelement3i) + rsurface.batchelement3i_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(int[3]), rsurface.batchelement3i, R_BUFFERDATA_INDEX32, &rsurface.batchelement3i_bufferoffset); + } + } + else + { + r_refdef.stats[r_stat_batch_fast_batches] += 1; + r_refdef.stats[r_stat_batch_fast_surfaces] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_fast_vertices] += batchnumvertices; + r_refdef.stats[r_stat_batch_fast_triangles] += batchnumtriangles; } return; } @@ -8779,14 +9502,21 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const // we only directly handle separate array data in this case and then // generate interleaved data if needed... rsurface.batchgeneratedvertex = true; + r_refdef.stats[r_stat_batch_dynamic_batches] += 1; + r_refdef.stats[r_stat_batch_dynamic_surfaces] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamic_vertices] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamic_triangles] += batchnumtriangles; // now copy the vertex data into a combined array and make an index array // (this is what Quake3 does all the time) - //if (gaps || rsurface.batchfirstvertex) + // we also apply any skeletal animation here that would have been done in + // the vertex shader, because most of the dynamic vertex animation cases + // need actual vertex positions and normals + //if (dynamicvertex) { - rsurface.batchvertex3fbuffer = NULL; rsurface.batchvertexmesh = NULL; - rsurface.batchvertexmeshbuffer = NULL; + rsurface.batchvertexmesh_vertexbuffer = NULL; + rsurface.batchvertexmesh_bufferoffset = 0; rsurface.batchvertex3f = NULL; rsurface.batchvertex3f_vertexbuffer = NULL; rsurface.batchvertex3f_bufferoffset = 0; @@ -8808,12 +9538,21 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const rsurface.batchtexcoordlightmap2f = NULL; rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL; rsurface.batchtexcoordlightmap2f_bufferoffset = 0; + rsurface.batchskeletalindex4ub = NULL; + rsurface.batchskeletalindex4ub_vertexbuffer = NULL; + rsurface.batchskeletalindex4ub_bufferoffset = 0; + rsurface.batchskeletalweight4ub = NULL; + rsurface.batchskeletalweight4ub_vertexbuffer = NULL; + rsurface.batchskeletalweight4ub_bufferoffset = 0; rsurface.batchelement3i = (int *)R_FrameData_Alloc(batchnumtriangles * sizeof(int[3])); rsurface.batchelement3i_indexbuffer = NULL; rsurface.batchelement3i_bufferoffset = 0; rsurface.batchelement3s = NULL; rsurface.batchelement3s_indexbuffer = NULL; rsurface.batchelement3s_bufferoffset = 0; + rsurface.batchskeletaltransform3x4buffer = NULL; + rsurface.batchskeletaltransform3x4offset = 0; + rsurface.batchskeletaltransform3x4size = 0; // we'll only be setting up certain arrays as needed if (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)) rsurface.batchvertexmesh = (r_vertexmesh_t *)R_FrameData_Alloc(batchnumvertices * sizeof(r_vertexmesh_t)); @@ -8832,6 +9571,11 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); if (batchneed & BATCHNEED_ARRAY_LIGHTMAP) rsurface.batchtexcoordlightmap2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); + if (batchneed & BATCHNEED_ARRAY_SKELETAL) + { + rsurface.batchskeletalindex4ub = (unsigned char *)R_FrameData_Alloc(batchnumvertices * sizeof(unsigned char[4])); + rsurface.batchskeletalweight4ub = (unsigned char *)R_FrameData_Alloc(batchnumvertices * sizeof(unsigned char[4])); + } numvertices = 0; numtriangles = 0; for (i = 0;i < texturenumsurfaces;i++) @@ -8893,6 +9637,22 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const else memset(rsurface.batchtexcoordlightmap2f + 2*numvertices, 0, surfacenumvertices * sizeof(float[2])); } + if (batchneed & BATCHNEED_ARRAY_SKELETAL) + { + if (rsurface.modelskeletalindex4ub) + { + memcpy(rsurface.batchskeletalindex4ub + 4*numvertices, rsurface.modelskeletalindex4ub + 4*surfacefirstvertex, surfacenumvertices * sizeof(unsigned char[4])); + memcpy(rsurface.batchskeletalweight4ub + 4*numvertices, rsurface.modelskeletalweight4ub + 4*surfacefirstvertex, surfacenumvertices * sizeof(unsigned char[4])); + } + else + { + memset(rsurface.batchskeletalindex4ub + 4*numvertices, 0, surfacenumvertices * sizeof(unsigned char[4])); + memset(rsurface.batchskeletalweight4ub + 4*numvertices, 0, surfacenumvertices * sizeof(unsigned char[4])); + ub = rsurface.batchskeletalweight4ub + 4*numvertices; + for (j = 0;j < surfacenumvertices;j++) + ub[j*4] = 255; + } + } } RSurf_RenumberElements(rsurface.modelelement3i + 3*surfacefirsttriangle, rsurface.batchelement3i + 3*numtriangles, 3*surfacenumtriangles, numvertices - surfacefirstvertex); numvertices += surfacenumvertices; @@ -8915,6 +9675,133 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const rsurface.batchnumtriangles = batchnumtriangles; } + // apply skeletal animation that would have been done in the vertex shader + if (rsurface.batchskeletaltransform3x4) + { + const unsigned char *si; + const unsigned char *sw; + const float *t[4]; + const float *b = rsurface.batchskeletaltransform3x4; + float *vp, *vs, *vt, *vn; + float w[4]; + float m[3][4], n[3][4]; + float tp[3], ts[3], tt[3], tn[3]; + r_refdef.stats[r_stat_batch_dynamicskeletal_batches] += 1; + r_refdef.stats[r_stat_batch_dynamicskeletal_surfaces] += batchnumsurfaces; + r_refdef.stats[r_stat_batch_dynamicskeletal_vertices] += batchnumvertices; + r_refdef.stats[r_stat_batch_dynamicskeletal_triangles] += batchnumtriangles; + si = rsurface.batchskeletalindex4ub; + sw = rsurface.batchskeletalweight4ub; + vp = rsurface.batchvertex3f; + vs = rsurface.batchsvector3f; + vt = rsurface.batchtvector3f; + vn = rsurface.batchnormal3f; + memset(m[0], 0, sizeof(m)); + memset(n[0], 0, sizeof(n)); + for (i = 0;i < batchnumvertices;i++) + { + t[0] = b + si[0]*12; + if (sw[0] == 255) + { + // common case - only one matrix + m[0][0] = t[0][ 0]; + m[0][1] = t[0][ 1]; + m[0][2] = t[0][ 2]; + m[0][3] = t[0][ 3]; + m[1][0] = t[0][ 4]; + m[1][1] = t[0][ 5]; + m[1][2] = t[0][ 6]; + m[1][3] = t[0][ 7]; + m[2][0] = t[0][ 8]; + m[2][1] = t[0][ 9]; + m[2][2] = t[0][10]; + m[2][3] = t[0][11]; + } + else if (sw[2] + sw[3]) + { + // blend 4 matrices + t[1] = b + si[1]*12; + t[2] = b + si[2]*12; + t[3] = b + si[3]*12; + w[0] = sw[0] * (1.0f / 255.0f); + w[1] = sw[1] * (1.0f / 255.0f); + w[2] = sw[2] * (1.0f / 255.0f); + w[3] = sw[3] * (1.0f / 255.0f); + // blend the matrices + m[0][0] = t[0][ 0] * w[0] + t[1][ 0] * w[1] + t[2][ 0] * w[2] + t[3][ 0] * w[3]; + m[0][1] = t[0][ 1] * w[0] + t[1][ 1] * w[1] + t[2][ 1] * w[2] + t[3][ 1] * w[3]; + m[0][2] = t[0][ 2] * w[0] + t[1][ 2] * w[1] + t[2][ 2] * w[2] + t[3][ 2] * w[3]; + m[0][3] = t[0][ 3] * w[0] + t[1][ 3] * w[1] + t[2][ 3] * w[2] + t[3][ 3] * w[3]; + m[1][0] = t[0][ 4] * w[0] + t[1][ 4] * w[1] + t[2][ 4] * w[2] + t[3][ 4] * w[3]; + m[1][1] = t[0][ 5] * w[0] + t[1][ 5] * w[1] + t[2][ 5] * w[2] + t[3][ 5] * w[3]; + m[1][2] = t[0][ 6] * w[0] + t[1][ 6] * w[1] + t[2][ 6] * w[2] + t[3][ 6] * w[3]; + m[1][3] = t[0][ 7] * w[0] + t[1][ 7] * w[1] + t[2][ 7] * w[2] + t[3][ 7] * w[3]; + m[2][0] = t[0][ 8] * w[0] + t[1][ 8] * w[1] + t[2][ 8] * w[2] + t[3][ 8] * w[3]; + m[2][1] = t[0][ 9] * w[0] + t[1][ 9] * w[1] + t[2][ 9] * w[2] + t[3][ 9] * w[3]; + m[2][2] = t[0][10] * w[0] + t[1][10] * w[1] + t[2][10] * w[2] + t[3][10] * w[3]; + m[2][3] = t[0][11] * w[0] + t[1][11] * w[1] + t[2][11] * w[2] + t[3][11] * w[3]; + } + else + { + // blend 2 matrices + t[1] = b + si[1]*12; + w[0] = sw[0] * (1.0f / 255.0f); + w[1] = sw[1] * (1.0f / 255.0f); + // blend the matrices + m[0][0] = t[0][ 0] * w[0] + t[1][ 0] * w[1]; + m[0][1] = t[0][ 1] * w[0] + t[1][ 1] * w[1]; + m[0][2] = t[0][ 2] * w[0] + t[1][ 2] * w[1]; + m[0][3] = t[0][ 3] * w[0] + t[1][ 3] * w[1]; + m[1][0] = t[0][ 4] * w[0] + t[1][ 4] * w[1]; + m[1][1] = t[0][ 5] * w[0] + t[1][ 5] * w[1]; + m[1][2] = t[0][ 6] * w[0] + t[1][ 6] * w[1]; + m[1][3] = t[0][ 7] * w[0] + t[1][ 7] * w[1]; + m[2][0] = t[0][ 8] * w[0] + t[1][ 8] * w[1]; + m[2][1] = t[0][ 9] * w[0] + t[1][ 9] * w[1]; + m[2][2] = t[0][10] * w[0] + t[1][10] * w[1]; + m[2][3] = t[0][11] * w[0] + t[1][11] * w[1]; + } + si += 4; + sw += 4; + // modify the vertex + VectorCopy(vp, tp); + vp[0] = tp[0] * m[0][0] + tp[1] * m[0][1] + tp[2] * m[0][2] + m[0][3]; + vp[1] = tp[0] * m[1][0] + tp[1] * m[1][1] + tp[2] * m[1][2] + m[1][3]; + vp[2] = tp[0] * m[2][0] + tp[1] * m[2][1] + tp[2] * m[2][2] + m[2][3]; + vp += 3; + if (vn) + { + // the normal transformation matrix is a set of cross products... + CrossProduct(m[1], m[2], n[0]); + CrossProduct(m[2], m[0], n[1]); + CrossProduct(m[0], m[1], n[2]); // is actually transpose(inverse(m)) * det(m) + VectorCopy(vn, tn); + vn[0] = tn[0] * n[0][0] + tn[1] * n[0][1] + tn[2] * n[0][2]; + vn[1] = tn[0] * n[1][0] + tn[1] * n[1][1] + tn[2] * n[1][2]; + vn[2] = tn[0] * n[2][0] + tn[1] * n[2][1] + tn[2] * n[2][2]; + VectorNormalize(vn); + vn += 3; + if (vs) + { + VectorCopy(vs, ts); + vs[0] = ts[0] * n[0][0] + ts[1] * n[0][1] + ts[2] * n[0][2]; + vs[1] = ts[0] * n[1][0] + ts[1] * n[1][1] + ts[2] * n[1][2]; + vs[2] = ts[0] * n[2][0] + ts[1] * n[2][1] + ts[2] * n[2][2]; + VectorNormalize(vs); + vs += 3; + VectorCopy(vt, tt); + vt[0] = tt[0] * n[0][0] + tt[1] * n[0][1] + tt[2] * n[0][2]; + vt[1] = tt[0] * n[1][0] + tt[1] * n[1][1] + tt[2] * n[1][2]; + vt[2] = tt[0] * n[2][0] + tt[1] * n[2][1] + tt[2] * n[2][2]; + VectorNormalize(vt); + vt += 3; + } + } + } + rsurface.batchskeletaltransform3x4 = NULL; + rsurface.batchskeletalnumtransforms = 0; + } + // q1bsp surfaces rendered in vertex color mode have to have colors // calculated based on lightstyles if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo) @@ -8983,6 +9870,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const // in place for (deformindex = 0, deform = rsurface.texture->deforms;deformindex < Q3MAXDEFORMS && deform->deform && r_deformvertexes.integer;deformindex++, deform++) { + float scale; switch (deform->deform) { default: @@ -9254,77 +10142,80 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const } } - // generate texcoords based on the chosen texcoord source - switch(rsurface.texture->tcgen.tcgen) + if (rsurface.batchtexcoordtexture2f && rsurface.texture->materialshaderpass) { - default: - case Q3TCGEN_TEXTURE: - break; - case Q3TCGEN_LIGHTMAP: -// rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); -// rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; -// rsurface.batchtexcoordtexture2f_bufferoffset = 0; - if (rsurface.batchtexcoordlightmap2f) - memcpy(rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordtexture2f, batchnumvertices * sizeof(float[2])); - break; - case Q3TCGEN_VECTOR: -// rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); -// rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; -// rsurface.batchtexcoordtexture2f_bufferoffset = 0; - for (j = 0;j < batchnumvertices;j++) - { - rsurface.batchtexcoordtexture2f[j*2+0] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms); - rsurface.batchtexcoordtexture2f[j*2+1] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms + 3); - } - break; - case Q3TCGEN_ENVIRONMENT: - // make environment reflections using a spheremap - rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); - rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; - rsurface.batchtexcoordtexture2f_bufferoffset = 0; - for (j = 0;j < batchnumvertices;j++) + // generate texcoords based on the chosen texcoord source + switch(rsurface.texture->materialshaderpass->tcgen.tcgen) { - // identical to Q3A's method, but executed in worldspace so - // carried models can be shiny too + default: + case Q3TCGEN_TEXTURE: + break; + case Q3TCGEN_LIGHTMAP: + // rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); + // rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; + // rsurface.batchtexcoordtexture2f_bufferoffset = 0; + if (rsurface.batchtexcoordlightmap2f) + memcpy(rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordlightmap2f, batchnumvertices * sizeof(float[2])); + break; + case Q3TCGEN_VECTOR: + // rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); + // rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; + // rsurface.batchtexcoordtexture2f_bufferoffset = 0; + for (j = 0;j < batchnumvertices;j++) + { + rsurface.batchtexcoordtexture2f[j*2+0] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->materialshaderpass->tcgen.parms); + rsurface.batchtexcoordtexture2f[j*2+1] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->materialshaderpass->tcgen.parms + 3); + } + break; + case Q3TCGEN_ENVIRONMENT: + // make environment reflections using a spheremap + rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); + rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; + rsurface.batchtexcoordtexture2f_bufferoffset = 0; + for (j = 0;j < batchnumvertices;j++) + { + // identical to Q3A's method, but executed in worldspace so + // carried models can be shiny too - float viewer[3], d, reflected[3], worldreflected[3]; + float viewer[3], d, reflected[3], worldreflected[3]; - VectorSubtract(rsurface.localvieworigin, rsurface.batchvertex3f + 3*j, viewer); - // VectorNormalize(viewer); + VectorSubtract(rsurface.localvieworigin, rsurface.batchvertex3f + 3*j, viewer); + // VectorNormalize(viewer); - d = DotProduct(rsurface.batchnormal3f + 3*j, viewer); + d = DotProduct(rsurface.batchnormal3f + 3*j, viewer); - reflected[0] = rsurface.batchnormal3f[j*3+0]*2*d - viewer[0]; - reflected[1] = rsurface.batchnormal3f[j*3+1]*2*d - viewer[1]; - reflected[2] = rsurface.batchnormal3f[j*3+2]*2*d - viewer[2]; - // note: this is proportinal to viewer, so we can normalize later + reflected[0] = rsurface.batchnormal3f[j*3+0]*2*d - viewer[0]; + reflected[1] = rsurface.batchnormal3f[j*3+1]*2*d - viewer[1]; + reflected[2] = rsurface.batchnormal3f[j*3+2]*2*d - viewer[2]; + // note: this is proportinal to viewer, so we can normalize later - Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected); - VectorNormalize(worldreflected); + Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected); + VectorNormalize(worldreflected); - // note: this sphere map only uses world x and z! - // so positive and negative y will LOOK THE SAME. - rsurface.batchtexcoordtexture2f[j*2+0] = 0.5 + 0.5 * worldreflected[1]; - rsurface.batchtexcoordtexture2f[j*2+1] = 0.5 - 0.5 * worldreflected[2]; + // note: this sphere map only uses world x and z! + // so positive and negative y will LOOK THE SAME. + rsurface.batchtexcoordtexture2f[j*2+0] = 0.5 + 0.5 * worldreflected[1]; + rsurface.batchtexcoordtexture2f[j*2+1] = 0.5 - 0.5 * worldreflected[2]; + } + break; } - break; - } - // the only tcmod that needs software vertex processing is turbulent, so - // check for it here and apply the changes if needed - // and we only support that as the first one - // (handling a mixture of turbulent and other tcmods would be problematic - // without punting it entirely to a software path) - if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT) - { - amplitude = rsurface.texture->tcmods[0].parms[1]; - animpos = rsurface.texture->tcmods[0].parms[2] + rsurface.shadertime * rsurface.texture->tcmods[0].parms[3]; -// rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); -// rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; -// rsurface.batchtexcoordtexture2f_bufferoffset = 0; - for (j = 0;j < batchnumvertices;j++) - { - rsurface.batchtexcoordtexture2f[j*2+0] += amplitude * sin(((rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+2]) * 1.0 / 1024.0f + animpos) * M_PI * 2); - rsurface.batchtexcoordtexture2f[j*2+1] += amplitude * sin(((rsurface.batchvertex3f[j*3+1] ) * 1.0 / 1024.0f + animpos) * M_PI * 2); + // the only tcmod that needs software vertex processing is turbulent, so + // check for it here and apply the changes if needed + // and we only support that as the first one + // (handling a mixture of turbulent and other tcmods would be problematic + // without punting it entirely to a software path) + if (rsurface.texture->materialshaderpass->tcmods[0].tcmod == Q3TCMOD_TURBULENT) + { + amplitude = rsurface.texture->materialshaderpass->tcmods[0].parms[1]; + animpos = rsurface.texture->materialshaderpass->tcmods[0].parms[2] + rsurface.shadertime * rsurface.texture->materialshaderpass->tcmods[0].parms[3]; + // rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2])); + // rsurface.batchtexcoordtexture2f_vertexbuffer = NULL; + // rsurface.batchtexcoordtexture2f_bufferoffset = 0; + for (j = 0;j < batchnumvertices;j++) + { + rsurface.batchtexcoordtexture2f[j*2+0] += amplitude * sin(((rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+2]) * 1.0 / 1024.0f + animpos) * M_PI * 2); + rsurface.batchtexcoordtexture2f[j*2+1] += amplitude * sin(((rsurface.batchvertex3f[j*3+1] ) * 1.0 / 1024.0f + animpos) * M_PI * 2); + } } } @@ -9332,7 +10223,8 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const { // convert the modified arrays to vertex structs // rsurface.batchvertexmesh = R_FrameData_Alloc(batchnumvertices * sizeof(r_vertexmesh_t)); -// rsurface.batchvertexmeshbuffer = NULL; +// rsurface.batchvertexmesh_vertexbuffer = NULL; +// rsurface.batchvertexmesh_bufferoffset = 0; if (batchneed & BATCHNEED_VERTEXMESH_VERTEX) for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++) VectorCopy(rsurface.batchvertex3f + 3*j, vertexmesh->vertex3f); @@ -9356,6 +10248,46 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const if ((batchneed & BATCHNEED_VERTEXMESH_LIGHTMAP) && rsurface.batchtexcoordlightmap2f) for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++) Vector2Copy(rsurface.batchtexcoordlightmap2f + 2*j, vertexmesh->texcoordlightmap2f); + if ((batchneed & BATCHNEED_VERTEXMESH_SKELETAL) && rsurface.batchskeletalindex4ub) + { + for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++) + { + Vector4Copy(rsurface.batchskeletalindex4ub + 4*j, vertexmesh->skeletalindex4ub); + Vector4Copy(rsurface.batchskeletalweight4ub + 4*j, vertexmesh->skeletalweight4ub); + } + } + } + + // upload buffer data for the dynamic batch + if (((r_batch_dynamicbuffer.integer || gl_vbo_dynamicvertex.integer || gl_vbo_dynamicindex.integer) && vid.support.arb_vertex_buffer_object && gl_vbo.integer) || vid.forcevbo) + { + if (rsurface.batchvertexmesh) + rsurface.batchvertexmesh_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(r_vertexmesh_t), rsurface.batchvertexmesh, R_BUFFERDATA_VERTEX, &rsurface.batchvertexmesh_bufferoffset); + else + { + if (rsurface.batchvertex3f) + rsurface.batchvertex3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f, R_BUFFERDATA_VERTEX, &rsurface.batchvertex3f_bufferoffset); + if (rsurface.batchsvector3f) + rsurface.batchsvector3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchsvector3f, R_BUFFERDATA_VERTEX, &rsurface.batchsvector3f_bufferoffset); + if (rsurface.batchtvector3f) + rsurface.batchtvector3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchtvector3f, R_BUFFERDATA_VERTEX, &rsurface.batchtvector3f_bufferoffset); + if (rsurface.batchnormal3f) + rsurface.batchnormal3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f, R_BUFFERDATA_VERTEX, &rsurface.batchnormal3f_bufferoffset); + if (rsurface.batchlightmapcolor4f) + rsurface.batchlightmapcolor4f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[4]), rsurface.batchlightmapcolor4f, R_BUFFERDATA_VERTEX, &rsurface.batchlightmapcolor4f_bufferoffset); + if (rsurface.batchtexcoordtexture2f) + rsurface.batchtexcoordtexture2f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[2]), rsurface.batchtexcoordtexture2f, R_BUFFERDATA_VERTEX, &rsurface.batchtexcoordtexture2f_bufferoffset); + if (rsurface.batchtexcoordlightmap2f) + rsurface.batchtexcoordlightmap2f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[2]), rsurface.batchtexcoordlightmap2f, R_BUFFERDATA_VERTEX, &rsurface.batchtexcoordlightmap2f_bufferoffset); + if (rsurface.batchskeletalindex4ub) + rsurface.batchskeletalindex4ub_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(unsigned char[4]), rsurface.batchskeletalindex4ub, R_BUFFERDATA_VERTEX, &rsurface.batchskeletalindex4ub_bufferoffset); + if (rsurface.batchskeletalweight4ub) + rsurface.batchskeletalweight4ub_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(unsigned char[4]), rsurface.batchskeletalweight4ub, R_BUFFERDATA_VERTEX, &rsurface.batchskeletalweight4ub_bufferoffset); + } + if (rsurface.batchelement3s) + rsurface.batchelement3s_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(short[3]), rsurface.batchelement3s, R_BUFFERDATA_INDEX16, &rsurface.batchelement3s_bufferoffset); + else if (rsurface.batchelement3i) + rsurface.batchelement3i_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(int[3]), rsurface.batchelement3i, R_BUFFERDATA_INDEX32, &rsurface.batchelement3i_bufferoffset); } } @@ -9391,7 +10323,31 @@ void RSurf_DrawBatch(void) } } #endif - R_Mesh_Draw(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchfirsttriangle, rsurface.batchnumtriangles, rsurface.batchelement3i, rsurface.batchelement3i_indexbuffer, rsurface.batchelement3i_bufferoffset, rsurface.batchelement3s, rsurface.batchelement3s_indexbuffer, rsurface.batchelement3s_bufferoffset); + if (rsurface.batchmultidraw) + { + // issue multiple draws rather than copying index data + int numsurfaces = rsurface.batchmultidrawnumsurfaces; + const msurface_t **surfacelist = rsurface.batchmultidrawsurfacelist; + int i, j, k, firstvertex, endvertex, firsttriangle, endtriangle; + for (i = 0;i < numsurfaces;) + { + // combine consecutive surfaces as one draw + for (k = i, j = i + 1;j < numsurfaces;k = j, j++) + if (surfacelist[j] != surfacelist[k] + 1) + break; + firstvertex = surfacelist[i]->num_firstvertex; + endvertex = surfacelist[k]->num_firstvertex + surfacelist[k]->num_vertices; + firsttriangle = surfacelist[i]->num_firsttriangle; + endtriangle = surfacelist[k]->num_firsttriangle + surfacelist[k]->num_triangles; + R_Mesh_Draw(firstvertex, endvertex - firstvertex, firsttriangle, endtriangle - firsttriangle, rsurface.batchelement3i, rsurface.batchelement3i_indexbuffer, rsurface.batchelement3i_bufferoffset, rsurface.batchelement3s, rsurface.batchelement3s_indexbuffer, rsurface.batchelement3s_bufferoffset); + i = j; + } + } + else + { + // there is only one consecutive run of index data (may have been combined) + R_Mesh_Draw(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchfirsttriangle, rsurface.batchnumtriangles, rsurface.batchelement3i, rsurface.batchelement3i_indexbuffer, rsurface.batchelement3i_bufferoffset, rsurface.batchelement3s, rsurface.batchelement3s_indexbuffer, rsurface.batchelement3s_bufferoffset); + } } static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface) @@ -9411,7 +10367,7 @@ static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface) d = 0; if(!prepared) { - RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, 1, &surface); + RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, 1, &surface); prepared = true; if(rsurface.batchnumvertices == 0) break; @@ -9458,7 +10414,7 @@ static void RSurf_DrawBatch_GL11_ApplyFog(void) 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, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4, c2 += 4) + for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4, c2 += 4) { f = RSurf_FogVertex(v); c2[0] = c[0] * f; @@ -9557,6 +10513,8 @@ static void RSurf_DrawBatch_GL11_Lightmap(float r, float g, float b, float a, qb R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset); GL_Color(r, g, b, a); R_Mesh_TexBind(0, rsurface.lightmaptexture); + R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1); + R_Mesh_TexMatrix(0, NULL); RSurf_DrawBatch(); } @@ -9747,22 +10705,19 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_ // in Quake3 maps as it causes problems with q3map2 sky tricks, // and skymasking also looks very bad when noclipping outside the // level, so don't use it then either. - if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis && !r_trippy.integer) + if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.skymasking && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis && !r_trippy.integer) { R_Mesh_ResetTextureState(); if (skyrendermasked) { - R_SetupShader_DepthOrShadow(false, false); + R_SetupShader_DepthOrShadow(false, false, false); // depth-only (masking) GL_ColorMask(0,0,0,0); // just to make sure that braindead drivers don't draw // anything despite that colormask... GL_BlendFunc(GL_ZERO, GL_ONE); - RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist); - if (rsurface.batchvertex3fbuffer) - R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer); - else - R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer); + RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist); + R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset); } else { @@ -9936,8 +10891,8 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface R_Mesh_TexBind(1, 0); R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0); // generate a color array for the fog pass - R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, 0, 0); RSurf_DrawBatch_GL11_MakeFogColor(layercolor[0], layercolor[1], layercolor[2], layercolor[3]); + R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, 0, 0); RSurf_DrawBatch(); break; default: @@ -9979,7 +10934,7 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface switch (layer->type) { case TEXTURELAYERTYPE_LITTEXTURE: - if (layer->blendfunc1 == GL_ONE && layer->blendfunc2 == GL_ZERO) + if (layer->blendfunc1 == GL_ONE && layer->blendfunc2 == GL_ZERO && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)) { // two-pass lit texture with 2x rgbscale // first the lightmap pass @@ -10012,6 +10967,8 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset); if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) RSurf_DrawBatch_GL11_VertexShade(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog); + else if (FAKELIGHT_ENABLED) + RSurf_DrawBatch_GL11_FakeLight(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog); else RSurf_DrawBatch_GL11_VertexColor(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog); } @@ -10039,8 +10996,8 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0); } // generate a color array for the fog pass - R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, 0, 0); RSurf_DrawBatch_GL11_MakeFogColor(layer->color[0], layer->color[1], layer->color[2], layer->color[3]); + R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, 0, 0); RSurf_DrawBatch(); break; default: @@ -10175,7 +11132,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const { RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist); batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchnumvertices); - for (j = 0, vi = rsurface.batchfirstvertex;j < rsurface.batchnumvertices;j++, vi++) + for (j = 0, vi = 0;j < rsurface.batchnumvertices;j++, vi++) { VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f); Vector4Set(batchvertex[vi].color4f, 0, 0, 0, 1); @@ -10187,11 +11144,11 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const { RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist); batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchnumvertices); - for (j = 0, vi = rsurface.batchfirstvertex;j < rsurface.batchnumvertices;j++, vi++) + for (j = 0, vi = 0;j < rsurface.batchnumvertices;j++, vi++) { - unsigned char c = (vi << 3) * (1.0f / 256.0f); + unsigned char d = (vi << 3) * (1.0f / 256.0f); VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f); - Vector4Set(batchvertex[vi].color4f, c, c, c, 1); + Vector4Set(batchvertex[vi].color4f, d, d, d, 1); } R_Mesh_PrepareVertices_Generic_Unlock(); RSurf_DrawBatch(); @@ -10203,13 +11160,13 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const batchvertex = R_Mesh_PrepareVertices_Generic_Lock(3*rsurface.batchnumtriangles); for (j = 0, e = rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle;j < rsurface.batchnumtriangles;j++, e += 3) { - unsigned char c = ((j + rsurface.batchfirsttriangle) << 3) * (1.0f / 256.0f); + unsigned char d = ((j + rsurface.batchfirsttriangle) << 3) * (1.0f / 256.0f); VectorCopy(rsurface.batchvertex3f + 3*e[0], batchvertex[j*3+0].vertex3f); VectorCopy(rsurface.batchvertex3f + 3*e[1], batchvertex[j*3+1].vertex3f); VectorCopy(rsurface.batchvertex3f + 3*e[2], batchvertex[j*3+2].vertex3f); - Vector4Set(batchvertex[j*3+0].color4f, c, c, c, 1); - Vector4Set(batchvertex[j*3+1].color4f, c, c, c, 1); - Vector4Set(batchvertex[j*3+2].color4f, c, c, c, 1); + Vector4Set(batchvertex[j*3+0].color4f, d, d, d, 1); + Vector4Set(batchvertex[j*3+1].color4f, d, d, d, 1); + Vector4Set(batchvertex[j*3+2].color4f, d, d, d, 1); } R_Mesh_PrepareVertices_Generic_Unlock(); R_Mesh_Draw(0, rsurface.batchnumtriangles*3, 0, rsurface.batchnumtriangles, NULL, NULL, 0, NULL, NULL, 0); @@ -10370,14 +11327,11 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const GL_BlendFunc(GL_ONE, GL_ZERO); GL_DepthMask(true); // R_Mesh_ResetTextureState(); - R_SetupShader_DepthOrShadow(false, false); } RSurf_SetupDepthAndCulling(); - RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist); - if (rsurface.batchvertex3fbuffer) - R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer); - else - R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer); + RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist); + R_SetupShader_DepthOrShadow(false, false, !!rsurface.batchskeletaltransform3x4); + R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset); RSurf_DrawBatch(); } if (setup) @@ -10457,7 +11411,7 @@ static void R_ProcessTransparentTextureSurfaceList(int texturenumsurfaces, const center[1] += r_refdef.view.forward[1]*rsurface.entity->transparent_offset; center[2] += r_refdef.view.forward[2]*rsurface.entity->transparent_offset; } - R_MeshQueue_AddTransparent((rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) ? MESHQUEUE_SORT_HUD : ((rsurface.entity->flags & RENDER_WORLDOBJECT) ? MESHQUEUE_SORT_SKY : MESHQUEUE_SORT_DISTANCE), center, R_DrawSurface_TransparentCallback, rsurface.entity, surface - rsurface.modelsurfaces, rsurface.rtlight); + R_MeshQueue_AddTransparent((rsurface.entity->flags & RENDER_WORLDOBJECT) ? TRANSPARENTSORT_SKY : (rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) ? TRANSPARENTSORT_HUD : rsurface.texture->transparentsort, center, R_DrawSurface_TransparentCallback, rsurface.entity, surface - rsurface.modelsurfaces, rsurface.rtlight); } } @@ -10468,11 +11422,9 @@ static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msu if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))) return; RSurf_SetupDepthAndCulling(); - RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist); - if (rsurface.batchvertex3fbuffer) - R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer); - else - R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer); + RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist); + R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset); + R_SetupShader_DepthOrShadow(false, false, !!rsurface.batchskeletaltransform3x4); RSurf_DrawBatch(); } @@ -10704,7 +11656,7 @@ void R_DrawLocs(void) for (loc = cl.locnodes, index = 0;loc;loc = loc->next, index++) { VectorLerp(loc->mins, 0.5f, loc->maxs, center); - R_MeshQueue_AddTransparent(MESHQUEUE_SORT_DISTANCE, center, R_DrawLoc_Callback, (entity_render_t *)loc, loc == nearestloc ? -1 : index, NULL); + R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, center, R_DrawLoc_Callback, (entity_render_t *)loc, loc == nearestloc ? -1 : index, NULL); } } @@ -10715,7 +11667,7 @@ void R_DecalSystem_Reset(decalsystem_t *decalsystem) memset(decalsystem, 0, sizeof(*decalsystem)); } -static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float *v0, const float *v1, const float *v2, const float *t0, const float *t1, const float *t2, const float *c0, const float *c1, const float *c2, int triangleindex, int surfaceindex, int decalsequence) +static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float *v0, const float *v1, const float *v2, const float *t0, const float *t1, const float *t2, const float *c0, const float *c1, const float *c2, int triangleindex, int surfaceindex, unsigned int decalsequence) { tridecal_t *decal; tridecal_t *decals; @@ -10795,7 +11747,7 @@ extern cvar_t cl_decals_bias; extern cvar_t cl_decals_models; extern cvar_t cl_decals_newsystem_intensitymultiplier; // baseparms, parms, temps -static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, float g, float b, float a, float s1, float t1, float s2, float t2, int decalsequence, qboolean dynamic, float (*planes)[4], matrix4x4_t *projection, int triangleindex, int surfaceindex) +static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, float g, float b, float a, float s1, float t1, float s2, float t2, unsigned int decalsequence, qboolean dynamic, float (*planes)[4], matrix4x4_t *projection, int triangleindex, int surfaceindex) { int cornerindex; int index; @@ -10888,7 +11840,7 @@ static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, flo for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++) R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence); } -static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, int decalsequence) +static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, unsigned int decalsequence) { matrix4x4_t projection; decalsystem_t *decalsystem; @@ -11049,7 +12001,7 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor } // do not call this outside of rendering code - use R_DecalSystem_SplatEntities instead -static void R_DecalSystem_ApplySplatEntities(const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, int decalsequence) +static void R_DecalSystem_ApplySplatEntities(const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, unsigned int decalsequence) { int renderentityindex; float worldmins[3]; @@ -11085,7 +12037,7 @@ typedef struct r_decalsystem_splatqueue_s float color[4]; float tcrange[4]; float worldsize; - int decalsequence; + unsigned int decalsequence; } r_decalsystem_splatqueue_t; @@ -11124,7 +12076,7 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent) int i; decalsystem_t *decalsystem = &ent->decalsystem; int numdecals; - int killsequence; + unsigned int killsequence; tridecal_t *decal; float frametime; float lifetime; @@ -11141,7 +12093,7 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent) return; } - killsequence = cl.decalsequence - max(1, cl_decals_max.integer); + killsequence = cl.decalsequence - bound(1, (unsigned int) cl_decals_max.integer, cl.decalsequence); lifetime = cl_decals_time.value + cl_decals_fadetime.value; if (decalsystem->lastupdatetime) @@ -11149,7 +12101,6 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent) else frametime = 0; decalsystem->lastupdatetime = r_refdef.scene.time; - decal = decalsystem->decals; numdecals = decalsystem->numdecals; for (i = 0, decal = decalsystem->decals;i < numdecals;i++, decal++) @@ -11157,7 +12108,7 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent) if (decal->color4f[0][3]) { decal->lived += frametime; - if (killsequence - decal->decalsequence > 0 || decal->lived >= lifetime) + if (killsequence > decal->decalsequence || decal->lived >= lifetime) { memset(decal, 0, sizeof(*decal)); if (decalsystem->freedecal > i) @@ -11226,7 +12177,6 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent) RSurf_ActiveModelEntity(ent, false, false, false); decalsystem->lastupdatetime = r_refdef.scene.time; - decal = decalsystem->decals; faderate = 1.0f / max(0.001f, cl_decals_fadetime.value); @@ -11305,7 +12255,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent) if (numtris > 0) { - r_refdef.stats.drawndecals += numtris; + r_refdef.stats[r_stat_drawndecals] += numtris; // now render the decals all at once // (this assumes they all use one particle font texture!) @@ -11343,7 +12293,7 @@ static void R_DrawModelDecals(void) for (i = 0;i < r_refdef.scene.numentities;i++) numdecals += r_refdef.scene.entities[i]->decalsystem.numdecals; - r_refdef.stats.totaldecals += numdecals; + r_refdef.stats[r_stat_totaldecals] += numdecals; if (r_showsurfaces.integer) return; @@ -11363,10 +12313,9 @@ extern cvar_t mod_collision_bih; static void R_DrawDebugModel(void) { entity_render_t *ent = rsurface.entity; - int i, j, k, l, flagsmask; + int i, j, flagsmask; const msurface_t *surface; dp_model_t *model = ent->model; - vec3_t v; if (!sv.active && !cls.demoplayback && ent != r_refdef.scene.worldentity) return; @@ -11415,13 +12364,12 @@ static void R_DrawDebugModel(void) { int triangleindex; int bihleafindex; - qboolean cullbox = ent == r_refdef.scene.worldentity; + qboolean cullbox = false; const q3mbrush_t *brush; const bih_t *bih = &model->collision_bih; const bih_leaf_t *bihleaf; float vertex3f[3][3]; GL_PolygonOffset(r_refdef.polygonfactor + r_showcollisionbrushes_polygonfactor.value, r_refdef.polygonoffset + r_showcollisionbrushes_polygonoffset.value); - cullbox = false; for (bihleafindex = 0, bihleaf = bih->leafs;bihleafindex < bih->numleafs;bihleafindex++, bihleaf++) { if (cullbox && R_CullBox(bihleaf->mins, bihleaf->maxs)) @@ -11499,6 +12447,8 @@ static void R_DrawDebugModel(void) if (r_shownormals.value != 0 && qglBegin) { + int l, k; + vec3_t v; if (r_showdisabledepthtest.integer) { GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -11664,9 +12614,9 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep // add to stats if desired if (r_speeds.integer && !skysurfaces && !depthonly) { - r_refdef.stats.world_surfaces += numsurfacelist; + r_refdef.stats[r_stat_world_surfaces] += numsurfacelist; for (j = 0;j < numsurfacelist;j++) - r_refdef.stats.world_triangles += r_surfacelist[j]->num_triangles; + r_refdef.stats[r_stat_world_triangles] += r_surfacelist[j]->num_triangles; } rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity @@ -11794,18 +12744,15 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr } } } - if (update) - for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++) - if (update[j]) - R_BuildLightMap(ent, surfaces + j); + R_QueueModelSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, prepass); // add to stats if desired if (r_speeds.integer && !skysurfaces && !depthonly) { - r_refdef.stats.entities_surfaces += numsurfacelist; + r_refdef.stats[r_stat_entities_surfaces] += numsurfacelist; for (j = 0;j < numsurfacelist;j++) - r_refdef.stats.entities_triangles += r_surfacelist[j]->num_triangles; + r_refdef.stats[r_stat_entities_triangles] += r_surfacelist[j]->num_triangles; } rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity @@ -11821,12 +12768,16 @@ void R_DrawCustomSurface(skinframe_t *skinframe, const matrix4x4_t *texmatrix, i texture.update_lastrenderframe = -1; // regenerate this texture texture.basematerialflags = materialflags | MATERIALFLAG_CUSTOMSURFACE | MATERIALFLAG_WALL; + texture.basealpha = 1.0f; texture.currentskinframe = skinframe; texture.currenttexmatrix = *texmatrix; // requires MATERIALFLAG_CUSTOMSURFACE texture.offsetmapping = OFFSETMAPPING_OFF; texture.offsetscale = 1; texture.specularscalemod = 1; texture.specularpowermod = 1; + texture.transparentsort = TRANSPARENTSORT_DISTANCE; + // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS + // JUST GREP FOR "specularscalemod = 1". surface.texture = &texture; surface.num_triangles = numtriangles;