]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
implement digest_hex()
[xonotic/darkplaces.git] / gl_rmain.c
index eba8d7ad22f47c109df56e6707da3aa15f1ea0b3..a7116cfee4fccd20be42f70c790441edde154a3c 100644 (file)
@@ -375,7 +375,7 @@ static void R_BuildBlankTextures(void)
        data[2] = 128; // normal X
        data[1] = 128; // normal Y
        data[0] = 255; // normal Z
-       data[3] = 128; // height
+       data[3] = 255; // height
        r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 255;
        data[1] = 255;
@@ -828,6 +828,7 @@ typedef struct r_glsl_permutation_s
        int loc_LightPosition;
        int loc_OffsetMapping_ScaleSteps;
        int loc_OffsetMapping_LodDistance;
+       int loc_OffsetMapping_Bias;
        int loc_PixelSize;
        int loc_ReflectColor;
        int loc_ReflectFactor;
@@ -1150,6 +1151,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                p->loc_LightPosition              = qglGetUniformLocation(p->program, "LightPosition");
                p->loc_OffsetMapping_ScaleSteps   = qglGetUniformLocation(p->program, "OffsetMapping_ScaleSteps");
                p->loc_OffsetMapping_LodDistance  = qglGetUniformLocation(p->program, "OffsetMapping_LodDistance");
+               p->loc_OffsetMapping_Bias         = qglGetUniformLocation(p->program, "OffsetMapping_Bias");
                p->loc_PixelSize                  = qglGetUniformLocation(p->program, "PixelSize");
                p->loc_ReflectColor               = qglGetUniformLocation(p->program, "ReflectColor");
                p->loc_ReflectFactor              = qglGetUniformLocation(p->program, "ReflectFactor");
@@ -1390,6 +1392,7 @@ typedef enum D3DPSREGISTER_e
        D3DPSREGISTER_ModelToReflectCube = 48, // float4x4
        D3DPSREGISTER_NormalmapScrollBlend = 52,
        D3DPSREGISTER_OffsetMapping_LodDistance = 53,
+       D3DPSREGISTER_OffsetMapping_Bias = 54,
        // next at 54
 }
 D3DPSREGISTER_t;
@@ -1550,7 +1553,7 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c
                                if (vslog)
                                {
                                        strlcpy(temp, (const char *)vslog->GetBufferPointer(), min(sizeof(temp), vslog->GetBufferSize()));
-                                       Con_Printf("HLSL vertex shader compile output for %s follows:\n%s\n", cachename, temp);
+                                       Con_DPrintf("HLSL vertex shader compile output for %s follows:\n%s\n", cachename, temp);
                                        vslog->Release();
                                }
                        }
@@ -1575,23 +1578,23 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c
                                if (pslog)
                                {
                                        strlcpy(temp, (const char *)pslog->GetBufferPointer(), min(sizeof(temp), pslog->GetBufferSize()));
-                                       Con_Printf("HLSL pixel shader compile output for %s follows:\n%s\n", cachename, temp);
+                                       Con_DPrintf("HLSL pixel shader compile output for %s follows:\n%s\n", cachename, temp);
                                        pslog->Release();
                                }
                        }
                        Sys_UnloadLibrary(&d3dx9_dll);
                }
                else
-                       Con_Printf("Unable to compile shader - D3DXCompileShader function not found\n");
+                       Con_DPrintf("Unable to compile shader - D3DXCompileShader function not found\n");
        }
        if (vsbin && psbin)
        {
                vsresult = IDirect3DDevice9_CreateVertexShader(vid_d3d9dev, vsbin, &p->vertexshader);
                if (FAILED(vsresult))
-                       Con_Printf("HLSL CreateVertexShader failed for %s (hresult = %8x)\n", cachename, vsresult);
+                       Con_DPrintf("HLSL CreateVertexShader failed for %s (hresult = %8x)\n", cachename, vsresult);
                psresult = IDirect3DDevice9_CreatePixelShader(vid_d3d9dev, psbin, &p->pixelshader);
                if (FAILED(psresult))
-                       Con_Printf("HLSL CreatePixelShader failed for %s (hresult = %8x)\n", cachename, psresult);
+                       Con_DPrintf("HLSL CreatePixelShader failed for %s (hresult = %8x)\n", cachename, psresult);
        }
        // free the shader data
        vsbin = (DWORD *)Mem_Realloc(tempmempool, vsbin, 0);
@@ -2181,7 +2184,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurfacepass == RSURFPASS_DEFERREDGEOMETRY)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2202,7 +2205,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurfacepass == RSURFPASS_RTLIGHT)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2248,7 +2251,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2301,7 +2304,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT_DIRECTIONAL)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2364,7 +2367,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2424,7 +2427,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2628,7 +2631,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer),
                                max(1, r_glsl_offsetmapping_reliefmapping_refinesteps.integer)
                        );
-               hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer)
+               hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer);
+               hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_Bias, rsurface.texture->offsetbias);
                hlslPSSetParameter2f(D3DPSREGISTER_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);
 
@@ -2790,6 +2794,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                max(1, r_glsl_offsetmapping_reliefmapping_refinesteps.integer)
                        );
                if (r_glsl_permutation->loc_OffsetMapping_LodDistance >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer);
+               if (r_glsl_permutation->loc_OffsetMapping_Bias >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_Bias, rsurface.texture->offsetbias);
                if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2f(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
                if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegridmatrix, &rsurface.matrix);Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BounceGridMatrix, 1, false, m16f);}
@@ -2935,6 +2940,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer),
                                max(1, r_glsl_offsetmapping_reliefmapping_refinesteps.integer)
                        );
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_OffsetMapping_Bias, rsurface.texture->offsetbias);
                DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
 
@@ -3453,7 +3460,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        mymiplevel = savemiplevel;
        if (skinframe->gloss == NULL && r_loadgloss && (pixels = loadimagepixelsbgra(va("%s_gloss", skinframe->basename), false, false, false, &mymiplevel)))
        {
-               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags & (gl_texturecompression_gloss.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, (TEXF_ALPHA | textureflags) & (gl_texturecompression_gloss.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
 #ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->gloss)
                        R_SaveTextureDDSFile(skinframe->gloss, va("dds/%s_gloss.dds", skinframe->basename), r_texture_dds_save.integer < 2, true);
@@ -4794,8 +4801,8 @@ static void R_View_UpdateEntityLighting (void)
        {
                ent = r_refdef.scene.entities[i];
 
-               // skip unseen models
-               if (!r_refdef.viewcache.entityvisible[i] && skipunseen)
+               // skip unseen models and models that updated by CSQC
+               if ((!r_refdef.viewcache.entityvisible[i] && skipunseen) || ent->flags & RENDER_CUSTOMIZEDMODELLIGHT)
                        continue;
 
                // skip bsp models
@@ -4951,7 +4958,7 @@ static void R_View_UpdateEntityVisible (void)
                        ent = r_refdef.scene.entities[i];
                        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)))
-                       if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
+                       if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_WORLDOBJECT | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
                                r_refdef.viewcache.entityvisible[i] = true;
                }
        }
@@ -4972,7 +4979,7 @@ static void R_View_UpdateEntityVisible (void)
                        if (!r_refdef.viewcache.entityvisible[i])
                                continue;
                        ent = r_refdef.scene.entities[i];
-                       if(!(ent->flags & (RENDER_VIEWMODEL | RENDER_NOCULL | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*')))
+                       if(!(ent->flags & (RENDER_VIEWMODEL | RENDER_WORLDOBJECT | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*')))
                        {
                                samples = ent->entitynumber ? r_cullentities_trace_samples.integer : r_cullentities_trace_tempentitysamples.integer;
                                if (samples < 0)
@@ -5318,6 +5325,7 @@ void R_View_UpdateWithScissor(const int *myscissor)
        R_View_WorldVisibility(r_refdef.view.useclipplane);
        R_View_UpdateEntityVisible();
        R_View_UpdateEntityLighting();
+       R_AnimCache_CacheVisibleEntities();
 }
 
 void R_View_Update(void)
@@ -5327,6 +5335,7 @@ 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;
@@ -5619,36 +5628,48 @@ static void R_Water_StartFrame(void)
 
 void R_Water_AddWaterPlane(msurface_t *surface, int entno)
 {
-       int triangleindex, planeindex;
-       const int *e;
-       vec3_t vert[3];
-       vec3_t normal;
-       vec3_t center;
+       int planeindex, bestplaneindex, vertexindex;
+       vec3_t mins, maxs, normal, center, v, n;
+       vec_t planescore, bestplanescore;
        mplane_t plane;
        r_waterstate_waterplane_t *p;
        texture_t *t = R_GetCurrentTexture(surface->texture);
 
-       // just use the first triangle with a valid normal for any decisions
-       VectorClear(normal);
-       for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
-       {
-               Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[0]*3, vert[0]);
-               Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[1]*3, vert[1]);
-               Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[2]*3, vert[2]);
-               TriangleNormal(vert[0], vert[1], vert[2], normal);
-               if (VectorLength2(normal) >= 0.001)
-                       break;
-       }
+       rsurface.texture = t;
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS, 1, ((const msurface_t **)&surface));
+       // if the model has no normals, it's probably off-screen and they were not generated, so don't add it anyway
+       if (!rsurface.batchnormal3f || rsurface.batchnumvertices < 1)
+               return;
+       // average the vertex normals, find the surface bounds (after deformvertexes)
+       Matrix4x4_Transform(&rsurface.matrix, rsurface.batchvertex3f, v);
+       Matrix4x4_Transform3x3(&rsurface.matrix, rsurface.batchnormal3f, n);
+       VectorCopy(n, normal);
+       VectorCopy(v, mins);
+       VectorCopy(v, maxs);
+       for (vertexindex = 1;vertexindex < rsurface.batchnumvertices;vertexindex++)
+       {
+               Matrix4x4_Transform(&rsurface.matrix, rsurface.batchvertex3f + vertexindex*3, v);
+               Matrix4x4_Transform3x3(&rsurface.matrix, rsurface.batchnormal3f + vertexindex*3, n);
+               VectorAdd(normal, n, normal);
+               mins[0] = min(mins[0], v[0]);
+               mins[1] = min(mins[1], v[1]);
+               mins[2] = min(mins[2], v[2]);
+               maxs[0] = max(maxs[0], v[0]);
+               maxs[1] = max(maxs[1], v[1]);
+               maxs[2] = max(maxs[2], v[2]);
+       }
+       VectorNormalize(normal);
+       VectorMAM(0.5f, mins, 0.5f, maxs, center);
 
        VectorCopy(normal, plane.normal);
        VectorNormalize(plane.normal);
-       plane.dist = DotProduct(vert[0], plane.normal);
+       plane.dist = DotProduct(center, plane.normal);
        PlaneClassify(&plane);
        if (PlaneDiff(r_refdef.view.origin, &plane) < 0)
        {
                // skip backfaces (except if nocullface is set)
-               if (!(t->currentmaterialflags & MATERIALFLAG_NOCULLFACE))
-                       return;
+//             if (!(t->currentmaterialflags & MATERIALFLAG_NOCULLFACE))
+//                     return;
                VectorNegate(plane.normal, plane.normal);
                plane.dist *= -1;
                PlaneClassify(&plane);
@@ -5656,42 +5677,52 @@ void R_Water_AddWaterPlane(msurface_t *surface, int entno)
 
 
        // find a matching plane if there is one
+       bestplaneindex = -1;
+       bestplanescore = 1048576.0f;
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+       {
                if(p->camera_entity == t->camera_entity)
-                       if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
-                               break;
-       if (planeindex >= r_waterstate.maxwaterplanes)
-               return; // nothing we can do, out of planes
+               {
+                       planescore = 1.0f - DotProduct(plane.normal, p->plane.normal) + fabs(plane.dist - p->plane.dist) * 0.001f;
+                       if (bestplaneindex < 0 || bestplanescore > planescore)
+                       {
+                               bestplaneindex = planeindex;
+                               bestplanescore = planescore;
+                       }
+               }
+       }
+       planeindex = bestplaneindex;
+       p = r_waterstate.waterplanes + planeindex;
 
-       // if this triangle does not fit any known plane rendered this frame, add one
-       if (planeindex >= r_waterstate.numwaterplanes)
+       // if this surface does not fit any known plane rendered this frame, add one
+       if ((planeindex < 0 || bestplanescore > 0.001f) && r_waterstate.numwaterplanes < r_waterstate.maxwaterplanes)
        {
                // store the new plane
-               r_waterstate.numwaterplanes++;
+               planeindex = r_waterstate.numwaterplanes++;
+               p = r_waterstate.waterplanes + planeindex;
                p->plane = plane;
                // clear materialflags and pvs
                p->materialflags = 0;
                p->pvsvalid = false;
                p->camera_entity = t->camera_entity;
-               VectorCopy(surface->mins, p->mins);
-               VectorCopy(surface->maxs, p->maxs);
+               VectorCopy(mins, p->mins);
+               VectorCopy(maxs, p->maxs);
        }
        else
        {
-               // merge mins/maxs
-               p->mins[0] = min(p->mins[0], surface->mins[0]);
-               p->mins[1] = min(p->mins[1], surface->mins[1]);
-               p->mins[2] = min(p->mins[2], surface->mins[2]);
-               p->maxs[0] = max(p->maxs[0], surface->maxs[0]);
-               p->maxs[1] = max(p->maxs[1], surface->maxs[1]);
-               p->maxs[2] = max(p->maxs[2], surface->maxs[2]);
+               // merge mins/maxs when we're adding this surface to the plane
+               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]);
+               p->maxs[0] = max(p->maxs[0], maxs[0]);
+               p->maxs[1] = max(p->maxs[1], maxs[1]);
+               p->maxs[2] = max(p->maxs[2], maxs[2]);
        }
        // merge this surface's materialflags into the waterplane
        p->materialflags |= t->currentmaterialflags;
        if(!(p->materialflags & MATERIALFLAG_CAMERA))
        {
                // merge this surface's PVS into the waterplane
-               VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA) && 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)
                {
@@ -7018,10 +7049,6 @@ void R_RenderScene(void)
                }
        }
 
-       R_AnimCache_CacheVisibleEntities();
-       if (r_timereport_active)
-               R_TimeReport("animation");
-
        R_Shadow_PrepareLights();
        if (r_shadows.integer > 0 && r_refdef.lightmapintensity > 0)
                R_Shadow_PrepareModelShadows();
@@ -7666,8 +7693,9 @@ void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *t
                        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, tcmod->parms[0] * rsurface.shadertime, 0, 0, 1);
+                       Matrix4x4_ConcatRotate(&matrix, (f / 360 - floor(f / 360)) * 360, 0, 0, 1);
                        Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
                        break;
                case Q3TCMOD_SCALE:
@@ -7826,6 +7854,9 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                t->currentmaterialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        else if (t->currentalpha < 1)
                t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
+       // LordHavoc: prevent bugs where code checks add or alpha at higher priority than customblend by clearing these flags
+       if (t->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
+               t->currentmaterialflags &= ~(MATERIALFLAG_ADD | MATERIALFLAG_ALPHA);
        if (rsurface.ent_flags & RENDER_DOUBLESIDED)
                t->currentmaterialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
        if (rsurface.ent_flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL))