+static void R_Viewport_ApplyNearClipPlane(r_viewport_t *v, double normalx, double normaly, double normalz, double dist)
+{
+ double q[4];
+ double d;
+ float clipPlane[4], v3[3], v4[3];
+ float normal[3];
+
+ // This is inspired by Oblique Depth Projection from http://www.terathon.com/code/oblique.php
+
+ VectorSet(normal, normalx, normaly, normalz);
+ Matrix4x4_Transform3x3(&v->viewmatrix, normal, clipPlane);
+ VectorScale(normal, dist, v3);
+ Matrix4x4_Transform(&v->viewmatrix, v3, v4);
+ // FIXME: LordHavoc: I think this can be done more efficiently somehow but I can't remember the technique
+ clipPlane[3] = -DotProduct(v4, clipPlane);
+
+#if 0
+{
+ // testing code for comparing results
+ float clipPlane2[4];
+ VectorCopy4(clipPlane, clipPlane2);
+ R_Mesh_Matrix(&identitymatrix);
+ VectorSet(q, normal[0], normal[1], normal[2], -dist);
+ qglClipPlane(GL_CLIP_PLANE0, q);
+ qglGetClipPlane(GL_CLIP_PLANE0, q);
+ VectorCopy4(q, clipPlane);
+}
+#endif
+
+ // Calculate the clip-space corner point opposite the clipping plane
+ // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
+ // transform it into camera space by multiplying it
+ // by the inverse of the projection matrix
+ q[0] = ((clipPlane[0] < 0.0f ? -1.0f : clipPlane[0] > 0.0f ? 1.0f : 0.0f) + v->m[8]) / v->m[0];
+ q[1] = ((clipPlane[1] < 0.0f ? -1.0f : clipPlane[1] > 0.0f ? 1.0f : 0.0f) + v->m[9]) / v->m[5];
+ q[2] = -1.0f;
+ q[3] = (1.0f + v->m[10]) / v->m[14];
+
+ // Calculate the scaled plane vector
+ d = 2.0f / DotProduct4(clipPlane, q);
+
+ // Replace the third row of the projection matrix
+ v->m[2] = clipPlane[0] * d;
+ v->m[6] = clipPlane[1] * d;
+ v->m[10] = clipPlane[2] * d + 1.0f;
+ v->m[14] = clipPlane[3] * d;
+}
+
+void R_Viewport_InitOrtho(r_viewport_t *v, const matrix4x4_t *cameramatrix, int x, int y, int width, int height, double x1, double y1, double x2, double y2, double nearclip, double farclip, const double *nearplane)
+{
+ float left = x1, right = x2, bottom = y2, top = y1, zNear = nearclip, zFar = farclip;
+ memset(v, 0, sizeof(*v));
+ v->type = R_VIEWPORTTYPE_ORTHO;
+ v->cameramatrix = *cameramatrix;
+ v->x = x;
+ v->y = y;
+ v->z = 0;
+ v->width = width;
+ v->height = height;
+ v->depth = 1;
+ v->m[0] = 2/(right - left);
+ v->m[5] = 2/(top - bottom);
+ v->m[10] = -2/(zFar - zNear);
+ v->m[12] = - (right + left)/(right - left);
+ v->m[13] = - (top + bottom)/(top - bottom);
+ v->m[14] = - (zFar + zNear)/(zFar - zNear);
+ v->m[15] = 1;
+
+ Matrix4x4_Invert_Full(&v->viewmatrix, &v->cameramatrix);
+ Matrix4x4_FromArrayDoubleGL(&v->projectmatrix, v->m);
+
+ if (nearplane)
+ R_Viewport_ApplyNearClipPlane(v, nearplane[0], nearplane[1], nearplane[2], nearplane[3]);
+
+#if 0
+ {
+ vec4_t test1;
+ vec4_t test2;
+ Vector4Set(test1, (x1+x2)*0.5f, (y1+y2)*0.5f, 0.0f, 1.0f);
+ R_Viewport_TransformToScreen(v, test1, test2);
+ Con_Printf("%f %f %f -> %f %f %f\n", test1[0], test1[1], test1[2], test2[0], test2[1], test2[2]);
+ }
+#endif
+}
+
+void R_Viewport_InitPerspective(r_viewport_t *v, const matrix4x4_t *cameramatrix, int x, int y, int width, int height, double frustumx, double frustumy, double nearclip, double farclip, const double *nearplane)