aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2025-01-02 13:25:14 +0100
committervimene <vincent.menegaux@gmail.com>2025-01-02 13:25:14 +0100
commit26b4b82a7be2c029491f3009b66b3cbf8db5d93c (patch)
treeddc247a0600b5933185677212f158ebea5a87530
parent9fdb5881d46f5d80626f961f9c9f133cc25dab70 (diff)
downloadengine-26b4b82a7be2c029491f3009b66b3cbf8db5d93c.tar.gz
various improvements
- cleaned up the computation of the camera's matrix - changed VertexData to being a struct which transmit data between the "vertex shader" and the "fragment shader" - started working on keyboard and mouse controls - added fov (field of view) - changed quaternion to euler angles conversion, from zyx to zxy - fixed computations of z coordinates in triangle rendering - improved naming in the triangle rasterizer
-rw-r--r--src/engine.cpp142
-rw-r--r--src/fb/chfb.cpp10
-rw-r--r--src/fb/pixfb.cpp26
-rw-r--r--src/math/mat4.h21
-rw-r--r--src/math/quat.h16
-rw-r--r--src/math/tform.h15
-rw-r--r--src/math/vector.h7
-rw-r--r--src/o3d/camera.h12
-rw-r--r--src/o3d/mesh.cpp27
-rw-r--r--src/o3d/mesh.h6
-rw-r--r--src/o3d/tri_deriv.cpp8
-rw-r--r--src/o3d/vertex_data.h13
-rw-r--r--src/obj_parser.cpp14
-rw-r--r--src/renderer.cpp73
14 files changed, 274 insertions, 116 deletions
diff --git a/src/engine.cpp b/src/engine.cpp
index 9b2f673..0d99578 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -41,6 +41,8 @@ using
engine::o3d::Scene,
engine::o3d::Mesh,
engine::o3d::Triangle,
+ engine::o3d::Camera,
+ engine::o3d::VertexData,
engine::math::Vector3,
engine::math::Vector4,
engine::math::Matrix4,
@@ -54,6 +56,12 @@ using
#define MODE_TERM 1
#define MODE_GRAPHICAL 2
+#define GAME_PLANE 0
+#define GAME_SUZANNE 1
+#define GAME_PHYSICS 2
+
+#define GAME GAME_PHYSICS
+
static void print_usage(std::ostream& output_stream) {
output_stream << "Usage: ./engine [-htg] [--help] [--term] [--graphical]\n"
<< " -h, --help show usage (this)\n"
@@ -68,61 +76,77 @@ static void usage_error_exit() {
std::exit(EXIT_FAILURE);
}
-extern Quaternion camera_quat;
-Quaternion camera_quat = Quaternion::one();
+extern Camera* camera;
+Camera* camera;
template<typename FrameBuffer, typename UpdateFrameFn>
static void scene_main(Renderer<FrameBuffer>& renderer, const Matrix4& final_transform_mat, UpdateFrameFn update_frame) {
- float dist = 1.5f;
- float rad = 5.f;
bool cont = true;
Scene scene{
- {{{0.f, 0.f, rad * dist}, {1.f, 0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}},
+ {90.f * PI / 180.f, {{0.f, 1.8f, 7.f}, Quaternion::one(), {1.f, 1.f, 1.f}}},
{
-#if 0
+#if GAME == GAME_PLANE
{
- Mesh::plane(),
+ Mesh::plane(2.f, 2.f),
{
Vector3(0.f, 0.f, 0.f),
- {1.f, 0.f, 0.f, 0.f},
- Vector3(rad, rad, rad),
+ Quaternion::one(),
+ Vector3(1.f, 1.f, 1.f),
}
},
-#else
+#elif GAME == GAME_SUZANNE
{
engine::parse_object("../assets/suzanne.obj"),
{
Vector3(0.f, 0.f, 0.f),
- {1.f, 0.f, 0.f, 0.f},
- Vector3(rad, rad, rad),
+ Quaternion::one(),
+ Vector3(1.f, 1.f, 1.f),
+ }
+ },
+#elif GAME == GAME_PHYSICS
+ {
+ Mesh::plane(10.f, 10.f),
+ {
+ Vector3(0.f, 0.f, 0.f),
+ Quaternion::one(),
+ Vector3(1.f, 1.f, 1.f),
+ }
+ },
+ {
+ engine::parse_object("../assets/suzanne.obj"),
+ {
+ Vector3(0.f, 1.f, 0.f),
+ Quaternion::one(),
+ Vector3(1.f, 1.f, 1.f),
}
},
#endif
}
};
- float mul_angle = 0.f;
- while (cont) {
- camera_quat = Quaternion::euler_zyx(mul_angle * .0050f, mul_angle * .0065f, mul_angle * .0080f);
- mul_angle += 1.f;
- scene.camera.transform.rot = camera_quat;
+ camera = &scene.camera;
+
+ while (cont) {
renderer.clear();
auto pre_final_mat = final_transform_mat
- * Matrix4::projection(static_cast<float>(renderer.height()) / static_cast<float>(renderer.width()), 2.f, 50.f)
- * scene.camera.transform.opposite().to_mat4();
+ * scene.camera.to_mat4(static_cast<float>(renderer.height()) / static_cast<float>(renderer.width()), .5f, 12.f);
for (const auto& obj : scene.objs) {
- auto final_mat = pre_final_mat * obj.transform.to_mat4();
+ auto obj_mat = obj.transform.to_mat4();
+ auto final_mat = pre_final_mat * obj_mat;
const auto& mesh = obj.mesh;
std::vector<Vector4> vertices;
- for (const auto& vertex : mesh.vertices)
+ std::vector<VertexData> vertices_data;
+ for (const auto& vertex : mesh.vertices) {
vertices.push_back(final_mat * vertex);
+ vertices_data.push_back(VertexData((obj_mat * vertex).xyz()));
+ }
for (const auto& triangle_indices : mesh.indices) {
[&]<std::size_t... j>(std::integer_sequence<std::size_t, j...>) {
- renderer.draw_triangle({{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], mesh.vertices_data[triangle_indices[j][2]]}...});
+ renderer.draw_triangle({{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...});
}(std::make_integer_sequence<std::size_t, 3>());
}
}
- cont = update_frame();
+ cont = update_frame(scene);
}
}
@@ -149,7 +173,8 @@ static int main_term() {
getmaxyx(stdscr, h, w);
Renderer<CharacterFrameBuffer> renderer{CharacterFrameBuffer{static_cast<unsigned int>(w), static_cast<unsigned int>(h)}};
- scene_main(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), [&]() {
+ scene_main(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), [&](Scene& scene) {
+ (void) scene;
mvaddnstr(0, 0, renderer.fb.chars(), renderer.width() * renderer.height());
bool cont = true;
@@ -219,20 +244,85 @@ static int main_graphical() {
SDL_Event e;
- scene_main(engine_renderer, Matrix4::idty(), [&]() {
+ float rx = 0.f, ry = 0.f;
+ bool fw_down = false, left_down = false, bw_down = false, right_down = false, zoom_down = false;
+
+ scene_main(engine_renderer, Matrix4::idty(), [&](Scene& scene) {
+ (void) scene;
SDL_UpdateTexture(texture, nullptr, engine_renderer.fb.pixels(), SCREEN_WIDTH * 4);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer);
SDL_UpdateWindowSurface(window);
+ SDL_SetRelativeMouseMode(SDL_TRUE);
bool cont = true;
if (SDL_WaitEventTimeout(&e, 10)) {
do {
- if (e.type == SDL_QUIT) {
+ switch (e.type) {
+ case SDL_QUIT:
cont = false;
+ break;
+ case SDL_KEYDOWN:
+ switch (e.key.keysym.sym) {
+ case SDLK_z:
+ fw_down = true;
+ break;
+ case SDLK_q:
+ left_down = true;
+ break;
+ case SDLK_s:
+ bw_down = true;
+ break;
+ case SDLK_d:
+ right_down = true;
+ break;
+ case SDLK_LCTRL:
+ zoom_down = true;
+ break;
+ }
+ break;
+ case SDL_KEYUP:
+ switch (e.key.keysym.sym) {
+ case SDLK_z:
+ fw_down = false;
+ break;
+ case SDLK_q:
+ left_down = false;
+ break;
+ case SDLK_s:
+ bw_down = false;
+ break;
+ case SDLK_d:
+ right_down = false;
+ break;
+ case SDLK_LCTRL:
+ zoom_down = false;
+ break;
+ case SDLK_ESCAPE:
+ cont = false;
+ break;
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ rx += -static_cast<float>(e.motion.yrel) * .01f;
+ ry += -static_cast<float>(e.motion.xrel) * .01f;
+ if (rx < -PI / 2.f) rx = -PI / 2.f;
+ if (rx > PI / 2.f) rx = PI / 2.f;
+ scene.camera.transform.rot = Quaternion::euler_zxy(rx, ry, 0.f);
+ break;
}
} while (SDL_PollEvent(&e));
}
+
+ Vector3 movement(0.f, 0.f, 0.f);
+ if (fw_down) movement.z += -1.f;
+ if (left_down) movement.x += -1.f;
+ if (bw_down) movement.z += +1.f;
+ if (right_down) movement.x += +1.f;
+ if (fw_down || left_down || bw_down || right_down) movement.normalize();
+ scene.camera.transform.loc += movement.rot(Quaternion::rot_y(ry)) * .05f;
+ scene.camera.fov = (zoom_down ? 40.f : 80.f) * PI / 180.f;
+
return cont;
});
diff --git a/src/fb/chfb.cpp b/src/fb/chfb.cpp
index 71259aa..8068d8f 100644
--- a/src/fb/chfb.cpp
+++ b/src/fb/chfb.cpp
@@ -4,9 +4,13 @@
#include "math/vector.h"
#include "math/quat.h"
#include "o3d/vertex_data.h"
+#include "o3d/camera.h"
using namespace engine::fb;
-using engine::math::Vector3, engine::math::Quaternion, engine::o3d::VertexData;
+using
+ engine::math::Vector3,
+ engine::o3d::VertexData,
+ engine::o3d::Camera;
CharacterFrameBuffer::CharacterFrameBuffer(unsigned int w, unsigned int h) {
resize(w, h);
@@ -25,12 +29,12 @@ void CharacterFrameBuffer::clear() {
// taken from https://stackoverflow.com/a/74186686
char brightness_chars[] = " `.-':_,^=;><+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpOGbUAKXHm8RD#$Bg0MNWQ%&@";
-extern Quaternion camera_quat;
+extern Camera* camera;
void CharacterFrameBuffer::draw_point(int x, int y, const Vector3& loc, const VertexData& vd, const Vector3& normal) {
(void) loc;
(void) vd;
- auto v = normal.rot(camera_quat.conjugate());
+ auto v = normal.rot(camera->transform.rot.conjugate());
float light = .1f + (v.z < 0.f ? 0.f : v.z) * .9f;
std::uint32_t c = (int) (light * static_cast<float>(sizeof(brightness_chars) - 1));
chars_vector[x + y * w] = brightness_chars[c];
diff --git a/src/fb/pixfb.cpp b/src/fb/pixfb.cpp
index a8d6bee..d49566e 100644
--- a/src/fb/pixfb.cpp
+++ b/src/fb/pixfb.cpp
@@ -4,9 +4,13 @@
#include "math/vector.h"
#include "math/quat.h"
#include "o3d/vertex_data.h"
+#include "o3d/camera.h"
using namespace engine::fb;
-using engine::math::Vector3, engine::o3d::VertexData;
+using
+ engine::math::Vector3,
+ engine::o3d::VertexData,
+ engine::o3d::Camera;
PixelFrameBuffer::PixelFrameBuffer(unsigned int w, unsigned int h) {
resize(w, h);
@@ -23,16 +27,22 @@ void PixelFrameBuffer::clear() {
std::fill(pixels_vector.begin(), pixels_vector.end(), 0x000000FF);
}
-extern engine::math::Quaternion camera_quat;
+extern Camera* camera;
void PixelFrameBuffer::draw_point(int x, int y, const Vector3& loc, const VertexData& vd, const Vector3& normal) {
(void) loc;
(void) vd;
- // int ir = ((int) (((float) ((int) (vd.tx * 10.f))) * 25.5f));
- // int ig = ((int) (((float) ((int) (vd.ty * 10.f))) * 25.5f));
- // pixels_vector[x + y * w] = (ir << 24) | (ig << 16) | 0xFFFF;
- auto v = normal.rot(camera_quat.conjugate());
- float light = .1f + (v.z < 0.f ? 0.f : v.z) * .9f;
- std::uint32_t c = (int) (light * 255.f);
+ auto u = vd.world_loc - camera->transform.loc;
+ float u_len_sq = u.length_squared();
+ u = u.normalize();
+ float attenuation = Vector3(0.f, 0.f, -1.f).rot(camera->transform.rot).dot(u);
+ if (attenuation < .7f) attenuation = 0.f;
+ else if (attenuation > .8f) attenuation = 1.f;
+ else attenuation = (attenuation - .7f) / .1f;
+ float light = -normal.dot(u) / u_len_sq;
+ if (light < 0.f) light = 0.f;
+ float final_light = .05f + light * attenuation * .8f * .95f;
+ if (final_light > 1.f) final_light = 1.f;
+ std::uint32_t c = (int) (final_light * 255.f);
pixels_vector[x + y * w] = c << 24 | c << 16 | c << 8 | 0xff;
}
diff --git a/src/math/mat4.h b/src/math/mat4.h
index adb2059..df8f533 100644
--- a/src/math/mat4.h
+++ b/src/math/mat4.h
@@ -77,15 +77,26 @@ struct Matrix4 {
};
}
- static constexpr Matrix4 projection(float aspect_ratio, float min_z, float max_z) {
+ static constexpr Matrix4 projection(float fov, float aspect_ratio, float min_z, float max_z) {
+ float inv_tan = 1.f / std::tan(fov / 2.f);
return {{
- aspect_ratio, 0.f, 0.f, 0.f,
- 0.f, -1.f, 0.f, 0.f,
- 0.f, 0.f, -2.f / (max_z - min_z), -(max_z + min_z) / (max_z - min_z),
- 0.f, 0.f, -1.f, 0.f,
+ aspect_ratio * inv_tan, 0.f, 0.f, 0.f,
+ 0.f, -inv_tan, 0.f, 0.f,
+ 0.f, 0.f, -2.f / (max_z - min_z), -(max_z + min_z) / (max_z - min_z),
+ 0.f, 0.f, -1.f, 0.f,
}};
}
+ // TODO: should be Quaternion::to_mat4
+ static constexpr Matrix4 from_quaternion(const Quaternion& q) {
+ return {
+ 2.f * (q.w * q.w + q.x * q.x) - 1.f, 2.f * (q.x * q.y - q.w * q.z) , 2.f * (q.x * q.z + q.w * q.y) , 0.f,
+ 2.f * (q.x * q.y + q.w * q.z) , 2.f * (q.w * q.w + q.y * q.y) - 1.f, 2.f * (q.y * q.z - q.w * q.x) , 0.f,
+ 2.f * (q.x * q.z - q.w * q.y) , 2.f * (q.y * q.z + q.w * q.x) , 2.f * (q.w * q.w + q.z * q.z) - 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f,
+ };
+ }
+
std::array<float, 4 * 4> values;
constexpr Matrix4 operator+() const & {
diff --git a/src/math/quat.h b/src/math/quat.h
index 763253b..271070a 100644
--- a/src/math/quat.h
+++ b/src/math/quat.h
@@ -14,18 +14,22 @@ struct Quaternion {
return {1.f, 0.f, 0.f, 0.f};
}
- static constexpr Quaternion euler_zyx(float a, float b, float c) {
- float ca = std::cos(a / 2.f), sa = std::sin(a / 2.f),
- cb = std::cos(b / 2.f), sb = std::sin(b / 2.f),
- cc = std::cos(c / 2.f), sc = std::sin(c / 2.f);
+ static constexpr Quaternion euler_zxy(float rx, float ry, float rz) {
+ float ca = std::cos(rx / 2.f), sa = std::sin(rx / 2.f),
+ cb = std::cos(ry / 2.f), sb = std::sin(ry / 2.f),
+ cc = std::cos(rz / 2.f), sc = std::sin(rz / 2.f);
return {
- ca * cb * cc - sa * sb * sc,
+ ca * cb * cc + sa * sb * sc,
sa * cb * cc + ca * sb * sc,
ca * sb * cc - sa * cb * sc,
- ca * cb * sc + sa * sb * cc,
+ ca * cb * sc - sa * sb * cc,
};
}
+ static constexpr Quaternion rot_y(float a) {
+ return {std::cos(a / 2.f), 0.f, std::sin(a / 2.f), 0.f};
+ }
+
float w, x, y, z;
constexpr Quaternion() {}
diff --git a/src/math/tform.h b/src/math/tform.h
index 2d61494..2a654b8 100644
--- a/src/math/tform.h
+++ b/src/math/tform.h
@@ -1,6 +1,7 @@
#ifndef MATH_TFORM_H
#define MATH_TFORM_H
+#include <array>
#include "math/vector.h"
#include "math/mat4.h"
#include "math/quat.h"
@@ -28,6 +29,20 @@ class Transform {
0.f, 0.f, 0.f, 1.f,
};
}
+
+ constexpr Matrix4 to_inverse_mat4() const & {
+ std::array<float, 3 * 3> m{{
+ (2.f * (rot.w * rot.w + rot.x * rot.x) - 1.f) / scale.x, (2.f * (rot.x * rot.y + rot.w * rot.z) ) / scale.x, (2.f * (rot.x * rot.z - rot.w * rot.y) ) / scale.x,
+ (2.f * (rot.x * rot.y - rot.w * rot.z) ) / scale.y, (2.f * (rot.w * rot.w + rot.y * rot.y) - 1.f) / scale.y, (2.f * (rot.y * rot.z + rot.w * rot.x) ) / scale.y,
+ (2.f * (rot.x * rot.z + rot.w * rot.y) ) / scale.z, (2.f * (rot.y * rot.z - rot.w * rot.x) ) / scale.z, (2.f * (rot.w * rot.w + rot.z * rot.z) - 1.f) / scale.z,
+ }};
+ return {
+ m[0], m[1], m[2], - (m[0] * loc.x + m[1] * loc.y + m[2] * loc.z),
+ m[3], m[4], m[5], - (m[3] * loc.x + m[4] * loc.y + m[5] * loc.z),
+ m[6], m[7], m[8], - (m[6] * loc.x + m[7] * loc.y + m[8] * loc.z),
+ 0.f, 0.f, 0.f, 1.0f,
+ };
+ }
};
}
diff --git a/src/math/vector.h b/src/math/vector.h
index b26c5fe..d755c89 100644
--- a/src/math/vector.h
+++ b/src/math/vector.h
@@ -101,6 +101,13 @@ struct Vector3 {
return *this + (-other);
}
+ constexpr Vector3 operator+=(const Vector3& other) & {
+ x += other.x;
+ y += other.y;
+ z += other.z;
+ return *this;
+ }
+
constexpr Vector3 round() {
return { std::round(x), std::round(y), std::round(z) };
}
diff --git a/src/o3d/camera.h b/src/o3d/camera.h
index 27c31eb..832f473 100644
--- a/src/o3d/camera.h
+++ b/src/o3d/camera.h
@@ -1,12 +1,22 @@
#ifndef O3D_CAMERA_H
#define O3D_CAMERA_H
+#include "math/mat4.h"
#include "math/tform.h"
+using
+ engine::math::Matrix4,
+ engine::math::Transform;
+
namespace engine::o3d {
struct Camera {
- engine::math::Transform transform;
+ float fov;
+ Transform transform;
+
+ constexpr Matrix4 to_mat4(float aspect_ratio, float min_z, float max_z) const & {
+ return Matrix4::projection(fov, aspect_ratio, min_z, max_z) * transform.to_inverse_mat4();
+ }
};
}
diff --git a/src/o3d/mesh.cpp b/src/o3d/mesh.cpp
index 60c7f8b..2423e88 100644
--- a/src/o3d/mesh.cpp
+++ b/src/o3d/mesh.cpp
@@ -6,26 +6,25 @@
using namespace engine::o3d;
-Mesh Mesh::plane() {
+Mesh Mesh::plane(float width, float height) {
+ const float w2 = width / 2,
+ h2 = height / 2;
return {
{
- {-1.f, 0.f, -1.f, 1.f},
- {+1.f, 0.f, -1.f, 1.f},
- {+1.f, 0.f, +1.f, 1.f},
- {-1.f, 0.f, +1.f, 1.f},
+ {-w2 / 2, 0.f, -h2 / 2, 1.f},
+ {+w2 / 2, 0.f, -h2 / 2, 1.f},
+ {+w2 / 2, 0.f, +h2 / 2, 1.f},
+ {-w2 / 2, 0.f, +h2 / 2, 1.f},
},
- { {0.f, 0.f, -1.f} },
{
- {0.f, 0.f},
- {1.f, 0.f},
- {1.f, 1.f},
- {0.f, 1.f},
+ {0.f, -1.f, 0.f},
+ {0.f, +1.f, 0.f},
},
{
- {{ {{0, 0, 0}}, {{1, 0, 1}}, {{2, 0, 2}} }},
- {{ {{2, 0, 2}}, {{3, 0, 3}}, {{0, 0, 0}} }},
- {{ {{0, 0, 0}}, {{3, 0, 3}}, {{2, 0, 2}} }},
- {{ {{2, 0, 2}}, {{1, 0, 1}}, {{0, 0, 0}} }},
+ {{ {{0, 0}}, {{1, 0}}, {{2, 0}} }},
+ {{ {{2, 0}}, {{3, 0}}, {{0, 0}} }},
+ {{ {{0, 1}}, {{3, 1}}, {{2, 1}} }},
+ {{ {{2, 1}}, {{1, 1}}, {{0, 1}} }},
}
};
}
diff --git a/src/o3d/mesh.h b/src/o3d/mesh.h
index 1c70ca4..acc48b5 100644
--- a/src/o3d/mesh.h
+++ b/src/o3d/mesh.h
@@ -6,19 +6,17 @@
#include <iterator>
#include <cstddef>
#include "math/vector.h"
-#include "o3d/vertex_data.h"
namespace engine::o3d {
using engine::math::Vector3, engine::math::Vector4;
struct Mesh {
- static Mesh plane();
+ static Mesh plane(float width, float height);
std::vector<Vector4> vertices;
std::vector<Vector3> normals;
- std::vector<VertexData> vertices_data;
- std::vector<std::array<std::array<std::size_t, 3>, 3>> indices;
+ std::vector<std::array<std::array<std::size_t, 2>, 3>> indices;
};
}
diff --git a/src/o3d/tri_deriv.cpp b/src/o3d/tri_deriv.cpp
index b4184bc..a22067e 100644
--- a/src/o3d/tri_deriv.cpp
+++ b/src/o3d/tri_deriv.cpp
@@ -51,7 +51,7 @@ static void _perspective_crop_x(std::vector<TriangleDerived>& tris, TriangleDeri
{
x,
q2->vertex.y + fac2 * dq2.y,
- fac2_b * q1->vertex.z + (1.f - fac2_b) * q2->vertex.z,
+ (fac2_b * q1->vertex.w * q1->vertex.z + (1.f - fac2_b) * q2->vertex.w * q2->vertex.z) / r2w,
r2w
},
fac2_b * q1->b0 + (1.f - fac2_b) * q2->b0,
@@ -65,7 +65,7 @@ static void _perspective_crop_x(std::vector<TriangleDerived>& tris, TriangleDeri
{
x,
q3->vertex.y + fac3 * dq3.y,
- fac3_b * q1->vertex.z + (1.f - fac3_b) * q3->vertex.z,
+ (fac3_b * q1->vertex.w * q1->vertex.z + (1.f - fac3_b) * q3->vertex.w * q3->vertex.z) / r3w,
r3w
},
fac3_b * q1->b0 + (1.f - fac3_b) * q3->b0,
@@ -144,7 +144,7 @@ static void _perspective_crop_y(std::vector<TriangleDerived>& tris, TriangleDeri
{
q2->vertex.x + fac2 * dq2.x,
y,
- fac2_b * q1->vertex.z + (1.f - fac2_b) * q2->vertex.z,
+ (fac2_b * q1->vertex.w * q1->vertex.z + (1.f - fac2_b) * q2->vertex.w * q2->vertex.z) / r2w,
r2w
},
fac2_b * q1->b0 + (1.f - fac2_b) * q2->b0,
@@ -158,7 +158,7 @@ static void _perspective_crop_y(std::vector<TriangleDerived>& tris, TriangleDeri
{
q3->vertex.x + fac3 * dq3.x,
y,
- fac3_b * q1->vertex.z + (1.f - fac3_b) * q3->vertex.z,
+ (fac3_b * q1->vertex.w * q1->vertex.z + (1.f - fac3_b) * q3->vertex.w * q3->vertex.z) / r3w,
r3w
},
fac3_b * q1->b0 + (1.f - fac3_b) * q3->b0,
diff --git a/src/o3d/vertex_data.h b/src/o3d/vertex_data.h
index 71f42cd..22fa76b 100644
--- a/src/o3d/vertex_data.h
+++ b/src/o3d/vertex_data.h
@@ -1,24 +1,27 @@
#ifndef O3D_VERTEX_DATA_H
#define O3D_VERTEX_DATA_H
+#include "math/vector.h"
+
+using
+ engine::math::Vector3;
+
namespace engine::o3d {
struct VertexData {
static constexpr VertexData lerp(const VertexData& vd1, const VertexData& vd2, float b0) {
return {
- b0 * vd1.tx + (1.f - b0) * vd2.tx,
- b0 * vd1.ty + (1.f - b0) * vd2.ty
+ b0 * vd1.world_loc + (1.f - b0) * vd2.world_loc,
};
}
static constexpr VertexData bilerp(const VertexData& vd1, const VertexData& vd2, const VertexData& vd3, float b0, float b1) {
return {
- b0 * vd1.tx + b1 * vd2.tx + (1.f - b0 - b1) * vd3.tx,
- b0 * vd1.ty + b1 * vd2.ty + (1.f - b0 - b1) * vd3.ty
+ b0 * vd1.world_loc + b1 * vd2.world_loc + (1.f - b0 - b1) * vd3.world_loc,
};
}
- float tx, ty;
+ Vector3 world_loc;
};
}
diff --git a/src/obj_parser.cpp b/src/obj_parser.cpp
index ad71935..f765f5c 100644
--- a/src/obj_parser.cpp
+++ b/src/obj_parser.cpp
@@ -34,7 +34,7 @@ float parse_float(const std::string& s) {
if (s[ind++] == '-')
positive = !positive;
- // improve error checking
+ // TODO: improve error checking
if (ind == s.length())
return 0.f;
@@ -45,7 +45,7 @@ float parse_float(const std::string& s) {
if (ind == s.length())
return (positive ? 1.f : -1.f) * static_cast<float>(n);
- // improve error checking
+ // TODO: improve error checking
if (s[ind] != '.')
return 0.f;
@@ -58,7 +58,7 @@ float parse_float(const std::string& s) {
decimal_fac *= .1f;
}
- // improve error checking
+ // TODO: improve error checking
if (ind != s.length())
return 0.f;
@@ -72,7 +72,7 @@ float parse_int(const std::string& s) {
if (s[ind++] == '-')
positive = !positive;
- // improve error checking
+ // TODO: improve error checking
if (ind == s.length())
return 0.f;
@@ -80,7 +80,7 @@ float parse_int(const std::string& s) {
while (ind < s.length() && s[ind] >= '0' && s[ind] <= '9')
n = n * 10 + static_cast<int>(s[ind++]) - static_cast<int>('0');
- // improve error checking
+ // TODO: improve error checking
if (ind != s.length())
return 0.f;
@@ -91,7 +91,6 @@ float parse_int(const std::string& s) {
o3d::Mesh parse_object(const std::string& obj_path) {
o3d::Mesh mesh;
- mesh.vertices_data.push_back(o3d::VertexData(0.f, 0.f));
std::ifstream obj_file(obj_path);
std::string line;
while (std::getline(obj_file, line)) {
@@ -119,13 +118,12 @@ o3d::Mesh parse_object(const std::string& obj_path) {
// }
// std::cout << "Smooth: " << std::boolalpha << smooth << std::endl;
} else if (line.rfind("f ", 0) == 0) {
- std::array<std::array<std::size_t, 3>, 3> indices;
+ std::array<std::array<std::size_t, 2>, 3> indices;
auto line_split = split(line.substr(2), ' ');
for (int i = 0; i < 3; i++) {
auto indices_s_group = split(line_split[i], '/');
indices[i][0] = parse_int(indices_s_group[0]) - 1;
indices[i][1] = parse_int(indices_s_group[2]) - 1;
- indices[i][2] = 0;
}
// std::cout << "Face:"
// << " 1: vertex: " << indices[0][0] << " normal: " << indices[0][1]
diff --git a/src/renderer.cpp b/src/renderer.cpp
index ad67ecc..7e868b4 100644
--- a/src/renderer.cpp
+++ b/src/renderer.cpp
@@ -14,6 +14,7 @@ using
engine::o3d::Triangle,
engine::o3d::TriangleDerived,
engine::o3d::VertexData,
+ engine::o3d::DerivedVertex,
engine::fb::CharacterFrameBuffer,
engine::fb::PixelFrameBuffer;
@@ -59,31 +60,31 @@ void Renderer<FrameBuffer>::draw_triangle(const Triangle& triangle) {
template<typename FrameBuffer>
void Renderer<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const TriangleDerived& triangle) {
- std::array<const o3d::DerivedVertex*, 3> sorted_vs = { &triangle.derived_vertex1, &triangle.derived_vertex2, &triangle.derived_vertex3 };
+ std::array<const DerivedVertex*, 3> sorted_vs = { &triangle.derived_vertex1, &triangle.derived_vertex2, &triangle.derived_vertex3 };
{
- const auto swap_if_lt = [&](auto x, auto y) {
- if (sorted_vs[x]->vertex.y < sorted_vs[y]->vertex.y) {
+ const auto swap_if_gt = [&](auto x, auto y) {
+ if (sorted_vs[x]->vertex.y > sorted_vs[y]->vertex.y) {
auto temp = sorted_vs[x];
sorted_vs[x] = sorted_vs[y];
sorted_vs[y] = temp;
}
};
- swap_if_lt(1, 0);
- swap_if_lt(2, 1);
- swap_if_lt(1, 0);
+ swap_if_gt(0, 1);
+ swap_if_gt(1, 2);
+ swap_if_gt(0, 1);
}
auto middle_vl = *sorted_vs[1];
const float fac = (sorted_vs[1]->vertex.y - sorted_vs[0]->vertex.y) / (sorted_vs[2]->vertex.y - sorted_vs[0]->vertex.y);
const float middle_vr_vertex_w = 1.f / (1.f / sorted_vs[0]->vertex.w + fac * (1.f / sorted_vs[2]->vertex.w - 1.f / sorted_vs[0]->vertex.w));
const float fac_b0 = middle_vr_vertex_w * (1.f - fac) / sorted_vs[0]->vertex.w;
- o3d::DerivedVertex middle_vr{
+ DerivedVertex middle_vr{
{
sorted_vs[0]->vertex.x + fac * (sorted_vs[2]->vertex.x - sorted_vs[0]->vertex.x),
sorted_vs[1]->vertex.y,
- fac_b0 * sorted_vs[0]->vertex.z + (1.f - fac_b0) * sorted_vs[2]->vertex.z,
+ (fac_b0 * sorted_vs[0]->vertex.w * sorted_vs[0]->vertex.z + (1.f - fac_b0) * sorted_vs[2]->vertex.w * sorted_vs[2]->vertex.z) / middle_vr_vertex_w,
middle_vr_vertex_w
},
fac_b0 * sorted_vs[0]->b0 + (1.f - fac_b0) * sorted_vs[2]->b0,
@@ -97,7 +98,7 @@ void Renderer<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const T
const auto _draw_cropped_triangle_side = [&]<TriangleSide side>() {
const int vertex_end_index = ([&]() { if constexpr (side == TriangleSide::top) return 0; else return 2; })();
- const o3d::DerivedVertex& vertex_end = *sorted_vs[vertex_end_index];
+ const DerivedVertex& vertex_end = *sorted_vs[vertex_end_index];
if (vertex_end.vertex.y == sorted_vs[1]->vertex.y)
return;
int top_y;
@@ -110,34 +111,42 @@ void Renderer<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const T
top_y = static_cast<int>(std::ceil(sorted_vs[1]->vertex.y));
}
for (int y = top_y; y <= bottom_y; y++) {
- float iy = static_cast<float>(y);
- float s = (iy - vertex_end.vertex.y) / (sorted_vs[1]->vertex.y - vertex_end.vertex.y);
- float sub_bb0 = 1.f - s;
+ float s = (static_cast<float>(y) - vertex_end.vertex.y)
+ / (sorted_vs[1]->vertex.y - vertex_end.vertex.y);
+ float projected_relative_b0 = 1.f - s;
float xl = vertex_end.vertex.x + s * (middle_vl.vertex.x - vertex_end.vertex.x);
float xr = vertex_end.vertex.x + s * (middle_vr.vertex.x - vertex_end.vertex.x);
int left_x = static_cast<int>(std::ceil(xl));
int right_x = static_cast<int>(std::ceil(xr - 1.f));
for (int x = left_x; x <= right_x; x++) {
- float ix = static_cast<float>(x);
- float t = (ix - xl) / (xr - xl);
- float sub_bb1 = s * (1.f - t);
- float b_fac = 1.f / (sub_bb0 / vertex_end.vertex.w + sub_bb1 / middle_vl.vertex.w + (1.f - sub_bb0 - sub_bb1) / middle_vr.vertex.w);
- float sub_b0 = b_fac * sub_bb0 / vertex_end.vertex.w;
- float sub_b1 = b_fac * sub_bb1 / middle_vl.vertex.w;
- float loc_z = sub_b0 * vertex_end.vertex.z + sub_b1 * middle_vl.vertex.z + (1.f - sub_b0 - sub_b1) * middle_vr.vertex.z;
- if (loc_z < depth_buf[x + y * fb.width()]) {
- depth_buf[x + y * fb.width()] = loc_z;
- float b0 = sub_b0 * vertex_end.b0 + sub_b1 * middle_vl.b0 + (1.f - sub_b0 - sub_b1) * middle_vr.b0;
- float b1 = sub_b0 * vertex_end.b1 + sub_b1 * middle_vl.b1 + (1.f - sub_b0 - sub_b1) * middle_vr.b1;
- Vector3 loc{
- b0 * root.vertex1.vertex.x + b1 * root.vertex2.vertex.x + (1.f - b0 - b1) * root.vertex3.vertex.x,
- b0 * root.vertex1.vertex.y + b1 * root.vertex2.vertex.y + (1.f - b0 - b1) * root.vertex3.vertex.y,
- loc_z
- };
- fb.draw_point(x, y, loc,
- VertexData::bilerp(root.vertex1.data, root.vertex2.data, root.vertex3.data, b0, b1),
- Vector3::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1).normalize());
- }
+ float t = (static_cast<float>(x) - xl)
+ / (xr - xl);
+ float projected_relative_b1 = s * (1.f - t);
+ float point_w = 1.f /
+ (
+ projected_relative_b0 / vertex_end.vertex.w
+ + projected_relative_b1 / middle_vl.vertex.w
+ + (1.f - projected_relative_b0 - projected_relative_b1) / middle_vr.vertex.w
+ );
+ float relative_b0 = point_w * projected_relative_b0 / vertex_end.vertex.w;
+ float relative_b1 = point_w * projected_relative_b1 / middle_vl.vertex.w;
+ float loc_z = relative_b0 * vertex_end.vertex.w * vertex_end.vertex.z
+ + relative_b1 * middle_vl.vertex.w * middle_vl.vertex.z
+ + (1.f - relative_b0 - relative_b1) * middle_vr.vertex.w * middle_vr.vertex.z;
+
+ if (loc_z >= depth_buf[x + y * fb.width()]) continue;
+
+ depth_buf[x + y * fb.width()] = loc_z;
+ float b0 = relative_b0 * vertex_end.b0 + relative_b1 * middle_vl.b0 + (1.f - relative_b0 - relative_b1) * middle_vr.b0;
+ float b1 = relative_b0 * vertex_end.b1 + relative_b1 * middle_vl.b1 + (1.f - relative_b0 - relative_b1) * middle_vr.b1;
+ Vector3 loc{
+ b0 * root.vertex1.vertex.x + b1 * root.vertex2.vertex.x + (1.f - b0 - b1) * root.vertex3.vertex.x,
+ b0 * root.vertex1.vertex.y + b1 * root.vertex2.vertex.y + (1.f - b0 - b1) * root.vertex3.vertex.y,
+ loc_z
+ };
+ fb.draw_point(x, y, loc,
+ VertexData::bilerp(root.vertex1.data, root.vertex2.data, root.vertex3.data, b0, b1),
+ Vector3::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1).normalize());
}
}
};