* clip-space: after applying the projection matrix.
* ndc: after division by w
* NDC cube: the range of x-coordinate from [l, r] to [-1, 1], the y-coordinate from [b, t] to [-1, 1] and the z-coordinate from [n, f] to [-1, 1].
*
* Note/Todo: I read that in screen space, OpenGL transform the z-values again to be between 0 and 1?
*
* In contrast to OGL, this renderer doesn't have state, it's just a function that gets called with all
* necessary parameters. It's easiest for our purposes.
*
* Here's the whole rendering pipeline:
* Model space
* -> model transforms
* World space
* -> camera (view/eye) transform
* View / eye / camera space ("truncated pyramid frustum". In case of ortho, it's already rectangular.)
* -> perspective/ortho projection
* Clip coords (x_c, y_c, z_c, w_c); the z-axis is flipped now. z [z=-n, z=-f] is mapped to [-1, +1] in case of ortho, but not yet in case of persp (it's also flipped though), but the not-[-1,1]-range is fine as we test against w_c. I.e. the larger the z-value, the further back we are.
* Do frustum culling (clipping) here. Test the clip-coords with w_c, and discard if a tri is completely outside.
* Of the partially visible tris, clip them against the near-plane and construct the visible part of the triangle.
* We only do this for the near-plane here. Clipping to the near plane must be done here because after w-division triangles crossing it would get distorted.
* "Then, OpenGL will reconstruct the edges of the polygon where clipping occurs."
* -> Then divide by the w component of the clip coordinates
* NDC. (now only 3D vectors: [x_ndc, y_ndc, z_ndc]). nearest points have z=-1, points on far plane have z=+1.
* -> window transform. (also, OGL does some more to the z-buffer?)
* Screen / window space
* Directly after window-transform (still processing triangles), do backface culling with areVerticesCCWInScreenSpace()
* Directly afterwards we calculate the triangle's bounding box and clip x/y (screen) against 0 and the viewport width/height.
* Rasterising: Clipping against the far plane here by only drawing those pixels with a z-value of <= 1.0f.
*
* OGL: "both clipping (frustum culling) and NDC transformations are integrated into GL_PROJECTION matrix"
*
* Note: In both the ortho and persp case, points at z=-n end up at -1, z=-f at +1. In case of persp proj., this happens only after the divide by w.
*/
/**
* Renders the given mesh onto a 2D image using 4x4 model-view and
* projection matrices. Conforms to OpenGL conventions.
*
* @param[in] mesh A 3D mesh.
* @param[in] model_view_matrix A 4x4 OpenGL model-view matrix.
* @param[in] projection_matrix A 4x4 orthographic or perspective OpenGL projection matrix.
* @param[in] viewport_width Screen width.
* @param[in] viewport_height Screen height.
* @param[in] texture An optional texture map (TODO: Not optional yet!).
* @param[in] enable_backface_culling Whether the renderer should perform backface culling. If true, only draw triangles with vertices ordered CCW in screen-space.
* @param[in] enable_near_clipping Screen height.
* @param[in] enable_far_clipping Screen height.
* @return A pair with the colourbuffer as its first element and the depthbuffer as the second element.
// Some internal documentation / old todos or notes:
// maybe change and pass depthBuffer as an optional arg (&?), because usually we never need it outside the renderer. Or maybe even a getDepthBuffer().
// modelViewMatrix goes to eye-space (camera space), projection does ortho or perspective proj.
// bool enable_texturing = false; Maybe re-add later, not sure
// take a cv::Mat texture instead and convert to Texture internally? no, we don't want to recreate mipmap levels on each render() call.
assert(mesh.vertices.size()==mesh.colors.size()||mesh.colors.empty());// The number of vertices has to be equal for both shape and colour, or, alternatively, it has to be a shape-only model.
assert(mesh.vertices.size()==mesh.texcoords.size()||mesh.texcoords.empty());// same for the texcoords
// another assert: If cv::Mat texture != empty, then we need texcoords?
usingcv::Mat;
usingstd::vector;
Matcolourbuffer=Mat::zeros(viewport_height,viewport_width,CV_8UC4);// make sure it's CV_8UC4?
// Todo: Split this whole stuff up. Make a "clip" function, ... rename "processProspective..".. what is "process"... get rid of "continue;"-stuff by moving stuff inside process...
// classify vertices visibility with respect to the planes of the view frustum
// we're in clip-coords (NDC), so just check if outside [-1, 1] x ...
// Actually we're in clip-coords and it's not the same as NDC. We're only in NDC after the division by w.
// We should do the clipping in clip-coords though. See http://www.songho.ca/opengl/gl_projectionmatrix.html for more details.
// However, when comparing against w_c below, we might run into the trouble of the sign again in the affine case.
// 'w' is always positive, as it is -z_camspace, and all z_camspace are negative.
// split the triangle if it intersects the near plane:
if(enable_near_clipping)
{
vertices=detail::clip_polygon_to_plane_in_4d(vertices,cv::Vec4f(0.0f,0.0f,-1.0f,-1.0f));// "Normal" (or "4D hyperplane") of the near-plane. I tested it and it works like this but I'm a little bit unsure because Songho says the normal of the near-plane is (0,0,-1,1) (maybe I have to switch around the < 0 checks in the function?)
}
// triangulation of the polygon formed of vertices array