Compare commits
No commits in common. "263c36bd9a02088452d52e68bed8495317f1539a" and "aee55ce4ae5e15bede749e98cdf665f73786bdd0" have entirely different histories.
263c36bd9a
...
aee55ce4ae
|
@ -1,49 +0,0 @@
|
||||||
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)
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# 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)
|
|
@ -16,6 +16,13 @@ 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,
|
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).
|
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
|
### Good cache performance
|
||||||
|
|
||||||
The biggest bottleneck of modern CPU performance is latency between memory and
|
The biggest bottleneck of modern CPU performance is latency between memory and
|
||||||
|
@ -30,5 +37,3 @@ understand how OBJ files are constructed, along with their respective MTL files.
|
||||||
I also wrote my own linear algebra functions and classes (rather than use `glm`)
|
I also wrote my own linear algebra functions and classes (rather than use `glm`)
|
||||||
to better understand the low-level math operations.
|
to better understand the low-level math operations.
|
||||||
|
|
||||||
![Demo](https://austinmorlan.com/files/soft-3d-renderer/garrus.gif)
|
|
||||||
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Engine.hpp"
|
|
||||||
#include "Point.hpp"
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
116
Source/Color.hpp
116
Source/Color.hpp
|
@ -1,116 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,345 +0,0 @@
|
||||||
#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 <cstring>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
|
|
||||||
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<float>(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Geometry.hpp"
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
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();
|
|
|
@ -1,85 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Color.hpp"
|
|
||||||
#include "Point.hpp"
|
|
||||||
#include "Vec.hpp"
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
|
@ -1,58 +0,0 @@
|
||||||
#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;
|
|
||||||
};
|
|
|
@ -1,256 +0,0 @@
|
||||||
#include "Engine.hpp"
|
|
||||||
#include "Loader.hpp"
|
|
||||||
#include <cctype>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
|
|
||||||
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<unsigned long>(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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Engine.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
int ParseOBJ(char* filename, EngineMemory& memory);
|
|
||||||
int ParseMTL(char* filename, EngineMemory& memory);
|
|
|
@ -1,60 +0,0 @@
|
||||||
#include "Engine.hpp"
|
|
||||||
#include "Platform.hpp"
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
|
|
||||||
// MAIN
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
if (argc != 3)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: %s <OBJ> <MTL>\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<uint32_t*>(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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,243 +0,0 @@
|
||||||
#include "Engine.hpp"
|
|
||||||
#include "Platform.hpp"
|
|
||||||
#include <cstdint>
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
|
@ -1,88 +0,0 @@
|
||||||
#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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,199 +0,0 @@
|
||||||
#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<int>(std::max<float>(ceilf(box.yMin), 0));
|
|
||||||
int yMax = static_cast<int>(std::min<float>(ceilf(box.yMax) - 1, buffer.height - 1));
|
|
||||||
int xMin = static_cast<int>(std::max<float>(ceilf(box.xMin), 0));
|
|
||||||
int xMax = static_cast<int>(std::min<float>(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<unsigned int>(uInterp);
|
|
||||||
unsigned int v = static_cast<unsigned int>(vInterp);
|
|
||||||
float du = uInterp - u;
|
|
||||||
float dv = vInterp - v;
|
|
||||||
float duDiff = 1 - du;
|
|
||||||
float dvDiff = 1 - dv;
|
|
||||||
|
|
||||||
ColorU32 color(
|
|
||||||
static_cast<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)),
|
|
||||||
|
|
||||||
static_cast<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)),
|
|
||||||
|
|
||||||
static_cast<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)),
|
|
||||||
|
|
||||||
static_cast<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 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<ColorU32*>(&buffer.buffer[y * buffer.width + x]);
|
|
||||||
float alpha = static_cast<float>(color.a) / 255.0f;
|
|
||||||
float alphaDiff = 1.0f - alpha;
|
|
||||||
|
|
||||||
ColorU32 blended =
|
|
||||||
{
|
|
||||||
static_cast<uint8_t>(((alpha * static_cast<float>(color.b)) +
|
|
||||||
(alphaDiff * static_cast<float>(pixel->b)))),
|
|
||||||
|
|
||||||
static_cast<uint8_t>(((alpha * static_cast<float>(color.g)) +
|
|
||||||
(alphaDiff * static_cast<float>(pixel->g)))),
|
|
||||||
|
|
||||||
static_cast<uint8_t>(((alpha * static_cast<float>(color.r)) +
|
|
||||||
(alphaDiff * static_cast<float>(pixel->r)))),
|
|
||||||
|
|
||||||
static_cast<uint8_t>(((alpha * static_cast<float>(color.a)) +
|
|
||||||
(alphaDiff * static_cast<float>(pixel->a))))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
buffer.buffer[buffer.width * y + x] = blended.u32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Engine.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
void Render(EngineBuffer& buffer, EngineMemory& memory);
|
|
|
@ -1,105 +0,0 @@
|
||||||
#include "Matrix.hpp"
|
|
||||||
#include "Transform.hpp"
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
#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);
|
|
159
Source/Vec.hpp
159
Source/Vec.hpp
|
@ -1,159 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Matrix.hpp"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef CAMERA_H
|
||||||
|
|
||||||
|
#include "engine.h"
|
||||||
|
#include "point.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
#ifndef COLOR_H
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
#ifndef ENGINE_H
|
||||||
|
|
||||||
|
#include "geometry.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef GEOMETRY_H
|
||||||
|
|
||||||
|
#include "color.h"
|
||||||
|
#include "point.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#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
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#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
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
#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
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef PLATFORM_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
#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
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef RENDER_H
|
||||||
|
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
|
|
||||||
|
// PUBLIC FUNCTIONS
|
||||||
|
void Render(EngineBuffer &buffer, EngineMemory &memory);
|
||||||
|
|
||||||
|
|
||||||
|
#define RENDER_H
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#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
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef UTIL_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
#ifndef VEC_H
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -0,0 +1,344 @@
|
||||||
|
#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 <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
#include "engine.h"
|
||||||
|
#include "loader.h"
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "engine.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
|
// MAIN
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 3)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <OBJ> <MTL>\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;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
#include "engine.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "matrix.h"
|
||||||
|
#include "transform.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
Loading…
Reference in New Issue