197 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "color.h"
 | |
| #include "engine.h"
 | |
| #include "geometry.h"
 | |
| #include "util.h"
 | |
| #include <cstdio>
 | |
| 
 | |
| 
 | |
| // 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<TextureCoord> &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;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 |