+void R_RenderScene(qboolean addwaterplanes);
+
+static void R_Water_StartFrame(void)
+{
+ int i;
+ int texturewidth, textureheight;
+ r_waterstate_waterplane_t *p;
+
+ r_waterstate.maxwaterplanes = 0;
+
+ // set waterwidth and waterheight to the water resolution that will be
+ // used (often less than the screen resolution for faster rendering)
+ r_waterstate.waterwidth = (int)bound(1, r_view.width * r_glsl_water_resolutionmultiplier.value, r_view.width);
+ r_waterstate.waterheight = (int)bound(1, r_view.height * r_glsl_water_resolutionmultiplier.value, r_view.height);
+
+ // calculate desired texture sizes
+ if (gl_support_arb_texture_non_power_of_two)
+ {
+ texturewidth = r_waterstate.waterwidth;
+ textureheight = r_waterstate.waterheight;
+ }
+ else
+ {
+ for (texturewidth = 1;texturewidth < r_waterstate.waterwidth ;texturewidth *= 2);
+ for (textureheight = 1;textureheight < r_waterstate.waterheight;textureheight *= 2);
+ }
+
+ if (!r_glsl_water.integer)
+ texturewidth = textureheight = 0;
+
+ // allocate textures as needed
+ if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
+ {
+ for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
+ {
+ if (p->texture_refraction)
+ R_FreeTexture(p->texture_refraction);
+ p->texture_refraction = NULL;
+ if (p->texture_reflection)
+ R_FreeTexture(p->texture_reflection);
+ p->texture_reflection = NULL;
+ }
+ r_waterstate.texturewidth = texturewidth;
+ r_waterstate.textureheight = textureheight;
+ }
+
+ if ((!texturewidth && !textureheight) || texturewidth > gl_max_texture_size || textureheight > gl_max_texture_size)
+ {
+ // can't use water if the parameters are too weird
+ // can't use water if the card does not support the texture size
+ memset(&r_waterstate, 0, sizeof(r_waterstate));
+ return;
+ }
+
+ r_waterstate.enabled = true;
+
+ r_waterstate.maxwaterplanes = MAX_WATERPLANES;
+
+ // set up variables that will be used in shader setup
+ r_waterstate.screenscale[0] = 0.5f * (float)r_waterstate.waterwidth / (float)r_waterstate.texturewidth;
+ r_waterstate.screenscale[1] = 0.5f * (float)r_waterstate.waterheight / (float)r_waterstate.textureheight;
+ r_waterstate.screencenter[0] = 0.5f * (float)r_waterstate.waterwidth / (float)r_waterstate.texturewidth;
+ r_waterstate.screencenter[1] = 0.5f * (float)r_waterstate.waterheight / (float)r_waterstate.textureheight;
+}
+
+static void R_Water_AddWaterPlane(msurface_t *surface)
+{
+ int triangleindex, planeindex;
+ const int *e;
+ vec3_t vert[3];
+ vec3_t normal;
+ r_waterstate_waterplane_t *p;
+ // 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;
+ }
+ for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+ if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
+ break;
+ // if this triangle does not fit any known plane rendered this frame, render textures for it
+ if (planeindex >= r_waterstate.numwaterplanes && planeindex < r_waterstate.maxwaterplanes)
+ {
+ // store the new plane
+ r_waterstate.numwaterplanes++;
+ VectorCopy(normal, p->plane.normal);
+ VectorNormalize(p->plane.normal);
+ p->plane.dist = DotProduct(vert[0], p->plane.normal);
+ PlaneClassify(&p->plane);
+ // flip the plane if it does not face the viewer
+ if (PlaneDiff(r_view.origin, &p->plane) < 0)
+ {
+ VectorNegate(p->plane.normal, p->plane.normal);
+ p->plane.dist *= -1;
+ PlaneClassify(&p->plane);
+ }
+ }
+}
+
+static void R_Water_ProcessPlanes(void)
+{
+ r_view_t originalview;
+ int planeindex;
+ r_waterstate_waterplane_t *p;
+
+ for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+ {
+ if (!p->texture_refraction)
+ {
+ p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ }
+
+ originalview = r_view;
+ r_view.showdebug = false;
+ r_view.width = r_waterstate.waterwidth;
+ r_view.height = r_waterstate.waterheight;
+ r_view.useclipplane = true;
+
+ r_view.clipplane = p->plane;
+ VectorNegate(r_view.clipplane.normal, r_view.clipplane.normal);
+ r_view.clipplane.dist = -r_view.clipplane.dist;
+ PlaneClassify(&r_view.clipplane);
+ r_waterstate.renderingscene = true;
+ // render the normal view scene and copy into texture
+ // (except that a clipping plane should be used to hide everything on one side of the water, and the viewer's weapon model should be omitted)
+ R_RenderScene(false);
+
+ // copy view into the screen texture
+ R_Mesh_TexBind(0, R_GetTexture(p->texture_refraction));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+
+ // render reflected scene and copy into texture
+ Matrix4x4_Reflect(&r_view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2);
+ r_view.clipplane = p->plane;
+ // reverse the cullface settings for this render
+ r_view.cullface_front = GL_FRONT;
+ r_view.cullface_back = GL_BACK;
+
+ R_ResetViewRendering3D();
+ R_ClearScreen();
+
+ R_RenderScene(false);
+
+ R_Mesh_TexBind(0, R_GetTexture(p->texture_reflection));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+
+ R_ResetViewRendering3D();
+ R_ClearScreen();
+
+ r_view = originalview;
+ r_waterstate.renderingscene = false;
+ }
+}