#include "color.h" #include "engine.h" #include "geometry.h" #include "util.h" #include // MACROS #define DrawPixel(buffer, width, color, x, y)\ {\ buffer[width * y + x] = color;\ } // STRUCTURES struct BoundingBox { BoundingBox(Point &v0, Point &v1, Point &v2) { yMin = MIN(v0.y, MIN(v1.y, v2.y)); yMax = MAX(v0.y, MAX(v1.y, v2.y)); xMin = MIN(v0.x, MIN(v1.x, v2.x)); xMax = MAX(v0.x, MAX(v1.x, v2.x)); } float yMin, yMax; float xMin, xMax; }; // PUBLIC FUNCTIONS void CullBackfaces( Mesh_LocalData &local, Mesh_TransformedData &transformed, Point &camPosition) { transformed.faces.clear(); for (size_t f = 0; f < local.faces.size(); ++f) { unsigned int v0 = local.faces[f].vertIndex[0]; unsigned int v1 = local.faces[f].vertIndex[1]; unsigned int v2 = local.faces[f].vertIndex[2]; Vector v01 = transformed.verts[v1].point - transformed.verts[v0].point; Vector v02 = transformed.verts[v2].point - transformed.verts[v0].point; Vector normal = Vector::Cross(v01, v02); // Store normal for flat shading local.faces[f].normal = normal; local.faces[f].normal.Normalize(); // Invert for Blender-compatibility normal = -normal; // Eye vector to viewport Vector view = camPosition - transformed.verts[v0].point; float dot = Vector::Dot(normal, view); if (dot < EPSILON_E3) { transformed.faces.push_back(local.faces[f]); } } } void RenderMesh(Engine_Buffer &buffer, Mesh_TransformedData &mesh, bool smooth, Texture &texture, std::vector &uvs) { for(size_t f = 0; f < mesh.faces.size(); ++f) { // The vertices of this face unsigned int vIndex0 = mesh.faces[f].vertIndex[0]; unsigned int vIndex1 = mesh.faces[f].vertIndex[1]; unsigned int vIndex2 = mesh.faces[f].vertIndex[2]; Vertex &v0 = mesh.verts[vIndex0]; Vertex &v1 = mesh.verts[vIndex1]; Vertex &v2 = mesh.verts[vIndex2]; // The UVs of this face's vertices unsigned int tIndex0 = mesh.faces[f].textureIndex[0]; unsigned int tIndex1 = mesh.faces[f].textureIndex[1]; unsigned int tIndex2 = mesh.faces[f].textureIndex[2]; TextureCoord &t0 = uvs[tIndex0]; TextureCoord &t1 = uvs[tIndex1]; TextureCoord &t2 = uvs[tIndex2]; // Bounding box for barycentric calculations (top-left fill convention) BoundingBox box(v0.point, v1.point, v2.point); int yMin = (int)ceilf(box.yMin); int yMax = (int)ceilf(box.yMax) - 1; int xMin = (int)ceilf(box.xMin); int xMax = (int)ceilf(box.xMax) - 1; // Constants for this triangle used for barycentric calculations Vector v01 = v1.point - v0.point; Vector v02 = v2.point - v0.point; float dot0101 = Vector::Dot(v01, v01); float dot0102 = Vector::Dot(v01, v02); float dot0202 = Vector::Dot(v02, v02); // Iterate over the bounding box and determine if each point is in the triangle for (int y = yMin; y <= yMax; ++y) { for (int x = xMin; x <= xMax; ++x) { // Constant terms used for barycentric calculation Point p(x, y, 1.0f); Vector v0P = p - v0.point; float dot0P01 = Vector::Dot(v0P, v01); float dot0P02 = Vector::Dot(v0P, v02); float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102)); // Calculate the barycentric coordinate of this point float barycenter[3]; barycenter[1] = (dot0202 * dot0P01 - dot0102 * dot0P02) * denomInv; barycenter[2] = (dot0101 * dot0P02 - dot0102 * dot0P01) * denomInv; barycenter[0] = 1.0f - barycenter[1] - barycenter[2]; // Point is inside the triangle if ( (barycenter[0] >= 0.0f) && (barycenter[1] >= 0.0f) && (barycenter[2] >= 0.0f)) { // Constant terms used in the U and V interpolation float a = barycenter[0] * v1.point.w * v2.point.w; float b = barycenter[1] * v0.point.w * v2.point.w; float c = barycenter[2] * v0.point.w * v1.point.w; float abc = 1.0f / (a + b + c); // Interpolate U and V float u = ((a * t0.u) + (b * t1.u) + (c * t2.u)) * abc; float v = ((a * t0.v) + (b * t1.v) + (c * t2.v)) * abc; // Convert U and V to pixels in the texture image unsigned int uPixel = (unsigned int)(u * texture.width); unsigned int vPixel = (unsigned int)(v * texture.height); ColorF32 shading; // Gouraud shading - interpolate color based on vertices if (smooth) { shading = (barycenter[0] * v0.color) + (barycenter[1] * v1.color) + (barycenter[2] * v2.color); } // Flat shading - base color on single face color else { shading = mesh.faces[f].color; } // Shade the texel with lighting calculations ColorU32 texel = texture.texels[vPixel][uPixel]; texel.r *= shading.r; texel.g *= shading.g; texel.b *= shading.b; // Interpolate 1/z for the z-buffer float zInv = 1.0f / ((barycenter[0] * v0.point.w) + (barycenter[1] * v1.point.w) + (barycenter[2] * v2.point.w)); // Draw the pixel if it's closer than what's in the z-buffer int pixel = (y * buffer.width + x); if (zInv > buffer.zbuffer[pixel]) { DrawPixel(buffer.buffer, buffer.width, texel.u32, x, y); buffer.zbuffer[pixel] = zInv; } } } } } }