+extern cvar_t r_shadows_focus;
+extern cvar_t r_shadows_shadowmapscale;
+
+void R_Shadow_PrepareModelShadows(void)
+{
+ int i;
+ float scale, size, radius, dot1, dot2;
+ vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus, shadowmins, shadowmaxs;
+ entity_render_t *ent;
+
+ if (!r_refdef.scene.numentities)
+ return;
+
+ switch (r_shadow_shadowmode)
+ {
+ case R_SHADOW_SHADOWMODE_SHADOWMAP2D:
+ if (r_shadows.integer >= 2)
+ break;
+ // fall through
+ case R_SHADOW_SHADOWMODE_STENCIL:
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ {
+ ent = r_refdef.scene.entities[i];
+ if (!ent->animcache_vertex3f && ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+ R_AnimCache_GetEntity(ent, false, false);
+ }
+ return;
+ default:
+ return;
+ }
+
+ size = 2*r_shadow_shadowmapmaxsize;
+ scale = r_shadow_shadowmapping_precision.value * r_shadows_shadowmapscale.value;
+ radius = 0.5f * size / scale;
+
+ Math_atov(r_shadows_throwdirection.string, shadowdir);
+ VectorNormalize(shadowdir);
+ dot1 = DotProduct(r_refdef.view.forward, shadowdir);
+ dot2 = DotProduct(r_refdef.view.up, shadowdir);
+ if (fabs(dot1) <= fabs(dot2))
+ VectorMA(r_refdef.view.forward, -dot1, shadowdir, shadowforward);
+ else
+ VectorMA(r_refdef.view.up, -dot2, shadowdir, shadowforward);
+ VectorNormalize(shadowforward);
+ CrossProduct(shadowdir, shadowforward, shadowright);
+ Math_atov(r_shadows_focus.string, shadowfocus);
+ VectorM(shadowfocus[0], r_refdef.view.right, shadoworigin);
+ VectorMA(shadoworigin, shadowfocus[1], r_refdef.view.up, shadoworigin);
+ VectorMA(shadoworigin, -shadowfocus[2], r_refdef.view.forward, shadoworigin);
+ VectorAdd(shadoworigin, r_refdef.view.origin, shadoworigin);
+ if (shadowfocus[0] || shadowfocus[1] || shadowfocus[2])
+ dot1 = 1;
+ VectorMA(shadoworigin, (1.0f - fabs(dot1)) * radius, shadowforward, shadoworigin);
+
+ shadowmins[0] = shadoworigin[0] - r_shadows_throwdistance.value * fabs(shadowdir[0]) - radius * (fabs(shadowforward[0]) + fabs(shadowright[0]));
+ shadowmins[1] = shadoworigin[1] - r_shadows_throwdistance.value * fabs(shadowdir[1]) - radius * (fabs(shadowforward[1]) + fabs(shadowright[1]));
+ shadowmins[2] = shadoworigin[2] - r_shadows_throwdistance.value * fabs(shadowdir[2]) - radius * (fabs(shadowforward[2]) + fabs(shadowright[2]));
+ shadowmaxs[0] = shadoworigin[0] + r_shadows_throwdistance.value * fabs(shadowdir[0]) + radius * (fabs(shadowforward[0]) + fabs(shadowright[0]));
+ shadowmaxs[1] = shadoworigin[1] + r_shadows_throwdistance.value * fabs(shadowdir[1]) + radius * (fabs(shadowforward[1]) + fabs(shadowright[1]));
+ shadowmaxs[2] = shadoworigin[2] + r_shadows_throwdistance.value * fabs(shadowdir[2]) + radius * (fabs(shadowforward[2]) + fabs(shadowright[2]));
+
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ {
+ ent = r_refdef.scene.entities[i];
+ if (!BoxesOverlap(ent->mins, ent->maxs, shadowmins, shadowmaxs))
+ continue;
+ // cast shadows from anything of the map (submodels are optional)
+ if (!ent->animcache_vertex3f && ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+ R_AnimCache_GetEntity(ent, false, false);
+ }
+}
+
+void R_DrawModelShadowMaps(void)
+{
+ int i;
+ float relativethrowdistance, scale, size, radius, nearclip, farclip, bias, dot1, dot2;
+ entity_render_t *ent;
+ vec3_t relativelightorigin;
+ vec3_t relativelightdirection, relativeforward, relativeright;
+ vec3_t relativeshadowmins, relativeshadowmaxs;
+ vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus;
+ float m[12];
+ matrix4x4_t shadowmatrix, cameramatrix, mvpmatrix, invmvpmatrix, scalematrix, texmatrix;
+ r_viewport_t viewport;
+ GLuint fbo = 0;
+ float clearcolor[4];
+
+ if (!r_refdef.scene.numentities)
+ return;
+
+ switch (r_shadow_shadowmode)
+ {
+ case R_SHADOW_SHADOWMODE_SHADOWMAP2D:
+ break;
+ default:
+ return;
+ }
+
+ R_ResetViewRendering3D();
+ R_Shadow_RenderMode_Begin();
+ R_Shadow_RenderMode_ActiveLight(NULL);
+
+ switch (r_shadow_shadowmode)
+ {
+ case R_SHADOW_SHADOWMODE_SHADOWMAP2D:
+ if (!r_shadow_shadowmap2dtexture)
+ R_Shadow_MakeShadowMap(0, r_shadow_shadowmapmaxsize);
+ fbo = r_shadow_fbo2d;
+ r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmap2dtexture);
+ r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureHeight(r_shadow_shadowmap2dtexture);
+ r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAP2D;
+ break;
+ default:
+ break;
+ }
+
+ size = 2*r_shadow_shadowmapmaxsize;
+ scale = (r_shadow_shadowmapping_precision.value * r_shadows_shadowmapscale.value) / size;
+ radius = 0.5f / scale;
+ nearclip = -r_shadows_throwdistance.value;
+ farclip = r_shadows_throwdistance.value;
+ bias = r_shadow_shadowmapping_bias.value * r_shadow_shadowmapping_nearclip.value / (2 * r_shadows_throwdistance.value) * (1024.0f / size);
+
+ r_shadow_shadowmap_parameters[0] = size;
+ r_shadow_shadowmap_parameters[1] = size;
+ r_shadow_shadowmap_parameters[2] = 1.0;
+ r_shadow_shadowmap_parameters[3] = bound(0.0f, 1.0f - r_shadows_darken.value, 1.0f);
+
+ Math_atov(r_shadows_throwdirection.string, shadowdir);
+ VectorNormalize(shadowdir);
+ Math_atov(r_shadows_focus.string, shadowfocus);
+ VectorM(shadowfocus[0], r_refdef.view.right, shadoworigin);
+ VectorMA(shadoworigin, shadowfocus[1], r_refdef.view.up, shadoworigin);
+ VectorMA(shadoworigin, -shadowfocus[2], r_refdef.view.forward, shadoworigin);
+ VectorAdd(shadoworigin, r_refdef.view.origin, shadoworigin);
+ dot1 = DotProduct(r_refdef.view.forward, shadowdir);
+ dot2 = DotProduct(r_refdef.view.up, shadowdir);
+ if (fabs(dot1) <= fabs(dot2))
+ VectorMA(r_refdef.view.forward, -dot1, shadowdir, shadowforward);
+ else
+ VectorMA(r_refdef.view.up, -dot2, shadowdir, shadowforward);
+ VectorNormalize(shadowforward);
+ VectorM(scale, shadowforward, &m[0]);
+ if (shadowfocus[0] || shadowfocus[1] || shadowfocus[2])
+ dot1 = 1;
+ m[3] = fabs(dot1) * 0.5f - DotProduct(shadoworigin, &m[0]);
+ CrossProduct(shadowdir, shadowforward, shadowright);
+ VectorM(scale, shadowright, &m[4]);
+ m[7] = 0.5f - DotProduct(shadoworigin, &m[4]);
+ VectorM(1.0f / (farclip - nearclip), shadowdir, &m[8]);
+ m[11] = 0.5f - DotProduct(shadoworigin, &m[8]);
+ Matrix4x4_FromArray12FloatD3D(&shadowmatrix, m);
+ Matrix4x4_Invert_Full(&cameramatrix, &shadowmatrix);
+ R_Viewport_InitOrtho(&viewport, &cameramatrix, 0, 0, size, size, 0, 0, 1, 1, 0, -1, NULL);
+
+ VectorMA(shadoworigin, (1.0f - fabs(dot1)) * radius, shadowforward, shadoworigin);
+
+ R_Mesh_SetRenderTargets(fbo, r_shadow_shadowmap2dtexture, r_shadow_shadowmap2dcolortexture, NULL, NULL, NULL);
+ R_SetupShader_DepthOrShadow();
+ GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value);
+ GL_DepthMask(true);
+ GL_DepthTest(true);
+ R_SetViewport(&viewport);
+ GL_Scissor(viewport.x, viewport.y, min(viewport.width + r_shadow_shadowmapborder, 2*r_shadow_shadowmapmaxsize), viewport.height + r_shadow_shadowmapborder);
+ Vector4Set(clearcolor, 1,1,1,1);
+ // in D3D9 we have to render to a color texture shadowmap
+ // in GL we render directly to a depth texture only
+ if (r_shadow_shadowmap2dtexture)
+ GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
+ else
+ GL_Clear(GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
+ // render into a slightly restricted region so that the borders of the
+ // shadowmap area fade away, rather than streaking across everything
+ // outside the usable area
+ GL_Scissor(viewport.x + r_shadow_shadowmapborder, viewport.y + r_shadow_shadowmapborder, viewport.width - 2*r_shadow_shadowmapborder, viewport.height - 2*r_shadow_shadowmapborder);
+
+#if 0
+ // debugging
+ R_Mesh_ResetRenderTargets();
+ R_SetupShader_ShowDepth();
+ GL_ColorMask(1,1,1,1);
+ GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
+#endif
+
+ for (i = 0;i < r_refdef.scene.numentities;i++)
+ {
+ ent = r_refdef.scene.entities[i];
+
+ // cast shadows from anything of the map (submodels are optional)
+ if (ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+ {
+ relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
+ Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativelightorigin);
+ Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection);
+ Matrix4x4_Transform3x3(&ent->inversematrix, shadowforward, relativeforward);
+ Matrix4x4_Transform3x3(&ent->inversematrix, shadowright, relativeright);
+ relativeshadowmins[0] = relativelightorigin[0] - r_shadows_throwdistance.value * fabs(relativelightdirection[0]) - radius * (fabs(relativeforward[0]) + fabs(relativeright[0]));
+ relativeshadowmins[1] = relativelightorigin[1] - r_shadows_throwdistance.value * fabs(relativelightdirection[1]) - radius * (fabs(relativeforward[1]) + fabs(relativeright[1]));
+ relativeshadowmins[2] = relativelightorigin[2] - r_shadows_throwdistance.value * fabs(relativelightdirection[2]) - radius * (fabs(relativeforward[2]) + fabs(relativeright[2]));
+ relativeshadowmaxs[0] = relativelightorigin[0] + r_shadows_throwdistance.value * fabs(relativelightdirection[0]) + radius * (fabs(relativeforward[0]) + fabs(relativeright[0]));
+ relativeshadowmaxs[1] = relativelightorigin[1] + r_shadows_throwdistance.value * fabs(relativelightdirection[1]) + radius * (fabs(relativeforward[1]) + fabs(relativeright[1]));
+ relativeshadowmaxs[2] = relativelightorigin[2] + r_shadows_throwdistance.value * fabs(relativelightdirection[2]) + radius * (fabs(relativeforward[2]) + fabs(relativeright[2]));
+ RSurf_ActiveModelEntity(ent, false, false, false);
+ ent->model->DrawShadowMap(0, ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, NULL, relativeshadowmins, relativeshadowmaxs);
+ rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
+ }
+ }
+
+#if 0
+ if (r_test.integer)
+ {
+ unsigned char *rawpixels = Z_Malloc(viewport.width*viewport.height*4);
+ CHECKGLERROR
+ qglReadPixels(viewport.x, viewport.y, viewport.width, viewport.height, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, rawpixels);
+ CHECKGLERROR
+ Image_WriteTGABGRA("r_shadows_2.tga", viewport.width, viewport.height, rawpixels);
+ Cvar_SetValueQuick(&r_test, 0);
+ Z_Free(rawpixels);
+ }
+#endif
+
+ R_Shadow_RenderMode_End();
+
+ Matrix4x4_Concat(&mvpmatrix, &r_refdef.view.viewport.projectmatrix, &r_refdef.view.viewport.viewmatrix);
+ Matrix4x4_Invert_Full(&invmvpmatrix, &mvpmatrix);
+ Matrix4x4_CreateScale3(&scalematrix, size, -size, 1);
+ Matrix4x4_AdjustOrigin(&scalematrix, 0, size, -0.5f * bias);
+ Matrix4x4_Concat(&texmatrix, &scalematrix, &shadowmatrix);
+ Matrix4x4_Concat(&r_shadow_shadowmapmatrix, &texmatrix, &invmvpmatrix);
+
+ switch (vid.renderpath)
+ {
+ case RENDERPATH_GL11:
+ case RENDERPATH_GL13:
+ case RENDERPATH_GL20:
+ case RENDERPATH_CGGL:
+ break;
+ case RENDERPATH_D3D9:
+ case RENDERPATH_D3D10:
+ case RENDERPATH_D3D11:
+#ifdef OPENGL_ORIENTATION
+ r_shadow_shadowmapmatrix.m[0][0] *= -1.0f;
+ r_shadow_shadowmapmatrix.m[0][1] *= -1.0f;
+ r_shadow_shadowmapmatrix.m[0][2] *= -1.0f;
+ r_shadow_shadowmapmatrix.m[0][3] *= -1.0f;
+#else
+ r_shadow_shadowmapmatrix.m[0][0] *= -1.0f;
+ r_shadow_shadowmapmatrix.m[1][0] *= -1.0f;
+ r_shadow_shadowmapmatrix.m[2][0] *= -1.0f;
+ r_shadow_shadowmapmatrix.m[3][0] *= -1.0f;
+#endif
+ break;
+ }
+
+ r_shadow_usingshadowmaportho = true;
+ switch (r_shadow_shadowmode)
+ {
+ case R_SHADOW_SHADOWMODE_SHADOWMAP2D:
+ r_shadow_usingshadowmap2d = true;
+ break;
+ default:
+ break;
+ }
+}
+