diff --git a/Makefile b/Makefile index f8ca7bd..ef2717f 100644 --- a/Makefile +++ b/Makefile @@ -41,11 +41,11 @@ CFLAGS=$(D) $(O) -std=c++11 $(WARNINGS_ON) $(WARNINGS_OFF) -I$(INCLUDE_DIR) LIBS=-lSDL2 _HEADERS = camera.h color.h engine.h geometry.h light.h loader.h matrix.h\ - platform.h point.h transform.h util.h vec.h + platform.h point.h render.h transform.h util.h vec.h HEADERS = $(patsubst %,$(INCLUDE_DIR)/%,$(_HEADERS)) -_OBJS = color.o engine.o geometry.o light.o loader.o main.o platform.o transform.o\ - vec.o +_OBJS = color.o engine.o light.o loader.o main.o platform.o render.o\ + transform.o vec.o OBJS = $(patsubst %,$(BUILD_DIR)/%,$(_OBJS)) $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp $(HEADERS) diff --git a/include/engine.h b/include/engine.h index 9bea429..9d5af22 100644 --- a/include/engine.h +++ b/include/engine.h @@ -1,5 +1,6 @@ #ifndef ENGINE_H +#include "geometry.h" #include @@ -13,15 +14,9 @@ #define CAMERA_NEAR_CLIP (5.0f) #define CAMERA_FAR_CLIP (600.0f) -// GEOMETRY CONFIGURATION -#define FACE_LIMIT (30000) -#define MATERIAL_LIMIT (5) -#define TEXTURE_SIZE_LIMIT (1024) -#define VERTEX_LIMIT (20000) - // ENUMS -enum Engine_Input +enum EngineInput { TRANSLATE_X_POS, TRANSLATE_X_NEG, @@ -41,19 +36,30 @@ enum Engine_Input // STRUCTURES -struct Engine_Buffer +struct EngineBuffer { uint32_t *buffer; - float zbuffer[WINDOW_HEIGHT][WINDOW_WIDTH]; int width; int height; }; +struct EngineMemory +{ + float zbuffer[WINDOW_HEIGHT][WINDOW_WIDTH]; + VertexList localVerts; + VertexList transVerts; + FaceList localFaces; + FaceList transFaces; + UVList uvs; + MaterialList materials; + TextureList textures; +}; + // FUNCTIONS -int Engine_Init(char *objFilename, char *mtlFilename); -void Engine_Render(Engine_Buffer &buffer, uint32_t input); -void Engine_Shutdown(void); +int EngineInit(char *objFilename, char *mtlFilename); +void EngineRender(EngineBuffer &buffer, uint32_t input); +void EngineShutdown(void); #define ENGINE_H diff --git a/include/geometry.h b/include/geometry.h index 4ac4ab6..e43fdab 100644 --- a/include/geometry.h +++ b/include/geometry.h @@ -1,11 +1,17 @@ #ifndef GEOMETRY_H #include "color.h" -#include "engine.h" #include "point.h" #include +// CONSTANTS +#define FACE_LIMIT (30000) +#define MATERIAL_LIMIT (5) +#define TEXTURE_SIZE_LIMIT (1024) +#define VERTEX_LIMIT (20000) + + // STRUCTURES struct Texture { @@ -81,16 +87,6 @@ struct Mesh }; -// PUBLIC FUNCTIONS -void ClipAndCull( - VertexList &verts, FaceList &localFaces, - FaceList &transFaces, Point &camPosition); - -void RenderMesh( - Engine_Buffer &buffer, FaceList &faces, VertexList &verts, - UVList &uvs, TextureList &textures); - - #define GEOMETRY_H #endif diff --git a/include/loader.h b/include/loader.h index 3ffa3c9..ec083a6 100644 --- a/include/loader.h +++ b/include/loader.h @@ -1,10 +1,11 @@ #ifndef LOADER_H +#include "engine.h" #include "geometry.h" -int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs); -int ParseMTL(char *filename, MaterialList &materials, TextureList &textures); +int ParseOBJ(char *filename, EngineMemory &memory); +int ParseMTL(char *filename, EngineMemory &memory); #define LOADER_H diff --git a/include/render.h b/include/render.h new file mode 100644 index 0000000..cfbdba6 --- /dev/null +++ b/include/render.h @@ -0,0 +1,12 @@ +#ifndef RENDER_H + +#include "engine.h" + + +// PUBLIC FUNCTIONS +void Render(EngineBuffer &buffer, EngineMemory &memory); + + +#define RENDER_H +#endif + diff --git a/src/engine.cpp b/src/engine.cpp index 090fc4b..a99e76e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -5,6 +5,7 @@ #include "light.h" #include "loader.h" #include "matrix.h" +#include "render.h" #include "transform.h" #include "util.h" #include "vec.h" @@ -16,51 +17,46 @@ 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; +static EngineMemory memory; // PRIVATE PROTOTYPES -static void CheckInputs(uint32_t input); static void ComputeNormals(void); -static void ClearDepthBuffer(Engine_Buffer &buffer); +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 Engine_Init(char *objFilename, char *mtlFilename) +int EngineInit(char *objFilename, char *mtlFilename) { int result; - result = ParseOBJ(objFilename, localVerts, localFaces, uvs); + result = ParseOBJ(objFilename, memory); if (result < 0) { return -1; } - result = ParseMTL(mtlFilename, materials, textures); + result = ParseMTL(mtlFilename, memory); if (result < 0) { return -1; } - printf("Verts: %lu\n", localVerts.size); - printf("Faces: %lu\n", localFaces.size); - printf("Materials: %lu\n", materials.size); + printf("Verts: %lu\n", memory.localVerts.size); + printf("Faces: %lu\n", memory.localFaces.size); + printf("Materials: %lu\n", memory.materials.size); - transVerts.size = localVerts.size; + memory.transVerts.size = memory.localVerts.size; // Compute vertex and face normals for lighting calculation @@ -68,8 +64,8 @@ int Engine_Init(char *objFilename, char *mtlFilename) // Mesh configuration - mesh.position.z = 50; - mesh.position.y = -75; + mesh.position.z = 125; + mesh.position.y = -125; mesh.scale = 1.0f; @@ -89,14 +85,14 @@ int Engine_Init(char *objFilename, char *mtlFilename) return 0; } -void Engine_Render(Engine_Buffer &buffer, uint32_t input) +void EngineRender(EngineBuffer &buffer, uint32_t input) { // Check for user input CheckInputs(input); // Clear the z-buffer - ClearDepthBuffer(buffer); + ClearDepthBuffer(); // Transform vertices to clip space @@ -104,7 +100,7 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) // Clip near/far Z and cull backfaces - ClipAndCull(transVerts, localFaces, transFaces, camera.position); + ClipAndCull(); // Light vertices and/or faces @@ -116,10 +112,10 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) // Render - RenderMesh(buffer, transFaces, transVerts, uvs, textures); + Render(buffer, memory); } -void Engine_Shutdown(void) +void EngineShutdown(void) { } @@ -198,53 +194,58 @@ static void CheckInputs(uint32_t input) static void ComputeNormals(void) { + VertexList &verts = memory.localVerts; + FaceList &faces = memory.localFaces; + int vertexNormalCount[VERTEX_LIMIT] = {}; - for (size_t f = 0; f < localFaces.size; ++f) + for (size_t f = 0; f < faces.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]; + Face &face = faces.data[f]; - Point &p0 = localVerts.data[v0].point; - Point &p1 = localVerts.data[v1].point; - Point &p2 = localVerts.data[v2].point; + Vertex &vert0 = verts.data[face.vertIndex[0]]; + Vertex &vert1 = verts.data[face.vertIndex[1]]; + Vertex &vert2 = verts.data[face.vertIndex[2]]; - Vector v01 = p1 - p0; - Vector v02 = p2 - p0; + + 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 - localVerts.data[v0].normal += normal; - localVerts.data[v1].normal += normal; - localVerts.data[v2].normal += normal; + vert0.normal += normal; + vert1.normal += normal; + vert2.normal += normal; - ++vertexNormalCount[v0]; - ++vertexNormalCount[v1]; - ++vertexNormalCount[v2]; + ++vertexNormalCount[face.vertIndex[0]]; + ++vertexNormalCount[face.vertIndex[1]]; + ++vertexNormalCount[face.vertIndex[2]]; } - for (size_t v = 0; v < localVerts.size; ++v) + for (size_t v = 0; v < verts.size; ++v) { if (vertexNormalCount[v] > 0) { // Compute the average normal for this vertex - localVerts.data[v].normal /= vertexNormalCount[v]; - VectorNormalize(localVerts.data[v].normal); + verts.data[v].normal /= vertexNormalCount[v]; + VectorNormalize(verts.data[v].normal); } } } -static void ClearDepthBuffer(Engine_Buffer &buffer) +static void ClearDepthBuffer(void) { - memset(buffer.zbuffer, 0, sizeof(float) * (size_t)(buffer.width * buffer.height)); + 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); @@ -260,28 +261,82 @@ static void TransformToClipSpace(void) } } +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) { - for (size_t v = 0; v < transVerts.size; ++v) + VertexList &verts = memory.transVerts; + + for (size_t v = 0; v < verts.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; + 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) { - for (size_t f = 0; f < transFaces.size; ++f) + VertexList &verts = memory.transVerts; + FaceList &faces = memory.transFaces; + MaterialList &materials = memory.materials; + + for (size_t f = 0; f < faces.size; ++f) { - Face &face = transFaces.data[f]; + Face &face = faces.data[f]; Material &material = materials.data[face.materialIndex]; // Gouraud shading for (int i = 0; i < 3; ++i) { - Vertex &vert = transVerts.data[face.vertIndex[i]]; + Vertex &vert = verts.data[face.vertIndex[i]]; vert.color = ComputeLight(vert, material, light); } diff --git a/src/loader.cpp b/src/loader.cpp index 1c0bee3..efc7e78 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -1,3 +1,4 @@ +#include "engine.h" #include "loader.h" #include #include @@ -34,7 +35,7 @@ struct BMP_Header // PUBLIC FUNCTIONS -int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs) +int ParseOBJ(char *filename, EngineMemory &memory) { FILE *fp = fopen(filename, "r"); @@ -51,6 +52,9 @@ int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs) int faceIndex = 0; int materialIndex = -1; + VertexList &verts = memory.localVerts; + FaceList &faces = memory.localFaces; + UVList &uvs = memory.uvs; while (fgets(line, sizeof(line), fp)) { @@ -69,6 +73,8 @@ int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs) &verts.data[vertIndex].point.y, &verts.data[vertIndex].point.z); + verts.data[vertIndex].point.w = 1.0f; + ++vertIndex; } @@ -117,7 +123,7 @@ int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs) return 0; } -int ParseMTL(char *filename, MaterialList &materials, TextureList &textures) +int ParseMTL(char *filename, EngineMemory &memory) { FILE *fp = fopen(filename, "r"); @@ -130,6 +136,9 @@ int ParseMTL(char *filename, MaterialList &materials, TextureList &textures) char line[256]; int materialIndex = -1; + MaterialList &materials = memory.materials; + TextureList &textures = memory.textures; + while (fgets(line, sizeof(line), fp)) { char *separator = strchr(line, ' '); diff --git a/src/main.cpp b/src/main.cpp index 717e5cb..15496a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,12 +23,12 @@ int main(int argc, char *argv[]) if (result == PLATFORM_OK) { - Engine_Buffer buffer = {}; + EngineBuffer buffer = {}; buffer.buffer = (uint32_t*)platform.surface->pixels; buffer.width = platform.surface->w; buffer.height = platform.surface->h; - result = Engine_Init(objFilename, mtlFilename); + result = EngineInit(objFilename, mtlFilename); if (result < 0) { @@ -47,14 +47,14 @@ int main(int argc, char *argv[]) Platform_ClearWindow(platform); - Engine_Render(buffer, platform.input); + EngineRender(buffer, platform.input); Platform_UpdateWindow(platform); Platform_SyncToFramerate(platform); } - Engine_Shutdown(); + EngineShutdown(); Platform_Shutdown(platform); } diff --git a/src/geometry.cpp b/src/render.cpp similarity index 80% rename from src/geometry.cpp rename to src/render.cpp index d8ad1a1..44006a1 100644 --- a/src/geometry.cpp +++ b/src/render.cpp @@ -1,6 +1,7 @@ #include "color.h" #include "engine.h" #include "geometry.h" +#include "render.h" #include "util.h" @@ -29,57 +30,13 @@ struct BoundingBox // PUBLIC FUNCTIONS -void ClipAndCull( - VertexList &verts, FaceList &localFaces, - FaceList &transFaces, Point &camPosition) +void Render(EngineBuffer &buffer, EngineMemory &memory) { - int faceIndex = 0; + FaceList &faces = memory.transFaces; + VertexList &verts = memory.transVerts; + TextureList &textures = memory.textures; + UVList &uvs = memory.uvs; - 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 = camPosition - 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; - } - } -} - -void RenderMesh( - Engine_Buffer &buffer, FaceList &faces, VertexList &verts, - UVList &uvs, TextureList &textures) -{ for(size_t f = 0; f < faces.size; ++f) { Face &face = faces.data[f]; @@ -206,11 +163,11 @@ void RenderMesh( // Draw the pixel if it's closer than what's in the z-buffer - if (zInv > buffer.zbuffer[y][x]) + if (zInv > memory.zbuffer[y][x]) { DrawPixel(buffer.buffer, buffer.width, color.u32, x, y); - buffer.zbuffer[y][x] = zInv; + memory.zbuffer[y][x] = zInv; } } }