X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=gl_rmain.c;h=3d9686e0c7f851f0c5dada0cb014f14c935b7f81;hb=32079cc7d9d5dcfa17451589e9b8ef93aab7ca9b;hp=14e2551c9681609e382df2f894c171eaeb84a208;hpb=fbecf87086c94afa24e82c09f8211c24bf6ffcc7;p=xonotic%2Fdarkplaces.git diff --git a/gl_rmain.c b/gl_rmain.c index 14e2551c..3d9686e0 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -184,6 +184,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"}; @@ -678,7 +679,8 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] = {"#define USETRIPPY\n", " trippy"}, {"#define USEDEPTHRGB\n", " depthrgb"}, {"#define USEALPHAGENVERTEX\n", " alphagenvertex"}, - {"#define USESKELETAL\n", " skeletal"} + {"#define USESKELETAL\n", " skeletal"}, + {"#define USEOCCLUDE\n", " occlude"} }; // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS! @@ -881,8 +883,9 @@ enum 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 13 +#define SHADERSTATICPARMS_COUNT 14 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT]; static int shaderstaticparms_count = 0; @@ -894,7 +897,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)); @@ -916,6 +919,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); @@ -956,6 +961,7 @@ static void R_CompileShader_AddStaticParms(unsigned int mode, unsigned int permu 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 @@ -1163,6 +1169,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"); @@ -1311,15 +1337,19 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode 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); @@ -1341,7 +1371,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 @@ -1598,7 +1631,7 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c 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 = ID3DXBuffer_GetBufferSize(vsbuffer); @@ -1621,7 +1654,7 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c 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 = ID3DXBuffer_GetBufferSize(psbuffer); @@ -1749,23 +1782,23 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode 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 @@ -1869,7 +1902,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))) @@ -1896,7 +1929,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))) @@ -2068,7 +2101,9 @@ void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb, qboolean 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: @@ -2169,6 +2204,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) @@ -2726,7 +2763,9 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, 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) { @@ -3131,7 +3170,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]; } @@ -3365,6 +3404,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) { @@ -4008,9 +4048,11 @@ static void gl_main_start(void) r_loadnormalmap = true; r_loadgloss = true; r_loadfog = false; +#ifdef GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT if (vid.support.arb_uniform_buffer_object) qglGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &r_uniformbufferalignment); - break; +#endif + break; case RENDERPATH_GL13: case RENDERPATH_GLES1: Cvar_SetValueQuick(&r_textureunits, vid.texunits); @@ -4078,6 +4120,27 @@ static void gl_main_start(void) 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) @@ -4095,7 +4158,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 @@ -4313,6 +4376,7 @@ void GL_Main_Init(void) 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); @@ -4361,6 +4425,13 @@ void GL_Main_Init(void) 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); } @@ -4409,7 +4480,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); @@ -4424,9 +4499,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) { @@ -4766,7 +4838,7 @@ r_meshbuffer_t *R_BufferData_Store(size_t datasize, const void *data, r_bufferda Sys_Error("R_BufferData_Store: failed to create a new buffer of sufficient size\n"); mem = r_bufferdata_buffer[r_bufferdata_cycle][type]; - offset = mem->current; + offset = (int)mem->current; mem->current += padsize; // upload the data to the buffer at the chosen offset @@ -5144,18 +5216,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]; @@ -5171,10 +5245,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++) @@ -5637,7 +5713,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); @@ -5925,25 +6003,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]); @@ -5956,7 +6042,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); @@ -6008,6 +6094,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) @@ -6063,6 +6151,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; @@ -6090,7 +6180,7 @@ 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) @@ -6119,7 +6209,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); @@ -6340,7 +6430,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 @@ -7934,10 +8024,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: @@ -7953,16 +8055,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); @@ -8058,7 +8161,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]; @@ -8089,7 +8194,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) t->backgroundcurrentskinframe = t->backgroundskinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->backgroundskinframerate, t->backgroundnumskinframes)]; 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) @@ -8332,7 +8437,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) } } - return t->currentframe; + return t; } rsurfacestate_t rsurface; @@ -10010,77 +10115,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) { - 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->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->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++) + { + // 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->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); + } } } @@ -10570,7 +10678,7 @@ 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) @@ -11532,7 +11640,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; @@ -11612,7 +11720,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; @@ -11705,7 +11813,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; @@ -11866,7 +11974,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]; @@ -11902,7 +12010,7 @@ typedef struct r_decalsystem_splatqueue_s float color[4]; float tcrange[4]; float worldsize; - int decalsequence; + unsigned int decalsequence; } r_decalsystem_splatqueue_t; @@ -11941,7 +12049,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; @@ -11958,7 +12066,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) @@ -11973,7 +12081,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) @@ -12178,10 +12286,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; @@ -12313,6 +12420,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); @@ -12632,6 +12741,7 @@ 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;