//
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)"};
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"};
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)"};
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)"};
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"};
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_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)"};
{"#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"}
};
// NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS!
int loc_ShadowMap_Parameters;
int loc_ShadowMap_TextureScale;
int loc_SpecularPower;
+ int loc_Skeletal_Transform12;
int loc_UserVec1;
int loc_UserVec2;
int loc_UserVec3;
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)
};
-#define SHADERSTATICPARMS_COUNT 11
+#define SHADERSTATICPARMS_COUNT 13
static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT];
static int shaderstaticparms_count = 0;
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;
}
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");
}
/// information about each possible shader permutation
p->loc_ShadowMap_Parameters = qglGetUniformLocation(p->program, "ShadowMap_Parameters");
p->loc_ShadowMap_TextureScale = qglGetUniformLocation(p->program, "ShadowMap_TextureScale");
p->loc_SpecularPower = qglGetUniformLocation(p->program, "SpecularPower");
+ p->loc_Skeletal_Transform12 = qglGetUniformLocation(p->program, "Skeletal_Transform12");
p->loc_UserVec1 = qglGetUniformLocation(p->program, "UserVec1");
p->loc_UserVec2 = qglGetUniformLocation(p->program, "UserVec2");
p->loc_UserVec3 = qglGetUniformLocation(p->program, "UserVec3");
{
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);
}
vsresult = qD3DXCompileShader(vertstring, 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);
}
psresult = qD3DXCompileShader(fragstring, 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);
float m16f[16];
matrix4x4_t tempmatrix;
r_waterstate_waterplane_t *waterplane = (r_waterstate_waterplane_t *)surfacewaterplane;
+ if (rsurface.entityskeletaltransform3x4)
+ permutation |= SHADERPERMUTATION_SKELETAL;
if (r_trippy.integer && !notrippy)
permutation |= SHADERPERMUTATION_TRIPPY;
if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
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);
{
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);
+ 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.batchvertexmeshbuffer);
R_SetupShader_SetPermutationHLSL(mode, permutation);
Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);hlslPSSetParameter16f(D3DPSREGISTER_ModelToReflectCube, m16f);
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);
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);
+ 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.batchvertexmeshbuffer);
}
R_SetupShader_SetPermutationGLSL(mode, permutation);
}
}
if (r_glsl_permutation->tex_Texture_BounceGrid >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_BounceGrid, r_shadow_bouncegridtexture);
+ if (r_glsl_permutation->loc_Skeletal_Transform12 >= 0 && rsurface.entityskeletalnumtransforms > 0)
+ qglUniform4fv(r_glsl_permutation->loc_Skeletal_Transform12, rsurface.entityskeletalnumtransforms*3, rsurface.entityskeletaltransform3x4);
CHECKGLERROR
break;
case RENDERPATH_GL11:
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);}
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)
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
{
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
// 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;
// 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;
// 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;
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);
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_resolutionmultiplier);
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);
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_glsl_skeletal);
Cvar_RegisterVariable(&r_glsl_saturation);
Cvar_RegisterVariable(&r_glsl_saturation_redcompensate);
Cvar_RegisterVariable(&r_glsl_vertextextureblend_usebothalphas);
ent->animcache_vertexmesh = NULL;
ent->animcache_vertex3fbuffer = NULL;
ent->animcache_vertexmeshbuffer = NULL;
+ ent->animcache_skeletaltransform3x4 = NULL;
}
}
{
dp_model_t *model = ent->model;
int numvertices;
+
+ // cache skeletal animation data first (primarily for gpu-skinning)
+ if (!ent->animcache_skeletaltransform3x4 && model->num_bones > 0)
+ {
+ int i;
+ int blends;
+ const skeleton_t *skeleton = ent->skeleton;
+ const frameblend_t *frameblend = ent->frameblend;
+ float *boneposerelative;
+ float m[12];
+ static float bonepose[256][12];
+ ent->animcache_skeletaltransform3x4 = R_FrameData_Alloc(sizeof(float[3][4]) * model->num_bones);
+ boneposerelative = ent->animcache_skeletaltransform3x4;
+ if (skeleton && !skeleton->relativetransforms)
+ skeleton = NULL;
+ // resolve hierarchy and make relative transforms (deforms) which the shader wants
+ if (skeleton)
+ {
+ for (i = 0;i < model->num_bones;i++)
+ {
+ Matrix4x4_ToArray12FloatD3D(&skeleton->relativetransforms[i], m);
+ if (model->data_bones[i].parent >= 0)
+ R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
+ else
+ memcpy(bonepose[i], m, sizeof(m));
+
+ // create a relative deformation matrix to describe displacement
+ // from the base mesh, which is used by the actual weighting
+ R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative + i * 12);
+ }
+ }
+ else
+ {
+ for (i = 0;i < model->num_bones;i++)
+ {
+ const short * RESTRICT pose7s = model->data_poses7s + 7 * (frameblend[0].subframe * model->num_bones + i);
+ float lerp = frameblend[0].lerp,
+ tx = pose7s[0], ty = pose7s[1], tz = pose7s[2],
+ rx = pose7s[3] * lerp,
+ ry = pose7s[4] * lerp,
+ rz = pose7s[5] * lerp,
+ rw = pose7s[6] * lerp,
+ dx = tx*rw + ty*rz - tz*ry,
+ dy = -tx*rz + ty*rw + tz*rx,
+ dz = tx*ry - ty*rx + tz*rw,
+ dw = -tx*rx - ty*ry - tz*rz,
+ scale, sx, sy, sz, sw;
+ for (blends = 1;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
+ {
+ const short * RESTRICT pose7s = model->data_poses7s + 7 * (frameblend[blends].subframe * model->num_bones + i);
+ float lerp = frameblend[blends].lerp,
+ tx = pose7s[0], ty = pose7s[1], tz = pose7s[2],
+ qx = pose7s[3], qy = pose7s[4], qz = pose7s[5], qw = pose7s[6];
+ if(rx*qx + ry*qy + rz*qz + rw*qw < 0) lerp = -lerp;
+ qx *= lerp;
+ qy *= lerp;
+ qz *= lerp;
+ qw *= lerp;
+ rx += qx;
+ ry += qy;
+ rz += qz;
+ rw += qw;
+ dx += tx*qw + ty*qz - tz*qy;
+ dy += -tx*qz + ty*qw + tz*qx;
+ dz += tx*qy - ty*qx + tz*qw;
+ dw += -tx*qx - ty*qy - tz*qz;
+ }
+ scale = 1.0f / (rx*rx + ry*ry + rz*rz + rw*rw);
+ sx = rx * scale;
+ sy = ry * scale;
+ sz = rz * scale;
+ sw = rw * scale;
+ m[0] = sw*rw + sx*rx - sy*ry - sz*rz;
+ m[1] = 2*(sx*ry - sw*rz);
+ m[2] = 2*(sx*rz + sw*ry);
+ m[3] = model->num_posescale*(dx*sw - dy*sz + dz*sy - dw*sx);
+ m[4] = 2*(sx*ry + sw*rz);
+ m[5] = sw*rw + sy*ry - sx*rx - sz*rz;
+ m[6] = 2*(sy*rz - sw*rx);
+ m[7] = model->num_posescale*(dx*sz + dy*sw - dz*sx - dw*sy);
+ m[8] = 2*(sx*rz - sw*ry);
+ m[9] = 2*(sy*rz + sw*rx);
+ m[10] = sw*rw + sz*rz - sx*rx - sy*ry;
+ m[11] = model->num_posescale*(dy*sx + dz*sw - dx*sy - dw*sz);
+ if (i == r_skeletal_debugbone.integer)
+ m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
+ m[3] *= r_skeletal_debugtranslatex.value;
+ m[7] *= r_skeletal_debugtranslatey.value;
+ m[11] *= r_skeletal_debugtranslatez.value;
+ if (model->data_bones[i].parent >= 0)
+ R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
+ else
+ memcpy(bonepose[i], m, sizeof(m));
+ // create a relative deformation matrix to describe displacement
+ // from the base mesh, which is used by the actual weighting
+ R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative + i * 12);
+ }
+ }
+ }
+
// see if it's already cached this frame
if (ent->animcache_vertex3f)
{
// see if this ent is worth caching
if (!model || !model->Draw || !model->surfmesh.isanimated || !model->AnimateVertices)
return false;
+ // skip entity if the shader backend has a cheaper way
+ if (model->surfmesh.data_skeletalindex4ub && r_glsl_skeletal.integer)
+ {
+ switch (vid.renderpath)
+ {
+ case RENDERPATH_GL20:
+ return false;
+ case RENDERPATH_GL11:
+ case RENDERPATH_GL13:
+ case RENDERPATH_GLES1:
+ case RENDERPATH_GLES2:
+ case RENDERPATH_D3D9:
+ case RENDERPATH_D3D10:
+ case RENDERPATH_D3D11:
+ case RENDERPATH_SOFT:
+ break;
+ }
+ }
// get some memory for this entity and generate mesh data
numvertices = model->surfmesh.num_vertices;
ent->animcache_vertex3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
R_View_WorldVisibility(r_refdef.view.useclipplane);
R_View_UpdateEntityVisible();
R_View_UpdateEntityLighting();
- R_AnimCache_CacheVisibleEntities();
}
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;
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;
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;
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);
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);
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)
if (!r_fb.water.depthtexture)
R_ClearScreen(r_refdef.fogenabled);
R_View_Update();
+ R_AnimCache_CacheVisibleEntities();
goto finish;
error:
r_refdef.view = originalview;
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;
// 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);
}
// 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);
float colorscale = r_bloom_colorscale.value;
r_refdef.stats.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;
}
+#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...
unsigned int permutation;
float uservecs[4][4];
+ R_EntityMatrix(&identitymatrix);
+
switch (vid.renderpath)
{
case RENDERPATH_GL20:
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
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");
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);
}
}
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;
{
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;
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;
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)
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;
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.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)
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_vertex3f ? NULL : ent->animcache_skeletaltransform3x4;
+ rsurface.entityskeletalnumtransforms = rsurface.entityskeletaltransform3x4 ? model->num_bones : 0;
+ if (model->surfmesh.isanimated && model->AnimateVertices && !rsurface.entityskeletaltransform3x4)
{
if (ent->animcache_vertex3f)
{
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;
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.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)
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;
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.passcolor4f = NULL;
rsurface.passcolor4f_vertexbuffer = NULL;
rsurface.passcolor4f_bufferoffset = 0;
+ rsurface.forcecurrenttextureupdate = true;
if (rsurface.modelnumvertices && rsurface.modelelement3i)
{
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;
if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo)
{
dynamicvertex = true;
- batchneed |= BATCHNEED_NOGAPS;
needsupdate |= BATCHNEED_VERTEXMESH_VERTEXCOLOR;
}
break;
case Q3DEFORM_AUTOSPRITE:
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:
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:
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
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:
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
dynamicvertex = true;
- batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+ batchneed |= BATCHNEED_ARRAY_VERTEX;
needsupdate |= BATCHNEED_VERTEXMESH_VERTEX;
break;
}
break;
case Q3TCGEN_LIGHTMAP:
dynamicvertex = true;
- batchneed |= BATCHNEED_ARRAY_LIGHTMAP | BATCHNEED_NOGAPS;
+ batchneed |= BATCHNEED_ARRAY_LIGHTMAP;
needsupdate |= BATCHNEED_VERTEXMESH_LIGHTMAP;
break;
case Q3TCGEN_VECTOR:
dynamicvertex = true;
- batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+ batchneed |= BATCHNEED_ARRAY_VERTEX;
needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
break;
case Q3TCGEN_ENVIRONMENT:
dynamicvertex = true;
- batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS;
+ batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL;
needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
break;
}
if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
{
dynamicvertex = true;
- batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+ batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD;
needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
}
if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)))
{
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)
+ // 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;
+
+ // 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))
+ dynamicvertex = true;
+
+ 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;
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)
dynamicvertex = true;
if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)))
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;
-
rsurface.batchvertex3f = rsurface.modelvertex3f;
rsurface.batchvertex3f_vertexbuffer = rsurface.modelvertex3f_vertexbuffer;
rsurface.batchvertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
rsurface.batchtexcoordlightmap2f = rsurface.modeltexcoordlightmap2f;
rsurface.batchtexcoordlightmap2f_vertexbuffer = rsurface.modeltexcoordlightmap2f_vertexbuffer;
rsurface.batchtexcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
+ 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.batchvertex3fbuffer = rsurface.modelvertex3fbuffer;
rsurface.batchvertexmesh = rsurface.modelvertexmesh;
rsurface.batchvertexmeshbuffer = rsurface.modelvertexmeshbuffer;
// 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)
{
// otherwise use the original static buffer with an appropriate offset
if (gaps)
{
+ 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;
// 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)
+ //if (dynamicvertex)
{
rsurface.batchvertex3fbuffer = NULL;
rsurface.batchvertexmesh = NULL;
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.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++)
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;
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);
+ }
+ }
}
}
}
}
#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)
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;
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;
// 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);
+ RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
if (rsurface.batchvertex3fbuffer)
R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
else
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:
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
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);
}
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:
{
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);
{
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);
VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
R_SetupShader_DepthOrShadow(false, false);
}
RSurf_SetupDepthAndCulling();
- RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
+ RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
if (rsurface.batchvertex3fbuffer)
R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
else
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);
}
}
if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
return;
RSurf_SetupDepthAndCulling();
- RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
+ RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
if (rsurface.batchvertex3fbuffer)
R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
else
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);
}
}
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++)
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);
{
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))
}
}
}
- 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
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;