From dae8fa4ba7c848699394c1db26a66e49bf508866 Mon Sep 17 00:00:00 2001 From: Austin Morlan Date: Fri, 9 Aug 2019 17:03:10 -0700 Subject: [PATCH] Code cleanup --- CMakeLists.txt | 49 ++++++ Makefile | 61 -------- README.md | 7 - Source/Camera.hpp | 47 ++++++ Source/Color.hpp | 116 +++++++++++++++ Source/Engine.cpp | 345 +++++++++++++++++++++++++++++++++++++++++++ Source/Engine.hpp | 55 +++++++ Source/Geometry.hpp | 85 +++++++++++ Source/Light.hpp | 58 ++++++++ Source/Loader.cpp | 256 ++++++++++++++++++++++++++++++++ Source/Loader.hpp | 7 + Source/Main.cpp | 60 ++++++++ Source/Matrix.hpp | 49 ++++++ Source/Platform.cpp | 243 ++++++++++++++++++++++++++++++ Source/Platform.hpp | 38 +++++ Source/Point.hpp | 88 +++++++++++ Source/Render.cpp | 199 +++++++++++++++++++++++++ Source/Render.hpp | 6 + Source/Transform.cpp | 105 +++++++++++++ Source/Transform.hpp | 12 ++ Source/Vec.hpp | 159 ++++++++++++++++++++ include/camera.h | 47 ------ include/color.h | 132 ----------------- include/engine.h | 67 --------- include/geometry.h | 92 ------------ include/light.h | 27 ---- include/loader.h | 13 -- include/matrix.h | 108 -------------- include/platform.h | 44 ------ include/point.h | 74 ---------- include/render.h | 12 -- include/transform.h | 17 --- include/util.h | 23 --- include/vec.h | 163 -------------------- src/engine.cpp | 344 ------------------------------------------ src/light.cpp | 48 ------ src/loader.cpp | 250 ------------------------------- src/main.cpp | 68 --------- src/platform.cpp | 245 ------------------------------ src/render.cpp | 203 ------------------------- src/transform.cpp | 106 ------------- 41 files changed, 1977 insertions(+), 2151 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 Source/Camera.hpp create mode 100644 Source/Color.hpp create mode 100644 Source/Engine.cpp create mode 100644 Source/Engine.hpp create mode 100644 Source/Geometry.hpp create mode 100644 Source/Light.hpp create mode 100644 Source/Loader.cpp create mode 100644 Source/Loader.hpp create mode 100644 Source/Main.cpp create mode 100644 Source/Matrix.hpp create mode 100644 Source/Platform.cpp create mode 100644 Source/Platform.hpp create mode 100644 Source/Point.hpp create mode 100644 Source/Render.cpp create mode 100644 Source/Render.hpp create mode 100644 Source/Transform.cpp create mode 100644 Source/Transform.hpp create mode 100644 Source/Vec.hpp delete mode 100644 include/camera.h delete mode 100644 include/color.h delete mode 100644 include/engine.h delete mode 100644 include/geometry.h delete mode 100644 include/light.h delete mode 100644 include/loader.h delete mode 100644 include/matrix.h delete mode 100644 include/platform.h delete mode 100644 include/point.h delete mode 100644 include/render.h delete mode 100644 include/transform.h delete mode 100644 include/util.h delete mode 100644 include/vec.h delete mode 100644 src/engine.cpp delete mode 100644 src/light.cpp delete mode 100644 src/loader.cpp delete mode 100644 src/main.cpp delete mode 100644 src/platform.cpp delete mode 100644 src/render.cpp delete mode 100644 src/transform.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..74161db --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.14) +project(renderer) + + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +find_package(SDL2 REQUIRED) + + +add_executable(renderer) + +target_compile_options( + renderer + PRIVATE + -fno-exceptions + -Wall) + +target_sources( + renderer + PRIVATE + Source/Engine.cpp + Source/Loader.cpp + Source/Main.cpp + Source/Platform.cpp + Source/Render.cpp + Source/Transform.cpp) + +target_sources( + renderer + PRIVATE + Source/Camera.hpp + Source/Color.hpp + Source/Geometry.hpp + Source/Matrix.hpp + Source/Point.hpp + Source/Vec.hpp) + +target_include_directories( + renderer + PRIVATE + Source) + +target_link_libraries( + renderer + PRIVATE + SDL2::SDL2) diff --git a/Makefile b/Makefile deleted file mode 100644 index 8e723f9..0000000 --- a/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# Verbosity of make output -ifeq ("$(VERBOSE)","1") -V := -else -V := @ -endif - -# Optimizations -ifeq ("$(OPTIMIZE)","0") -O := -O0 -else ifeq ("$(OPTIMIZE)","1") -O := -O1 -else ifeq ("$(OPTIMIZE)", "2") -O := -O2 -else ifeq ("$(OPTIMIZE)", "3") -O := -O3 -else -O := -O3 -endif - -# Debugging -ifeq ("$(DEBUG)","1") -D := -g -else -D := -endif - - -SRC_DIR=src -INCLUDE_DIR=include -BUILD_DIR=build - -CC=clang++ -WARNINGS_ON=-Weverything -WARNINGS_OFF=-Wno-missing-braces -Wno-gnu-anonymous-struct -Wno-old-style-cast\ - -Wno-zero-as-null-pointer-constant -Wno-nested-anon-types\ - -Wno-padded -Wno-exit-time-destructors -Wno-global-constructors\ - -Wno-c++98-compat - -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 render.h transform.h util.h vec.h -HEADERS = $(patsubst %,$(INCLUDE_DIR)/%,$(_HEADERS)) - -_OBJS = engine.o light.o loader.o main.o platform.o render.o\ - transform.o -OBJS = $(patsubst %,$(BUILD_DIR)/%,$(_OBJS)) - -$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp $(HEADERS) - @ if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi - $(V) $(CC) -c -o $@ $< $(CFLAGS) - -$(BUILD_DIR)/engine: $(OBJS) - $(V) $(CC) -o $@ $^ $(CFLAGS) $(LIBS) - -.PHONY: clean - -clean: - rm -rf $(BUILD_DIR) diff --git a/README.md b/README.md index 6133c85..e51f113 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,6 @@ To best learn what a GPU is doing, I wanted to recreate the functionality of the GPU in software. It's not anywhere near as fast as a GPU of course, but it's not slow either (for a single model that is relatively low-poly). -### Simple code - -Coming from a C background, I wanted to use features of C++ that I found made -the code cleaner and easier to understand while avoiding many of the fancier -ones that seemed unnecessary. Primarily I leveraged operator overloading for -vector and matrix operations, and used some simple classes with constructors. - ### Good cache performance The biggest bottleneck of modern CPU performance is latency between memory and diff --git a/Source/Camera.hpp b/Source/Camera.hpp new file mode 100644 index 0000000..3aa7e3e --- /dev/null +++ b/Source/Camera.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "Engine.hpp" +#include "Point.hpp" +#include + + +constexpr float DegToRad(float degrees) +{ + return degrees * (float)M_PI / 180.0f; +} + + +class Camera +{ +public: + Camera() + { + position.x = 0.0f; + position.y = 0.0f; + position.z = 0.0f; + + rotation.x = 0.0f; + rotation.y = 0.0f; + rotation.z = 0.0f; + + zClipBias0 = + (CAMERA_FAR_CLIP + CAMERA_NEAR_CLIP) + / (CAMERA_FAR_CLIP - CAMERA_NEAR_CLIP); + + zClipBias1 = + (-2.0f * CAMERA_FAR_CLIP * CAMERA_NEAR_CLIP) + / (CAMERA_FAR_CLIP - CAMERA_NEAR_CLIP); + + xZoom = 1.0f / tanf(DegToRad(CAMERA_FOV / 2.0f)); + yZoom = (xZoom * WINDOW_WIDTH) / WINDOW_HEIGHT; + + xScale = (0.5f * WINDOW_WIDTH) - 0.5f; + yScale = (0.5f * WINDOW_HEIGHT) - 0.5f; + } + + Point position; + Point rotation; + float zClipBias0, zClipBias1; + float xZoom, yZoom; + float xScale, yScale; +}; diff --git a/Source/Color.hpp b/Source/Color.hpp new file mode 100644 index 0000000..ef950d8 --- /dev/null +++ b/Source/Color.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include +#include + + +class ColorU32 +{ +public: + ColorU32() + : b(0), g(0), r(0), a(0) + {} + + ColorU32(uint8_t b, uint8_t g, uint8_t r, uint8_t a) + : b(b), g(g), r(r), a(a) + {} + + union + { + struct + { + uint8_t b, g, r, a; + }; + + uint32_t u32; + }; +}; + +class ColorF32 +{ +public: + ColorF32() + : b(0.0f), g(0.0f), r(0.0f), a(0.0f) + {} + + ColorF32(float b, float g, float r, float a) + : b(b), g(g), r(r), a(a) + {} + + void Scale() + { + float blue = std::max(b, 0.0f); + float green = std::max(g, 0.0f); + float red = std::max(r, 0.0f); + float alpha = std::max(a, 0.0f); + float max = std::max(std::max(std::max(blue, green), red), 1.0f); + + ColorF32 scaled(blue, green, red, alpha); + scaled /= max; + + b = scaled.b; + g = scaled.g; + r = scaled.r; + a = scaled.a; + } + + float b, g, r, a; + + ColorF32 operator+(ColorF32 const& rhs) const + { + return ColorF32( + b + rhs.b, + g + rhs.g, + r + rhs.r, + a + rhs.a); + } + + ColorF32& operator+=(ColorF32 const& rhs) + { + b += rhs.b; + g += rhs.g; + r += rhs.r; + a += rhs.a; + + return *this; + } + + ColorF32 operator*(float f) const + { + return ColorF32( + b * f, + g * f, + r * f, + a * f); + } + + ColorF32 operator*(ColorF32 const& rhs) const + { + return ColorF32( + b * rhs.b, + g * rhs.g, + r * rhs.r, + a * rhs.a); + } + + ColorF32 operator/(float f) const + { + float fInv = 1.0f / f; + + return ColorF32( + b * fInv, + g * fInv, + r * fInv, + a * fInv); + } + + ColorF32& operator/=(float f) + { + b /= f; + g /= f; + r /= f; + a /= f; + + return *this; + } +}; diff --git a/Source/Engine.cpp b/Source/Engine.cpp new file mode 100644 index 0000000..3e22c82 --- /dev/null +++ b/Source/Engine.cpp @@ -0,0 +1,345 @@ +#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); + } + } +} diff --git a/Source/Engine.hpp b/Source/Engine.hpp new file mode 100644 index 0000000..e8b0c8a --- /dev/null +++ b/Source/Engine.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "Geometry.hpp" +#include + + +const int WINDOW_WIDTH = 1920; +const int WINDOW_HEIGHT = 1080; +const int WINDOW_FPS = 30; +const float CAMERA_FOV = 90.0f; +const float CAMERA_NEAR_CLIP = 5.0f; +const float CAMERA_FAR_CLIP = 600.0f; + + +enum EngineInput +{ + TRANSLATE_X_POS, + TRANSLATE_X_NEG, + TRANSLATE_Y_POS, + TRANSLATE_Y_NEG, + TRANSLATE_Z_POS, + TRANSLATE_Z_NEG, + ROTATE_X_POS, + ROTATE_X_NEG, + ROTATE_Y_POS, + ROTATE_Y_NEG, + ROTATE_Z_POS, + ROTATE_Z_NEG, + SCALE_UP, + SCALE_DOWN +}; + + +struct EngineBuffer +{ + uint32_t* buffer; + 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; +}; + +int EngineInit(char* objFilename, char* mtlFilename); +void EngineRender(EngineBuffer& buffer, uint32_t input); +void EngineShutdown(); diff --git a/Source/Geometry.hpp b/Source/Geometry.hpp new file mode 100644 index 0000000..cfd7295 --- /dev/null +++ b/Source/Geometry.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "Color.hpp" +#include "Point.hpp" +#include "Vec.hpp" +#include + + +const int FACE_LIMIT = 30000; +const int MATERIAL_LIMIT = 5; +const int TEXTURE_SIZE_LIMIT = 1024; +const int VERTEX_LIMIT = 20000; + + +struct Texture +{ + ColorU32 texels[TEXTURE_SIZE_LIMIT][TEXTURE_SIZE_LIMIT]; + unsigned int width; + unsigned int height; +}; + +struct TextureList +{ + Texture data[MATERIAL_LIMIT]; +}; + +struct Material +{ + ColorF32 ambient; + ColorF32 diffuse; + ColorF32 specular; + float glossiness; + float opacity; +}; + +struct MaterialList +{ + Material data[MATERIAL_LIMIT]; + unsigned long size; +}; + +struct UV +{ + float u; + float v; +}; + +struct UVList +{ + UV data[VERTEX_LIMIT]; + unsigned long size; +}; + +struct Vertex +{ + Point position; + Vector normal; + ColorF32 color; +}; + +struct VertexList +{ + Vertex data[VERTEX_LIMIT]; + unsigned long size; +}; + +struct Face +{ + int vertIndex[3]; + int uvIndex[3]; + int materialIndex; +}; + +struct FaceList +{ + Face data[FACE_LIMIT]; + unsigned long size; +}; + +struct Mesh +{ + Point position; + Point rotation; + float scale; +}; diff --git a/Source/Light.hpp b/Source/Light.hpp new file mode 100644 index 0000000..44a95f7 --- /dev/null +++ b/Source/Light.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "Camera.hpp" +#include "Color.hpp" +#include "Geometry.hpp" +#include "Point.hpp" + + +class Light +{ +public: + ColorF32 Compute(Point& eye, Vector& normal, Material& material, Camera& camera) + { + // Point light intensity is a function of the falloff factors and the distance + Vector direction = position - eye; + direction.Normalize(); + float distance = direction.Length(); + + ColorF32 totalIntensity = + (color * intensity) + / (falloffConstant + (falloffLinear * distance)); + + + // Diffuse Light = Kr * I * (alpha + (1 - alpha) * n.d) + float dotNormalDirection = std::max(Vector::Dot(normal, direction), 0.0f); + float alpha = 0.2f; + + ColorF32 diffuseLight = + material.diffuse + * totalIntensity + * (alpha + (1.0f - alpha) * dotNormalDirection); + + + // Specular Light = Ks * I * (r.v)^sp + Vector view = camera.position - eye; + view.Normalize(); + Vector reflection = (normal * 2.0f * dotNormalDirection) - direction; + float dotReflectionView = std::max(Vector::Dot(reflection, view), 0.0f); + + ColorF32 specularLight = + material.specular + * totalIntensity + * powf(dotReflectionView, material.glossiness); + + + // Total light is sum of all lights + ColorF32 result = diffuseLight + specularLight; + + + return result; + } + + Point position; + ColorF32 color; + float intensity; + float falloffConstant; + float falloffLinear; +}; diff --git a/Source/Loader.cpp b/Source/Loader.cpp new file mode 100644 index 0000000..3894965 --- /dev/null +++ b/Source/Loader.cpp @@ -0,0 +1,256 @@ +#include "Engine.hpp" +#include "Loader.hpp" +#include +#include +#include +#include +#include + + +static int LoadTexture(char* filename, Texture& texture, float opacity); + + +#pragma pack(push, 1) +struct BMP_Header +{ + uint16_t fileType; + uint32_t fileSize; + uint16_t reserved0; + uint16_t reserved1; + uint32_t bitmapOffset; + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bitsPerPixel; + uint32_t compression; + uint32_t sizeOfBitmap; + int32_t horizRes; + int32_t vertRes; + uint32_t colorsUsed; + uint32_t colorsImportant; +}; +#pragma pack(pop) + + +int ParseOBJ(char* filename, EngineMemory& memory) +{ + FILE* fp = fopen(filename, "r"); + + if (fp == nullptr) + { + fprintf(stderr, "Error loading file: %s\n", filename); + return -1; + } + + + char line[256]; + unsigned long vertIndex = 0; + unsigned long uvIndex = 0; + unsigned long faceIndex = 0; + int materialIndex = -1; + + VertexList& verts = memory.localVerts; + FaceList& faces = memory.localFaces; + UVList& uvs = memory.uvs; + + while (fgets(line, sizeof(line), fp)) + { + char* separator = strchr(line, ' '); + + if (separator != nullptr) + { + *separator = '\0'; + char* type = line; + char* data = separator + 1; + + if (strcmp(type, "v") == 0) + { + sscanf( + data, "%f %f %f", + &verts.data[vertIndex].position.x, + &verts.data[vertIndex].position.y, + &verts.data[vertIndex].position.z); + + verts.data[vertIndex].position.w = 1.0f; + + ++vertIndex; + } + + else if (strcmp(type, "vt") == 0) + { + sscanf( + data, "%f %f", + &uvs.data[uvIndex].u, + &uvs.data[uvIndex].v); + + ++uvIndex; + } + else if (strcmp(type, "usemtl") == 0) + { + ++materialIndex; + } + + else if (strcmp(type, "f") == 0) + { + sscanf( + data, "%d/%d %d/%d %d/%d", + &faces.data[faceIndex].vertIndex[0], + &faces.data[faceIndex].uvIndex[0], + &faces.data[faceIndex].vertIndex[1], + &faces.data[faceIndex].uvIndex[1], + &faces.data[faceIndex].vertIndex[2], + &faces.data[faceIndex].uvIndex[2]); + + // Convert to 0-indexed + faces.data[faceIndex].vertIndex[0] -= 1; + faces.data[faceIndex].vertIndex[1] -= 1; + faces.data[faceIndex].vertIndex[2] -= 1; + faces.data[faceIndex].uvIndex[0] -= 1; + faces.data[faceIndex].uvIndex[1] -= 1; + faces.data[faceIndex].uvIndex[2] -= 1; + + faces.data[faceIndex].materialIndex = materialIndex; + + ++faceIndex; + } + } + } + + verts.size = vertIndex; + uvs.size = uvIndex; + faces.size = faceIndex; + + return 0; +} + +int ParseMTL(char* filename, EngineMemory& memory) +{ + FILE* fp = fopen(filename, "r"); + + if (fp == nullptr) + { + fprintf(stderr, "Error loading file: %s\n", filename); + return -1; + } + + char line[256]; + int materialIndex = -1; + + MaterialList& materials = memory.materials; + TextureList& textures = memory.textures; + + while (fgets(line, sizeof(line), fp)) + { + char* separator = strchr(line, ' '); + + if (separator != nullptr) + { + *separator = '\0'; + char* type = line; + char* data = separator + 1; + + if (strcmp(type, "newmtl") == 0) + { + ++materialIndex; + } + + else if (strcmp(type, "Ns") == 0) + { + sscanf( + data, "%f", + &materials.data[materialIndex].glossiness); + } + + else if (strcmp(type, "Ka") == 0) + { + sscanf( + data, "%f %f %f", + &materials.data[materialIndex].ambient.r, + &materials.data[materialIndex].ambient.g, + &materials.data[materialIndex].ambient.b); + } + + else if (strcmp(type, "Kd") == 0) + { + sscanf( + data, "%f %f %f", + &materials.data[materialIndex].diffuse.r, + &materials.data[materialIndex].diffuse.g, + &materials.data[materialIndex].diffuse.b); + } + + else if (strcmp(type, "Ks") == 0) + { + sscanf( + data, "%f %f %f", + &materials.data[materialIndex].specular.r, + &materials.data[materialIndex].specular.g, + &materials.data[materialIndex].specular.b); + } + + else if (strcmp(type, "d") == 0) + { + sscanf( + data, "%f", + &materials.data[materialIndex].opacity); + } + + else if (strcmp(type, "map_Kd") == 0) + { + char* textureFilename = data; + textureFilename[strcspn(textureFilename, "\r\n")] = 0; + LoadTexture( + textureFilename, textures.data[materialIndex], + materials.data[materialIndex].opacity); + } + } + } + + materials.size = materialIndex + 1; + + return 0; +} + +static int LoadTexture(char* filename, Texture& texture, float opacity) +{ + FILE* fp = fopen(filename, "r"); + if (fp == nullptr) + { + fprintf(stderr, "Could not open file: %s\n", filename); + return -1; + } + + BMP_Header header = {}; + fread((void*)&header, sizeof(BMP_Header), 1, fp); + fseek(fp, header.bitmapOffset, SEEK_SET); + + // Padding is added to image to align to 4-byte boundaries + unsigned long paddingSize = static_cast(header.width % 4); + + for (int y = 0; y < header.height; ++y) + { + for (int x = 0; x < header.width; ++x) + { + fread(&texture.texels[y][x].b, 1, 1, fp); + fread(&texture.texels[y][x].g, 1, 1, fp); + fread(&texture.texels[y][x].r, 1, 1, fp); + texture.texels[y][x].a = (uint8_t)(255 * opacity); + } + + // Discard padding byte + if (paddingSize != 0) + { + uint32_t padding; + fread(&padding, paddingSize, 1, fp); + } + } + + texture.width = (unsigned int)header.width; + texture.height = (unsigned int)header.height; + + fclose(fp); + + return 0; +} + diff --git a/Source/Loader.hpp b/Source/Loader.hpp new file mode 100644 index 0000000..94c7d33 --- /dev/null +++ b/Source/Loader.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "Engine.hpp" + + +int ParseOBJ(char* filename, EngineMemory& memory); +int ParseMTL(char* filename, EngineMemory& memory); diff --git a/Source/Main.cpp b/Source/Main.cpp new file mode 100644 index 0000000..7b1108d --- /dev/null +++ b/Source/Main.cpp @@ -0,0 +1,60 @@ +#include "Engine.hpp" +#include "Platform.hpp" +#include +#include +#include + + +// MAIN +int main(int argc, char* argv[]) +{ + if (argc != 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + char* objFilename = argv[1]; + char* mtlFilename = argv[2]; + Platform platform{}; + + if (Platform_Init(platform, WINDOW_WIDTH, WINDOW_HEIGHT) == PlatformStatus::Error) + { + return EXIT_FAILURE; + } + + if (EngineInit(objFilename, mtlFilename) < 0) + { + return EXIT_FAILURE; + } + + EngineBuffer buffer{}; + buffer.buffer = reinterpret_cast(platform.surface->pixels); + buffer.width = platform.surface->w; + buffer.height = platform.surface->h; + + while (true) + { + Platform_GetFrameTime(platform); + + if (Platform_CheckForEvents(platform) == PlatformStatus::Quit) + { + break; + } + + Platform_ClearWindow(platform); + + EngineRender(buffer, platform.input); + + Platform_UpdateWindow(platform); + + Platform_SyncToFramerate(platform); + } + + EngineShutdown(); + + Platform_Shutdown(platform); + + return EXIT_SUCCESS; +} + diff --git a/Source/Matrix.hpp b/Source/Matrix.hpp new file mode 100644 index 0000000..3727c19 --- /dev/null +++ b/Source/Matrix.hpp @@ -0,0 +1,49 @@ +#pragma once + + +class Matrix +{ +public: + Matrix() + { + e11 = 1.0; e12 = 0.0; e13 = 0.0; e14 = 0.0; + e21 = 0.0; e22 = 1.0; e23 = 0.0; e24 = 0.0; + e31 = 0.0; e32 = 0.0; e33 = 1.0; e34 = 0.0; + e41 = 0.0; e42 = 0.0; e43 = 0.0; e44 = 1.0; + } + + union + { + float e[4][4]; + + struct + { + float e11, e12, e13, e14; + float e21, e22, e23, e24; + float e31, e32, e33, e34; + float e41, e42, e43, e44; + }; + }; + + Matrix operator*(Matrix const& rhs) + { + Matrix result; + + for (int row = 0; row < 4; ++row) + { + for (int col = 0; col < 4; ++col) + { + float sum = 0.0; + + for (int i = 0; i < 4; ++i) + { + sum += e[row][i] * rhs.e[i][col]; + } + + result.e[row][col] = sum; + } + } + + return result; + } +}; diff --git a/Source/Platform.cpp b/Source/Platform.cpp new file mode 100644 index 0000000..7fbbf42 --- /dev/null +++ b/Source/Platform.cpp @@ -0,0 +1,243 @@ +#include "Engine.hpp" +#include "Platform.hpp" +#include +#include + + +void constexpr SetBit(uint32_t& x, unsigned long bit) +{ + x |= (1UL << bit); +} + +void constexpr ClearBit(uint32_t& x, unsigned long bit) +{ + x &= ~(1UL << bit); +} + +static void HandleEvent(Platform& platform, SDL_Event& event); + + +PlatformStatus Platform_Init(Platform& platform, int width, int height) +{ + int result = SDL_Init(SDL_INIT_VIDEO); + if (result < 0) + { + fprintf(stderr, "Error initializing SDL: %s\n", SDL_GetError()); + return PlatformStatus::Error; + } + + + SDL_Window* window = SDL_CreateWindow( + "Soft 3D Engine", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width, height, + SDL_WINDOW_SHOWN); + if (window == nullptr) + { + fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); + return PlatformStatus::Error; + } + + + SDL_Surface* surface = SDL_GetWindowSurface(window); + if (surface == nullptr) + { + fprintf(stderr, "Error getting SDL window surface: %s\n", SDL_GetError()); + return PlatformStatus::Error; + } + + + result = SDL_ShowCursor(SDL_DISABLE); + if (result < 0) + { + fprintf(stderr, "Error disabling cursor in SDL window: %s\n", SDL_GetError()); + return PlatformStatus::Error; + } + + platform.framerateMillis = (1000 / WINDOW_FPS); + platform.window = window; + platform.surface = surface; + + return PlatformStatus::Ok; +} + +PlatformStatus Platform_CheckForEvents(Platform& platform) +{ + SDL_Event event; + + while (SDL_PollEvent(&event) != 0) + { + if (event.type == SDL_QUIT) + { + return PlatformStatus::Quit; + } + else + { + HandleEvent(platform, event); + } + } + + return PlatformStatus::Ok; +} + +void Platform_ClearWindow(Platform& platform) +{ + SDL_LockSurface(platform.surface); + SDL_FillRect(platform.surface, nullptr, 0); +} + +void Platform_UpdateWindow(Platform& platform) +{ + SDL_UnlockSurface(platform.surface); + SDL_UpdateWindowSurface(platform.window); +} + +void Platform_GetFrameTime(Platform& platform) +{ + platform.frameStartMillis = SDL_GetTicks(); +} + +void Platform_SyncToFramerate(Platform& platform) +{ + uint32_t stopTimeMillis = SDL_GetTicks(); + uint32_t framerateMillis = stopTimeMillis - platform.frameStartMillis; + + // Delay if time to spare + if (framerateMillis < platform.framerateMillis) + { + uint32_t delayMillis = platform.framerateMillis - framerateMillis; + SDL_Delay(delayMillis); + } +} + +void Platform_Shutdown(Platform& platform) +{ + SDL_DestroyWindow(platform.window); + SDL_Quit(); +} + + +// PRIVATE FUNCTIONS +static void HandleEvent( + Platform& platform, SDL_Event& event) +{ + if (event.type == SDL_KEYDOWN) + { + if (event.key.keysym.sym == SDLK_w) + { + SetBit(platform.input, TRANSLATE_Z_POS); + } + else if (event.key.keysym.sym == SDLK_s) + { + SetBit(platform.input, TRANSLATE_Z_NEG); + } + else if (event.key.keysym.sym == SDLK_a) + { + SetBit(platform.input, TRANSLATE_X_NEG); + } + else if (event.key.keysym.sym == SDLK_d) + { + SetBit(platform.input, TRANSLATE_X_POS); + } + else if (event.key.keysym.sym == SDLK_q) + { + SetBit(platform.input, TRANSLATE_Y_POS); + } + else if (event.key.keysym.sym == SDLK_e) + { + SetBit(platform.input, TRANSLATE_Y_NEG); + } + else if (event.key.keysym.sym == SDLK_i) + { + SetBit(platform.input, ROTATE_X_POS); + } + else if (event.key.keysym.sym == SDLK_k) + { + SetBit(platform.input, ROTATE_X_NEG); + } + else if (event.key.keysym.sym == SDLK_j) + { + SetBit(platform.input, ROTATE_Y_POS); + } + else if (event.key.keysym.sym == SDLK_l) + { + SetBit(platform.input, ROTATE_Y_NEG); + } + else if (event.key.keysym.sym == SDLK_u) + { + SetBit(platform.input, ROTATE_Z_POS); + } + else if (event.key.keysym.sym == SDLK_o) + { + SetBit(platform.input, ROTATE_Z_NEG); + } + else if (event.key.keysym.sym == SDLK_UP) + { + SetBit(platform.input, SCALE_UP); + } + else if (event.key.keysym.sym == SDLK_DOWN) + { + SetBit(platform.input, SCALE_DOWN); + } + } + else if (event.type == SDL_KEYUP) + { + if (event.key.keysym.sym == SDLK_w) + { + ClearBit(platform.input, TRANSLATE_Z_POS); + } + else if (event.key.keysym.sym == SDLK_s) + { + ClearBit(platform.input, TRANSLATE_Z_NEG); + } + else if (event.key.keysym.sym == SDLK_a) + { + ClearBit(platform.input, TRANSLATE_X_NEG); + } + else if (event.key.keysym.sym == SDLK_d) + { + ClearBit(platform.input, TRANSLATE_X_POS); + } + else if (event.key.keysym.sym == SDLK_q) + { + ClearBit(platform.input, TRANSLATE_Y_POS); + } + else if (event.key.keysym.sym == SDLK_e) + { + ClearBit(platform.input, TRANSLATE_Y_NEG); + } + else if (event.key.keysym.sym == SDLK_i) + { + ClearBit(platform.input, ROTATE_X_POS); + } + else if (event.key.keysym.sym == SDLK_k) + { + ClearBit(platform.input, ROTATE_X_NEG); + } + else if (event.key.keysym.sym == SDLK_j) + { + ClearBit(platform.input, ROTATE_Y_POS); + } + else if (event.key.keysym.sym == SDLK_l) + { + ClearBit(platform.input, ROTATE_Y_NEG); + } + else if (event.key.keysym.sym == SDLK_u) + { + ClearBit(platform.input, ROTATE_Z_POS); + } + else if (event.key.keysym.sym == SDLK_o) + { + ClearBit(platform.input, ROTATE_Z_NEG); + } + else if (event.key.keysym.sym == SDLK_UP) + { + ClearBit(platform.input, SCALE_UP); + } + else if (event.key.keysym.sym == SDLK_DOWN) + { + ClearBit(platform.input, SCALE_DOWN); + } + } +} + diff --git a/Source/Platform.hpp b/Source/Platform.hpp new file mode 100644 index 0000000..2105243 --- /dev/null +++ b/Source/Platform.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + + +enum class PlatformStatus +{ + Error = -1, + Ok, + Quit +}; + + +// STRUCTURES +struct Platform +{ + SDL_Window* window; + SDL_Surface* surface; + uint32_t input; + uint32_t framerateMillis; + uint32_t frameStartMillis; +}; + + +// FUNCTIONS +PlatformStatus Platform_Init(Platform& platform, int width, int height); + +PlatformStatus Platform_CheckForEvents(Platform& platform); + +void Platform_ClearWindow(Platform& platform); + +void Platform_UpdateWindow(Platform& platform); + +void Platform_GetFrameTime(Platform& platform); + +void Platform_SyncToFramerate(Platform& platform); + +void Platform_Shutdown(Platform& platform); diff --git a/Source/Point.hpp b/Source/Point.hpp new file mode 100644 index 0000000..2a347c0 --- /dev/null +++ b/Source/Point.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include "Matrix.hpp" +#include "Vec.hpp" + + +class Point +{ +public: + Point() + : x(0), y(0), z(0), w(1) + {} + + Point(float x, float y, float z) + : x(x), y(y), z(z), w(1) + {} + + Point operator-() const + { + return Point( + -x, + -y, + -z); + } + + Vector operator-(Point const& rhs) const + { + return Vector( + x - rhs.x, + y - rhs.y, + z - rhs.z); + } + + Point operator*(Matrix const& rhs) + { + Point result; + + for (int col = 0; col < 4; ++col) + { + float sum = 0.0; + + for (int row = 0; row < 4; ++row) + { + sum += e[row] * rhs.e[row][col]; + } + + result.e[col] = sum; + } + + return result; + } + + Point& operator*=(Matrix const& rhs) + { + *this = *this * rhs; + + return *this; + } + + Point operator/(float rhs) const + { + float inverse = 1.0f / rhs; + + return Point( + x * inverse, + y * inverse, + z * inverse); + } + + Point& operator/=(float rhs) + { + x /= rhs; + y /= rhs; + z /= rhs; + + return *this; + } + + union + { + float e[4]; + + struct + { + float x, y, z, w; + }; + }; +}; diff --git a/Source/Render.cpp b/Source/Render.cpp new file mode 100644 index 0000000..065b31b --- /dev/null +++ b/Source/Render.cpp @@ -0,0 +1,199 @@ +#include "Color.hpp" +#include "Engine.hpp" +#include "Geometry.hpp" +#include "Render.hpp" + + +class BoundingBox +{ +public: + BoundingBox(Point const& v0, Point const& v1, Point const& v2) + { + yMin = std::min(v0.y, std::min(v1.y, v2.y)); + yMax = std::max(v0.y, std::max(v1.y, v2.y)); + + xMin = std::min(v0.x, std::min(v1.x, v2.x)); + xMax = std::max(v0.x, std::max(v1.x, v2.x)); + } + + float yMin, yMax; + float xMin, xMax; +}; + + +void Render(EngineBuffer& buffer, EngineMemory& memory) +{ + FaceList const& faces = memory.transFaces; + VertexList const& verts = memory.transVerts; + TextureList const& textures = memory.textures; + UVList const& uvs = memory.uvs; + + for (size_t f = 0; f < faces.size; ++f) + { + Face const& face = faces.data[f]; + + Point const& p0 = verts.data[face.vertIndex[0]].position; + Point const& p1 = verts.data[face.vertIndex[1]].position; + Point const& p2 = verts.data[face.vertIndex[2]].position; + + + // Bounding box for barycentric calculations (top-left fill convention) + BoundingBox box(p0, p1, p2); + int yMin = static_cast(std::max(ceilf(box.yMin), 0)); + int yMax = static_cast(std::min(ceilf(box.yMax) - 1, buffer.height - 1)); + int xMin = static_cast(std::max(ceilf(box.xMin), 0)); + int xMax = static_cast(std::min(ceilf(box.xMax) - 1, buffer.width - 1)); + + + // Constants for this triangle used for barycentric calculations + Vector v01 = p1 - p0; + Vector v02 = p2 - p0; + 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) + { + // Calculate the barycentric coordinate of this point + Point p(x, y, 1.0f); + Vector v0P = p - p0; + float dot0P01 = Vector::Dot(v0P, v01); + float dot0P02 = Vector::Dot(v0P, v02); + float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102)); + 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)) + { + // Interpolate 1/z for the z-buffer + float zInv = + 1.0f / + ((barycenter[0] * p0.w) + + (barycenter[1] * p1.w) + + (barycenter[2] * p2.w)); + + + // Compute pixel color if it's closer than what's in the z-buffer + if (zInv > memory.zbuffer[y][x]) + { + // Update the depth buffer + memory.zbuffer[y][x] = zInv; + + + // Interpolate U and V of the texture for this point + Texture const& texture = textures.data[faces.data[f].materialIndex]; + UV const& uv0 = uvs.data[face.uvIndex[0]]; + UV const& uv1 = uvs.data[face.uvIndex[1]]; + UV const& uv2 = uvs.data[face.uvIndex[2]]; + + float a = barycenter[0] * p1.w * p2.w; + float b = barycenter[1] * p0.w * p2.w; + float c = barycenter[2] * p0.w * p1.w; + float abc = 1.0f / (a + b + c); + + float uInterp = + ((a * uv0.u) + (b * uv1.u) + (c * uv2.u)) + * abc + * texture.width; + + float vInterp = + ((a * uv0.v) + (b * uv1.v) + (c * uv2.v)) + * abc + * texture.height; + + + // Bilinear filtering + unsigned int u = static_cast(uInterp); + unsigned int v = static_cast(vInterp); + float du = uInterp - u; + float dv = vInterp - v; + float duDiff = 1 - du; + float dvDiff = 1 - dv; + + ColorU32 color( + static_cast( + (duDiff * dvDiff * texture.texels[v][u].b + + du * dvDiff * texture.texels[v][u + 1].b + + du * dv * texture.texels[v + 1][u + 1].b + + duDiff * dv * texture.texels[v + 1][u].b)), + + static_cast( + (duDiff * dvDiff * texture.texels[v][u].g + + du * dvDiff * texture.texels[v][u + 1].g + + du * dv * texture.texels[v + 1][u + 1].g + + duDiff * dv * texture.texels[v + 1][u].g)), + + static_cast( + (duDiff * dvDiff * texture.texels[v][u].r + + du * dvDiff * texture.texels[v][u + 1].r + + du * dv * texture.texels[v + 1][u + 1].r + + duDiff * dv * texture.texels[v + 1][u].r)), + + static_cast( + (duDiff * dvDiff * texture.texels[v][u].a + + du * dvDiff * texture.texels[v][u + 1].a + + du * dv * texture.texels[v + 1][u + 1].a + + duDiff * dv * texture.texels[v + 1][u].a))); + + + // Perform Gouraud shading on the texture + ColorF32 const& c0 = verts.data[face.vertIndex[0]].color; + ColorF32 const& c1 = verts.data[face.vertIndex[1]].color; + ColorF32 const& c2 = verts.data[face.vertIndex[2]].color; + + ColorF32 shading = + (c0 * barycenter[0]) + + (c1 * barycenter[1]) + + (c2 * barycenter[2]); + + + // Ensure no color channel exceeds max + shading.Scale(); + + + // Light the texture + color.b *= shading.b; + color.g *= shading.g; + color.r *= shading.r; + + + // Alpha blend the pixel + ColorU32* pixel = reinterpret_cast(&buffer.buffer[y * buffer.width + x]); + float alpha = static_cast(color.a) / 255.0f; + float alphaDiff = 1.0f - alpha; + + ColorU32 blended = + { + static_cast(((alpha * static_cast(color.b)) + + (alphaDiff * static_cast(pixel->b)))), + + static_cast(((alpha * static_cast(color.g)) + + (alphaDiff * static_cast(pixel->g)))), + + static_cast(((alpha * static_cast(color.r)) + + (alphaDiff * static_cast(pixel->r)))), + + static_cast(((alpha * static_cast(color.a)) + + (alphaDiff * static_cast(pixel->a)))) + }; + + + // Draw + buffer.buffer[buffer.width * y + x] = blended.u32; + } + } + } + } + } +} + diff --git a/Source/Render.hpp b/Source/Render.hpp new file mode 100644 index 0000000..86924c4 --- /dev/null +++ b/Source/Render.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "Engine.hpp" + + +void Render(EngineBuffer& buffer, EngineMemory& memory); diff --git a/Source/Transform.cpp b/Source/Transform.cpp new file mode 100644 index 0000000..7df7dc5 --- /dev/null +++ b/Source/Transform.cpp @@ -0,0 +1,105 @@ +#include "Matrix.hpp" +#include "Transform.hpp" +#include + + +Matrix Transform_Translate(Point& translation) +{ + Matrix result; + + result.e41 = translation.x; + result.e42 = translation.y; + result.e43 = translation.z; + + return result; +} + +Matrix Transform_Rotate(Point& rotation) +{ + // YXZ Euler rotation + float cosThetaY = cosf(rotation.y); + float sinThetaY = sinf(rotation.y); + + Matrix tRotateY; + + tRotateY.e11 = cosThetaY; + tRotateY.e13 = -sinThetaY; + tRotateY.e31 = sinThetaY; + tRotateY.e33 = cosThetaY; + + + float cosThetaX = cosf(rotation.x); + float sinThetaX = sinf(rotation.x); + + Matrix tRotateX; + + tRotateX.e22 = cosThetaX; + tRotateX.e23 = sinThetaX; + tRotateX.e32 = -sinThetaX; + tRotateX.e33 = cosThetaX; + + + float cosThetaZ = cosf(rotation.z); + float sinThetaZ = sinf(rotation.z); + + Matrix tRotateZ; + + tRotateZ.e11 = cosThetaZ; + tRotateZ.e12 = sinThetaZ; + tRotateZ.e21 = -sinThetaZ; + tRotateZ.e22 = cosThetaZ; + + + Matrix result = tRotateY * tRotateX * tRotateZ; + + return result; +} + +Matrix Transform_Scale(float scale) +{ + Matrix result; + + result.e11 = scale; + result.e22 = scale; + result.e33 = scale; + + return result; +} + +Matrix Transform_View(Camera& camera) +{ + Point invPosition = -camera.position; + Matrix tInvTranslate = Transform_Translate(invPosition); + + Point invRotation = -camera.rotation; + Matrix tInvRotate = Transform_Rotate(invRotation); + + Matrix result = tInvTranslate * tInvRotate; + + return result; +} + +Matrix Transform_Perspective(Camera& camera) +{ + Matrix result; + + result.e11 = camera.xZoom; + result.e22 = camera.yZoom; + result.e33 = camera.zClipBias0; + result.e34 = 1; + result.e43 = camera.zClipBias1; + + return result; +} + +Matrix Transform_Screen(Camera& camera) +{ + Matrix result; + + result.e11 = camera.xScale; + result.e41 = camera.xScale; + result.e22 = -camera.yScale; + result.e42 = camera.yScale; + + return result; +} diff --git a/Source/Transform.hpp b/Source/Transform.hpp new file mode 100644 index 0000000..d16caab --- /dev/null +++ b/Source/Transform.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Camera.hpp" +#include "Matrix.hpp" + + +Matrix Transform_Translate(Point& translation); +Matrix Transform_Rotate(Point& rotation); +Matrix Transform_Scale(float scale); +Matrix Transform_View(Camera& camera); +Matrix Transform_Perspective(Camera& camera); +Matrix Transform_Screen(Camera& camera); diff --git a/Source/Vec.hpp b/Source/Vec.hpp new file mode 100644 index 0000000..a1a6079 --- /dev/null +++ b/Source/Vec.hpp @@ -0,0 +1,159 @@ +#pragma once + +#include "Matrix.hpp" +#include +#include + + +class Vector +{ +public: + Vector() + : x(0), y(0), z(0), w(0) + {} + + Vector(float x, float y, float z) + : x(x), y(y), z(z), w(0) + {} + + Vector operator-() const + { + return Vector( + -x, + -y, + -z); + } + + Vector operator-(Vector const& rhs) const + { + return Vector( + x - rhs.x, + y - rhs.y, + z - rhs.z); + } + + Vector operator+(Vector const& rhs) const + { + return Vector( + x + rhs.x, + y + rhs.y, + z + rhs.z); + } + + Vector& operator+=(Vector const& rhs) + { + x += rhs.x; + y += rhs.y; + z += rhs.z; + + return *this; + } + + Vector operator*(float rhs) const + { + return Vector( + x * rhs, + y * rhs, + z * rhs); + } + + Vector operator*(Matrix const& rhs) + { + Vector result; + + for (int col = 0; col < 4; ++col) + { + float sum = 0.0; + + for (int row = 0; row < 4; ++row) + { + sum += e[row] * rhs.e[row][col]; + } + + result.e[col] = sum; + } + + return result; + } + + Vector operator/(float rhs) const + { + float inverse = 1.0f / rhs; + + return Vector( + x * inverse, + y * inverse, + z * inverse); + } + + Vector& operator/=(float rhs) + { + x /= rhs; + y /= rhs; + z /= rhs; + + return *this; + } + + float Length() + { + float result = sqrtf(x * x + y * y + z * z); + + if (result < 0.0f) + { + result = 0.0f; + } + + return result; + } + + void Normalize() + { + float length = Length(); + + if (length == 0.0f) + { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + else + { + float lengthInv = 1.0f / length; + + x *= lengthInv; + y *= lengthInv; + z *= lengthInv; + } + } + + static Vector Cross(Vector& v1, Vector& v2) + { + Vector result; + + result.x = (v1.y * v2.z) - (v1.z * v2.y); + result.y = (v1.z * v2.x) - (v1.x * v2.z); + result.z = (v1.x * v2.y) - (v1.y * v2.x); + + return result; + } + + static float Dot(Vector& v1, Vector& v2) + { + float result; + + result = (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); + + return result; + } + + union + { + float e[4]; + + struct + { + float x, y, z, w; + }; + }; +}; diff --git a/include/camera.h b/include/camera.h deleted file mode 100644 index bef7754..0000000 --- a/include/camera.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef CAMERA_H - -#include "engine.h" -#include "point.h" -#include "util.h" -#include - - -// STRUCTURE -struct Camera -{ - inline Camera(void) - { - position.x = 0.0f; - position.y = 0.0f; - position.z = 0.0f; - - rotation.x = 0.0f; - rotation.y = 0.0f; - rotation.z = 0.0f; - - zClipBias0 = - (CAMERA_FAR_CLIP + CAMERA_NEAR_CLIP) - / (CAMERA_FAR_CLIP - CAMERA_NEAR_CLIP); - - zClipBias1 = - (-2.0f * CAMERA_FAR_CLIP * CAMERA_NEAR_CLIP) - / (CAMERA_FAR_CLIP - CAMERA_NEAR_CLIP); - - xZoom = 1.0f / tanf(DEG_TO_RAD(CAMERA_FOV/2.0f)); - yZoom = (xZoom * WINDOW_WIDTH) / WINDOW_HEIGHT; - - xScale = (0.5f * WINDOW_WIDTH) - 0.5f; - yScale = (0.5f * WINDOW_HEIGHT) - 0.5f; - } - - Point position; - Point rotation; - float zClipBias0, zClipBias1; - float xZoom, yZoom; - float xScale, yScale; -}; - - -#define CAMERA_H -#endif - diff --git a/include/color.h b/include/color.h deleted file mode 100644 index 577aa9c..0000000 --- a/include/color.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef COLOR_H - -#include "util.h" -#include - - -// STRUCTURES -struct ColorU32 -{ - union - { - struct - { - uint8_t b, g, r, a; - }; - - uint32_t u32; - }; -}; - -struct ColorF32 -{ - float b, g, r, a; -}; - - - - -// OPERATORS -// c1 + c2 -inline ColorF32 operator+(ColorF32 const &c1, ColorF32 const &c2) -{ - ColorF32 result; - - result.b = c1.b + c2.b; - result.g = c1.g + c2.g; - result.r = c1.r + c2.r; - result.a = c1.a + c2.a; - - return result; -} - -// c1 += c2 -inline ColorF32 &operator+=(ColorF32 &c1, ColorF32 const &c2) -{ - c1 = c1 + c2; - - return c1; -} - -// c * f -inline ColorF32 operator*(ColorF32 const &c, float f) -{ - ColorF32 result; - - result.b = f * c.b; - result.g = f * c.g; - result.r = f * c.r; - result.a = f * c.a; - - return result; -} - -// f * c -inline ColorF32 operator*(float f, ColorF32 const &c) -{ - ColorF32 result; - - result = c * f; - - return result; -} - -// c1 * c2 -inline ColorF32 operator*(ColorF32 const &c1, ColorF32 const &c2) -{ - ColorF32 result; - - result.b = c1.b * c2.b; - result.g = c1.g * c2.g; - result.r = c1.r * c2.r; - result.a = c1.a * c2.a; - - return result; -} - -// c / f -inline ColorF32 operator/(ColorF32 const &c, float f) -{ - ColorF32 result; - - float invF = 1.0f / f; - - result.b = c.b * invF; - result.g = c.g * invF; - result.r = c.r * invF; - result.a = c.a * invF; - - return result; -} - -// c /= f -inline ColorF32 &operator/=(ColorF32 &c, float f) -{ - c = c / f; - - return c; -} - - -// PUBLIC FUNCTIONS -inline void ScaleColor(ColorF32 &c) -{ - float blue = MAX(c.b, 0.0f); - float green = MAX(c.g, 0.0f); - float red = MAX(c.r, 0.0f); - float alpha = MAX(c.a, 0.0f); - float max = MAX(MAX(MAX(blue,green),red),1.0f); - - ColorF32 scaled = {blue, green, red, alpha}; - scaled /= max; - - c.b = scaled.b; - c.g = scaled.g; - c.r = scaled.r; - c.a = scaled.a; -} - - -#define COLOR_H -#endif - diff --git a/include/engine.h b/include/engine.h deleted file mode 100644 index 9d5af22..0000000 --- a/include/engine.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef ENGINE_H - -#include "geometry.h" -#include - - -// WINDOW CONFIGURATION -#define WINDOW_WIDTH (1920) -#define WINDOW_HEIGHT (1080) -#define WINDOW_FPS (30) - -// CAMERA CONFIGURATION -#define CAMERA_FOV (90.0f) -#define CAMERA_NEAR_CLIP (5.0f) -#define CAMERA_FAR_CLIP (600.0f) - - -// ENUMS -enum EngineInput -{ - TRANSLATE_X_POS, - TRANSLATE_X_NEG, - TRANSLATE_Y_POS, - TRANSLATE_Y_NEG, - TRANSLATE_Z_POS, - TRANSLATE_Z_NEG, - ROTATE_X_POS, - ROTATE_X_NEG, - ROTATE_Y_POS, - ROTATE_Y_NEG, - ROTATE_Z_POS, - ROTATE_Z_NEG, - SCALE_UP, - SCALE_DOWN -}; - - -// STRUCTURES -struct EngineBuffer -{ - uint32_t *buffer; - 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 EngineInit(char *objFilename, char *mtlFilename); -void EngineRender(EngineBuffer &buffer, uint32_t input); -void EngineShutdown(void); - - -#define ENGINE_H -#endif - diff --git a/include/geometry.h b/include/geometry.h deleted file mode 100644 index 32d1c67..0000000 --- a/include/geometry.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef GEOMETRY_H - -#include "color.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 -{ - ColorU32 texels[TEXTURE_SIZE_LIMIT][TEXTURE_SIZE_LIMIT]; - unsigned int width; - unsigned int height; -}; - -struct TextureList -{ - Texture data[MATERIAL_LIMIT]; - size_t size; -}; - -struct Material -{ - ColorF32 ambient; - ColorF32 diffuse; - ColorF32 specular; - float glossiness; - float opacity; -}; - -struct MaterialList -{ - Material data[MATERIAL_LIMIT]; - size_t size; -}; - -struct UV -{ - float u; - float v; -}; - -struct UVList -{ - UV data[VERTEX_LIMIT]; - size_t size; -}; - -struct Vertex -{ - Point position; - Vector normal; - ColorF32 color; -}; - -struct VertexList -{ - Vertex data[VERTEX_LIMIT]; - size_t size; -}; - -struct Face -{ - int vertIndex[3]; - int uvIndex[3]; - int materialIndex; -}; - -struct FaceList -{ - Face data[FACE_LIMIT]; - size_t size; -}; - -struct Mesh -{ - Point position; - Point rotation; - float scale; -}; - - -#define GEOMETRY_H -#endif - diff --git a/include/light.h b/include/light.h deleted file mode 100644 index 959738b..0000000 --- a/include/light.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef LIGHT_H - - -#include "camera.h" -#include "color.h" -#include "geometry.h" -#include "point.h" - - -// STRUCTURES -struct Light -{ - Point position; - ColorF32 color; - float intensity; - float falloffConstant; - float falloffLinear; -}; - - -// PUBLIC FUNCTIONS -ColorF32 ComputeLight(Point &position, Vector &normal, Material &material, Light &light, Camera &camera); - - -#define LIGHT_H -#endif - diff --git a/include/loader.h b/include/loader.h deleted file mode 100644 index ec083a6..0000000 --- a/include/loader.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef LOADER_H - -#include "engine.h" -#include "geometry.h" - - -int ParseOBJ(char *filename, EngineMemory &memory); -int ParseMTL(char *filename, EngineMemory &memory); - - -#define LOADER_H -#endif - diff --git a/include/matrix.h b/include/matrix.h deleted file mode 100644 index 592fa9d..0000000 --- a/include/matrix.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef MATRIX_H - - -#include "point.h" - - -// STRUCTURE -struct Matrix -{ - inline Matrix(void) - { - e11 = 1.0; e12 = 0.0; e13 = 0.0; e14 = 0.0; - e21 = 0.0; e22 = 1.0; e23 = 0.0; e24 = 0.0; - e31 = 0.0; e32 = 0.0; e33 = 1.0; e34 = 0.0; - e41 = 0.0; e42 = 0.0; e43 = 0.0; e44 = 1.0; - } - - union - { - float e[4][4]; - - struct - { - float e11, e12, e13, e14; - float e21, e22, e23, e24; - float e31, e32, e33, e34; - float e41, e42, e43, e44; - }; - }; -}; - - -// OPERATORS -// m1 * m2 -inline Matrix operator*(Matrix const &m1, Matrix const &m2) -{ - Matrix result; - - for (int row = 0; row < 4; ++row) - { - for (int col = 0; col < 4; ++col) - { - float sum = 0.0; - - for (int i = 0; i < 4; ++i) - { - sum += m1.e[row][i] * m2.e[i][col]; - } - - result.e[row][col] = sum; - } - } - - return result; -} - -// v * m -inline Point operator*(Point const &v, Matrix const &m) -{ - Point result; - - for (int col = 0; col < 4; ++col) - { - float sum = 0.0; - - for (int row = 0; row < 4; ++row) - { - sum += v.e[row] * m.e[row][col]; - } - - result.e[col] = sum; - } - - return result; -} - -// v *=m -inline Point &operator*=(Point &v, Matrix const &m) -{ - v = v * m; - - return v; -} - -// v * m -inline Vector operator*(Vector const &v, Matrix const &m) -{ - Vector result; - - for (int col = 0; col < 4; ++col) - { - float sum = 0.0; - - for (int row = 0; row < 4; ++row) - { - sum += v.e[row] * m.e[row][col]; - } - - result.e[col] = sum; - } - - return result; -} - - -#define MATRIX_H -#endif - diff --git a/include/platform.h b/include/platform.h deleted file mode 100644 index 4f475a9..0000000 --- a/include/platform.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef PLATFORM_H - -#include - - -// ENUMERATIONS -enum Platform_Status -{ - PLATFORM_ERROR = -1, - PLATFORM_OK, - PLATFORM_QUIT -}; - - -// STRUCTURES -struct Platform -{ - SDL_Window *window; - SDL_Surface *surface; - uint32_t input; - uint32_t framerateMillis; - uint32_t frameStartMillis; -}; - - -// FUNCTIONS -int Platform_Init(Platform &platform, int width, int height); - -int Platform_CheckForEvents(Platform &platform); - -void Platform_ClearWindow(Platform &platform); - -void Platform_UpdateWindow(Platform &platform); - -void Platform_GetFrameTime(Platform &platform); - -void Platform_SyncToFramerate(Platform &platform); - -void Platform_Shutdown(Platform &platform); - - -#define PLATFORM_H -#endif - diff --git a/include/point.h b/include/point.h deleted file mode 100644 index abf579f..0000000 --- a/include/point.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef POINT_H - -#include "vec.h" - - -// STRUCTURE -struct Point -{ - inline Point(void) : x(0), y(0), z(0), w(1) {} - inline Point(float x, float y, float z) : x(x), y(y), z(z), w(1) {} - - union - { - float e[4]; - - struct - { - float x, y, z, w; - }; - }; -}; - - -// OPERATORS -// p / f -inline Point operator/(Point const &p, float f) -{ - Point result; - - float inverse = 1.0f / f; - - result.x = p.x * inverse; - result.y = p.y * inverse; - result.z = p.z * inverse; - - return result; -} - -// v /= f -inline Point &operator/=(Point &p, float f) -{ - p = p / f; - - return p; -} - -// p1 - p2 -inline Vector operator-(Point const &p1, Point const &p2) -{ - Vector result; - - result.x = p1.x - p2.x; - result.y = p1.y - p2.y; - result.z = p1.z - p2.z; - - return result; -} - -// -p -inline Point operator-(Point const &p) -{ - Point result; - - result.x = -p.x; - result.y = -p.y; - result.z = -p.z; - - return result; -} - - -#define POINT_H -#endif - diff --git a/include/render.h b/include/render.h deleted file mode 100644 index cfbdba6..0000000 --- a/include/render.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef RENDER_H - -#include "engine.h" - - -// PUBLIC FUNCTIONS -void Render(EngineBuffer &buffer, EngineMemory &memory); - - -#define RENDER_H -#endif - diff --git a/include/transform.h b/include/transform.h deleted file mode 100644 index 39f8aad..0000000 --- a/include/transform.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TRANSFORM_H - -#include "camera.h" -#include "matrix.h" - - -Matrix Transform_Translate(Point &translation); -Matrix Transform_Rotate(Point &rotation); -Matrix Transform_Scale(float scale); -Matrix Transform_View(Camera &camera); -Matrix Transform_Perspective(Camera &camera); -Matrix Transform_Screen(Camera &camera); - - -#define TRANSFORM_H -#endif - diff --git a/include/util.h b/include/util.h deleted file mode 100644 index 056a9e0..0000000 --- a/include/util.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef UTIL_H - -#include - - -// CONSTANTS -const float EPSILON_E3 = 1E-3f; - - -// MACROS -#define SET_BIT(x, bit) (x |= (1UL << bit)) -#define CLEAR_BIT(x, bit) (x &= ~(1UL << bit)) -#define CHECK_BIT(x, bit) (x & (1UL << bit)) -#define DEG_TO_RAD(deg) ((deg * (float)M_PI) / 180.0f) -#define SWAP(a, b, temp) {temp = a; a = b; b = temp;} -#define MIN(a, b) ((a < b) ? a : b) -#define MAX(a, b) ((a > b) ? a : b) -#define FLOAT_EQUAL(a, b) ((fabsf(a - b) < EPSILON_E3) ? 1 : 0) - - -#define UTIL_H -#endif - diff --git a/include/vec.h b/include/vec.h deleted file mode 100644 index 1b9f6a3..0000000 --- a/include/vec.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef VEC_H - -#include "util.h" -#include - - -// STRUCTURE -struct Vector -{ - inline Vector(void) : x(0), y(0), z(0), w(0) {} - inline Vector(float x, float y, float z) : x(x), y(y), z(z), w(0) {} - - union - { - float e[4]; - - struct - { - float x, y, z, w; - }; - }; -}; - - -// OPERATORS -// -v -inline Vector operator-(Vector const &v) -{ - Vector result; - - result.x = -v.x; - result.y = -v.y; - result.z = -v.z; - - return result; -} - -// v1 - v2 -inline Vector operator-(Vector const &v1, Vector const &v2) -{ - Vector result; - - result.x = v1.x - v2.x; - result.y = v1.y - v2.y; - result.z = v1.z - v2.z; - - return result; -} - -// v1 + v2 -inline Vector operator+(Vector const &v1, Vector const &v2) -{ - Vector result; - - result.x = v1.x + v2.x; - result.y = v1.y + v2.y; - result.z = v1.z + v2.z; - - return result; -} - -// v1 += v2 -inline Vector &operator+=(Vector &v1, Vector const &v2) -{ - v1 = v1 + v2; - - return v1; -} - -// f * v -inline Vector operator*(float f, Vector const &v) -{ - Vector result; - - result.x = v.x * f; - result.y = v.y * f; - result.z = v.z * f; - - return result; -} - -// v / f -inline Vector operator/(Vector const &v, float f) -{ - Vector result; - - float inverse = 1.0f / f; - - result.x = v.x * inverse; - result.y = v.y * inverse; - result.z = v.z * inverse; - - return result; -} - -// v /= f -inline Vector &operator/=(Vector &v, float f) -{ - v = v / f; - - return v; -} - - -// PUBLIC FUNCTIONS -inline float VectorLength(Vector &v) -{ - float result = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); - - // zero length - if (result < EPSILON_E3) - { - result = 0.0f; - } - - return result; -} - -inline void VectorNormalize(Vector &v) -{ - float length = VectorLength(v); - - // zero length - if (length == 0.0f) - { - v.x = 0.0f; - v.y = 0.0f; - v.z = 0.0f; - } - else - { - float lengthInv = 1.0f / length; - - v.x *= lengthInv; - v.y *= lengthInv; - v.z *= lengthInv; - } -} - -inline Vector VectorCross(Vector &v1, Vector &v2) -{ - Vector result; - - result.x = (v1.y * v2.z) - (v1.z * v2.y); - result.y = (v1.z * v2.x) - (v1.x * v2.z); - result.z = (v1.x * v2.y) - (v1.y * v2.x); - - return result; -} - -inline float VectorDot(Vector &v1, Vector &v2) -{ - float result; - - result = (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); - - return result; -} - - -#define VEC_H -#endif - diff --git a/src/engine.cpp b/src/engine.cpp deleted file mode 100644 index d80f692..0000000 --- a/src/engine.cpp +++ /dev/null @@ -1,344 +0,0 @@ -#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 = 200; - mesh.position.y = -100; - mesh.scale = 1.0f; - - - // Light configuration - light.position = Point(-300.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 += 0.10f; - } - else if (CHECK_BIT(input, ROTATE_X_NEG)) - { - mesh.rotation.x -= 0.10f; - } - - if (CHECK_BIT(input, ROTATE_Y_POS)) - { - mesh.rotation.y += 0.10f; - } - else if (CHECK_BIT(input, ROTATE_Y_NEG)) - { - mesh.rotation.y -= 0.10f; - } - - if (CHECK_BIT(input, ROTATE_Z_POS)) - { - mesh.rotation.z += 0.10f; - } - else if (CHECK_BIT(input, ROTATE_Z_NEG)) - { - mesh.rotation.z -= 0.10f; - } - - 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.position - vert0.position; - Vector v02 = vert2.position - vert0.position; - - 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].position = - localVerts.data[v].position * 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]].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 = -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].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(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.position, vert.normal, material, light, camera); - } - } -} diff --git a/src/light.cpp b/src/light.cpp deleted file mode 100644 index da9eeb4..0000000 --- a/src/light.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "camera.h" -#include "color.h" -#include "light.h" - - -// PUBLIC FUNCTIONS -ColorF32 ComputeLight( - Point &position, Vector &normal, Material &material, Light &light, Camera &camera) -{ - // Point light intensity is a function of the falloff factors and the distance - Vector direction = light.position - position; - VectorNormalize(direction); - float distance = VectorLength(direction); - - ColorF32 intensity = - (light.intensity * light.color) - / (light.falloffConstant + (light.falloffLinear * distance)); - - - // Diffuse Light = Kr * I * (alpha + (1 - alpha) * n.d) - float dotNormalDirection = MAX(VectorDot(normal, direction), 0.0f); - float alpha = 0.2f; - - ColorF32 diffuseLight = - material.diffuse - * intensity - * (alpha + (1.0f - alpha) * dotNormalDirection); - - - // Specular Light = Ks * I * (r.v)^sp - Vector view = camera.position - position; - VectorNormalize(view); - Vector reflection = (2.0f * dotNormalDirection * normal) - direction; - float dotReflectionView = MAX(VectorDot(reflection, view), 0.0f); - - ColorF32 specularLight = - material.specular - * intensity - * powf(dotReflectionView, material.glossiness); - - - // Total light is sum of all lights - ColorF32 result = diffuseLight + specularLight; - - - return result; -} - diff --git a/src/loader.cpp b/src/loader.cpp deleted file mode 100644 index 5f544b8..0000000 --- a/src/loader.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include "engine.h" -#include "loader.h" -#include -#include -#include -#include -#include - - -// STATIC PROTOTYPES -static int LoadTexture(char *filename, Texture &texture, float opacity); - - -#pragma pack(push, 1) -struct BMP_Header -{ - uint16_t fileType; - uint32_t fileSize; - uint16_t reserved0; - uint16_t reserved1; - uint32_t bitmapOffset; - uint32_t size; - int32_t width; - int32_t height; - uint16_t planes; - uint16_t bitsPerPixel; - uint32_t compression; - uint32_t sizeOfBitmap; - int32_t horizRes; - int32_t vertRes; - uint32_t colorsUsed; - uint32_t colorsImportant; -}; -#pragma pack(pop) - - -// PUBLIC FUNCTIONS -int ParseOBJ(char *filename, EngineMemory &memory) -{ - FILE *fp = fopen(filename, "r"); - - if (fp == NULL) - { - fprintf(stderr, "Error loading file: %s\n", filename); - return -1; - } - - - char line[256]; - int vertIndex = 0; - int uvIndex = 0; - int faceIndex = 0; - int materialIndex = -1; - - VertexList &verts = memory.localVerts; - FaceList &faces = memory.localFaces; - UVList &uvs = memory.uvs; - - while (fgets(line, sizeof(line), fp)) - { - char *separator = strchr(line, ' '); - - if (separator != NULL) - { - *separator = '\0'; - char *type = line; - char *data = separator + 1; - - if (strcmp(type, "v") == 0) - { - sscanf( data, "%f %f %f", - &verts.data[vertIndex].position.x, - &verts.data[vertIndex].position.y, - &verts.data[vertIndex].position.z); - - verts.data[vertIndex].position.w = 1.0f; - - ++vertIndex; - } - - else if (strcmp(type, "vt") == 0) - { - sscanf( data, "%f %f", - &uvs.data[uvIndex].u, - &uvs.data[uvIndex].v); - - ++uvIndex; - } - else if (strcmp(type, "usemtl") == 0) - { - ++materialIndex; - } - - else if (strcmp(type, "f") == 0) - { - sscanf( data, "%d/%d %d/%d %d/%d", - &faces.data[faceIndex].vertIndex[0], - &faces.data[faceIndex].uvIndex[0], - &faces.data[faceIndex].vertIndex[1], - &faces.data[faceIndex].uvIndex[1], - &faces.data[faceIndex].vertIndex[2], - &faces.data[faceIndex].uvIndex[2]); - - // Convert to 0-indexed - faces.data[faceIndex].vertIndex[0] -= 1; - faces.data[faceIndex].vertIndex[1] -= 1; - faces.data[faceIndex].vertIndex[2] -= 1; - faces.data[faceIndex].uvIndex[0] -= 1; - faces.data[faceIndex].uvIndex[1] -= 1; - faces.data[faceIndex].uvIndex[2] -= 1; - - faces.data[faceIndex].materialIndex = materialIndex; - - ++faceIndex; - } - } - } - - verts.size = (size_t)vertIndex; - uvs.size = (size_t)uvIndex; - faces.size = (size_t)faceIndex; - - return 0; -} - -int ParseMTL(char *filename, EngineMemory &memory) -{ - FILE *fp = fopen(filename, "r"); - - if (fp == NULL) - { - fprintf(stderr, "Error loading file: %s\n", filename); - return -1; - } - - char line[256]; - int materialIndex = -1; - - MaterialList &materials = memory.materials; - TextureList &textures = memory.textures; - - while (fgets(line, sizeof(line), fp)) - { - char *separator = strchr(line, ' '); - - if (separator != NULL) - { - *separator = '\0'; - char *type = line; - char *data = separator + 1; - - if (strcmp(type, "newmtl") == 0) - { - ++materialIndex; - } - - else if (strcmp(type, "Ns") == 0) - { - sscanf( data, "%f", - &materials.data[materialIndex].glossiness); - } - - else if (strcmp(type, "Ka") == 0) - { - sscanf( data, "%f %f %f", - &materials.data[materialIndex].ambient.r, - &materials.data[materialIndex].ambient.g, - &materials.data[materialIndex].ambient.b); - } - - else if (strcmp(type, "Kd") == 0) - { - sscanf( data, "%f %f %f", - &materials.data[materialIndex].diffuse.r, - &materials.data[materialIndex].diffuse.g, - &materials.data[materialIndex].diffuse.b); - } - - else if (strcmp(type, "Ks") == 0) - { - sscanf( data, "%f %f %f", - &materials.data[materialIndex].specular.r, - &materials.data[materialIndex].specular.g, - &materials.data[materialIndex].specular.b); - } - - else if (strcmp(type, "d") == 0) - { - sscanf( data, "%f", - &materials.data[materialIndex].opacity); - } - - else if (strcmp(type, "map_Kd") == 0) - { - char *textureFilename = data; - textureFilename[strcspn(textureFilename, "\r\n")] = 0; - LoadTexture( - textureFilename, textures.data[materialIndex], - materials.data[materialIndex].opacity); - } - } - } - - materials.size = (size_t)(materialIndex + 1); - - return 0; -} - -static int LoadTexture(char *filename, Texture &texture, float opacity) -{ - FILE *fp = fopen(filename, "r"); - if (fp == NULL) - { - fprintf(stderr, "Could not open file: %s\n", filename); - return -1; - } - - BMP_Header header = {}; - fread((void*)&header, sizeof(BMP_Header), 1, fp); - fseek(fp, header.bitmapOffset, SEEK_SET); - - // Padding is added to image to align to 4-byte boundaries - size_t paddingSize = header.width % 4; - - for (int y = 0; y < header.height; ++y) - { - for (int x = 0; x < header.width; ++x) - { - fread(&texture.texels[y][x].b, 1, 1, fp); - fread(&texture.texels[y][x].g, 1, 1, fp); - fread(&texture.texels[y][x].r, 1, 1, fp); - texture.texels[y][x].a = (uint8_t)(255 * opacity); - } - - // Discard padding byte - if (paddingSize != 0) - { - uint32_t padding; - fread(&padding, paddingSize, 1, fp); - } - } - - texture.width = (unsigned int)header.width; - texture.height = (unsigned int)header.height; - - fclose(fp); - - return 0; -} - diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 15496a9..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "engine.h" -#include "platform.h" -#include -#include -#include - - -// MAIN -int main(int argc, char *argv[]) -{ - if (argc != 3) - { - fprintf(stderr, "Usage: %s \n", argv[0]); - return EXIT_FAILURE; - } - - char *objFilename = argv[1]; - char *mtlFilename = argv[2]; - Platform platform = {}; - - int result = Platform_Init(platform, WINDOW_WIDTH, WINDOW_HEIGHT); - platform.framerateMillis = (1000 / WINDOW_FPS); - - if (result == PLATFORM_OK) - { - EngineBuffer buffer = {}; - buffer.buffer = (uint32_t*)platform.surface->pixels; - buffer.width = platform.surface->w; - buffer.height = platform.surface->h; - - result = EngineInit(objFilename, mtlFilename); - - if (result < 0) - { - return EXIT_FAILURE; - } - - while (true) - { - Platform_GetFrameTime(platform); - - result = Platform_CheckForEvents(platform); - if (result == PLATFORM_QUIT) - { - break; - } - - Platform_ClearWindow(platform); - - EngineRender(buffer, platform.input); - - Platform_UpdateWindow(platform); - - Platform_SyncToFramerate(platform); - } - - EngineShutdown(); - - Platform_Shutdown(platform); - } - else - { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - diff --git a/src/platform.cpp b/src/platform.cpp deleted file mode 100644 index 3d97a0d..0000000 --- a/src/platform.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "engine.h" -#include "platform.h" -#include "util.h" -#include -#include - - -// PRIVATE PROTOTYPES -static void HandleEvent(Platform &platform, SDL_Event &event); - - -// PUBLIC FUNCTIONS -int Platform_Init(Platform &platform, int width, int height) -{ - int result = SDL_Init(SDL_INIT_VIDEO); - if (result < 0) - { - fprintf(stderr, "Error initializing SDL: %s\n", SDL_GetError()); - return PLATFORM_ERROR; - } - - - SDL_Window *window = SDL_CreateWindow( - "Soft 3D Engine", - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - width, height, - SDL_WINDOW_SHOWN); - if (window == NULL) - { - fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); - return PLATFORM_ERROR; - } - - - SDL_Surface *surface = SDL_GetWindowSurface(window); - if (surface == NULL) - { - fprintf(stderr, "Error getting SDL window surface: %s\n", SDL_GetError()); - return PLATFORM_ERROR; - } - - - result = SDL_ShowCursor(SDL_DISABLE); - if (result < 0) - { - fprintf(stderr, "Error disabling cursor in SDL window: %s\n", SDL_GetError()); - return PLATFORM_ERROR; - } - - - platform.window = window; - platform.surface = surface; - - return PLATFORM_OK; -} - -int Platform_CheckForEvents(Platform &platform) -{ - SDL_Event event; - - while (SDL_PollEvent(&event) != 0) - { - if (event.type == SDL_QUIT) - { - return PLATFORM_QUIT; - } - else - { - HandleEvent(platform, event); - } - } - - return PLATFORM_OK; -} - -void Platform_ClearWindow(Platform &platform) -{ - SDL_LockSurface(platform.surface); - SDL_FillRect(platform.surface, NULL, 0); -} - -void Platform_UpdateWindow(Platform &platform) -{ - SDL_UnlockSurface(platform.surface); - SDL_UpdateWindowSurface(platform.window); -} - -void Platform_GetFrameTime(Platform &platform) -{ - platform.frameStartMillis = SDL_GetTicks(); -} - -void Platform_SyncToFramerate(Platform &platform) -{ - uint32_t stopTimeMillis = SDL_GetTicks(); - uint32_t framerateMillis = stopTimeMillis - platform.frameStartMillis; - - // Delay if time to spare - if (framerateMillis < platform.framerateMillis) - { - uint32_t delayMillis = platform.framerateMillis - framerateMillis; - SDL_Delay(delayMillis); - } -} - -void Platform_Shutdown(Platform &platform) -{ - SDL_DestroyWindow(platform.window); - SDL_Quit(); -} - - -// PRIVATE FUNCTIONS -static void HandleEvent( - Platform &platform, SDL_Event &event) -{ - switch(event.type) - { - case SDL_KEYDOWN: - { - switch (event.key.keysym.sym) - { - case SDLK_w: - { - SET_BIT(platform.input, TRANSLATE_Z_POS); - } break; - case SDLK_s: - { - SET_BIT(platform.input, TRANSLATE_Z_NEG); - } break; - case SDLK_a: - { - SET_BIT(platform.input, TRANSLATE_X_NEG); - } break; - case SDLK_d: - { - SET_BIT(platform.input, TRANSLATE_X_POS); - } break; - case SDLK_q: - { - SET_BIT(platform.input, TRANSLATE_Y_POS); - } break; - case SDLK_e: - { - SET_BIT(platform.input, TRANSLATE_Y_NEG); - } break; - case SDLK_i: - { - SET_BIT(platform.input, ROTATE_X_POS); - } break; - case SDLK_k: - { - SET_BIT(platform.input, ROTATE_X_NEG); - } break; - case SDLK_j: - { - SET_BIT(platform.input, ROTATE_Y_POS); - } break; - case SDLK_l: - { - SET_BIT(platform.input, ROTATE_Y_NEG); - } break; - case SDLK_u: - { - SET_BIT(platform.input, ROTATE_Z_POS); - } break; - case SDLK_o: - { - SET_BIT(platform.input, ROTATE_Z_NEG); - } break; - case SDLK_UP: - { - SET_BIT(platform.input, SCALE_UP); - } break; - case SDLK_DOWN: - { - SET_BIT(platform.input, SCALE_DOWN); - } break; - } - } break; - case SDL_KEYUP: - { - switch (event.key.keysym.sym) - { - case SDLK_w: - { - CLEAR_BIT(platform.input, TRANSLATE_Z_POS); - } break; - case SDLK_s: - { - CLEAR_BIT(platform.input, TRANSLATE_Z_NEG); - } break; - case SDLK_a: - { - CLEAR_BIT(platform.input, TRANSLATE_X_NEG); - } break; - case SDLK_d: - { - CLEAR_BIT(platform.input, TRANSLATE_X_POS); - } break; - case SDLK_q: - { - CLEAR_BIT(platform.input, TRANSLATE_Y_POS); - } break; - case SDLK_e: - { - CLEAR_BIT(platform.input, TRANSLATE_Y_NEG); - } break; - case SDLK_i: - { - CLEAR_BIT(platform.input, ROTATE_X_POS); - } break; - case SDLK_k: - { - CLEAR_BIT(platform.input, ROTATE_X_NEG); - } break; - case SDLK_j: - { - CLEAR_BIT(platform.input, ROTATE_Y_POS); - } break; - case SDLK_l: - { - CLEAR_BIT(platform.input, ROTATE_Y_NEG); - } break; - case SDLK_u: - { - CLEAR_BIT(platform.input, ROTATE_Z_POS); - } break; - case SDLK_o: - { - CLEAR_BIT(platform.input, ROTATE_Z_NEG); - } break; - case SDLK_UP: - { - CLEAR_BIT(platform.input, SCALE_UP); - } break; - case SDLK_DOWN: - { - CLEAR_BIT(platform.input, SCALE_DOWN); - } break; - } - } break; - } -} - diff --git a/src/render.cpp b/src/render.cpp deleted file mode 100644 index 9a90d33..0000000 --- a/src/render.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include "color.h" -#include "engine.h" -#include "geometry.h" -#include "render.h" -#include "util.h" - - -// 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 Render(EngineBuffer &buffer, EngineMemory &memory) -{ - FaceList &faces = memory.transFaces; - VertexList &verts = memory.transVerts; - TextureList &textures = memory.textures; - UVList &uvs = memory.uvs; - - for(size_t f = 0; f < faces.size; ++f) - { - Face &face = faces.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; - - - // Bounding box for barycentric calculations (top-left fill convention) - BoundingBox box(p0, p1, p2); - int yMin = (int)MAX(ceilf(box.yMin), 0); - int yMax = (int)MIN(ceilf(box.yMax) - 1, buffer.height - 1); - int xMin = (int)MAX(ceilf(box.xMin), 0); - int xMax = (int)MIN(ceilf(box.xMax) - 1, buffer.width - 1); - - - // Constants for this triangle used for barycentric calculations - Vector v01 = p1 - p0; - Vector v02 = p2 - p0; - float dot0101 = VectorDot(v01, v01); - float dot0102 = VectorDot(v01, v02); - float dot0202 = VectorDot(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) - { - // Calculate the barycentric coordinate of this point - Point p(x, y, 1.0f); - Vector v0P = p - p0; - float dot0P01 = VectorDot(v0P, v01); - float dot0P02 = VectorDot(v0P, v02); - float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102)); - 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)) - { - // Interpolate 1/z for the z-buffer - float zInv = - 1.0f / - ((barycenter[0] * p0.w) - + (barycenter[1] * p1.w) - + (barycenter[2] * p2.w)); - - - // Compute pixel color if it's closer than what's in the z-buffer - if (zInv > memory.zbuffer[y][x]) - { - // Update the depth buffer - memory.zbuffer[y][x] = zInv; - - - // Interpolate U and V of the texture for this point - Texture &texture = textures.data[faces.data[f].materialIndex]; - UV &uv0 = uvs.data[face.uvIndex[0]]; - UV &uv1 = uvs.data[face.uvIndex[1]]; - UV &uv2 = uvs.data[face.uvIndex[2]]; - - float a = barycenter[0] * p1.w * p2.w; - float b = barycenter[1] * p0.w * p2.w; - float c = barycenter[2] * p0.w * p1.w; - float abc = 1.0f / (a + b + c); - - float uInterp = - ((a * uv0.u) + (b * uv1.u) + (c * uv2.u)) - * abc - * texture.width; - - float vInterp = - ((a * uv0.v) + (b * uv1.v) + (c * uv2.v)) - * abc - * texture.height; - - - // Bilinear filtering - unsigned int u = (unsigned int)uInterp; - unsigned int v = (unsigned int)vInterp; - float du = uInterp - u; - float dv = vInterp - v; - float duDiff = 1 - du; - float dvDiff = 1 - dv; - - ColorU32 color = - { - (uint8_t) - (duDiff * dvDiff * texture.texels[v][u].b - + du * dvDiff * texture.texels[v][u+1].b - + du * dv * texture.texels[v+1][u+1].b - + duDiff * dv * texture.texels[v+1][u].b), - - (uint8_t) - (duDiff * dvDiff * texture.texels[v][u].g - + du * dvDiff * texture.texels[v][u+1].g - + du * dv * texture.texels[v+1][u+1].g - + duDiff * dv * texture.texels[v+1][u].g), - - (uint8_t) - (duDiff * dvDiff * texture.texels[v][u].r - + du * dvDiff * texture.texels[v][u+1].r - + du * dv * texture.texels[v+1][u+1].r - + duDiff * dv * texture.texels[v+1][u].r), - - (uint8_t) - (duDiff * dvDiff * texture.texels[v][u].a - + du * dvDiff * texture.texels[v][u+1].a - + du * dv * texture.texels[v+1][u+1].a - + duDiff * dv * texture.texels[v+1][u].a) - }; - - - // Perform Gouraud shading on the texture - ColorF32 &c0 = verts.data[face.vertIndex[0]].color; - ColorF32 &c1 = verts.data[face.vertIndex[1]].color; - ColorF32 &c2 = verts.data[face.vertIndex[2]].color; - - ColorF32 shading = - (barycenter[0] * c0) - + (barycenter[1] * c1) - + (barycenter[2] * c2); - - - // Ensure no color channel exceeds max - ScaleColor(shading); - - - // Light the texture - color.b *= shading.b; - color.g *= shading.g; - color.r *= shading.r; - - - // Alpha blend the pixel - ColorU32 *pixel = (ColorU32*)&buffer.buffer[y*buffer.width+x]; - float alpha = color.a / 255.0f; - float alphaDiff = 1.0f - alpha; - - ColorU32 blended = - { - (uint8_t)((alpha * color.b) + (alphaDiff * pixel->b)), - (uint8_t)((alpha * color.g) + (alphaDiff * pixel->g)), - (uint8_t)((alpha * color.r) + (alphaDiff * pixel->r)), - (uint8_t)((alpha * color.a) + (alphaDiff * pixel->a)) - }; - - - // Draw - DrawPixel(buffer.buffer, buffer.width, blended.u32, x, y); - } - } - } - } - } -} - diff --git a/src/transform.cpp b/src/transform.cpp deleted file mode 100644 index 63351b9..0000000 --- a/src/transform.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "matrix.h" -#include "transform.h" -#include - - -// PUBLIC FUNCTIONS -Matrix Transform_Translate(Point &translation) -{ - Matrix result; - - result.e41 = translation.x; - result.e42 = translation.y; - result.e43 = translation.z; - - return result; -} - -Matrix Transform_Rotate(Point &rotation) -{ - // YXZ Euler rotation - float cosThetaY = cosf(rotation.y); - float sinThetaY = sinf(rotation.y); - - Matrix tRotateY; - - tRotateY.e11 = cosThetaY; - tRotateY.e13 = -sinThetaY; - tRotateY.e31 = sinThetaY; - tRotateY.e33 = cosThetaY; - - - float cosThetaX = cosf(rotation.x); - float sinThetaX = sinf(rotation.x); - - Matrix tRotateX; - - tRotateX.e22 = cosThetaX; - tRotateX.e23 = sinThetaX; - tRotateX.e32 = -sinThetaX; - tRotateX.e33 = cosThetaX; - - - float cosThetaZ = cosf(rotation.z); - float sinThetaZ = sinf(rotation.z); - - Matrix tRotateZ; - - tRotateZ.e11 = cosThetaZ; - tRotateZ.e12 = sinThetaZ; - tRotateZ.e21 = -sinThetaZ; - tRotateZ.e22 = cosThetaZ; - - - Matrix result = tRotateY * tRotateX * tRotateZ; - - return result; -} - -Matrix Transform_Scale(float scale) -{ - Matrix result; - - result.e11 = scale; - result.e22 = scale; - result.e33 = scale; - - return result; -} - -Matrix Transform_View(Camera &camera) -{ - Point invPosition = -camera.position; - Matrix tInvTranslate = Transform_Translate(invPosition); - - Point invRotation = -camera.rotation; - Matrix tInvRotate = Transform_Rotate(invRotation); - - Matrix result = tInvTranslate * tInvRotate; - - return result; -} - -Matrix Transform_Perspective(Camera &camera) -{ - Matrix result; - - result.e11 = camera.xZoom; - result.e22 = camera.yZoom; - result.e33 = camera.zClipBias0; - result.e34 = 1; - result.e43 = camera.zClipBias1; - - return result; -} - -Matrix Transform_Screen(Camera &camera) -{ - Matrix result; - - result.e11 = camera.xScale; - result.e41 = camera.xScale; - result.e22 = -camera.yScale; - result.e42 = camera.yScale; - - return result; -}