diff --git a/Makefile b/Makefile index 657cada..536d870 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,8 @@ WARNINGS_OFF=-Wno-missing-braces -Wno-gnu-anonymous-struct -Wno-old-style-cast\ CFLAGS=$(D) $(O) -std=c++11 $(WARNINGS_ON) $(WARNINGS_OFF) -I$(INCLUDE_DIR) LIBS=-lSDL2 -_HEADERS = color.h engine.h geometry.h loader.h platform.h point.h transform.h util.h +_HEADERS = color.h engine.h geometry.h light.h loader.h platform.h point.h\ + transform.h util.h vec.h HEADERS = $(patsubst %,$(INCLUDE_DIR)/%,$(_HEADERS)) _OBJS = engine.o geometry.o loader.o main.o platform.o transform.o diff --git a/include/color.h b/include/color.h index 1fc67fb..3f1d731 100644 --- a/include/color.h +++ b/include/color.h @@ -1,5 +1,6 @@ #ifndef COLOR_H +#include "util.h" #include @@ -9,16 +10,65 @@ struct ColorU32 { struct { - uint8_t b; - uint8_t g; - uint8_t r; - uint8_t a; + uint8_t b, g, r, a; }; uint32_t u32; }; }; +struct ColorF32 +{ + float b, g, r, a; + + static inline ColorU32 ConvertToU32(ColorF32 c) + { + ColorU32 result; + + result.b = (uint8_t)(c.b * 255); + result.g = (uint8_t)(c.g * 255); + result.r = (uint8_t)(c.r * 255); + result.a = (uint8_t)(c.a * 255); + + return result; + } +}; + + +// OPERATORS +// c1 + c2 +inline ColorF32 operator+(ColorF32 c1, ColorF32 c2) +{ + ColorF32 result; + + result.b = MIN((c1.b + c2.b), 1.0f); + result.g = MIN((c1.g + c2.g), 1.0f); + result.r = MIN((c1.r + c2.r), 1.0f); + result.a = MIN((c1.a + c2.a), 1.0f); + + return result; +} + +// c1 += c2 +inline ColorF32 &operator+=(ColorF32 &c1, ColorF32 c2) +{ + c1 = c1 + c2; + + return(c1); +} + +// c * f +inline ColorF32 operator*(ColorF32 c, float f) +{ + ColorF32 result; + + result.b = MIN((f * c.b), 1.0f); + result.g = MIN((f * c.g), 1.0f); + result.r = MIN((f * c.r), 1.0f); + result.a = MIN((f * c.a), 1.0f); + + return result; +} #define COLOR_H #endif diff --git a/include/geometry.h b/include/geometry.h index 5083dde..b847a03 100644 --- a/include/geometry.h +++ b/include/geometry.h @@ -1,14 +1,22 @@ #ifndef GEOMETRY_H +#include "color.h" #include "point.h" #include #include // STRUCTURES +struct Material +{ + ColorF32 kAmbient; + ColorF32 kDiffuse; +}; + struct Face { unsigned int vertIndex[3]; + ColorU32 color; }; struct Mesh @@ -26,12 +34,44 @@ struct Mesh scale = 1.0f; } + inline void CullBackfaces(Point camPosition) + { + culledFaces.clear(); + + for (size_t f = 0; f < faces.size(); ++f) + { + unsigned int v0 = faces[f].vertIndex[0]; + unsigned int v1 = faces[f].vertIndex[1]; + unsigned int v2 = faces[f].vertIndex[2]; + + Vector v01 = vertsTransformed[v1] - vertsTransformed[v0]; + Vector v02 = vertsTransformed[v2] - vertsTransformed[v0]; + + Vector normal = Vector::Cross(v01, v02); + + // Invert for Blender-compatibility + normal = -normal; + + // Eye vector to viewport + Vector view = camPosition - vertsTransformed[v0]; + + float dot = Vector::Dot(normal, view); + + if (dot < EPSILON_E3) + { + culledFaces.push_back(faces[f]); + } + } + } + Point position; float rotation[3]; float scale; std::vector verts; std::vector vertsTransformed; std::vector faces; + std::vector culledFaces; + Material material; }; diff --git a/include/light.h b/include/light.h new file mode 100644 index 0000000..f8af087 --- /dev/null +++ b/include/light.h @@ -0,0 +1,51 @@ +#ifndef LIGHT_H + + +#include "color.h" +#include "vec.h" +#include + + +// STRUCTURES +struct LightAmbient +{ + inline ColorF32 ComputeColor(ColorF32 reflectivity) + { + ColorF32 result; + + result = reflectivity * intensity; + + return result; + } + + float intensity; +}; + +struct LightDiffuse +{ + inline ColorF32 ComputeColor(ColorF32 reflectivity, Vector normal) + { + ColorF32 result; + + float dot = Vector::Dot(normal, direction); + + result = reflectivity * intensity * dot; + + return result; + } + + float intensity; + Vector direction; +}; + +struct LightList +{ + LightAmbient ambient; + LightDiffuse *diffuse; + int diffuseCount; +}; + + +#define LIGHT_H +#endif + diff --git a/include/point.h b/include/point.h index 58d524b..92cdfba 100644 --- a/include/point.h +++ b/include/point.h @@ -1,5 +1,7 @@ #ifndef POINT_H +#include "vec.h" + // STRUCTURE struct Point @@ -34,6 +36,18 @@ inline Point operator/(Point v, float f) return result; } +// v1 - v2 +inline Vector operator-(Point v1, Point v2) +{ + Vector result; + + result.x = v1.x - v2.x; + result.y = v1.y - v2.y; + result.z = v1.z - v2.z; + + return result; +} + #define POINT_H #endif diff --git a/include/util.h b/include/util.h index 5bbb7c9..056a9e0 100644 --- a/include/util.h +++ b/include/util.h @@ -13,6 +13,8 @@ const float EPSILON_E3 = 1E-3f; #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) diff --git a/include/vec.h b/include/vec.h new file mode 100644 index 0000000..652e8c8 --- /dev/null +++ b/include/vec.h @@ -0,0 +1,82 @@ +#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) {} + + inline void Normalize(void) + { + float length = sqrtf(x*x + y*y + z*z); + + // zero length + if (length < EPSILON_E3) + { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + else + { + float lengthInv = 1.0f / length; + + x *= lengthInv; + y *= lengthInv; + z *= lengthInv; + } + } + + inline static float Dot(Vector v1, Vector v2) + { + float result; + + result = (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); + + return result; + } + + inline 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; + } + + union + { + float e[4]; + + struct + { + float x, y, z, w; + }; + }; +}; + + +// OPERATORS +// -v +inline Vector operator-(Vector v) +{ + Vector result; + + result.x = -v.x; + result.y = -v.y; + result.z = -v.z; + + return result; +} + + +#define VEC_H +#endif + diff --git a/src/engine.cpp b/src/engine.cpp index cc82c35..4d551cc 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -2,16 +2,19 @@ #include "color.h" #include "engine.h" #include "geometry.h" +#include "light.h" #include "loader.h" #include "matrix.h" #include "transform.h" #include "util.h" +#include "vec.h" #include // GLOBALS static Mesh mesh; static Camera camera; +static LightList lights; // PRIVATE PROTOTYPES @@ -29,9 +32,17 @@ int Engine_Init(Engine_Buffer &buffer, char *filename) } mesh.position.z = 250; + mesh.material.kDiffuse = {1.0,1.0,1.0,1.0}; + mesh.material.kAmbient = {1.0,1.0,1.0,1.0}; camera.SetFOV(90.0f, buffer.width, buffer.height); + lights.diffuse = (LightDiffuse*)malloc(sizeof(LightDiffuse)); + lights.diffuseCount = 1; + lights.ambient.intensity = 1.0; + lights.diffuse[0].intensity = 0.5; + lights.diffuse[0].direction = Vector(1,1,1); + return 0; } @@ -39,30 +50,69 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) { CheckInputs(input); + + // Local space to world space Matrix tTranslate = Transform_Translate( mesh.position.x, mesh.position.y, mesh.position.z); Matrix tRotate = Transform_Rotate( mesh.rotation[0], mesh.rotation[1], mesh.rotation[2]); - Matrix tScale = Transform_Scale( - mesh.scale); + Matrix tScale = Transform_Scale(mesh.scale); - Matrix tView = Transform_View( - camera.position, camera.rotation); + for (size_t v = 0; v < mesh.verts.size(); ++v) + { + mesh.vertsTransformed[v] = + mesh.verts[v] * tScale * tRotate * tTranslate; + } + + // Cull backfaces before computing colors + mesh.CullBackfaces(camera.position); + + + // Color the faces (flat shading) + for (size_t f = 0; f < mesh.culledFaces.size(); ++f) + { + unsigned int v0 = mesh.culledFaces[f].vertIndex[0]; + unsigned int v1 = mesh.culledFaces[f].vertIndex[1]; + unsigned int v2 = mesh.culledFaces[f].vertIndex[2]; + + Vector v01 = mesh.vertsTransformed[v1] - mesh.vertsTransformed[v0]; + Vector v02 = mesh.vertsTransformed[v2] - mesh.vertsTransformed[v0]; + + Vector normal = Vector::Cross(v01, v02); + normal.Normalize(); + + ColorF32 totalColor = lights.ambient.ComputeColor(mesh.material.kAmbient); + + for (int c = 0; c < lights.diffuseCount; ++c) + { + totalColor += lights.diffuse[c].ComputeColor( + mesh.material.kDiffuse, normal); + } + + mesh.faces[f].color = ColorF32::ConvertToU32(totalColor); + } + + + // World space to camera (view) space + Matrix tView = Transform_View(camera.position, camera.rotation); + + + // Camera space to perspective Matrix tPersp = Transform_Perspective( camera.xZoom, camera.yZoom, camera.nearClip, camera.farClip); for (size_t v = 0; v < mesh.verts.size(); ++v) { - mesh.vertsTransformed[v] = - mesh.verts[v] * tScale * tRotate * tTranslate * tView * tPersp; + mesh.vertsTransformed[v] = mesh.vertsTransformed[v] * tView * tPersp; mesh.vertsTransformed[v] = mesh.vertsTransformed[v] / mesh.vertsTransformed[v].w; } + // Perspective to screen Matrix tScreen = Transform_Screen(camera.xScale, camera.yScale); for (size_t v = 0; v < mesh.verts.size(); ++v) @@ -70,16 +120,16 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) mesh.vertsTransformed[v] = mesh.vertsTransformed[v] * tScreen; } - for(size_t f = 0; f < mesh.faces.size(); ++f) - { - unsigned int v0 = mesh.faces[f].vertIndex[0]; - unsigned int v1 = mesh.faces[f].vertIndex[1]; - unsigned int v2 = mesh.faces[f].vertIndex[2]; - ColorU32 white = {0xff,0xff,0xff,0xff}; + // Fill each face with its respective color + for(size_t f = 0; f < mesh.culledFaces.size(); ++f) + { + unsigned int v0 = mesh.culledFaces[f].vertIndex[0]; + unsigned int v1 = mesh.culledFaces[f].vertIndex[1]; + unsigned int v2 = mesh.culledFaces[f].vertIndex[2]; FillTriangle( - buffer.pixels, buffer.width, white.u32, + buffer.pixels, buffer.width, mesh.faces[f].color.u32, mesh.vertsTransformed[v0].x, mesh.vertsTransformed[v0].y, mesh.vertsTransformed[v1].x, mesh.vertsTransformed[v1].y, mesh.vertsTransformed[v2].x, mesh.vertsTransformed[v2].y);