1
0
Fork 0
2018-soft-3d-renderer/src/engine.cpp

345 lines
7.5 KiB
C++
Raw Normal View History

#include "camera.h"
#include "color.h"
#include "engine.h"
#include "geometry.h"
#include "light.h"
2018-09-05 02:50:27 +00:00
#include "loader.h"
#include "matrix.h"
2018-09-20 02:14:45 +00:00
#include "render.h"
#include "transform.h"
#include "util.h"
#include "vec.h"
2018-09-07 01:32:15 +00:00
#include <cstring>
2018-09-15 01:46:30 +00:00
#include <cstdio>
2018-09-05 02:50:27 +00:00
// GLOBALS
static Mesh mesh;
static Camera camera;
2018-09-19 03:02:12 +00:00
static Light light;
2018-09-19 02:18:58 +00:00
static Matrix tPersp;
static Matrix tScreen;
2018-09-20 02:14:45 +00:00
static EngineMemory memory;
2018-09-05 02:50:27 +00:00
// PRIVATE PROTOTYPES
2018-09-19 02:18:58 +00:00
static void ComputeNormals(void);
2018-09-20 02:14:45 +00:00
static void CheckInputs(uint32_t input);
static void ClearDepthBuffer(void);
2018-09-19 02:18:58 +00:00
static void TransformToClipSpace(void);
2018-09-20 02:14:45 +00:00
static void ClipAndCull(void);
2018-09-19 02:18:58 +00:00
static void TransformToScreenSpace(void);
2018-09-19 03:02:12 +00:00
static void LightMesh(void);
// PUBLIC FUNCTIONS
2018-09-20 02:14:45 +00:00
int EngineInit(char *objFilename, char *mtlFilename)
{
2018-09-19 02:18:58 +00:00
int result;
2018-09-05 02:50:27 +00:00
2018-09-19 02:18:58 +00:00
2018-09-20 02:14:45 +00:00
result = ParseOBJ(objFilename, memory);
2018-09-15 01:46:30 +00:00
if (result < 0)
{
2018-09-19 02:18:58 +00:00
return -1;
2018-09-15 01:46:30 +00:00
}
2018-09-19 02:18:58 +00:00
2018-09-20 02:14:45 +00:00
result = ParseMTL(mtlFilename, memory);
2018-09-05 02:50:27 +00:00
if (result < 0)
{
return -1;
}
2018-09-19 02:18:58 +00:00
2018-09-20 02:14:45 +00:00
printf("Verts: %lu\n", memory.localVerts.size);
printf("Faces: %lu\n", memory.localFaces.size);
printf("Materials: %lu\n", memory.materials.size);
2018-09-15 01:46:30 +00:00
2018-09-20 02:14:45 +00:00
memory.transVerts.size = memory.localVerts.size;
2018-09-15 01:46:30 +00:00
2018-09-19 02:18:58 +00:00
// Compute vertex and face normals for lighting calculation
ComputeNormals();
// Mesh configuration
2018-09-20 02:14:45 +00:00
mesh.position.z = 125;
mesh.position.y = -125;
2018-09-19 02:18:58 +00:00
mesh.scale = 1.0f;
2018-09-19 02:18:58 +00:00
// Light configuration
2018-09-19 03:02:12 +00:00
light.position = Point(-100.0f, 200.0f, 0.0f);
light.color = {1.0f, 1.0f, 1.0f, 1.0f};
light.intensity = 2.0f;
light.falloffConstant = 1.0f;
light.falloffLinear = 0.001f;
2018-09-19 02:18:58 +00:00
// Transformation matrices that do not change
tPersp = Transform_Perspective(camera);
tScreen = Transform_Screen(camera);
return 0;
}
2018-09-20 02:14:45 +00:00
void EngineRender(EngineBuffer &buffer, uint32_t input)
{
2018-09-19 02:18:58 +00:00
// Check for user input
CheckInputs(input);
// Clear the z-buffer
2018-09-20 02:14:45 +00:00
ClearDepthBuffer();
2018-09-19 02:18:58 +00:00
// Transform vertices to clip space
TransformToClipSpace();
2018-09-15 01:46:30 +00:00
2018-09-19 02:18:58 +00:00
// Clip near/far Z and cull backfaces
2018-09-20 02:14:45 +00:00
ClipAndCull();
2018-09-19 02:18:58 +00:00
// Light vertices and/or faces
2018-09-19 03:02:12 +00:00
LightMesh();
2018-09-19 02:18:58 +00:00
// Transform vertices to screen space
TransformToScreenSpace();
2018-09-15 01:46:30 +00:00
// Render
2018-09-20 02:14:45 +00:00
Render(buffer, memory);
}
2018-09-20 02:14:45 +00:00
void EngineShutdown(void)
{
}
// PRIVATE FUNCTIONS
static void CheckInputs(uint32_t input)
{
if (CHECK_BIT(input, TRANSLATE_X_POS))
{
2018-09-19 03:02:12 +00:00
light.position.x += 10;
}
else if (CHECK_BIT(input, TRANSLATE_X_NEG))
{
2018-09-19 03:02:12 +00:00
light.position.x -= 10;
}
if (CHECK_BIT(input, TRANSLATE_Z_POS))
{
2018-09-19 03:02:12 +00:00
light.position.z += 10;
}
else if (CHECK_BIT(input, TRANSLATE_Z_NEG))
{
2018-09-19 03:02:12 +00:00
light.position.z -= 10;
}
if (CHECK_BIT(input, TRANSLATE_Y_POS))
{
2018-09-19 03:02:12 +00:00
light.position.y += 10;
}
else if (CHECK_BIT(input, TRANSLATE_Y_NEG))
{
2018-09-19 03:02:12 +00:00
light.position.y -= 10;
}
if (CHECK_BIT(input, ROTATE_X_POS))
{
2018-09-19 02:18:58 +00:00
mesh.rotation.x += .10;
}
else if (CHECK_BIT(input, ROTATE_X_NEG))
{
2018-09-19 02:18:58 +00:00
mesh.rotation.x -= .10;
}
2018-09-19 02:18:58 +00:00
if (CHECK_BIT(input, ROTATE_Y_POS))
{
2018-09-19 02:18:58 +00:00
mesh.rotation.y += .10;
}
2018-09-19 02:18:58 +00:00
else if (CHECK_BIT(input, ROTATE_Y_NEG))
{
2018-09-19 02:18:58 +00:00
mesh.rotation.y -= .10;
}
2018-09-19 02:18:58 +00:00
if (CHECK_BIT(input, ROTATE_Z_POS))
{
2018-09-19 02:18:58 +00:00
mesh.rotation.z += .10;
}
2018-09-19 02:18:58 +00:00
else if (CHECK_BIT(input, ROTATE_Z_NEG))
{
2018-09-19 02:18:58 +00:00
mesh.rotation.z -= .10;
}
if (CHECK_BIT(input, SCALE_UP))
{
2018-09-19 03:02:12 +00:00
light.color.b = 0.0f;
light.color.g = 0.0f;
light.color.r = 1.0f;
}
else if (CHECK_BIT(input, SCALE_DOWN))
{
2018-09-19 03:02:12 +00:00
light.color.b = 1.0f;
light.color.g = 1.0f;
light.color.r = 1.0f;
}
}
2018-09-15 01:46:30 +00:00
2018-09-19 02:18:58 +00:00
static void ComputeNormals(void)
2018-09-15 01:46:30 +00:00
{
2018-09-20 02:14:45 +00:00
VertexList &verts = memory.localVerts;
FaceList &faces = memory.localFaces;
2018-09-19 02:18:58 +00:00
int vertexNormalCount[VERTEX_LIMIT] = {};
2018-09-15 01:46:30 +00:00
2018-09-20 02:14:45 +00:00
for (size_t f = 0; f < faces.size; ++f)
2018-09-15 01:46:30 +00:00
{
2018-09-20 02:14:45 +00:00
Face &face = faces.data[f];
2018-09-15 01:46:30 +00:00
2018-09-20 02:14:45 +00:00
Vertex &vert0 = verts.data[face.vertIndex[0]];
Vertex &vert1 = verts.data[face.vertIndex[1]];
Vertex &vert2 = verts.data[face.vertIndex[2]];
2018-09-15 01:46:30 +00:00
2018-09-20 02:14:45 +00:00
Vector v01 = vert1.point - vert0.point;
Vector v02 = vert2.point - vert0.point;
2018-09-15 01:46:30 +00:00
2018-09-19 05:33:52 +00:00
Vector normal = VectorCross(v01, v02);
2018-09-15 01:46:30 +00:00
// Add each vertex's normal to the sum for future averaging
2018-09-20 02:14:45 +00:00
vert0.normal += normal;
vert1.normal += normal;
vert2.normal += normal;
2018-09-15 01:46:30 +00:00
2018-09-20 02:14:45 +00:00
++vertexNormalCount[face.vertIndex[0]];
++vertexNormalCount[face.vertIndex[1]];
++vertexNormalCount[face.vertIndex[2]];
2018-09-15 01:46:30 +00:00
}
2018-09-20 02:14:45 +00:00
for (size_t v = 0; v < verts.size; ++v)
2018-09-15 01:46:30 +00:00
{
if (vertexNormalCount[v] > 0)
{
// Compute the average normal for this vertex
2018-09-20 02:14:45 +00:00
verts.data[v].normal /= vertexNormalCount[v];
VectorNormalize(verts.data[v].normal);
2018-09-19 02:18:58 +00:00
}
}
}
2018-09-20 02:14:45 +00:00
static void ClearDepthBuffer(void)
2018-09-19 02:18:58 +00:00
{
2018-09-20 02:14:45 +00:00
memset(memory.zbuffer, 0, sizeof(memory.zbuffer));
2018-09-19 02:18:58 +00:00
}
static void TransformToClipSpace(void)
{
2018-09-20 02:14:45 +00:00
VertexList &localVerts = memory.localVerts;
VertexList &transVerts = memory.transVerts;
2018-09-19 02:18:58 +00:00
Matrix tTranslate = Transform_Translate(mesh.position);
Matrix tRotate = Transform_Rotate(mesh.rotation);
Matrix tScale = Transform_Scale(mesh.scale);
Matrix tView = Transform_View(camera);
for (size_t v = 0; v < localVerts.size; ++v)
{
transVerts.data[v].point =
localVerts.data[v].point * tScale * tRotate * tTranslate * tView * tPersp;
transVerts.data[v].normal =
localVerts.data[v].normal * tScale * tRotate * tTranslate;
}
}
2018-09-20 02:14:45 +00:00
void ClipAndCull(void)
{
FaceList &localFaces = memory.localFaces;
FaceList &transFaces = memory.transFaces;
VertexList &verts = memory.transVerts;
int faceIndex = 0;
for (size_t f = 0; f < localFaces.size; ++f)
{
Face &face = localFaces.data[f];
Point &p0 = verts.data[face.vertIndex[0]].point;
Point &p1 = verts.data[face.vertIndex[1]].point;
Point &p2 = verts.data[face.vertIndex[2]].point;
// Ignore this face if its Z is outside the Z clip planes
if ( (p0.z < -p0.w)
|| (p0.z > p0.w)
|| (p1.z < -p1.w)
|| (p1.z > p1.w)
|| (p2.z < -p2.w)
|| (p2.z > p2.w))
{
continue;
}
// Calculate the face's normal (inverted for Blender-compatibility)
Vector v01 = p1 - p0;
Vector v02 = p2 - p0;
Vector normal = -VectorCross(v01, v02);
// Eye vector to viewport
Vector view = camera.position - p0;
float dot = VectorDot(normal, view);
// Not a backface; add it to the list
if (dot < EPSILON_E3)
{
transFaces.data[faceIndex] = face;
++faceIndex;
transFaces.size = (size_t)faceIndex;
}
}
}
2018-09-19 02:18:58 +00:00
static void TransformToScreenSpace(void)
{
2018-09-20 02:14:45 +00:00
VertexList &verts = memory.transVerts;
for (size_t v = 0; v < verts.size; ++v)
2018-09-19 02:18:58 +00:00
{
2018-09-20 02:14:45 +00:00
verts.data[v].point *= tScreen;
verts.data[v].point.x /= verts.data[v].point.w;
verts.data[v].point.y /= verts.data[v].point.w;
verts.data[v].point.z /= verts.data[v].point.w;
2018-09-19 02:18:58 +00:00
}
}
2018-09-19 03:02:12 +00:00
static void LightMesh(void)
2018-09-19 02:18:58 +00:00
{
2018-09-20 02:14:45 +00:00
VertexList &verts = memory.transVerts;
FaceList &faces = memory.transFaces;
MaterialList &materials = memory.materials;
for (size_t f = 0; f < faces.size; ++f)
2018-09-19 02:18:58 +00:00
{
2018-09-20 02:14:45 +00:00
Face &face = faces.data[f];
2018-09-19 02:18:58 +00:00
Material &material = materials.data[face.materialIndex];
// Gouraud shading
for (int i = 0; i < 3; ++i)
{
2018-09-20 02:14:45 +00:00
Vertex &vert = verts.data[face.vertIndex[i]];
2018-09-19 02:18:58 +00:00
2018-09-19 03:02:12 +00:00
vert.color = ComputeLight(vert, material, light);
2018-09-15 01:46:30 +00:00
}
}
}