#include "Camera.hpp" #include "Color.hpp" #include "Engine.hpp" #include "Geometry.hpp" #include "Light.hpp" #include "Loader.hpp" #include "Matrix.hpp" #include "Render.hpp" #include "Transform.hpp" #include "Vec.hpp" #include #include unsigned long constexpr CheckBit(uint32_t x, unsigned long bit) { return x & (1UL << bit); } static Mesh mesh; static Camera camera; static Light light; static Matrix tPersp; static Matrix tScreen; static EngineMemory memory; static void ComputeNormals(); static void CheckInputs(uint32_t input); static void ClearDepthBuffer(); static void TransformToClipSpace(); static void ClipAndCull(); static void TransformToScreenSpace(); static void LightMesh(); 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 = 200; mesh.position.y = -100; mesh.scale = 1.0f; // Light configuration light.position = Point(-300.0f, 200.0f, 0.0f); light.color = ColorF32(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() { } // PRIVATE FUNCTIONS static void CheckInputs(uint32_t input) { if (CheckBit(input, TRANSLATE_X_POS)) { light.position.x += 10; } else if (CheckBit(input, TRANSLATE_X_NEG)) { light.position.x -= 10; } if (CheckBit(input, TRANSLATE_Z_POS)) { light.position.z += 10; } else if (CheckBit(input, TRANSLATE_Z_NEG)) { light.position.z -= 10; } if (CheckBit(input, TRANSLATE_Y_POS)) { light.position.y += 10; } else if (CheckBit(input, TRANSLATE_Y_NEG)) { light.position.y -= 10; } if (CheckBit(input, ROTATE_X_POS)) { mesh.rotation.x += 0.10f; } else if (CheckBit(input, ROTATE_X_NEG)) { mesh.rotation.x -= 0.10f; } if (CheckBit(input, ROTATE_Y_POS)) { mesh.rotation.y += 0.10f; } else if (CheckBit(input, ROTATE_Y_NEG)) { mesh.rotation.y -= 0.10f; } if (CheckBit(input, ROTATE_Z_POS)) { mesh.rotation.z += 0.10f; } else if (CheckBit(input, ROTATE_Z_NEG)) { mesh.rotation.z -= 0.10f; } if (CheckBit(input, SCALE_UP)) { light.color.b = 0.0f; light.color.g = 0.0f; light.color.r = 1.0f; } else if (CheckBit(input, SCALE_DOWN)) { light.color.b = 1.0f; light.color.g = 1.0f; light.color.r = 1.0f; } } static void ComputeNormals() { 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.position - vert0.position; Vector v02 = vert2.position - vert0.position; Vector normal = Vector::Cross(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 /= static_cast(vertexNormalCount[v]); verts.data[v].normal.Normalize(); } } } static void ClearDepthBuffer() { memset(memory.zbuffer, 0, sizeof(memory.zbuffer)); } static void TransformToClipSpace() { 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].position = localVerts.data[v].position * tScale * tRotate * tTranslate * tView * tPersp; transVerts.data[v].normal = localVerts.data[v].normal * tScale * tRotate * tTranslate; } } void ClipAndCull() { 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]].position; Point& p1 = verts.data[face.vertIndex[1]].position; Point& p2 = verts.data[face.vertIndex[2]].position; // 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 = -Vector::Cross(v01, v02); // Eye vector to viewport Vector view = camera.position - p0; float dot = Vector::Dot(normal, view); // Not a backface; add it to the list if (dot < 0.0f) { transFaces.data[faceIndex] = face; ++faceIndex; transFaces.size = (size_t)faceIndex; } } } static void TransformToScreenSpace() { VertexList& verts = memory.transVerts; for (size_t v = 0; v < verts.size; ++v) { verts.data[v].position *= tScreen; verts.data[v].position.x /= verts.data[v].position.w; verts.data[v].position.y /= verts.data[v].position.w; verts.data[v].position.z /= verts.data[v].position.w; } } static void LightMesh() { 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 (auto index : face.vertIndex) { Vertex& vert = verts.data[index]; vert.color = light.Compute(vert.position, vert.normal, material, camera); } } }