#include "camera.h" #include "color.h" #include "engine.h" #include "geometry.h" #include "light.h" #include "loader.h" #include "matrix.h" #include "transform.h" #include "util.h" #include "vec.h" #include #include // GLOBALS static Mesh mesh; static Camera camera; static Light light; static VertexList localVerts; static VertexList transVerts; static FaceList localFaces; static FaceList transFaces; static UVList uvs; static MaterialList materials; static TextureList textures; static Matrix tPersp; static Matrix tScreen; // PRIVATE PROTOTYPES static void CheckInputs(uint32_t input); static void ComputeNormals(void); static void ClearDepthBuffer(Engine_Buffer &buffer); static void TransformToClipSpace(void); static void TransformToScreenSpace(void); static void LightMesh(void); // PUBLIC FUNCTIONS int Engine_Init(char *objFilename, char *mtlFilename) { int result; result = ParseOBJ(objFilename, localVerts, localFaces, uvs); if (result < 0) { return -1; } result = ParseMTL(mtlFilename, materials, textures); if (result < 0) { return -1; } printf("Verts: %lu\n", localVerts.size); printf("Faces: %lu\n", localFaces.size); printf("Materials: %lu\n", materials.size); transVerts.size = localVerts.size; // Compute vertex and face normals for lighting calculation ComputeNormals(); // Mesh configuration mesh.position.z = 50; mesh.position.y = -75; 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 Engine_Render(Engine_Buffer &buffer, uint32_t input) { // Check for user input CheckInputs(input); // Clear the z-buffer ClearDepthBuffer(buffer); // Transform vertices to clip space TransformToClipSpace(); // Clip near/far Z and cull backfaces ClipAndCull(transVerts, localFaces, transFaces, camera.position); // Light vertices and/or faces LightMesh(); // Transform vertices to screen space TransformToScreenSpace(); // Render RenderMesh(buffer, transFaces, transVerts, uvs, textures); } void Engine_Shutdown(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) { int vertexNormalCount[VERTEX_LIMIT] = {}; for (size_t f = 0; f < localFaces.size; ++f) { size_t v0 = (size_t)localFaces.data[f].vertIndex[0]; size_t v1 = (size_t)localFaces.data[f].vertIndex[1]; size_t v2 = (size_t)localFaces.data[f].vertIndex[2]; Point &p0 = localVerts.data[v0].point; Point &p1 = localVerts.data[v1].point; Point &p2 = localVerts.data[v2].point; Vector v01 = p1 - p0; Vector v02 = p2 - p0; Vector normal = VectorCross(v01, v02); // Add each vertex's normal to the sum for future averaging localVerts.data[v0].normal += normal; localVerts.data[v1].normal += normal; localVerts.data[v2].normal += normal; ++vertexNormalCount[v0]; ++vertexNormalCount[v1]; ++vertexNormalCount[v2]; } for (size_t v = 0; v < localVerts.size; ++v) { if (vertexNormalCount[v] > 0) { // Compute the average normal for this vertex localVerts.data[v].normal /= vertexNormalCount[v]; VectorNormalize(localVerts.data[v].normal); } } } static void ClearDepthBuffer(Engine_Buffer &buffer) { memset(buffer.zbuffer, 0, sizeof(float) * (size_t)(buffer.width * buffer.height)); } static void TransformToClipSpace(void) { 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; } } static void TransformToScreenSpace(void) { for (size_t v = 0; v < transVerts.size; ++v) { transVerts.data[v].point *= tScreen; transVerts.data[v].point.x /= transVerts.data[v].point.w; transVerts.data[v].point.y /= transVerts.data[v].point.w; transVerts.data[v].point.z /= transVerts.data[v].point.w; } } static void LightMesh(void) { for (size_t f = 0; f < transFaces.size; ++f) { Face &face = transFaces.data[f]; Material &material = materials.data[face.materialIndex]; // Gouraud shading for (int i = 0; i < 3; ++i) { Vertex &vert = transVerts.data[face.vertIndex[i]]; vert.color = ComputeLight(vert, material, light); } } }