#include "camera.h" #include "color.h" #include "engine.h" #include "geometry.h" #include "light.h" #include "loader.h" #include "matrix.h" #include "render.h" #include "transform.h" #include "util.h" #include "vec.h" #include #include // GLOBALS static Mesh mesh; static Camera camera; static Light light; static Matrix tPersp; static Matrix tScreen; static EngineMemory memory; // PRIVATE PROTOTYPES static void ComputeNormals(void); static void CheckInputs(uint32_t input); static void ClearDepthBuffer(void); static void TransformToClipSpace(void); static void ClipAndCull(void); static void TransformToScreenSpace(void); static void LightMesh(void); // PUBLIC FUNCTIONS int EngineInit(char *objFilename, char *mtlFilename) { int result; result = ParseOBJ(objFilename, memory); if (result < 0) { return -1; } result = ParseMTL(mtlFilename, memory); if (result < 0) { return -1; } printf("Verts: %lu\n", memory.localVerts.size); printf("Faces: %lu\n", memory.localFaces.size); printf("Materials: %lu\n", memory.materials.size); memory.transVerts.size = memory.localVerts.size; // Compute vertex and face normals for lighting calculation ComputeNormals(); // Mesh configuration mesh.position.z = 125; mesh.position.y = -125; mesh.scale = 1.0f; // Light configuration light.position = Point(-100.0f, 200.0f, 0.0f); light.color = {1.0f, 1.0f, 1.0f, 1.0f}; light.intensity = 2.0f; light.falloffConstant = 1.0f; light.falloffLinear = 0.001f; // Transformation matrices that do not change tPersp = Transform_Perspective(camera); tScreen = Transform_Screen(camera); return 0; } void EngineRender(EngineBuffer &buffer, uint32_t input) { // Check for user input CheckInputs(input); // Clear the z-buffer ClearDepthBuffer(); // Transform vertices to clip space TransformToClipSpace(); // Clip near/far Z and cull backfaces ClipAndCull(); // Light vertices and/or faces LightMesh(); // Transform vertices to screen space TransformToScreenSpace(); // Render Render(buffer, memory); } void EngineShutdown(void) { } // PRIVATE FUNCTIONS static void CheckInputs(uint32_t input) { if (CHECK_BIT(input, TRANSLATE_X_POS)) { light.position.x += 10; } else if (CHECK_BIT(input, TRANSLATE_X_NEG)) { light.position.x -= 10; } if (CHECK_BIT(input, TRANSLATE_Z_POS)) { light.position.z += 10; } else if (CHECK_BIT(input, TRANSLATE_Z_NEG)) { light.position.z -= 10; } if (CHECK_BIT(input, TRANSLATE_Y_POS)) { light.position.y += 10; } else if (CHECK_BIT(input, TRANSLATE_Y_NEG)) { light.position.y -= 10; } if (CHECK_BIT(input, ROTATE_X_POS)) { mesh.rotation.x += .10; } else if (CHECK_BIT(input, ROTATE_X_NEG)) { mesh.rotation.x -= .10; } if (CHECK_BIT(input, ROTATE_Y_POS)) { mesh.rotation.y += .10; } else if (CHECK_BIT(input, ROTATE_Y_NEG)) { mesh.rotation.y -= .10; } if (CHECK_BIT(input, ROTATE_Z_POS)) { mesh.rotation.z += .10; } else if (CHECK_BIT(input, ROTATE_Z_NEG)) { mesh.rotation.z -= .10; } if (CHECK_BIT(input, SCALE_UP)) { light.color.b = 0.0f; light.color.g = 0.0f; light.color.r = 1.0f; } else if (CHECK_BIT(input, SCALE_DOWN)) { light.color.b = 1.0f; light.color.g = 1.0f; light.color.r = 1.0f; } } static void ComputeNormals(void) { VertexList &verts = memory.localVerts; FaceList &faces = memory.localFaces; int vertexNormalCount[VERTEX_LIMIT] = {}; for (size_t f = 0; f < faces.size; ++f) { Face &face = faces.data[f]; Vertex &vert0 = verts.data[face.vertIndex[0]]; Vertex &vert1 = verts.data[face.vertIndex[1]]; Vertex &vert2 = verts.data[face.vertIndex[2]]; Vector v01 = vert1.point - vert0.point; Vector v02 = vert2.point - vert0.point; Vector normal = VectorCross(v01, v02); // Add each vertex's normal to the sum for future averaging vert0.normal += normal; vert1.normal += normal; vert2.normal += normal; ++vertexNormalCount[face.vertIndex[0]]; ++vertexNormalCount[face.vertIndex[1]]; ++vertexNormalCount[face.vertIndex[2]]; } for (size_t v = 0; v < verts.size; ++v) { if (vertexNormalCount[v] > 0) { // Compute the average normal for this vertex verts.data[v].normal /= vertexNormalCount[v]; VectorNormalize(verts.data[v].normal); } } } static void ClearDepthBuffer(void) { memset(memory.zbuffer, 0, sizeof(memory.zbuffer)); } static void TransformToClipSpace(void) { VertexList &localVerts = memory.localVerts; VertexList &transVerts = memory.transVerts; Matrix tTranslate = Transform_Translate(mesh.position); Matrix tRotate = Transform_Rotate(mesh.rotation); Matrix tScale = Transform_Scale(mesh.scale); Matrix tView = Transform_View(camera); for (size_t v = 0; v < localVerts.size; ++v) { transVerts.data[v].point = localVerts.data[v].point * tScale * tRotate * tTranslate * tView * tPersp; transVerts.data[v].normal = localVerts.data[v].normal * tScale * tRotate * tTranslate; } } void ClipAndCull(void) { FaceList &localFaces = memory.localFaces; FaceList &transFaces = memory.transFaces; VertexList &verts = memory.transVerts; int faceIndex = 0; for (size_t f = 0; f < localFaces.size; ++f) { Face &face = localFaces.data[f]; Point &p0 = verts.data[face.vertIndex[0]].point; Point &p1 = verts.data[face.vertIndex[1]].point; Point &p2 = verts.data[face.vertIndex[2]].point; // Ignore this face if its Z is outside the Z clip planes if ( (p0.z < -p0.w) || (p0.z > p0.w) || (p1.z < -p1.w) || (p1.z > p1.w) || (p2.z < -p2.w) || (p2.z > p2.w)) { continue; } // Calculate the face's normal (inverted for Blender-compatibility) Vector v01 = p1 - p0; Vector v02 = p2 - p0; Vector normal = -VectorCross(v01, v02); // Eye vector to viewport Vector view = camera.position - p0; float dot = VectorDot(normal, view); // Not a backface; add it to the list if (dot < EPSILON_E3) { transFaces.data[faceIndex] = face; ++faceIndex; transFaces.size = (size_t)faceIndex; } } } static void TransformToScreenSpace(void) { VertexList &verts = memory.transVerts; for (size_t v = 0; v < verts.size; ++v) { verts.data[v].point *= tScreen; verts.data[v].point.x /= verts.data[v].point.w; verts.data[v].point.y /= verts.data[v].point.w; verts.data[v].point.z /= verts.data[v].point.w; } } static void LightMesh(void) { VertexList &verts = memory.transVerts; FaceList &faces = memory.transFaces; MaterialList &materials = memory.materials; for (size_t f = 0; f < faces.size; ++f) { Face &face = faces.data[f]; Material &material = materials.data[face.materialIndex]; // Gouraud shading for (int i = 0; i < 3; ++i) { Vertex &vert = verts.data[face.vertIndex[i]]; vert.color = ComputeLight(vert, material, light); } } }