diff options
author | vimene <vincent.menegaux@gmail.com> | 2025-01-02 13:25:14 +0100 |
---|---|---|
committer | vimene <vincent.menegaux@gmail.com> | 2025-01-02 13:25:14 +0100 |
commit | 26b4b82a7be2c029491f3009b66b3cbf8db5d93c (patch) | |
tree | ddc247a0600b5933185677212f158ebea5a87530 | |
parent | 9fdb5881d46f5d80626f961f9c9f133cc25dab70 (diff) | |
download | engine-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.cpp | 142 | ||||
-rw-r--r-- | src/fb/chfb.cpp | 10 | ||||
-rw-r--r-- | src/fb/pixfb.cpp | 26 | ||||
-rw-r--r-- | src/math/mat4.h | 21 | ||||
-rw-r--r-- | src/math/quat.h | 16 | ||||
-rw-r--r-- | src/math/tform.h | 15 | ||||
-rw-r--r-- | src/math/vector.h | 7 | ||||
-rw-r--r-- | src/o3d/camera.h | 12 | ||||
-rw-r--r-- | src/o3d/mesh.cpp | 27 | ||||
-rw-r--r-- | src/o3d/mesh.h | 6 | ||||
-rw-r--r-- | src/o3d/tri_deriv.cpp | 8 | ||||
-rw-r--r-- | src/o3d/vertex_data.h | 13 | ||||
-rw-r--r-- | src/obj_parser.cpp | 14 | ||||
-rw-r--r-- | src/renderer.cpp | 73 |
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()); } } }; |