├── examples ├── rasterized_triangle_app │ ├── resources │ │ ├── triangle.obj │ │ ├── chess.png │ │ └── Ubuntu-L.ttf │ ├── UbuntuFontLicense.txt │ └── main.cpp └── empty_app │ └── main.cpp ├── lantern ├── src │ ├── geometry_stage.cpp │ ├── vector4.cpp │ ├── merging_stage.cpp │ ├── renderer.cpp │ ├── math_common.cpp │ ├── rasterizing_stage.cpp │ ├── ui_element_base.cpp │ ├── line.cpp │ ├── color.cpp │ ├── mesh.cpp │ ├── font.cpp │ ├── texture.cpp │ ├── camera.cpp │ ├── ui_label.cpp │ ├── matrix4x4.cpp │ ├── matrix3x3.cpp │ ├── app.cpp │ └── obj_import.cpp └── include │ ├── aabb.h │ ├── vector4.h │ ├── color.h │ ├── math_common.h │ ├── shader_bind_point_info.h │ ├── ui_element_base.h │ ├── line.h │ ├── ui_label.h │ ├── merging_stage.h │ ├── font.h │ ├── matrix3x3.h │ ├── mesh_attribute_info.h │ ├── color_shader.h │ ├── mesh.h │ ├── app.h │ ├── matrix4x4.h │ ├── texture.h │ ├── texture_shader.h │ ├── ui_label_shader.h │ ├── camera.h │ ├── geometry_stage.h │ ├── obj_import.h │ ├── vector2.h │ ├── renderer.h │ └── vector3.h ├── tests ├── images │ ├── rasterization_top_left_rule_test_case_1.png │ ├── rasterization_top_left_rule_test_case_2.png │ ├── rasterization_top_left_rule_test_case_3.png │ ├── rasterization_top_left_rule_test_case_4.png │ ├── rasterization_top_left_rule_test_case_5.png │ ├── rasterization_top_left_rule_test_case_6.png │ └── rasterize_test_triangle_no_ambiguities_test_case.png ├── src │ ├── main.cpp │ ├── vector4.cpp │ ├── camera.cpp │ ├── matrix3x3.cpp │ ├── vector3.cpp │ ├── matrix4x4.cpp │ ├── obj_import.cpp │ └── pipeline.cpp ├── resources │ ├── unit_cube_pos.obj │ ├── unit_cube_pos_texcoord.obj │ ├── unit_cube_pos_normal.obj │ └── unit_cube_pos_texcoord_normal.obj └── include │ └── assert_utils.h ├── .gitignore ├── cmake ├── FindSDL2IMAGE.cmake └── FindSDL2.cmake ├── LICENSE ├── README.md └── CMakeLists.txt /examples/rasterized_triangle_app/resources/triangle.obj: -------------------------------------------------------------------------------- 1 | v 0.0 0.5 0.0 2 | v -0.5 -0.5 0.0 3 | v 0.5 -0.5 0.0 4 | 5 | f 1 2 3 -------------------------------------------------------------------------------- /lantern/src/geometry_stage.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry_stage.h" 2 | 3 | using namespace lantern; 4 | 5 | geometry_stage::geometry_stage() 6 | { 7 | 8 | } -------------------------------------------------------------------------------- /examples/rasterized_triangle_app/resources/chess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/examples/rasterized_triangle_app/resources/chess.png -------------------------------------------------------------------------------- /examples/rasterized_triangle_app/resources/Ubuntu-L.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/examples/rasterized_triangle_app/resources/Ubuntu-L.ttf -------------------------------------------------------------------------------- /tests/images/rasterization_top_left_rule_test_case_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterization_top_left_rule_test_case_1.png -------------------------------------------------------------------------------- /tests/images/rasterization_top_left_rule_test_case_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterization_top_left_rule_test_case_2.png -------------------------------------------------------------------------------- /tests/images/rasterization_top_left_rule_test_case_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterization_top_left_rule_test_case_3.png -------------------------------------------------------------------------------- /tests/images/rasterization_top_left_rule_test_case_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterization_top_left_rule_test_case_4.png -------------------------------------------------------------------------------- /tests/images/rasterization_top_left_rule_test_case_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterization_top_left_rule_test_case_5.png -------------------------------------------------------------------------------- /tests/images/rasterization_top_left_rule_test_case_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterization_top_left_rule_test_case_6.png -------------------------------------------------------------------------------- /tests/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | int main(int argc, char** argv) 4 | { 5 | testing::InitGoogleTest(&argc, argv); 6 | return RUN_ALL_TESTS(); 7 | } 8 | -------------------------------------------------------------------------------- /tests/images/rasterize_test_triangle_no_ambiguities_test_case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loreglean/lantern/HEAD/tests/images/rasterize_test_triangle_no_ambiguities_test_case.png -------------------------------------------------------------------------------- /lantern/src/vector4.cpp: -------------------------------------------------------------------------------- 1 | #include "vector4.h" 2 | 3 | using namespace lantern; 4 | 5 | vector4f::vector4f() 6 | : x{0.0f}, y{0.0f}, z{0.0f}, w{0.0f} 7 | { 8 | } 9 | 10 | vector4f::vector4f(float const x, float const y, float const z, float const w) 11 | : x{x}, y{y}, z{z}, w{w} 12 | { 13 | 14 | } -------------------------------------------------------------------------------- /lantern/include/aabb.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_AABB_H 2 | #define LANTERN_AABB_H 3 | 4 | namespace lantern 5 | { 6 | /** Class that represents axis-aligned bounding box */ 7 | template 8 | class aabb final 9 | { 10 | public: 11 | /** Starting point */ 12 | TPoint from; 13 | 14 | /** End point */ 15 | TPoint to; 16 | }; 17 | } 18 | 19 | #endif // LANTERN_AABB_H -------------------------------------------------------------------------------- /tests/resources/unit_cube_pos.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | v 0.0 0.0 0.0 4 | v 0.0 0.0 1.0 5 | v 0.0 1.0 0.0 6 | v 0.0 1.0 1.0 7 | v 1.0 0.0 0.0 8 | v 1.0 0.0 1.0 9 | v 1.0 1.0 0.0 10 | v 1.0 1.0 1.0 11 | 12 | f 1 7 5 13 | f 1 3 7 14 | f 1 4 3 15 | f 1 2 4 16 | f 3 8 7 17 | f 3 4 8 18 | f 5 7 8 19 | f 5 8 6 20 | f 1 5 6 21 | f 1 6 2 22 | f 2 6 8 23 | f 2 8 4 24 | -------------------------------------------------------------------------------- /lantern/src/merging_stage.cpp: -------------------------------------------------------------------------------- 1 | #include "merging_stage.h" 2 | 3 | using namespace lantern; 4 | 5 | merging_stage::merging_stage() 6 | : m_alpha_blending_enabled{false} 7 | { 8 | 9 | } 10 | 11 | bool merging_stage::get_alpha_blending_enabled() const 12 | { 13 | return m_alpha_blending_enabled; 14 | } 15 | 16 | void merging_stage::set_alpha_blending_enabled(bool const enabled) 17 | { 18 | m_alpha_blending_enabled = enabled; 19 | } -------------------------------------------------------------------------------- /lantern/src/renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "renderer.h" 2 | 3 | using namespace lantern; 4 | 5 | renderer::renderer() 6 | { 7 | 8 | } 9 | 10 | geometry_stage& renderer::get_geometry_stage() 11 | { 12 | return m_geometry_stage; 13 | } 14 | 15 | rasterizing_stage& renderer::get_rasterizing_stage() 16 | { 17 | return m_rasterizing_stage; 18 | } 19 | 20 | merging_stage& renderer::get_merging_stage() 21 | { 22 | return m_merging_stage; 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | 23 | # CLion 24 | .idea 25 | 26 | # Doxygen 27 | doc 28 | 29 | # Build dir 30 | build/ 31 | 32 | # On Mac 33 | .DS_Store 34 | 35 | # Do not ignore resources 36 | !**/resources/* 37 | -------------------------------------------------------------------------------- /lantern/src/math_common.cpp: -------------------------------------------------------------------------------- 1 | #include "math_common.h" 2 | 3 | using namespace lantern; 4 | 5 | float lantern::triangle_2d_area( 6 | float const x0, float const y0, 7 | float const x1, float const y1, 8 | float const x2, float const y2) 9 | { 10 | return std::abs(0.5f * (x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1))); 11 | } 12 | 13 | float lantern::clamp(float const value, float const from, float const to) 14 | { 15 | return (value > to ? to : (value < from ? from : value)); 16 | } -------------------------------------------------------------------------------- /tests/src/vector4.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_utils.h" 2 | 3 | using namespace lantern; 4 | 5 | TEST(vector4f, constructors) 6 | { 7 | vector4f const v{2.0f, -4.0f, 15.0f, 1.0f}; 8 | assert_floats_near(v.x, 2.0f); 9 | assert_floats_near(v.y, -4.0f); 10 | assert_floats_near(v.z, 15.0f); 11 | assert_floats_near(v.w, 1.0f); 12 | 13 | vector4f const v_default{}; 14 | assert_floats_near(v_default.x, 0.0f); 15 | assert_floats_near(v_default.y, 0.0f); 16 | assert_floats_near(v_default.z, 0.0f); 17 | assert_floats_near(v_default.w, 0.0f); 18 | } 19 | -------------------------------------------------------------------------------- /tests/resources/unit_cube_pos_texcoord.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | v 0.0 0.0 0.0 4 | v 0.0 0.0 1.0 5 | v 0.0 1.0 0.0 6 | v 0.0 1.0 1.0 7 | v 1.0 0.0 0.0 8 | v 1.0 0.0 1.0 9 | v 1.0 1.0 0.0 10 | v 1.0 1.0 1.0 11 | 12 | vt 0.0 0.0 13 | vt 1.0 0.0 14 | vt 1.0 1.0 15 | vt 0.0 1.0 16 | 17 | f 1/1 7/3 5/2 18 | f 1/1 3/4 7/3 19 | f 1/1 4/3 3/4 20 | f 1/1 2/2 4/3 21 | f 3/1 8/3 7/4 22 | f 3/1 4/2 8/3 23 | f 5/2 7/3 8/4 24 | f 5/2 8/4 6/1 25 | f 1/1 5/2 6/3 26 | f 1/1 6/3 2/4 27 | f 2/1 6/2 8/3 28 | f 2/1 8/3 4/4 29 | -------------------------------------------------------------------------------- /lantern/src/rasterizing_stage.cpp: -------------------------------------------------------------------------------- 1 | #include "rasterizing_stage.h" 2 | 3 | using namespace lantern; 4 | 5 | rasterizing_stage::rasterizing_stage() 6 | : m_rasterization_algorithm{rasterization_algorithm_option::homogeneous} 7 | { 8 | 9 | } 10 | 11 | void rasterizing_stage::set_rasterization_algorithm(rasterization_algorithm_option algorithm_option) 12 | { 13 | m_rasterization_algorithm = algorithm_option; 14 | } 15 | 16 | rasterization_algorithm_option rasterizing_stage::get_rasterization_algorithm() const 17 | { 18 | return m_rasterization_algorithm; 19 | } -------------------------------------------------------------------------------- /examples/empty_app/main.cpp: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | 3 | using namespace lantern; 4 | 5 | class empty_app : public app 6 | { 7 | public: 8 | empty_app(unsigned int const width, unsigned int const height); 9 | 10 | protected: 11 | void frame(float const delta_since_last_frame) override; 12 | }; 13 | 14 | empty_app::empty_app(unsigned int const width, unsigned int const height) 15 | : app(width, height) 16 | { 17 | } 18 | 19 | void empty_app::frame(float delta_since_last_frame) 20 | { 21 | } 22 | 23 | int main(int argc, char* argv[]) 24 | { 25 | return empty_app{640, 480}.start(); 26 | } 27 | -------------------------------------------------------------------------------- /tests/resources/unit_cube_pos_normal.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | v 0.0 0.0 0.0 4 | v 0.0 0.0 1.0 5 | v 0.0 1.0 0.0 6 | v 0.0 1.0 1.0 7 | v 1.0 0.0 0.0 8 | v 1.0 0.0 1.0 9 | v 1.0 1.0 0.0 10 | v 1.0 1.0 1.0 11 | 12 | vn 0.0 0.0 1.0 13 | vn 0.0 0.0 -1.0 14 | vn 0.0 1.0 0.0 15 | vn 0.0 -1.0 0.0 16 | vn 1.0 0.0 0.0 17 | vn -1.0 0.0 0.0 18 | 19 | f 1//2 7//2 5//2 20 | f 1//2 3//2 7//2 21 | f 1//6 4//6 3//6 22 | f 1//6 2//6 4//6 23 | f 3//3 8//3 7//3 24 | f 3//3 4//3 8//3 25 | f 5//5 7//5 8//5 26 | f 5//5 8//5 6//5 27 | f 1//4 5//4 6//4 28 | f 1//4 6//4 2//4 29 | f 2//1 6//1 8//1 30 | f 2//1 8//1 4//1 31 | -------------------------------------------------------------------------------- /lantern/src/ui_element_base.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_element_base.h" 2 | 3 | using namespace lantern; 4 | 5 | ui_element_base::~ui_element_base() 6 | { 7 | 8 | } 9 | 10 | void ui_element_base::set_position(vector2f position) 11 | { 12 | m_position = position; 13 | 14 | on_position_changed(); 15 | } 16 | 17 | void ui_element_base::draw(renderer& pipeline, texture& target_texture) 18 | { 19 | for (ui_element_base* element : m_children) 20 | { 21 | element->draw(pipeline, target_texture); 22 | } 23 | } 24 | 25 | std::vector ui_element_base::get_children_storage() 26 | { 27 | return m_children; 28 | } 29 | 30 | void ui_element_base::on_position_changed() 31 | { 32 | 33 | } -------------------------------------------------------------------------------- /tests/resources/unit_cube_pos_texcoord_normal.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | v 0.0 0.0 0.0 4 | v 0.0 0.0 1.0 5 | v 0.0 1.0 0.0 6 | v 0.0 1.0 1.0 7 | v 1.0 0.0 0.0 8 | v 1.0 0.0 1.0 9 | v 1.0 1.0 0.0 10 | v 1.0 1.0 1.0 11 | 12 | vn 0.0 0.0 1.0 13 | vn 0.0 0.0 -1.0 14 | vn 0.0 1.0 0.0 15 | vn 0.0 -1.0 0.0 16 | vn 1.0 0.0 0.0 17 | vn -1.0 0.0 0.0 18 | 19 | vt 0.0 0.0 20 | vt 1.0 0.0 21 | vt 1.0 1.0 22 | vt 0.0 1.0 23 | 24 | f 1/1/2 7/3/2 5/2/2 25 | f 1/1/2 3/4/2 7/3/2 26 | f 1/1/6 4/3/6 3/4/6 27 | f 1/1/6 2/2/6 4/3/6 28 | f 3/1/3 8/3/3 7/4/3 29 | f 3/1/3 4/2/3 8/3/3 30 | f 5/2/5 7/3/5 8/4/5 31 | f 5/2/5 8/4/5 6/1/5 32 | f 1/1/4 5/2/4 6/3/4 33 | f 1/1/4 6/3/4 2/4/4 34 | f 2/1/1 6/2/1 8/3/1 35 | f 2/1/1 8/3/1 4/4/1 36 | -------------------------------------------------------------------------------- /lantern/src/line.cpp: -------------------------------------------------------------------------------- 1 | #include "line.h" 2 | 3 | using namespace lantern; 4 | 5 | line::line(float const a_coeff, float const b_coeff, float const c_coeff) 6 | : a{a_coeff}, b{b_coeff}, c{c_coeff} 7 | { 8 | 9 | } 10 | 11 | line::line(float const x0, float const y0, float const x1, float const y1) 12 | { 13 | a = -(y1 - y0); 14 | b = x1 - x0; 15 | c = (y1 - y0) * x0 - (x1 - x0) * y0; 16 | } 17 | 18 | float line::at(float const x, float const y) const 19 | { 20 | return x * a + y * b + c; 21 | } 22 | 23 | vector2f line::intersection(line const& line) const 24 | { 25 | float const y{(line.a * c - a * line.c) / (a * line.b - line.a * b)}; 26 | float const x{a == 0.0f ? ((line.b * c - b * line.c) / (line.a * b)) : (-(b / a) * y - (c / a))}; 27 | 28 | return vector2f{x, y}; 29 | } -------------------------------------------------------------------------------- /lantern/include/vector4.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_VECTOR4_H 2 | #define LANTERN_VECTOR4_H 3 | 4 | namespace lantern 5 | { 6 | /** Class representing 4-dimensional homogeneous vector. 7 | * Used to apply 3D affine transformations represented as 4x4 matrices 8 | * There is no templated version because there is no need for it 9 | */ 10 | class vector4f final 11 | { 12 | public: 13 | /** X-coordinate */ 14 | float x; 15 | 16 | /** Y-coordinate */ 17 | float y; 18 | 19 | /** Z-coordinate */ 20 | float z; 21 | 22 | /** W-coordinate */ 23 | float w; 24 | 25 | /** Constructs vector with zero coordinates */ 26 | vector4f(); 27 | 28 | /** Constructs vector with specified coordinates values */ 29 | vector4f(float const x, float const y, float const z, float const w); 30 | }; 31 | } 32 | 33 | #endif // LANTERN_VECTOR4_H 34 | -------------------------------------------------------------------------------- /lantern/include/color.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_COLOR_H 2 | #define LANTERN_COLOR_H 3 | 4 | #include "math_common.h" 5 | namespace lantern 6 | { 7 | /** Class representing RGB color */ 8 | class color final 9 | { 10 | public: 11 | 12 | /** Red component */ 13 | float r; 14 | 15 | /** Green component */ 16 | float g; 17 | 18 | /** Blue component */ 19 | float b; 20 | 21 | /** Alpha component */ 22 | float a; 23 | 24 | color with_alpha(float alpha) const; 25 | 26 | // Operators 27 | // 28 | 29 | bool operator==(color const& another) const; 30 | bool operator!=(color const& another) const; 31 | color operator*(float const s) const; 32 | color operator+(color const& another) const; 33 | 34 | static const color BLACK; 35 | static const color WHITE; 36 | static const color RED; 37 | static const color GREEN; 38 | static const color BLUE; 39 | }; 40 | } 41 | 42 | #endif // LANTERN_COLOR_H 43 | -------------------------------------------------------------------------------- /lantern/include/math_common.h: -------------------------------------------------------------------------------- 1 | #ifndef LATNERN_COMMON_MATH_H 2 | #define LATNERN_COMMON_MATH_H 3 | 4 | #include 5 | #include "vector2.h" 6 | 7 | namespace lantern 8 | { 9 | float const FLOAT_EPSILON = 0.00001f; 10 | 11 | template 12 | inline bool equals(T const first, T const second) 13 | { 14 | return (first == second); 15 | } 16 | 17 | template<> 18 | inline bool equals(float const first, float const second) 19 | { 20 | return std::abs(first - second) < FLOAT_EPSILON; 21 | } 22 | 23 | /** Calculates triangle area 24 | * @param a First triangle point 25 | * @param b Second triangle point 26 | * @param c Third triangle point 27 | * @returns Triangle area 28 | */ 29 | float triangle_2d_area( 30 | float const x0, float const y0, 31 | float const x1, float const y1, 32 | float const x2, float const y2); 33 | 34 | float clamp(float const value, float const from, float const to); 35 | } 36 | 37 | #endif // LATNERN_COMMON_MATH_H -------------------------------------------------------------------------------- /lantern/include/shader_bind_point_info.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_SHADER_H 2 | #define LANTERN_SHADER_H 3 | 4 | namespace lantern 5 | { 6 | /** @defgroup Shaders 7 | * Shader is a class representing shading algorithm. 8 | * It should not be treated as something "running on GPU" despite of common notation. 9 | * Its purpose is to move shading algorithm into separate object. 10 | * They do not derive from any base class to avoid virtual functions calls and allow inlining. 11 | * So they all should comply the same interface, \ref color_shader is an example 12 | */ 13 | 14 | /** Represents shader bind point information, holds required attribute ID and bind point itself. 15 | * Bind point is just an address of variable for rasterizer to put interpolated value into 16 | * @ingroup Shaders 17 | */ 18 | template 19 | class shader_bind_point_info 20 | { 21 | public: 22 | /** Attribute id */ 23 | unsigned int attribute_id; 24 | 25 | /** Address to put value into */ 26 | TAttr* bind_point; 27 | }; 28 | } 29 | 30 | #endif // LANTERN_SHADER_H -------------------------------------------------------------------------------- /cmake/FindSDL2IMAGE.cmake: -------------------------------------------------------------------------------- 1 | find_path(SDL2IMAGE_INCLUDE_DIR 2 | NAMES 3 | SDL_image.h 4 | HINTS 5 | ENV SDL2IMAGEDIR 6 | PATH_SUFFIXES 7 | include/ 8 | include/SDL2 9 | include/SDL2Image) 10 | 11 | find_library(SDL2IMAGE_LIBRARY 12 | NAMES 13 | SDL2_image 14 | HINTS 15 | ENV SDL2IMAGEDIR 16 | PATH_SUFFIXES 17 | lib 18 | lib/x86) 19 | 20 | find_file(SDL2IMAGE_DLL 21 | NAMES 22 | SDL2_image.dll 23 | HINTS 24 | ENV SDL2IMAGEDIR 25 | PATH_SUFFIXES 26 | bin 27 | lib/x86) 28 | 29 | # We need libpng and zlib for loading .png files on windows 30 | 31 | find_file(SDL2IMAGE_LIBPNG_DLL 32 | NAMES 33 | libpng16-16.dll 34 | HINTS 35 | ENV SDL2IMAGEDIR 36 | PATH_SUFFIXES 37 | bin 38 | lib/x86) 39 | 40 | find_file(SDL2IMAGE_ZLIB_DLL 41 | NAMES 42 | zlib1.dll 43 | HINTS 44 | ENV SDL2IMAGEDIR 45 | PATH_SUFFIXES 46 | bin 47 | lib/x86) 48 | 49 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2IMAGE 50 | REQUIRED_VARS SDL2IMAGE_LIBRARY SDL2IMAGE_INCLUDE_DIR) -------------------------------------------------------------------------------- /lantern/src/color.cpp: -------------------------------------------------------------------------------- 1 | #include "color.h" 2 | 3 | using namespace lantern; 4 | 5 | const color color::BLACK = color{0.0f, 0.0f, 0.0f}; 6 | const color color::WHITE = color{1.0f, 1.0f, 1.0f}; 7 | const color color::RED = color{1.0f, 0.0f, 0.0f}; 8 | const color color::GREEN = color{0.0f, 1.0f, 0.0f}; 9 | const color color::BLUE = color{0.0f, 0.0f, 1.0f}; 10 | 11 | color color::with_alpha(float alpha) const 12 | { 13 | return color{this->r, this->g, this->b, alpha}; 14 | } 15 | 16 | bool color::operator==(color const& another) const 17 | { 18 | return (equals(this->r, another.r) && equals(this->g, another.g) && equals(this->b, another.b) && equals(this->a, another.a)); 19 | } 20 | 21 | bool color::operator!=(color const& another) const 22 | { 23 | return !(*this == another); 24 | } 25 | 26 | color color::operator*(float const s) const 27 | { 28 | return color{this->r * s, this->g * s, this->b * s, this->a * s}; 29 | } 30 | 31 | color color::operator+(color const& another) const 32 | { 33 | return color{this->r + another.r, this->g + another.g, this->b + another.b, this->a + another.a}; 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lantern/include/ui_element_base.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_UI_ELEMENT_BASE_H 2 | #define LANTERN_UI_ELEMENT_BASE_H 3 | 4 | #include "renderer.h" 5 | 6 | namespace lantern 7 | { 8 | /** Base class for all the UI elements. 9 | * @ingroup UI 10 | */ 11 | class ui_element_base 12 | { 13 | public: 14 | /** Virtual destructor */ 15 | virtual ~ui_element_base(); 16 | 17 | /** Sets NDC position (left-bottom point) 18 | * @param position New position 19 | */ 20 | void set_position(vector2f position); 21 | 22 | /** Draws the element and all its children 23 | * @param pipeline Pipeline to use 24 | * @param target_texture Texture to draw to 25 | */ 26 | virtual void draw(renderer& pipeline, texture& target_texture); 27 | 28 | /** Gets children storage 29 | * @returns Children storage 30 | */ 31 | std::vector get_children_storage(); 32 | 33 | protected: 34 | /** Invokes whenever position is changed */ 35 | virtual void on_position_changed(); 36 | 37 | /** NDC position */ 38 | vector2f m_position; 39 | 40 | /** Subelements */ 41 | std::vector m_children; 42 | }; 43 | } 44 | 45 | #endif // LANTERN_UI_ELEMENT_BASE_H -------------------------------------------------------------------------------- /lantern/include/line.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_LINE_H 2 | #define LANTERN_LINE_H 3 | 4 | #include "vector2.h" 5 | 6 | namespace lantern 7 | { 8 | /** Class representing line described with equation ax + by + c = 0 */ 9 | class line final 10 | { 11 | public: 12 | /** X coefficient */ 13 | float a; 14 | 15 | /** Y coefficient */ 16 | float b; 17 | 18 | /** Free coefficient */ 19 | float c; 20 | 21 | /** Constructs line with given coefficients 22 | * @param a_coeff X coefficient 23 | * @param b_coeff Y coefficient 24 | * @param c_coeff Free coefficient 25 | */ 26 | line(float const a_coeff, float const b_coeff, float const c_coeff); 27 | 28 | /** Constructs line with two points 29 | * @param x0 First point x coordinate 30 | * @param y0 First point y coordinate 31 | * @param x1 Second point x coordinate 32 | * @param y1 Second point y coordinate 33 | */ 34 | line(float const x0, float const y0, float const x1, float const y1); 35 | 36 | /** Calculates line equation value for a given point 37 | * @param x Point x-coordinate 38 | * @param y Point y-coordinate 39 | * @returns Line equation value 40 | */ 41 | float at(float const x, float const y) const; 42 | 43 | /** Calculates intersection point between two lines 44 | * @param line The other line 45 | * @returns Intersection point 46 | */ 47 | vector2f intersection(line const& line) const; 48 | }; 49 | } 50 | 51 | #endif -------------------------------------------------------------------------------- /lantern/src/mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "mesh.h" 2 | 3 | using namespace lantern; 4 | 5 | mesh::mesh() 6 | { 7 | 8 | } 9 | 10 | mesh::mesh(std::vector vertices, std::vector indices) 11 | : m_vertices(vertices), m_indices(indices) 12 | { 13 | 14 | } 15 | 16 | std::vector const& mesh::get_vertices() const 17 | { 18 | return m_vertices; 19 | } 20 | 21 | std::vector& mesh::get_vertices() 22 | { 23 | return m_vertices; 24 | } 25 | 26 | std::vector const& mesh::get_indices() const 27 | { 28 | return m_indices; 29 | } 30 | 31 | std::vector& mesh::get_indices() 32 | { 33 | return m_indices; 34 | } 35 | 36 | std::vector>& mesh::get_color_attributes() 37 | { 38 | return m_color_attributes; 39 | } 40 | 41 | std::vector> const& mesh::get_color_attributes() const 42 | { 43 | return m_color_attributes; 44 | } 45 | 46 | std::vector>& mesh::get_float_attributes() 47 | { 48 | return m_float_attributes; 49 | } 50 | 51 | std::vector> const& mesh::get_float_attributes() const 52 | { 53 | return m_float_attributes; 54 | } 55 | 56 | std::vector>& mesh::get_vector2f_attributes() 57 | { 58 | return m_vector2f_attributes; 59 | } 60 | 61 | std::vector> const& mesh::get_vector2f_attributes() const 62 | { 63 | return m_vector2f_attributes; 64 | } 65 | 66 | std::vector>& mesh::get_vector3f_attributes() 67 | { 68 | return m_vector3f_attributes; 69 | } 70 | 71 | std::vector> const& mesh::get_vector3f_attributes() const 72 | { 73 | return m_vector3f_attributes; 74 | } -------------------------------------------------------------------------------- /lantern/include/ui_label.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_UI_LABEL_H 2 | #define LANTERN_UI_LABEL_H 3 | 4 | #include 5 | #include 6 | #include "ui_element_base.h" 7 | #include "font.h" 8 | #include "mesh.h" 9 | #include "ui_label_shader.h" 10 | 11 | namespace lantern 12 | { 13 | /** UI Label 14 | * @ingroup UI 15 | */ 16 | class ui_label final : public ui_element_base 17 | { 18 | public: 19 | /** Creates label object with specified font to use for rendering 20 | * @param font Font to use for rendering 21 | * @param target_texture Texture that will be used for rendering, required for calculating some metrics before rendering 22 | */ 23 | ui_label(font& font, texture const& target_texture); 24 | 25 | /** Changes label's text 26 | * @param text New text 27 | */ 28 | void set_text(std::string const& text); 29 | 30 | /** Changes label's color 31 | * @param color New color 32 | */ 33 | void set_color(color const& color); 34 | 35 | /** Draws the label and all its children 36 | * @param pipeline Pipeline to use 37 | * @param target_texture Texture to draw to 38 | */ 39 | void draw(renderer& pipeline, texture& target_texture) override; 40 | 41 | protected: 42 | /** Invokes whenever position is changed */ 43 | void on_position_changed() override; 44 | 45 | private: 46 | /** Recreates internal mesh for symbols */ 47 | void update_mesh(); 48 | 49 | /** How much do we move in ndc space for one pixel in screen space */ 50 | vector2f const m_ndc_per_pixel; 51 | 52 | /** Shader to use */ 53 | ui_label_shader m_shader; 54 | 55 | /** Font object to use */ 56 | font& m_font; 57 | 58 | /** Label's text */ 59 | std::string m_text; 60 | 61 | /** Label's color */ 62 | color m_color; 63 | 64 | /** Current symbols to use */ 65 | std::vector m_symbols; 66 | 67 | /** Current meshes to use */ 68 | std::vector m_meshes; 69 | }; 70 | } 71 | 72 | #endif // LANTERN_UI_LABEL_H -------------------------------------------------------------------------------- /lantern/include/merging_stage.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_MERGING_STAGE_H 2 | #define LANTERN_MERGING_STAGE_H 3 | 4 | #include "vector2.h" 5 | #include "vector3.h" 6 | #include "texture.h" 7 | 8 | namespace lantern 9 | { 10 | /** This stage is responsible for invoking pixel shader and merging results into a texture */ 11 | class merging_stage final 12 | { 13 | public: 14 | /** Constructs merging stage with default settings */ 15 | merging_stage(); 16 | 17 | /** Gets alpha blending mode 18 | * @returns Alpha blending mode 19 | */ 20 | bool get_alpha_blending_enabled() const; 21 | 22 | /** Sets alpha blending mode 23 | * @param enabled Alpha blending mode 24 | */ 25 | void set_alpha_blending_enabled(bool const enabled); 26 | 27 | /** Invokes stage 28 | * @param point Point coordinates to process 29 | * @param shader Shader to invoke 30 | * @param target_texture Texture to merge results into 31 | * @param delegate Delegate to pass results to for futher processing 32 | */ 33 | template 34 | void invoke(vector2ui const& pixel_coordinates, vector3f const& sample_point, TShader& shader, texture& target_texture, TDelegate& delegate); 35 | 36 | private: 37 | /** True = use alpha channel during merging */ 38 | bool m_alpha_blending_enabled; 39 | }; 40 | 41 | template 42 | inline void merging_stage::invoke(vector2ui const& pixel_coordinates, vector3f const& sample_point, TShader& shader, texture& target_texture, TDelegate& delegate) 43 | { 44 | color const color_from_shader = shader.process_pixel(pixel_coordinates); 45 | 46 | if (!m_alpha_blending_enabled) 47 | { 48 | target_texture.set_pixel_color(pixel_coordinates, color_from_shader); 49 | } 50 | else 51 | { 52 | color const current_texture_color = target_texture.get_pixel_color(pixel_coordinates); 53 | color const blended_color = color_from_shader * color_from_shader.a + current_texture_color * (1.0f - color_from_shader.a); 54 | target_texture.set_pixel_color(pixel_coordinates, blended_color); 55 | } 56 | } 57 | } 58 | 59 | #endif // LANTERN_MERGING_STAGE_H -------------------------------------------------------------------------------- /lantern/include/font.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_FONT_H 2 | #define LANTERN_FONT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include FT_FREETYPE_H 8 | #include "texture.h" 9 | #include "vector2.h" 10 | 11 | namespace lantern 12 | { 13 | /** Represents a symbol from a font. It holds symbol's texture and all the metrics required for rendering */ 14 | class symbol final 15 | { 16 | public: 17 | /** Constructor that takes required information from a FreeType glyph 18 | * @param ft_glyp FreeType glyph object 19 | */ 20 | symbol(FT_GlyphSlot ft_glyph); 21 | 22 | /** Gets symbol texture (in grayscale) 23 | * @returns Texture 24 | */ 25 | texture const& get_texture() const; 26 | 27 | /** Gets symbol's size in pixels 28 | * @returns Symbols's size 29 | */ 30 | vector2ui get_size() const; 31 | 32 | /** Gets bearing metrics in pixels 33 | * @returns Bearing metrics 34 | */ 35 | vector2ui get_bearing() const; 36 | 37 | /** Gets symbol's advance in pixels 38 | * @returns Advance length 39 | */ 40 | unsigned int get_advance() const; 41 | 42 | private: 43 | /** Symbol's texture */ 44 | texture m_texture; 45 | 46 | /** Size in pixels */ 47 | vector2ui const m_size; 48 | 49 | /** Bearing metrics */ 50 | vector2ui const m_bearing; 51 | 52 | /** Advance in pixels */ 53 | unsigned int const m_advance; 54 | }; 55 | 56 | /** This class is responsible for loading and keeping symbols. Symbols are only loaded when required and cached afterwards. Supports only ASCII for now */ 57 | class font final 58 | { 59 | public: 60 | /** Initializes instance with specified truetype font and required size 61 | * @param font_file_path Path to the font to load to 62 | * @param size Font size 63 | */ 64 | font(std::string font_file_path, int size); 65 | 66 | /** Cleans up all the resources */ 67 | ~font(); 68 | 69 | /** Gets symbol object for a specified character 70 | * @param c Character 71 | * @returns Symbol object for the character 72 | */ 73 | symbol const* get_symbol(char c); 74 | 75 | private: 76 | /** FreeType font object */ 77 | FT_Face m_font; 78 | 79 | /** Dictionary of already loaded symbols: character -> symbol object */ 80 | std::map m_symbols_map; 81 | }; 82 | } 83 | 84 | #endif // LANTERN_FONT_H -------------------------------------------------------------------------------- /tests/src/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_utils.h" 2 | #include "camera.h" 3 | 4 | using namespace lantern; 5 | 6 | TEST(camera, constructor) 7 | { 8 | float cos_value{std::cos((float)M_PI/3.0f)}; 9 | float sin_value{std::sin((float)M_PI/3.0f)}; 10 | camera const c{vector3f{0.0f, 0.0f, 0.0f}, vector3 < float > {2 * cos_value, 0.0f, 2 * sin_value}, vector3f{0.0f, 1.0f, 0.0f}, (float)M_PI / 2.0f, 0.5f, 0.01f, 100.0f}; 11 | assert_floats_near(c.get_aspect_ratio(), 0.5f); 12 | assert_floats_near(c.get_horizontal_fov(), (float) M_PI / 2.0f); 13 | assert_floats_near(c.get_vertical_fov(), (float) M_PI / 4.0f); 14 | assert_vectors3_near(c.get_forward(), vector3f{cos_value, 0.0f, sin_value}); 15 | assert_vectors3_near(c.get_right(), vector3f{sin_value, 0.0f, -cos_value}); 16 | assert_vectors3_near(c.get_up(), vector3f{0.0f, 1.0f, 0.0f}); 17 | assert_vectors3_near(c.get_position(), vector3f{0.0f, 0.0f, 0.0f}); 18 | } 19 | 20 | TEST(camera, moving) 21 | { 22 | float cos_value = std::cos((float)M_PI/3.0f); 23 | float sin_value = std::sin((float)M_PI/3.0f); 24 | camera c{vector3f{0.0f, 0.0f, 0.0f}, vector3f{cos_value, 0.0f, sin_value}, vector3f{0.0f, 1.0f, 0.0f}, (float)M_PI / 2.0f, 1.0f, 0.01f, 100.0f}; 25 | 26 | c.move_forward(1.0f); 27 | assert_vectors3_near(c.get_position(), vector3f{cos_value, 0.0f, sin_value}); 28 | c.move_backward(2.0f); 29 | assert_vectors3_near(c.get_position(), vector3f{-cos_value, 0.0f, -sin_value}); 30 | 31 | c.set_position(vector3f{0.0f, 0.0f, 0.0f}); 32 | 33 | c.move_right(2.0f); 34 | assert_vectors3_near(c.get_position(), vector3f{2 * sin_value, 0.0f, -2.0f * cos_value}); 35 | c.move_left(5.0f); 36 | assert_vectors3_near(c.get_position(), vector3f{-3.0f * sin_value, 0.0f, 3.0f * cos_value}); 37 | 38 | c.move_up(7.0f); 39 | assert_vectors3_near(c.get_position(), vector3f{-3.0f * sin_value, 7.0f, 3.0f * cos_value}); 40 | c.move_down(4.5f); 41 | assert_vectors3_near(c.get_position(), vector3f{-3.0f * sin_value, 2.5f, 3.0f * cos_value}); 42 | } 43 | 44 | TEST(camera, rotation) 45 | { 46 | camera c{vector3f{0.0f, 0.0f, 0.0f}, vector3f{0.0f, 0.0f, 1.0f}, vector3f{0.0f, 1.0f, 0.0f}, (float)M_PI / 2.0f, 1.0f, 0.01f, 100.0f}; 47 | 48 | c.pitch((float)M_PI / 2.0f); 49 | assert_vectors3_near(c.get_forward(), vector3f{0.0f, -1.0f, 0.0f}); 50 | assert_vectors3_near(c.get_right(), vector3f{1.0f, 0.0f, 0.0f}); 51 | assert_vectors3_near(c.get_up(), vector3f{0.0f, 0.0f, 1.0f}); 52 | 53 | c.yaw((float)M_PI / 2.0f); 54 | assert_vectors3_near(c.get_forward(), vector3f{0.0f, -1.0f, 0.0f}); 55 | assert_vectors3_near(c.get_right(), vector3f{0.0f, 0.0f, -1.0f}); 56 | assert_vectors3_near(c.get_up(), vector3f{1.0f, 0.0f, 0.0f}); 57 | } 58 | -------------------------------------------------------------------------------- /lantern/include/matrix3x3.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_MATRIX3X3_H 2 | #define LANTERN_MATRIX3X3_H 3 | 4 | #include 5 | #include "vector3.h" 6 | 7 | namespace lantern 8 | { 9 | /** Class representing 3x3 matrix. 10 | * Coordinate system assumed to be left-handed 11 | */ 12 | class matrix3x3f final 13 | { 14 | public: 15 | /** Data array, [row][column] */ 16 | float values[3][3]; 17 | 18 | /** Constructs matrix with zero values */ 19 | matrix3x3f(); 20 | 21 | /** Constructs matrix using given array 22 | * @param array Matrix values array 23 | */ 24 | matrix3x3f(float const array[3][3]); 25 | 26 | /** Constructs matrix with specified values */ 27 | matrix3x3f( 28 | float const m00, float const m01, float const m02, 29 | float const m10, float const m11, float const m12, 30 | float const m20, float const m21, float const m22); 31 | 32 | // Operators 33 | // 34 | 35 | matrix3x3f operator*(matrix3x3f const& m) const; 36 | 37 | /** Calculates matrix determinant */ 38 | float det() const; 39 | 40 | /** Calculates matrix inverse */ 41 | matrix3x3f inversed() const; 42 | 43 | /** Calculates matrix inverse using already calculated determinant to avoid calculating it twice */ 44 | matrix3x3f inversed_precalc_det(float const det) const; 45 | 46 | /** Generates scale matrix 47 | * @param x Scale along x-axis 48 | * @param y Scale along y-axis 49 | * @param z Scale along z-axis 50 | */ 51 | static matrix3x3f scale(float const x, float const y, float const z); 52 | 53 | /** Generates uniform scale matrix 54 | * @param s Scale along each axis 55 | */ 56 | static matrix3x3f uniform_scale(float const s); 57 | 58 | /** Generates rotation around x-axis 59 | * @param radians Radians to rotate for 60 | */ 61 | static matrix3x3f rotation_around_x_axis(float const radians); 62 | 63 | /** Generates rotation around y-axis 64 | * @param radians Radians to rotate for 65 | */ 66 | static matrix3x3f rotation_around_y_axis(float const radians); 67 | 68 | /** Generates rotation around z-axis 69 | * @param radians Radians to rotate for 70 | */ 71 | static matrix3x3f rotation_around_z_axis(float const radians); 72 | 73 | /** Generates rotation around specified axis 74 | * @param axis Axis to rotate around 75 | * @param radians Radians to rotate for 76 | */ 77 | static matrix3x3f rotation_around_axis(vector3f const& axis, float const radians); 78 | 79 | static const matrix3x3f IDENTITY; 80 | }; 81 | 82 | template 83 | vector3 operator*(vector3 const& v, matrix3x3f const& m) 84 | { 85 | return vector3{ 86 | v.x * m.values[0][0] + v.y * m.values[1][0] + v.z * m.values[2][0], 87 | v.x * m.values[0][1] + v.y * m.values[1][1] + v.z * m.values[2][1], 88 | v.x * m.values[0][2] + v.y * m.values[1][2] + v.z * m.values[2][2]}; 89 | } 90 | } 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /lantern/src/font.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "font.h" 3 | #include "app.h" 4 | 5 | using namespace lantern; 6 | 7 | // Symbol class implementation 8 | // 9 | 10 | symbol::symbol(FT_GlyphSlot ft_glyph) 11 | : m_texture{ft_glyph->bitmap.width, ft_glyph->bitmap.rows}, 12 | m_size{ft_glyph->bitmap.width, ft_glyph->bitmap.rows}, 13 | m_bearing{ft_glyph->bitmap_left, ft_glyph->bitmap_top}, 14 | m_advance(ft_glyph->advance.x >> 6) // FreeType keeps advance in 1/64 pixel 15 | { 16 | // Create texture object from the bitmap FreeType created 17 | // 18 | for (size_t y{0}; y < m_size.y; ++y) 19 | { 20 | for (size_t x{0}; x < m_size.x; ++x) 21 | { 22 | // Bitmap is 8-bit grayscale 23 | // 24 | 25 | // Take pixel channel 26 | unsigned char const grayscale{ft_glyph->bitmap.buffer[y * m_size.x + x]}; 27 | 28 | // Normalize it to pass to our color object 29 | float const grayscale_normalized{grayscale / 255.0f}; 30 | 31 | // Set pixel color in the texture 32 | m_texture.set_pixel_color( 33 | vector2ui{x, y}, 34 | color{grayscale_normalized, grayscale_normalized, grayscale_normalized, grayscale_normalized}); 35 | } 36 | } 37 | } 38 | 39 | texture const& symbol::get_texture() const 40 | { 41 | return m_texture; 42 | } 43 | vector2ui symbol::get_size() const 44 | { 45 | return m_size; 46 | } 47 | 48 | vector2ui symbol::get_bearing() const 49 | { 50 | return m_bearing; 51 | } 52 | 53 | unsigned int symbol::get_advance() const 54 | { 55 | return m_advance; 56 | } 57 | 58 | // Font class implementation 59 | // 60 | 61 | font::font(std::string font_file_path, int size) 62 | { 63 | // Load FreeType font object 64 | // 65 | if (FT_New_Face(app::get_instance()->get_freetype_library(), font_file_path.c_str(), 0, &m_font)) 66 | { 67 | throw std::runtime_error("Couldn't initialize font"); 68 | } 69 | 70 | // Set size. Width will be calculated automatically base on height we provide 71 | FT_Set_Pixel_Sizes(m_font, 0, size); 72 | } 73 | 74 | font::~font() 75 | { 76 | // Clean up 77 | // 78 | 79 | for (auto const& kvp : m_symbols_map) 80 | { 81 | delete kvp.second; 82 | } 83 | 84 | FT_Done_Face(m_font); 85 | } 86 | 87 | symbol const* font::get_symbol(char c) 88 | { 89 | // Check if we already got that symbol's metrics 90 | std::map::const_iterator find_result = m_symbols_map.find(c); 91 | 92 | if (find_result == m_symbols_map.end()) 93 | { 94 | // If we didn't - get it 95 | // 96 | 97 | // Load metrics and render bitmap 98 | // 99 | if (FT_Load_Char(m_font, c, FT_LOAD_RENDER)) 100 | { 101 | throw std::runtime_error("Couldn't load glyph"); 102 | } 103 | 104 | // Cache it 105 | m_symbols_map.insert(std::make_pair(c, new symbol{m_font->glyph})); 106 | 107 | // Return the result 108 | return m_symbols_map.find(c)->second; 109 | } 110 | else 111 | { 112 | // Just return cached symbol 113 | return find_result->second; 114 | } 115 | } -------------------------------------------------------------------------------- /lantern/src/texture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "texture.h" 4 | 5 | using namespace lantern; 6 | 7 | texture::texture(unsigned int const width, unsigned int const height) 8 | : m_width{width}, 9 | m_height{height}, 10 | m_data_total_size{width * height * 4}, 11 | m_data{new unsigned char[m_data_total_size]}, 12 | m_pitch{width * 4} 13 | { 14 | } 15 | 16 | texture::texture(texture const& another) 17 | : m_width{another.m_width}, 18 | m_height{another.m_height}, 19 | m_data_total_size{another.m_data_total_size}, 20 | m_data{nullptr}, 21 | m_pitch{another.m_pitch} 22 | { 23 | m_data = new unsigned char[m_data_total_size]; 24 | memcpy(m_data, another.m_data, m_data_total_size); 25 | } 26 | 27 | texture::texture(texture&& another) 28 | : m_width(another.m_width), 29 | m_height(another.m_height), 30 | m_data_total_size(another.m_data_total_size), 31 | m_data{another.m_data}, 32 | m_pitch(another.m_pitch) 33 | { 34 | another.m_data = nullptr; 35 | } 36 | 37 | texture::~texture() 38 | { 39 | if (m_data != nullptr) 40 | { 41 | delete[] m_data; 42 | m_data = nullptr; 43 | } 44 | } 45 | 46 | unsigned int texture::get_width() const 47 | { 48 | return m_width; 49 | } 50 | 51 | unsigned int texture::get_height() const 52 | { 53 | return m_height; 54 | } 55 | 56 | unsigned int texture::get_pitch() const 57 | { 58 | return m_pitch; 59 | } 60 | 61 | unsigned char const* texture::get_data() const 62 | { 63 | return m_data; 64 | } 65 | 66 | texture texture::load_from_file(std::string file) 67 | { 68 | SDL_Surface* surface = IMG_Load(file.c_str()); 69 | 70 | if (surface->format->format == SDL_PIXELFORMAT_ARGB8888) 71 | { 72 | texture result(surface->w, surface->h); 73 | memcpy(result.m_data, surface->pixels, result.m_data_total_size); 74 | SDL_FreeSurface(surface); 75 | return result; 76 | } 77 | else if (surface->format->format == SDL_PIXELFORMAT_ABGR8888) 78 | { 79 | Uint8* pixels = (Uint8*)surface->pixels; 80 | 81 | texture result(surface->w, surface->h); 82 | for (size_t i = 0; i < result.m_data_total_size; i += 4) 83 | { 84 | size_t first_byte_index = i; 85 | 86 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN 87 | result.m_data[first_byte_index + 0] = pixels[first_byte_index + 0]; 88 | result.m_data[first_byte_index + 1] = pixels[first_byte_index + 3]; 89 | result.m_data[first_byte_index + 2] = pixels[first_byte_index + 2]; 90 | result.m_data[first_byte_index + 3] = pixels[first_byte_index + 1]; 91 | #else 92 | result.m_data[first_byte_index + 0] = pixels[first_byte_index + 2]; 93 | result.m_data[first_byte_index + 1] = pixels[first_byte_index + 1]; 94 | result.m_data[first_byte_index + 2] = pixels[first_byte_index + 0]; 95 | result.m_data[first_byte_index + 3] = pixels[first_byte_index + 3]; 96 | #endif 97 | } 98 | SDL_FreeSurface(surface); 99 | return result; 100 | } 101 | else 102 | { 103 | SDL_FreeSurface(surface); 104 | throw std::runtime_error{"Unsupported format"}; 105 | } 106 | } -------------------------------------------------------------------------------- /lantern/src/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | #include "matrix3x3.h" 3 | 4 | using namespace lantern; 5 | 6 | camera::camera( 7 | vector3f const& position, 8 | vector3f const& forward, 9 | vector3f const& fake_up, 10 | float const horizontal_fov, 11 | float const aspect_ratio, 12 | float const near_plane_z, 13 | float const far_plane_z) 14 | : m_position{position}, 15 | m_forward{forward.normalized()}, 16 | m_horizontal_fov{horizontal_fov}, 17 | m_vertical_fov{horizontal_fov * aspect_ratio}, 18 | m_aspect_ratio{aspect_ratio}, 19 | m_near_plane_z{near_plane_z}, 20 | m_far_plane_z{far_plane_z} 21 | { 22 | establish_coordinate_system(fake_up.normalized()); 23 | } 24 | 25 | vector3f camera::get_position() const 26 | { 27 | return m_position; 28 | } 29 | 30 | void camera::set_position(vector3f const& position) 31 | { 32 | m_position = position; 33 | } 34 | 35 | vector3f camera::get_forward() const 36 | { 37 | return m_forward; 38 | } 39 | 40 | vector3f camera::get_right() const 41 | { 42 | return m_right; 43 | } 44 | 45 | vector3f camera::get_up() const 46 | { 47 | return m_up; 48 | } 49 | 50 | float camera::get_horizontal_fov() const 51 | { 52 | return m_horizontal_fov; 53 | } 54 | 55 | float camera::get_vertical_fov() const 56 | { 57 | return m_vertical_fov; 58 | } 59 | 60 | float camera::get_aspect_ratio() const 61 | { 62 | return m_aspect_ratio; 63 | } 64 | 65 | float camera::get_near_plane_z() const 66 | { 67 | return m_near_plane_z; 68 | } 69 | 70 | float camera::get_far_plane_z() const 71 | { 72 | return m_far_plane_z; 73 | } 74 | 75 | void camera::establish_coordinate_system(vector3f const& fake_up) 76 | { 77 | // fake_up vector does not represent the up vector itself 78 | // It might be different - together with m_forward it represents the plane where the correct up vector should be 79 | // So we use it to calculate right vector and then fix it 80 | // 81 | 82 | m_right = fake_up.cross(m_forward).normalized(); 83 | m_up = m_forward.cross(m_right).normalized(); 84 | } 85 | 86 | void camera::move_right(float const distance) 87 | { 88 | m_position += m_right * distance; 89 | } 90 | 91 | void camera::move_left(float const distance) 92 | { 93 | move_right(-distance); 94 | } 95 | 96 | void camera::move_up(float const distance) 97 | { 98 | m_position += m_up * distance; 99 | } 100 | 101 | void camera::move_down(float const distance) 102 | { 103 | move_up(-distance); 104 | } 105 | 106 | void camera::move_forward(float const distance) 107 | { 108 | m_position += m_forward * distance; 109 | } 110 | 111 | void camera::move_backward(float const distance) 112 | { 113 | move_forward(-distance); 114 | } 115 | 116 | void camera::yaw(float const radians) 117 | { 118 | matrix3x3f const rotation{matrix3x3f::rotation_around_y_axis(radians)}; 119 | m_forward = m_forward * rotation; 120 | m_right = m_right * rotation; 121 | m_up = m_up * rotation; 122 | } 123 | 124 | void camera::pitch(float const radians) 125 | { 126 | matrix3x3f const rotation{matrix3x3f::rotation_around_x_axis(radians)}; 127 | m_forward = m_forward * rotation; 128 | m_right = m_right * rotation; 129 | m_up = m_up * rotation; 130 | } 131 | -------------------------------------------------------------------------------- /tests/src/matrix3x3.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_utils.h" 2 | #include "matrix3x3.h" 3 | 4 | using namespace lantern; 5 | 6 | TEST(matrix3x3f, constructors) 7 | { 8 | matrix3x3f const m1{ 9 | 15.0f, -3.0f, 20.0f, 10 | 13.1f, -1.1f, 17.1f, 11 | 90.2f, 1.2f, -2.2f}; 12 | assert_floats_near(m1.values[0][0], 15.0f); 13 | assert_floats_near(m1.values[0][1], -3.0f); 14 | assert_floats_near(m1.values[0][2], 20.0f); 15 | assert_floats_near(m1.values[1][0], 13.1f); 16 | assert_floats_near(m1.values[1][1], -1.1f); 17 | assert_floats_near(m1.values[1][2], 17.1f); 18 | assert_floats_near(m1.values[2][0], 90.2f); 19 | assert_floats_near(m1.values[2][1], 1.2f); 20 | assert_floats_near(m1.values[2][2], -2.2f); 21 | } 22 | 23 | TEST(matrix3x3f, matrix_matrix_multiplication) 24 | { 25 | matrix3x3f const m1{ 26 | 2.0f, -3.0f, 11.0f, 27 | -1.0f, -1.0f, 0.15f, 28 | 1.0f, 0.77f, 0.33f}; 29 | matrix3x3f const m2{ 30 | 1.0f, 0.0f, 0.0f, 31 | -1.0f, 0.0f, 0.25f, 32 | 1.0f, 0.13f, 0.99f}; 33 | matrix3x3f const m_muled{m1 * m2}; 34 | assert_matrix3x3_near( 35 | m_muled, 36 | matrix3x3f{ 37 | 16.0f, 1.43f, 10.14f, 38 | 0.15f, 0.0195f, -0.1015f, 39 | 0.56f, 0.0429f, 0.5192f}); 40 | } 41 | 42 | TEST(matrix3x3f, vector_matrix_multiplication) 43 | { 44 | vector3f const v{1.0f, 3.0f, -0.15f}; 45 | matrix3x3f const m{ 46 | 1.0f, 0.0f, 0.0f, 47 | -1.0f, 0.0f, 0.25f, 48 | 1.0f, 0.13f, 0.99f}; 49 | vector3f const v_muled{v * m}; 50 | assert_vectors3_near(v_muled, vector3f{-2.15f, -0.0195f, 0.6015f}); 51 | } 52 | 53 | TEST(matrix3x3f, scaling) 54 | { 55 | vector3f const v{2.0f, 3.0f, -0.15f}; 56 | 57 | matrix3x3f const scale{matrix3x3f::scale(0.33f, 0.25f, 2.0f)}; 58 | vector3f const v_scaled{v * scale}; 59 | assert_vectors3_near(v_scaled, vector3f{0.66f, 0.75f, -0.30f}); 60 | 61 | matrix3x3f const uniform_scale{matrix3x3f::uniform_scale(5.0f)}; 62 | vector3f const v_uniformly_scaled{v * uniform_scale}; 63 | assert_vectors3_near(v_uniformly_scaled, vector3f{10.0f, 15.0f, -0.75f}); 64 | } 65 | 66 | TEST(matrix3x3f, rotation) 67 | { 68 | vector3f const v{0.5f, 1.0f, -1.0f}; 69 | 70 | matrix3x3f const m_rotation_around_x_axis{matrix3x3f::rotation_around_x_axis(static_cast(M_PI) / 3.0f)}; 71 | vector3f const v_rotated_around_x_axis{v * m_rotation_around_x_axis}; 72 | assert_vectors3_near(v_rotated_around_x_axis, vector3f{0.5f, 1.366f, 0.366f}); 73 | 74 | matrix3x3f const m_rotation_around_y_axis{matrix3x3f::rotation_around_y_axis(static_cast(M_PI) / 10.0f)}; 75 | vector3f const v_rotated_around_y_axis{v * m_rotation_around_y_axis}; 76 | assert_vectors3_near(v_rotated_around_y_axis, vector3f{0.1665f, 1.0f, -1.1055f}); 77 | 78 | matrix3x3f const m_rotation_around_z_axis{matrix3x3f::rotation_around_z_axis(static_cast(M_PI) / 4.0f)}; 79 | vector3f const v_rotated_around_z_axis{v * m_rotation_around_z_axis}; 80 | assert_vectors3_near(v_rotated_around_z_axis, vector3f{-0.3535f, 1.0606f, -1.0f}); 81 | 82 | vector3f const axis{0.55f, -1.0f, -1.0f}; 83 | matrix3x3f const m_rotation_around_axis{matrix3x3f::rotation_around_axis(axis, static_cast(M_PI) / 2.0f)}; 84 | vector3f const v_rotated_around_axis{v * m_rotation_around_axis}; 85 | assert_vectors3_near(v_rotated_around_axis, vector3f{1.3837f, -0.0864f, 0.5725f}); 86 | } 87 | -------------------------------------------------------------------------------- /lantern/include/mesh_attribute_info.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_MESH_ATTRIBUTE_INFO 2 | #define LANTERN_MESH_ATTRIBUTE_INFO 3 | 4 | #include 5 | 6 | namespace lantern 7 | { 8 | /** Specifies type of attribute interpolation */ 9 | enum class attribute_interpolation_option 10 | { 11 | /** Attribute should be interpolated linearly in screen space */ 12 | linear, 13 | 14 | /** Attribute should be interpolated with perspective correction */ 15 | perspective_correct 16 | }; 17 | 18 | /** Holds all the information associated with mesh attribute */ 19 | template 20 | class mesh_attribute_info final 21 | { 22 | public: 23 | /** Constructs attribute info 24 | * @param attribute_id Attribute ID, should be unique 25 | * @param data Attribute data 26 | * @param indices Attribute indices 27 | */ 28 | mesh_attribute_info( 29 | unsigned int const attribute_id, 30 | std::vector const data, 31 | std::vector const indices, 32 | attribute_interpolation_option interpolation_option); 33 | 34 | /** Gets attribute ID 35 | * @returns Attribute ID value 36 | */ 37 | unsigned int get_id() const; 38 | 39 | /** Gets attribute data 40 | * @returns Attribute data 41 | */ 42 | std::vector const& get_data() const; 43 | 44 | /** Gets attribute indices 45 | * @returns Indices 46 | */ 47 | std::vector const& get_indices() const; 48 | 49 | /** Gets interpolation option 50 | * @returns Interpolation type 51 | */ 52 | attribute_interpolation_option get_interpolation_option() const; 53 | 54 | private: 55 | /** Attribute id */ 56 | unsigned int const m_id; 57 | 58 | /** Attribute data */ 59 | std::vector const m_data; 60 | 61 | /** Attribute indices */ 62 | std::vector const m_indices; 63 | 64 | /** Interpolation option */ 65 | attribute_interpolation_option m_interpolation_option; 66 | }; 67 | 68 | // Predefined attributes ids 69 | // 70 | unsigned int const COLOR_ATTR_ID = 0; 71 | unsigned int const TEXCOORD_ATTR_ID = 1; 72 | unsigned int const NORMAL_ATTR_ID = 2; 73 | 74 | template 75 | mesh_attribute_info::mesh_attribute_info( 76 | unsigned int const attribute_id, 77 | std::vector const data, 78 | std::vector const indices, 79 | attribute_interpolation_option interpolation_option) 80 | : m_id{attribute_id}, 81 | m_data(data), 82 | m_indices(indices), 83 | m_interpolation_option{interpolation_option} 84 | { 85 | 86 | } 87 | 88 | template 89 | unsigned int mesh_attribute_info::get_id() const 90 | { 91 | return m_id; 92 | } 93 | 94 | template 95 | std::vector const& mesh_attribute_info::get_data() const 96 | { 97 | return m_data; 98 | } 99 | 100 | template 101 | std::vector const& mesh_attribute_info::get_indices() const 102 | { 103 | return m_indices; 104 | } 105 | 106 | template 107 | attribute_interpolation_option mesh_attribute_info::get_interpolation_option() const 108 | { 109 | return m_interpolation_option; 110 | } 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /lantern/include/color_shader.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_COLOR_SHADER_H 2 | #define LANTERN_COLOR_SHADER_H 3 | 4 | #include 5 | #include "shader_bind_point_info.h" 6 | #include "color.h" 7 | #include "vector2.h" 8 | #include "vector3.h" 9 | #include "vector4.h" 10 | #include "matrix4x4.h" 11 | #include "mesh_attribute_info.h" 12 | 13 | namespace lantern 14 | { 15 | /** Basic color shader 16 | * @ingroup Shaders 17 | */ 18 | class color_shader final 19 | { 20 | public: 21 | /** Gets info about color bind points required by shader 22 | * @returns Required color bind points 23 | */ 24 | std::vector> get_color_bind_points(); 25 | 26 | /** Gets info about float bind points required by shader 27 | * @returns Required float bind points 28 | */ 29 | std::vector> get_float_bind_points(); 30 | 31 | /** Gets info about vector2f bind points required by shader 32 | * @returns Required vector2f bind points 33 | */ 34 | std::vector> get_vector2f_bind_points(); 35 | 36 | /** Gets info about vector3ff bind points required by shader 37 | * @returns Required vector3f bind points 38 | */ 39 | std::vector> get_vector3f_bind_points(); 40 | 41 | /** Processes vertex 42 | * @param vertex Vertex in local space 43 | * @returns Processed vertex in homogeneous clip space 44 | */ 45 | vector4f process_vertex(vector4f const& vertex); 46 | 47 | /** Processes pixel 48 | * @param pixel Pixel coordinates on screen 49 | * @returns Final pixel color 50 | */ 51 | color process_pixel(vector2ui const& pixel); 52 | 53 | /** Sets model-view-projection matrix to use during vertex processing 54 | * @param mvp Model-view-projection matrix 55 | */ 56 | void set_mvp_matrix(matrix4x4f const& mvp); 57 | 58 | private: 59 | /** Movel-view-projection matrix */ 60 | matrix4x4f m_mvp; 61 | 62 | /** Color bind point, contains interpolated color value */ 63 | color m_color; 64 | }; 65 | 66 | inline void color_shader::set_mvp_matrix(matrix4x4f const& mvp) 67 | { 68 | m_mvp = mvp; 69 | } 70 | 71 | inline vector4f color_shader::process_vertex(vector4f const& vertex) 72 | { 73 | return vertex * m_mvp; 74 | } 75 | 76 | inline color color_shader::process_pixel(vector2ui const& pixel) 77 | { 78 | // Just return interpolated color value 79 | return m_color; 80 | } 81 | 82 | inline std::vector> color_shader::get_color_bind_points() 83 | { 84 | return std::vector>{ 85 | shader_bind_point_info { COLOR_ATTR_ID, &m_color }}; 86 | } 87 | 88 | inline std::vector> color_shader::get_float_bind_points() 89 | { 90 | return std::vector>{}; 91 | } 92 | 93 | inline std::vector> color_shader::get_vector2f_bind_points() 94 | { 95 | return std::vector>{}; 96 | } 97 | 98 | inline std::vector> color_shader::get_vector3f_bind_points() 99 | { 100 | return std::vector>{}; 101 | } 102 | } 103 | 104 | #endif // LANTERN_COLOR_SHADER_H 105 | -------------------------------------------------------------------------------- /lantern/include/mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_MESH_H 2 | #define LANTERN_MESH_H 3 | 4 | #include 5 | #include "mesh_attribute_info.h" 6 | #include "vector2.h" 7 | #include "vector3.h" 8 | #include "color.h" 9 | 10 | namespace lantern 11 | { 12 | /** Class representing mesh. 13 | * It can be viewed as a container that holds vertices, attributes and their indices 14 | */ 15 | class mesh final 16 | { 17 | public: 18 | /** Constructs empy mesh */ 19 | mesh(); 20 | 21 | /** Constructs mesh with specified vertices and indices 22 | * @param vertices Mesh vertices 23 | * @param indices Mesh indices 24 | */ 25 | mesh(std::vector vertices, std::vector indices); 26 | 27 | /** Gets mesh vertices 28 | * @returns Mesh vertices 29 | */ 30 | std::vector const& get_vertices() const; 31 | 32 | /** Gets mesh vertices 33 | * @returns Mesh vertices 34 | */ 35 | std::vector& get_vertices(); 36 | 37 | /** Get mesh indices 38 | * @returns Mesh indices 39 | */ 40 | std::vector const& get_indices() const; 41 | 42 | /** Get mesh indices 43 | * @returns Mesh indices 44 | */ 45 | std::vector& get_indices(); 46 | 47 | /** Gets color attributes 48 | * @returns Color attributes storage 49 | */ 50 | std::vector>& get_color_attributes(); 51 | 52 | /** Gets color attributes 53 | * @returns Color attributes const storage 54 | */ 55 | std::vector> const& get_color_attributes() const; 56 | 57 | /** Gets float attributes 58 | * @returns float attributes storage 59 | */ 60 | std::vector>& get_float_attributes(); 61 | 62 | /** Gets float attributes 63 | * @returns float attributes const storage 64 | */ 65 | std::vector> const& get_float_attributes() const; 66 | 67 | /** Gets vector2f attributes 68 | * @returns vector2f attributes storage 69 | */ 70 | std::vector>& get_vector2f_attributes(); 71 | 72 | /** Gets vector2f attributes 73 | * @returns vector2f attributes const storage 74 | */ 75 | std::vector> const& get_vector2f_attributes() const; 76 | 77 | /** Gets vector3f attributes 78 | * @returns vector3f attributes storage 79 | */ 80 | std::vector>& get_vector3f_attributes(); 81 | 82 | /** Gets vector3f attributes 83 | * @returns vector3f attributes const storage 84 | */ 85 | std::vector> const& get_vector3f_attributes() const; 86 | 87 | private: 88 | /** Mesh vertices */ 89 | std::vector m_vertices; 90 | 91 | /** Mesh indices */ 92 | std::vector m_indices; 93 | 94 | /** Mesh color attributes */ 95 | std::vector> m_color_attributes; 96 | 97 | /** Mesh float attributes */ 98 | std::vector> m_float_attributes; 99 | 100 | /** Mesh vector2f attributes */ 101 | std::vector> m_vector2f_attributes; 102 | 103 | /** Mesh vector3f attributes */ 104 | std::vector> m_vector3f_attributes; 105 | }; 106 | } 107 | 108 | #endif // LANTERN_MESH_H 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lantern is a cross-plaftorm 3D software renderer written for educational purposes. It depends of multiple libraries (full list is given below) but it doesn't use them for rendering (except using FreeType library for getting glyphs bitmaps), only for common tasks (such as presenting color buffer on a screen, handling input, loading textures from disk etc.). The aim is to create feature-full renderer from scratch, only using ability to set texture's pixel color. 2 | 3 | Note that because it doesn't use GPU, it is much slower comparing to DirectX or OpenGL, and, obviously, is not intended for using in real projects (unless you're targeting users without video cards!). 4 | 5 | ###Implemented features 6 | 7 | * Loading .obj files 8 | * Rasterization using three different algorithms: inversed slope, traversal (and its subtypes: aabb, backtracking, zigzag), homogeneous 9 | * Programmable vertex and pixel shaders 10 | * Perspective-correct attributes interpolation 11 | * Texture mapping 12 | * Alpha-blending 13 | * Truetype fonts rendering 14 | 15 | ###Dependencies 16 | 17 | * SDL2 - used for creating windows, copying resulting texture data to a screen, handling input and system events 18 | * SDL2_Image - used for loading images files 19 | * FreeType - used for loading truetype fonts, calculating their metrics and rendering glyphs bitmaps 20 | * Google C++ Tests - used for testing 21 | 22 | ###Building 23 | 24 | Lantern uses CMake as its build system. 25 | 26 | Simple scripts were created to simplify building process a little, though (output goes to `/build` folder): 27 | * `build_vs14.bat` - creates VS solution for Visual Studio 2015 28 | * `build_vs12.bat` - creates VS solution for Visual Studio 2013 29 | * `build_mingw_make.bat` - creates MinGW makefiles 30 | * `build_make.sh` - creates Linux makefiles 31 | * `build_xcode.sh` - creates XCode project 32 | 33 | On Linux and MacOS you probably have to `chmod +x build_*.sh` before running them. 34 | 35 | You also have to have all the dependencies and tools installed (obviously). As an example for Ubuntu, here are the commands for installing all of them (just skip what you don't need): 36 | * `sudo apt-get install cmake` 37 | * `sudo apt-get install g++` 38 | * `sudo apt-get install libsdl2-dev` 39 | * `sudo apt-get install libsdl2-image-dev` 40 | * `sudo apt-get install libfreetype6-dev` 41 | * `sudo apt-get install libgtest-dev` 42 | 43 | On Windows you have to specify a few environment variables for dependencies so that CMake's `find_package` will be able to find them: 44 | * `SDL2DIR` - path to SDL2 development library 45 | * `SDL2IMAGEDIR` - path to SDL2_Image development library 46 | * `FREETYPEDIR` - path to a folder containing FreeType's headers and library 47 | * `GTEST_ROOT` - path to Google Tests source code folder (required only if you're going to build tests target) 48 | 49 | Note for Windows FreeType library: if you're building it by yourself, make sure that output library's name is `freetype2.lib` and not `freetype26.lib` (that's what bundled FindFreeType.cmake looks for). It's also assumed for now that FreeType is compiled as a static library, otherwise you'll have to copy dll to resulting folder by yourself (or alter CMakeLists.txt a little) 50 | 51 | ###Known issues 52 | 53 | * If you're facing linking problems in SDL2main library on VS 2015, you can recompile SDL2 by yourself using VS 2015, or just download SDL2 build bot package here: https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/ 54 | -------------------------------------------------------------------------------- /lantern/include/app.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_APP_H 2 | #define LANTERN_APP_H 3 | 4 | #include 5 | #include FT_FREETYPE_H 6 | #include 7 | #include "SDL.h" 8 | #include "renderer.h" 9 | 10 | namespace lantern 11 | { 12 | /** Base class for all lantern applications. 13 | * It handles most part of low level stuff, like initializating libraries and according objects, running the main loop, etc. 14 | */ 15 | class app 16 | { 17 | public: 18 | /** Initializes application so that it is ready to start running main loop 19 | * @param width Window and framebuffer texture width 20 | * @param height Window and framebuffer texture height 21 | */ 22 | app(unsigned int const width, unsigned int const height); 23 | 24 | /** Uninitializes application */ 25 | virtual ~app(); 26 | 27 | /** Runs main loop 28 | * @returns Result error code 29 | */ 30 | int start(); 31 | 32 | /** Gets FreeType library main object 33 | * @returns Pointer to FreeType main object 34 | */ 35 | FT_Library get_freetype_library() const; 36 | 37 | /** Gets FPS 38 | * @returns Last saved framerate 39 | */ 40 | unsigned int get_last_fps() const; 41 | 42 | /** Gets platform-dependent separator symbol 43 | * @returns Separator 44 | */ 45 | char get_path_separator() const; 46 | 47 | /** Gets absolute path to resources folder, already contains ending separator 48 | * @returns Path to resources 49 | */ 50 | std::string get_resources_path() const; 51 | 52 | /** Gets application instance 53 | * @returns Instance 54 | */ 55 | static app const* get_instance(); 56 | 57 | protected: 58 | /** Gets texture used as a framebuffer 59 | * @returns Target texture 60 | */ 61 | texture& get_target_texture(); 62 | 63 | /** Gets rendering pipeline 64 | * @returns Pipeline 65 | */ 66 | renderer& get_renderer(); 67 | 68 | /** Sets target framerate 69 | * @param fps Target framerate 70 | */ 71 | void set_target_framerate(unsigned int const fps); 72 | 73 | /** Handles every frame changes, gets called from the main loop 74 | * @param delta_since_last_frame How many seconds passed since last frame 75 | */ 76 | virtual void frame(float const delta_since_last_frame) = 0; 77 | 78 | /** Handles pressed key 79 | * @param key Key that was pressed 80 | */ 81 | virtual void on_key_down(SDL_Keysym const key); 82 | 83 | private: 84 | /** FreeType library main object */ 85 | FT_Library m_freetype_library; 86 | 87 | /** SDL window object */ 88 | SDL_Window* m_window; 89 | 90 | /** SDL renderer object */ 91 | SDL_Renderer* m_sdl_renderer; 92 | 93 | /** SDL texture we are using as a framebuffer */ 94 | SDL_Texture* m_sdl_target_texture; 95 | 96 | /** Texture we are using as a framebuffer, gets copied into according SDL_Texture to be shown on a screen */ 97 | texture m_target_texture; 98 | 99 | /** Rendering pipeline */ 100 | renderer m_renderer; 101 | 102 | /** Delay between frames to stick to the target framerate */ 103 | Uint32 m_target_framerate_delay; 104 | 105 | /** Last saved framerate */ 106 | unsigned int m_last_fps; 107 | 108 | /** Platform-dependent path separator */ 109 | char const m_path_separator; 110 | 111 | /** Absolute path to resources folder */ 112 | std::string m_resources_path; 113 | 114 | /** Instance pointer */ 115 | static app* _instance; 116 | }; 117 | } 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /lantern/include/matrix4x4.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_MATRIX4X4_H 2 | #define LANTERN_MATRIX4X4_H 3 | 4 | #include 5 | #include "vector4.h" 6 | #include "vector3.h" 7 | 8 | namespace lantern 9 | { 10 | /** Class representing 4x4 matrix. 11 | * Used to represent 3D affine transformations. 12 | * Coordinate system assumed to be left-handed 13 | */ 14 | class matrix4x4f final 15 | { 16 | public: 17 | /** Data array, [row][column] */ 18 | float values[4][4]; 19 | 20 | /** Constructs matrix with zero values */ 21 | matrix4x4f(); 22 | 23 | /** Constructs matrix using given array 24 | * @param array Matrix values array 25 | */ 26 | matrix4x4f(float const m[4][4]); 27 | 28 | /** Constructs matrix with specified values */ 29 | matrix4x4f( 30 | float const m00, float const m01, float const m02, float const m03, 31 | float const m10, float const m11, float const m12, float const m13, 32 | float const m20, float const m21, float const m22, float const m23, 33 | float const m30, float const m31, float const m32, float const m33); 34 | 35 | // Operators 36 | // 37 | 38 | matrix4x4f operator*(matrix4x4f const& m) const; 39 | 40 | /** Generates translation matrix 41 | * @param x Distance along x-axis 42 | * @param y Distance along y-axis 43 | * @param z Distance along z-axis 44 | */ 45 | static matrix4x4f translation(float const x, float const y, float const z); 46 | 47 | /** Generates scale matrix 48 | * @param x Scale along x-axis 49 | * @param y Scale along y-axis 50 | * @param z Scale along z-axis 51 | */ 52 | static matrix4x4f scale(float const x, float const y, float const z); 53 | 54 | /** Generates uniform scale matrix 55 | * @param s Scale along each axis 56 | */ 57 | static matrix4x4f uniform_scale(float const s); 58 | 59 | /** Generates rotation around x-axis 60 | * @param radians Radians to rotate for 61 | */ 62 | static matrix4x4f rotation_around_x_axis(float const radians); 63 | 64 | /** Generates rotation around y-axis 65 | * @param radians Radians to rotate for 66 | */ 67 | static matrix4x4f rotation_around_y_axis(float const radians); 68 | 69 | /** Generates rotation around z-axis 70 | * @param radians Radians to rotate for 71 | */ 72 | static matrix4x4f rotation_around_z_axis(float const radians); 73 | 74 | /** Generates rotation around specified axis 75 | * @param axis Axis to rotate around 76 | * @param radians Radians to rotate for 77 | */ 78 | static matrix4x4f rotation_around_axis(vector3f const& axis, float const radians); 79 | 80 | /** Generates matrix that transforms point from camera space to homogeneous clip space 81 | * @param hfov Horizontal field of view 82 | * @param vfov Vertical field of view 83 | * @param near Near plane z-coordinate 84 | * @param far Far plane z-coordinate 85 | */ 86 | static matrix4x4f clip_space(float const hfov, float const vfov, float const near, float const far); 87 | 88 | static const matrix4x4f IDENTITY; 89 | }; 90 | 91 | inline vector4f operator*(vector4f const& v, matrix4x4f const& m) 92 | { 93 | return vector4f{ 94 | v.x * m.values[0][0] + v.y * m.values[1][0] + v.z * m.values[2][0] + v.w * m.values[3][0], 95 | v.x * m.values[0][1] + v.y * m.values[1][1] + v.z * m.values[2][1] + v.w * m.values[3][1], 96 | v.x * m.values[0][2] + v.y * m.values[1][2] + v.z * m.values[2][2] + v.w * m.values[3][2], 97 | v.x * m.values[0][3] + v.y * m.values[1][3] + v.z * m.values[2][3] + v.w * m.values[3][3]}; 98 | } 99 | } 100 | 101 | #endif // LANTERN_MATRIX4X4_H 102 | -------------------------------------------------------------------------------- /lantern/include/texture.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_TEXTURE_H 2 | #define LANTERN_TEXTURE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "vector2.h" 8 | #include "color.h" 9 | 10 | namespace lantern 11 | { 12 | /** Class representing ARGB8888 (big endian) or BGRA8888 (little endian) texture */ 13 | class texture final 14 | { 15 | public: 16 | /** Constructs texture with given width and height 17 | * @param width Texture's width 18 | * @param height Texture's height 19 | */ 20 | texture(unsigned int const width, unsigned int const height); 21 | 22 | /** Copies texture from another instance 23 | * @param another Texture to copy data from 24 | */ 25 | texture(texture const& another); 26 | 27 | /** Moving data from other texture to the new one 28 | * @param another Texture to move data from 29 | */ 30 | texture(texture&& another); 31 | 32 | /** Frees memory used by the texture */ 33 | ~texture(); 34 | 35 | /** Gets texture width 36 | * @returns Texture width 37 | */ 38 | unsigned int get_width() const; 39 | 40 | /** Gets texture height 41 | * @returns Texture height 42 | */ 43 | unsigned int get_height() const; 44 | 45 | /** Gets texture pitch (length of a row in bytes) 46 | * @returns Texture pitch 47 | */ 48 | unsigned int get_pitch() const; 49 | 50 | /** Gets texture raw data 51 | * @returns Texture raw data array 52 | */ 53 | unsigned char const* get_data() const; 54 | 55 | /** Gets pixel color at specified position 56 | * @param point Pixel coordinates to get color at 57 | * @returns Color at specified position 58 | */ 59 | color get_pixel_color(vector2ui const& point) const; 60 | 61 | /** Sets pixel color at specified position 62 | * @param point Pixel coordinates to set color at 63 | * @param color Color to set 64 | */ 65 | void set_pixel_color(vector2ui const& point, color const& color); 66 | 67 | /** Clears texture with specified byte value (thus clearing only with gray shade) */ 68 | void clear(unsigned char const bytes_value); 69 | 70 | /** Loads texture from specified file. Only PNG is supported for now 71 | * @param file File to load image from 72 | */ 73 | static texture load_from_file(std::string file); 74 | 75 | private: 76 | /** Texture width */ 77 | unsigned int m_width; 78 | 79 | /** Texture height */ 80 | unsigned int m_height; 81 | 82 | /** Size of texture data array in bytes */ 83 | size_t m_data_total_size; 84 | 85 | /** Raw texture data */ 86 | unsigned char* m_data; 87 | 88 | /** Texture pitch */ 89 | unsigned int m_pitch; 90 | }; 91 | 92 | inline color texture::get_pixel_color(vector2ui const& point) const 93 | { 94 | unsigned int const pixel_first_byte_index{m_pitch * point.y + point.x * 4}; 95 | 96 | return color{ 97 | m_data[pixel_first_byte_index + 2] / 255.0f, 98 | m_data[pixel_first_byte_index + 1] / 255.0f, 99 | m_data[pixel_first_byte_index + 0] / 255.0f, 100 | m_data[pixel_first_byte_index + 3] / 255.0f}; 101 | } 102 | 103 | inline void texture::set_pixel_color(vector2ui const& point, color const& color) 104 | { 105 | unsigned int const pixel_first_byte_index{m_pitch * point.y + point.x * 4}; 106 | 107 | m_data[pixel_first_byte_index + 0] = static_cast(color.b * 255); 108 | m_data[pixel_first_byte_index + 1] = static_cast(color.g * 255); 109 | m_data[pixel_first_byte_index + 2] = static_cast(color.r * 255); 110 | m_data[pixel_first_byte_index + 3] = static_cast(color.a * 255); 111 | } 112 | 113 | inline void texture::clear(unsigned char const bytes_value) 114 | { 115 | memset(m_data, bytes_value, m_data_total_size); 116 | } 117 | } 118 | 119 | #endif // LANTERN_TEXTURE_H -------------------------------------------------------------------------------- /lantern/include/texture_shader.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_TEXTURE_SHADER_H 2 | #define LANTERN_TEXTURE_SHADER_H 3 | 4 | #include 5 | #include "shader_bind_point_info.h" 6 | #include "color.h" 7 | #include "vector2.h" 8 | #include "vector3.h" 9 | #include "vector4.h" 10 | #include "matrix4x4.h" 11 | #include "mesh_attribute_info.h" 12 | #include "texture.h" 13 | 14 | namespace lantern 15 | { 16 | /** Texturing shader 17 | * @ingroup Shaders 18 | */ 19 | class texture_shader final 20 | { 21 | public: 22 | /** Gets info about color bind points required by shader 23 | * @returns Required color bind points 24 | */ 25 | std::vector> get_color_bind_points(); 26 | 27 | /** Gets info about float bind points required by shader 28 | * @returns Required float bind points 29 | */ 30 | std::vector> get_float_bind_points(); 31 | 32 | /** Gets info about vector2f bind points required by shader 33 | * @returns Required vector2f bind points 34 | */ 35 | std::vector> get_vector2f_bind_points(); 36 | 37 | /** Gets info about vector3f bind points required by shader 38 | * @returns Required vector3f bind points 39 | */ 40 | std::vector> get_vector3f_bind_points(); 41 | 42 | /** Processes vertex 43 | * @param vertex Vertex in local space 44 | * @returns Processed vertex in homogeneous clip space 45 | */ 46 | vector4f process_vertex(vector4f const& vertex); 47 | 48 | /** Processes pixel 49 | * @param pixel Pixel coordinates on screen 50 | * @returns Final pixel color 51 | */ 52 | color process_pixel(vector2ui const& pixel); 53 | 54 | /** Sets model-view-projection matrix to use during vertex processing 55 | * @param mvp Model-view-projection matrix 56 | */ 57 | void set_mvp_matrix(matrix4x4f const& mvp); 58 | 59 | /** Sets texture to use for texturing 60 | * @param tex Texture to use 61 | */ 62 | void set_texture(texture const* tex); 63 | 64 | private: 65 | /** UV coordinates bind point */ 66 | vector2f m_uv; 67 | 68 | /** Movel-view-projection matrix */ 69 | matrix4x4f m_mvp; 70 | 71 | /** Texture to use */ 72 | texture const* m_texture; 73 | }; 74 | 75 | inline void texture_shader::set_mvp_matrix(matrix4x4f const& mvp) 76 | { 77 | m_mvp = mvp; 78 | } 79 | 80 | inline void texture_shader::set_texture(texture const* tex) 81 | { 82 | m_texture = tex; 83 | } 84 | 85 | inline vector4f texture_shader::process_vertex(vector4f const& vertex) 86 | { 87 | return vertex * m_mvp; 88 | } 89 | 90 | inline color texture_shader::process_pixel(vector2ui const& pixel) 91 | { 92 | // No filtration for now, use nearest neighbour 93 | return m_texture->get_pixel_color( 94 | vector2ui{ 95 | static_cast(m_texture->get_width() * m_uv.x), 96 | static_cast(m_texture->get_height() * m_uv.y)}); 97 | } 98 | 99 | inline std::vector> texture_shader::get_color_bind_points() 100 | { 101 | return std::vector>{}; 102 | } 103 | 104 | inline std::vector> texture_shader::get_float_bind_points() 105 | { 106 | return std::vector>{}; 107 | } 108 | 109 | inline std::vector> texture_shader::get_vector2f_bind_points() 110 | { 111 | return std::vector>{ 112 | shader_bind_point_info { TEXCOORD_ATTR_ID, &m_uv }}; 113 | } 114 | 115 | inline std::vector> texture_shader::get_vector3f_bind_points() 116 | { 117 | return std::vector>{}; 118 | } 119 | } 120 | 121 | #endif // LANTERN_TEXTURE_SHADER_H 122 | -------------------------------------------------------------------------------- /lantern/src/ui_label.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_label.h" 2 | 3 | using namespace lantern; 4 | 5 | ui_label::ui_label(font& font, texture const& target_texture) 6 | : m_font(font), 7 | m_ndc_per_pixel{2.0f / target_texture.get_width(), 2.0f / target_texture.get_height()} 8 | { 9 | m_shader.set_color(color::WHITE); 10 | } 11 | 12 | void ui_label::set_text(std::string const& text) 13 | { 14 | if (text != m_text) 15 | { 16 | m_text = text; 17 | update_mesh(); 18 | } 19 | } 20 | 21 | void ui_label::set_color(color const& color) 22 | { 23 | m_shader.set_color(color); 24 | } 25 | 26 | void ui_label::draw(renderer& pipeline, texture& target_texture) 27 | { 28 | // Remember if alpha blending was enable 29 | bool const alpha_blending_was_enabled{pipeline.get_merging_stage().get_alpha_blending_enabled()}; 30 | 31 | // We need to enable it 32 | pipeline.get_merging_stage().set_alpha_blending_enabled(true); 33 | 34 | // Draw each symbol's mesh 35 | // 36 | size_t meshes_count{m_meshes.size()}; 37 | for (size_t i{0}; i < meshes_count; ++i) 38 | { 39 | m_shader.set_symbol_texture(&m_symbols.at(i)->get_texture()); 40 | pipeline.render_mesh(m_meshes.at(i), m_shader, target_texture); 41 | } 42 | 43 | // Return alpha blending to the old value 44 | pipeline.get_merging_stage().set_alpha_blending_enabled(alpha_blending_was_enabled); 45 | } 46 | 47 | void ui_label::on_position_changed() 48 | { 49 | // TODO: just move vertices 50 | update_mesh(); 51 | } 52 | 53 | void ui_label::update_mesh() 54 | { 55 | m_symbols.clear(); 56 | m_meshes.clear(); 57 | 58 | // Params for quads texturing 59 | // 60 | std::vector const quad_uvs{ 61 | vector2f{0.0f, 0.0f}, 62 | vector2f{0.0f, 1.0f}, 63 | vector2f{1.0f, 1.0f}, 64 | vector2f{1.0f, 0.0f}}; 65 | std::vector const quad_uvs_indices{1, 0, 3, 2}; 66 | 67 | float const quad_z = 0.0f; 68 | 69 | vector2f bottom_left_ndc_pos{m_position}; 70 | 71 | for (char const c : m_text) 72 | { 73 | mesh char_mesh; 74 | 75 | // Get font's symbol and save it 76 | // 77 | symbol const* symbol{m_font.get_symbol(c)}; 78 | m_symbols.push_back(symbol); 79 | 80 | // Calculate metrics in NDC 81 | // 82 | vector2f const quad_size_ndc{symbol->get_size().x * m_ndc_per_pixel.x, symbol->get_size().y * m_ndc_per_pixel.y}; 83 | vector2f const bearing_ndc{symbol->get_bearing().x * m_ndc_per_pixel.x, symbol->get_bearing().y * m_ndc_per_pixel.y}; 84 | float const advance_ndc{symbol->get_advance() * m_ndc_per_pixel.x}; 85 | float const baseline_offset_ndc{quad_size_ndc.y - bearing_ndc.y}; 86 | 87 | // Create quad's verices 88 | // 89 | vector3f v1{bottom_left_ndc_pos.x + bearing_ndc.x, bottom_left_ndc_pos.y - baseline_offset_ndc, quad_z}; 90 | vector3f v2{bottom_left_ndc_pos.x + bearing_ndc.x, bottom_left_ndc_pos.y - baseline_offset_ndc + quad_size_ndc.y, quad_z}; 91 | vector3f v3{bottom_left_ndc_pos.x + bearing_ndc.x + quad_size_ndc.x, bottom_left_ndc_pos.y - baseline_offset_ndc + quad_size_ndc.y, quad_z}; 92 | vector3f v4{bottom_left_ndc_pos.x + bearing_ndc.x + quad_size_ndc.x, bottom_left_ndc_pos.y - baseline_offset_ndc, quad_z}; 93 | 94 | // Move position for next char 95 | bottom_left_ndc_pos.x = bottom_left_ndc_pos.x + advance_ndc; 96 | 97 | // Initialize mesh 98 | // 99 | 100 | char_mesh.get_vertices().push_back(v1); 101 | char_mesh.get_vertices().push_back(v2); 102 | char_mesh.get_vertices().push_back(v3); 103 | char_mesh.get_vertices().push_back(v4); 104 | 105 | char_mesh.get_indices().push_back(0); 106 | char_mesh.get_indices().push_back(2); 107 | char_mesh.get_indices().push_back(1); 108 | 109 | char_mesh.get_indices().push_back(0); 110 | char_mesh.get_indices().push_back(3); 111 | char_mesh.get_indices().push_back(2); 112 | 113 | char_mesh.get_vector2f_attributes().push_back(mesh_attribute_info{TEXCOORD_ATTR_ID, quad_uvs, quad_uvs_indices, attribute_interpolation_option::linear}); 114 | 115 | // Save it 116 | m_meshes.push_back(char_mesh); 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /lantern/include/ui_label_shader.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_UI_LABEL_SHADER_H 2 | #define LANTERN_UI_LABEL_SHADER_H 3 | 4 | #include 5 | #include "shader_bind_point_info.h" 6 | #include "color.h" 7 | #include "vector2.h" 8 | #include "vector3.h" 9 | #include "vector4.h" 10 | #include "matrix4x4.h" 11 | #include "mesh_attribute_info.h" 12 | #include "texture.h" 13 | 14 | namespace lantern 15 | { 16 | /** Shader for font's symbol rendering, used by ui_label. 17 | * It doesn't make any vertex transformations - assume that there're already in NDC coordinate space 18 | * @ingroup Shaders 19 | */ 20 | class ui_label_shader final 21 | { 22 | public: 23 | /** Gets info about color bind points required by shader 24 | * @returns Required color bind points 25 | */ 26 | std::vector> get_color_bind_points(); 27 | 28 | /** Gets info about float bind points required by shader 29 | * @returns Required float bind points 30 | */ 31 | std::vector> get_float_bind_points(); 32 | 33 | /** Gets info about vector2f bind points required by shader 34 | * @returns Required vector2f bind points 35 | */ 36 | std::vector> get_vector2f_bind_points(); 37 | 38 | /** Gets info about vector3f bind points required by shader 39 | * @returns Required vector3f bind points 40 | */ 41 | std::vector> get_vector3f_bind_points(); 42 | 43 | /** Processes vertex 44 | * @param vertex Vertex in local space 45 | * @returns Processed vertex in homogeneous clip space 46 | */ 47 | vector4f process_vertex(vector4f const& vertex); 48 | 49 | /** Processes pixel 50 | * @param pixel Pixel coordinates on screen 51 | * @returns Final pixel color 52 | */ 53 | color process_pixel(vector2ui const& pixel); 54 | 55 | /** Sets symbols texture 56 | * @param symbol_texture Symbol's texture to use 57 | */ 58 | void set_symbol_texture(texture const* symbol_texture); 59 | 60 | /** Sets color to use for rendering 61 | * @param color Symbol's color 62 | */ 63 | void set_color(color const& color); 64 | 65 | private: 66 | /** UV coordinates bind point */ 67 | vector2f m_uv; 68 | 69 | /** Symbol texture to use */ 70 | texture const* m_symbol_texture; 71 | 72 | /** Color to render symbols with */ 73 | color m_color; 74 | }; 75 | 76 | inline std::vector> ui_label_shader::get_color_bind_points() 77 | { 78 | return std::vector>{}; 79 | } 80 | 81 | inline std::vector> ui_label_shader::get_float_bind_points() 82 | { 83 | return std::vector>{}; 84 | } 85 | 86 | inline std::vector> ui_label_shader::get_vector2f_bind_points() 87 | { 88 | return std::vector>{ 89 | shader_bind_point_info { TEXCOORD_ATTR_ID, &m_uv }}; 90 | } 91 | 92 | inline std::vector> ui_label_shader::get_vector3f_bind_points() 93 | { 94 | return std::vector>{}; 95 | } 96 | 97 | inline vector4f ui_label_shader::process_vertex(vector4f const& vertex) 98 | { 99 | return vertex; 100 | } 101 | 102 | inline color ui_label_shader::process_pixel(vector2ui const& pixel) 103 | { 104 | color symbol_color = m_symbol_texture->get_pixel_color( 105 | vector2ui{ 106 | static_cast(m_symbol_texture->get_width() * m_uv.x), 107 | static_cast(m_symbol_texture->get_height() * m_uv.y)}); 108 | 109 | // Copy all the channels except for alpha from required color 110 | // 111 | symbol_color.r = m_color.r; 112 | symbol_color.g = m_color.g; 113 | symbol_color.b = m_color.b; 114 | 115 | return symbol_color; 116 | } 117 | 118 | inline void ui_label_shader::set_symbol_texture(texture const* tex) 119 | { 120 | m_symbol_texture = tex; 121 | } 122 | 123 | inline void ui_label_shader::set_color(color const& color) 124 | { 125 | m_color = color; 126 | } 127 | } 128 | 129 | #endif // LANTERN_UI_LABEL_SHADER_H 130 | -------------------------------------------------------------------------------- /tests/src/vector3.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_utils.h" 2 | 3 | using namespace lantern; 4 | 5 | TEST(vector3, constructors) 6 | { 7 | vector3f const v{2.0f, -4.0f, 15.0f}; 8 | assert_floats_near(v.x, 2.0f); 9 | assert_floats_near(v.y, -4.0f); 10 | assert_floats_near(v.z, 15.0f); 11 | 12 | vector3f const v_default{}; 13 | assert_floats_near(v_default.x, 0.0f); 14 | assert_floats_near(v_default.y, 0.0f); 15 | assert_floats_near(v_default.z, 0.0f); 16 | } 17 | 18 | TEST(vector3, negation) 19 | { 20 | vector3f const v{13.0f, -99.0f, 0.0f}; 21 | vector3f const v_neg{-v}; 22 | assert_vectors3_near(v_neg, vector3f{-13.0f, 99.0f, 0.0f}); 23 | } 24 | 25 | TEST(vector3, addition) 26 | { 27 | vector3f const v{-15.0f, 0.0f, 7.0f}; 28 | vector3f const w{15.0f, -5.0f, 13.0f}; 29 | 30 | vector3f v_w_added{v + w}; 31 | assert_vectors3_near(v_w_added, vector3f{0.0f, -5.0f, 20.0f}); 32 | 33 | v_w_added = v; 34 | v_w_added += w; 35 | assert_vectors3_near(v_w_added, vector3f{0.0f, -5.0f, 20.0f}); 36 | } 37 | 38 | TEST(vector3, subtraction) 39 | { 40 | vector3f const v{-15.0f, 0.0f, 7.0f}; 41 | vector3f const w{15.0f, -5.0f, 13.0f}; 42 | 43 | vector3f v_w_subtracted{v - w}; 44 | assert_vectors3_near(v_w_subtracted, vector3f{-30.0f, 5.0f, -6.0f}); 45 | 46 | v_w_subtracted = v; 47 | v_w_subtracted -= w; 48 | assert_vectors3_near(v_w_subtracted, vector3f{-30.0f, 5.0f, -6.0f}); 49 | } 50 | 51 | TEST(vector3, multiplication_by_scalar) 52 | { 53 | vector3f const v{1.0f, -2.0f, 3.0f}; 54 | 55 | vector3f v_muled{v * 6.0f}; 56 | assert_vectors3_near(v_muled, vector3f{6.0f, -12.0f, 18.0f}); 57 | 58 | v_muled = 6.0f * v; 59 | assert_vectors3_near(v_muled, vector3f{6.0f, -12.0f, 18.0f}); 60 | 61 | v_muled = v; 62 | v_muled *= 6.0f; 63 | assert_vectors3_near(v_muled, vector3f{6.0f, -12.0f, 18.0f}); 64 | } 65 | 66 | TEST(vector3, division_by_scalar) 67 | { 68 | vector3f const v{1.0f, -2.0f, 3.0f}; 69 | 70 | vector3f v_divided{v / 5.0f}; 71 | assert_vectors3_near(v_divided, vector3f{0.2f, -0.4f, 0.6f}); 72 | 73 | v_divided = v; 74 | v_divided /= 5.0f; 75 | assert_vectors3_near(v_divided, vector3f{0.2f, -0.4f, 0.6f}); 76 | } 77 | 78 | TEST(vector3, length) 79 | { 80 | vector3f const v{5.0f, -4.0f, 7.0f}; 81 | assert_floats_near(v.length(), 9.4868f); 82 | assert_floats_near(v.length_sqr(), 90.0f); 83 | } 84 | 85 | TEST(vector3, normalization) 86 | { 87 | vector3f v(13.0f, -5.0f, 0.55f); 88 | 89 | v.normalize(); 90 | assert_vectors3_near(v, vector3f{0.9326f, -0.3587f, 0.0394f}); 91 | assert_floats_near(v.length(), 1.0f); 92 | 93 | vector3f const v_normalized{v.normalized()}; 94 | assert_vectors3_near(v_normalized, vector3f{0.9326f, -0.3587f, 0.0394f}); 95 | assert_floats_near(v_normalized.length(), 1.0f); 96 | } 97 | 98 | TEST(vector3, dot_product) 99 | { 100 | vector3f const v{12.0f, -3.0f, 5.0f}; 101 | vector3f const w{-5.0f, -3.0f, 0.5f}; 102 | assert_floats_near(v.dot(w), -48.5f); 103 | } 104 | 105 | TEST(vector3, cross_product) 106 | { 107 | vector3f const v{-5.0f, 17.0f, 3.0f}; 108 | vector3f const w{3.0f, 12.0f, 0.0f}; 109 | vector3f cross{v.cross(w)}; 110 | assert_vectors3_near(cross, vector3f{-36.0f, 9.0f, -111.0f}); 111 | } 112 | 113 | TEST(vector3, distance_between_points) 114 | { 115 | vector3f const p1{5.0f, 5.0f, 5.0f}; 116 | vector3f const p2{0.0f, 2.0f, 3.0f}; 117 | assert_floats_near(p1.distance_to(p2), 6.1644f); 118 | } 119 | 120 | TEST(vector3, angle_between_vectors) 121 | { 122 | vector3f const v1{1.0f, 2.0f, 5.0f}; 123 | vector3f const v2{3.0f, 0.0f, 2.0f}; 124 | assert_floats_near(v1.angle_with(v2), 0.8522f); 125 | } 126 | 127 | TEST(vector3, projection_on_vector) 128 | { 129 | vector3f const v1{0.3f, 1.0f, 2.0f}; 130 | vector3f const v2{-1.0f, -0.3f, 5.0f}; 131 | vector3f const projection{v1.projection_on(v2)}; 132 | assert_vectors3_near(projection, vector3f{-0.3602f, -0.1080f, 1.8014f}); 133 | } 134 | 135 | TEST(vector3, perpendicular_on_vector) 136 | { 137 | vector3f const v1{1.0f, 2.0f, 1.0f}; 138 | vector3f const v2{-1.0f, 3.0f, 2.0f}; 139 | vector3f const perpendicular{v1.perpendicular_on(v2)}; 140 | assert_vectors3_near(perpendicular, vector3f{1.5f, 0.5f, 0.0f}); 141 | } 142 | -------------------------------------------------------------------------------- /tests/include/assert_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_ASSERT_UTILS_H 2 | #define LANTERN_ASSERT_UTILS_H 3 | 4 | #include 5 | #include "gtest/gtest.h" 6 | #include "vector2.h" 7 | #include "vector3.h" 8 | #include "vector4.h" 9 | #include "matrix3x3.h" 10 | #include "matrix4x4.h" 11 | #include "color.h" 12 | #include "texture.h" 13 | 14 | using namespace lantern; 15 | 16 | static const float tests_epsilon = 0.0001f; 17 | 18 | inline void assert_floats_near(float const f1, float const f2) 19 | { 20 | ASSERT_NEAR(f1, f2, tests_epsilon); 21 | } 22 | 23 | inline void assert_vectors2_near(vector2 const& v1, vector2 const& v2) 24 | { 25 | assert_floats_near(v1.x, v2.x); 26 | assert_floats_near(v1.y, v2.y); 27 | } 28 | 29 | inline void assert_vectors3_near(vector3 const& v1, vector3 const& v2) 30 | { 31 | assert_floats_near(v1.x, v2.x); 32 | assert_floats_near(v1.y, v2.y); 33 | assert_floats_near(v1.z, v2.z); 34 | } 35 | 36 | inline void assert_vectors4_near(vector4f const& v1, vector4f const& v2) 37 | { 38 | assert_floats_near(v1.x, v2.x); 39 | assert_floats_near(v1.y, v2.y); 40 | assert_floats_near(v1.z, v2.z); 41 | assert_floats_near(v1.w, v2.w); 42 | } 43 | 44 | inline void assert_matrix3x3_near(matrix3x3f const& m1, matrix3x3f const& m2) 45 | { 46 | assert_floats_near(m1.values[0][0], m2.values[0][0]); 47 | assert_floats_near(m1.values[0][1], m2.values[0][1]); 48 | assert_floats_near(m1.values[0][2], m2.values[0][2]); 49 | assert_floats_near(m1.values[1][0], m2.values[1][0]); 50 | assert_floats_near(m1.values[1][1], m2.values[1][1]); 51 | assert_floats_near(m1.values[1][2], m2.values[1][2]); 52 | assert_floats_near(m1.values[2][0], m2.values[2][0]); 53 | assert_floats_near(m1.values[2][1], m2.values[2][1]); 54 | assert_floats_near(m1.values[2][2], m2.values[2][2]); 55 | } 56 | 57 | inline void assert_matrix4x4_near(matrix4x4f const& m1, matrix4x4f const& m2) 58 | { 59 | assert_floats_near(m1.values[0][0], m2.values[0][0]); 60 | assert_floats_near(m1.values[0][1], m2.values[0][1]); 61 | assert_floats_near(m1.values[0][2], m2.values[0][2]); 62 | assert_floats_near(m1.values[0][3], m2.values[0][3]); 63 | assert_floats_near(m1.values[1][0], m2.values[1][0]); 64 | assert_floats_near(m1.values[1][1], m2.values[1][1]); 65 | assert_floats_near(m1.values[1][2], m2.values[1][2]); 66 | assert_floats_near(m1.values[1][3], m2.values[1][3]); 67 | assert_floats_near(m1.values[2][0], m2.values[2][0]); 68 | assert_floats_near(m1.values[2][1], m2.values[2][1]); 69 | assert_floats_near(m1.values[2][2], m2.values[2][2]); 70 | assert_floats_near(m1.values[2][3], m2.values[2][3]); 71 | assert_floats_near(m1.values[3][0], m2.values[3][0]); 72 | assert_floats_near(m1.values[3][1], m2.values[3][1]); 73 | assert_floats_near(m1.values[3][2], m2.values[3][2]); 74 | assert_floats_near(m1.values[3][3], m2.values[3][3]); 75 | } 76 | 77 | inline void assert_pixel_color(texture const& texture, vector2ui const& point, color const& c) 78 | { 79 | color const current_color = texture.get_pixel_color(point); 80 | bool const pixel_color_matches{current_color == c}; 81 | ASSERT_TRUE(pixel_color_matches); 82 | } 83 | 84 | inline void assert_pixels_colors( 85 | texture const& texture, 86 | std::vector const& points, 87 | color const& points_color) 88 | { 89 | for (unsigned int i{0}; i < texture.get_width(); i++) 90 | { 91 | for (unsigned int j{0}; j < texture.get_height(); j++) 92 | { 93 | vector2ui p{i, j}; 94 | if (std::find(std::begin(points), std::end(points), p) != std::end(points)) 95 | { 96 | assert_pixel_color(texture, p, points_color); 97 | } 98 | } 99 | } 100 | } 101 | 102 | inline void assert_pixels_two_colors( 103 | texture const& texture, 104 | std::vector const& points, 105 | color const& points_color, color const& other_pixels_color) 106 | { 107 | for (unsigned int i{0}; i < texture.get_width(); i++) 108 | { 109 | for (unsigned int j{0}; j < texture.get_height(); j++) 110 | { 111 | vector2ui p{i, j}; 112 | if (std::find(std::begin(points), std::end(points), p) != std::end(points)) 113 | { 114 | assert_pixel_color(texture, p, points_color); 115 | } 116 | else 117 | { 118 | assert_pixel_color(texture, p, other_pixels_color); 119 | } 120 | } 121 | } 122 | } 123 | 124 | #endif // LANTERN_ASSERT_UTILS_H 125 | -------------------------------------------------------------------------------- /tests/src/matrix4x4.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_utils.h" 2 | #include "matrix4x4.h" 3 | 4 | using namespace lantern; 5 | 6 | TEST(matrix4x4f, constructors) 7 | { 8 | matrix4x4f const m1{ 9 | 15.0f, -3.0f, 20.0f, 1.0f, 10 | 13.1f, -1.1f, 17.1f, -0.15f, 11 | 90.2f, 1.2f, -2.2f, 0.0f, 12 | 0.0f, 2.0f, 1.0f, -3.0f}; 13 | assert_floats_near(m1.values[0][0], 15.0f); 14 | assert_floats_near(m1.values[0][1], -3.0f); 15 | assert_floats_near(m1.values[0][2], 20.0f); 16 | assert_floats_near(m1.values[0][3], 1.0f); 17 | assert_floats_near(m1.values[1][0], 13.1f); 18 | assert_floats_near(m1.values[1][1], -1.1f); 19 | assert_floats_near(m1.values[1][2], 17.1f); 20 | assert_floats_near(m1.values[1][3], -0.15f); 21 | assert_floats_near(m1.values[2][0], 90.2f); 22 | assert_floats_near(m1.values[2][1], 1.2f); 23 | assert_floats_near(m1.values[2][2], -2.2f); 24 | assert_floats_near(m1.values[2][3], 0.0f); 25 | assert_floats_near(m1.values[3][0], 0.0f); 26 | assert_floats_near(m1.values[3][1], 2.0f); 27 | assert_floats_near(m1.values[3][2], 1.0f); 28 | assert_floats_near(m1.values[3][3], -3.0f); 29 | } 30 | 31 | TEST(matrix4x4f, matrix_matrix_multiplication) 32 | { 33 | matrix4x4f const m1{ 34 | 2.0f, -3.0f, 11.0f, 4.0f, 35 | -1.0f, -1.0f, 0.15f, 3.0f, 36 | 1.0f, 0.77f, 0.33f, 0.25f, 37 | 0.3f, -1.0f, -1.0f, -1.0f}; 38 | matrix4x4f const m2{ 39 | 1.0f, 0.0f, 0.0f, -2.0f, 40 | -1.0f, 0.0f, 0.25f, 3.0f, 41 | 1.0f, 0.13f, 0.99f, 2.0f, 42 | 7.0f, 2.0f, -1.0f, 1.0f}; 43 | matrix4x4f const m_muled{m1 * m2}; 44 | assert_matrix4x4_near( 45 | m_muled, 46 | matrix4x4f{ 47 | 44.0f, 9.43f, 6.14f, 13.0f, 48 | 21.15f, 6.0195f, -3.1015f, 2.3f, 49 | 2.31f, 0.5429f, 0.2692f, 1.22f, 50 | -6.7f, -2.13f, -0.24f, -6.6f}); 51 | } 52 | 53 | TEST(matrix4x4f, vector_matrix_multiplication) 54 | { 55 | vector4f const v{1.0f, 3.0f, -0.15f, 2.0f}; 56 | matrix4x4f const m{ 57 | 1.0f, 0.0f, 0.0f, 0.1f, 58 | -1.0f, 0.0f, 0.25f, 3.0f, 59 | 1.0f, 0.13f, 0.99f, 2.0f, 60 | -1.0f, 2.0f, 0.15f, 0.01f}; 61 | vector4f const v_muled{v * m}; 62 | assert_vectors4_near(v_muled, vector4f{-4.15f, 3.9805f, 0.9015f, 8.8200f}); 63 | } 64 | 65 | TEST(matrix4x4f, translation) 66 | { 67 | matrix4x4f const translation{matrix4x4f::translation(0.15f, -0.2f, 1.3f)}; 68 | 69 | vector4f const v_point{1.0f, 2.0f, -3.0f, 1.0f}; 70 | vector4f const v_point_translated{v_point * translation}; 71 | assert_vectors4_near(v_point_translated, vector4f{1.15f, 1.8f, -1.7f, 1.0f}); 72 | 73 | vector4f const v_vector{1.0f, 2.0f, -3.0f, 0.0f}; 74 | vector4f const v_vector_translated{v_vector * translation}; 75 | assert_vectors4_near(v_vector_translated, v_vector); 76 | } 77 | 78 | TEST(matrix4x4f, scaling) 79 | { 80 | vector4f const v{2.0f, 3.0f, -0.15f, 1.0f}; 81 | 82 | matrix4x4f const scale{matrix4x4f::scale(0.33f, 0.25f, 2.0f)}; 83 | vector4f const v_scaled{v * scale}; 84 | assert_vectors4_near(v_scaled, vector4f{0.66f, 0.75f, -0.30f, v.w}); 85 | 86 | matrix4x4f const uniform_scale{matrix4x4f::uniform_scale(5.0f)}; 87 | vector4f const v_uniformly_scaled{v * uniform_scale}; 88 | assert_vectors4_near(v_uniformly_scaled, vector4f{10.0f, 15.0f, -0.75f, v.w}); 89 | } 90 | 91 | TEST(matrix4x4f, rotation) 92 | { 93 | vector4f const v{0.5f, 1.0f, -1.0f, 1.0f}; 94 | 95 | matrix4x4f const m_rotation_around_x_axis{matrix4x4f::rotation_around_x_axis(static_cast(M_PI) / 3.0f)}; 96 | vector4f const v_rotated_around_x_axis{v * m_rotation_around_x_axis}; 97 | assert_vectors4_near(v_rotated_around_x_axis, vector4f{0.5f, 1.366f, 0.366f, v.w}); 98 | 99 | matrix4x4f const m_rotation_around_y_axis{matrix4x4f::rotation_around_y_axis(static_cast(M_PI) / 10.0f)}; 100 | vector4f const v_rotated_around_y_axis{v * m_rotation_around_y_axis}; 101 | assert_vectors4_near(v_rotated_around_y_axis, vector4f{0.1665f, 1.0f, -1.1055f, v.w}); 102 | 103 | matrix4x4f const m_rotation_around_z_axis{matrix4x4f::rotation_around_z_axis(static_cast(M_PI) / 4.0f)}; 104 | vector4f const v_rotated_around_z_axis{v * m_rotation_around_z_axis}; 105 | assert_vectors4_near(v_rotated_around_z_axis, vector4f{-0.3535f, 1.0606f, -1.0f, v.w}); 106 | 107 | vector3f const axis{0.55f, -1.0f, -1.0f}; 108 | matrix4x4f const m_rotation_around_axis{matrix4x4f::rotation_around_axis(axis, static_cast(M_PI) / 2.0f)}; 109 | vector4f const v_rotated_around_axis{v * m_rotation_around_axis}; 110 | assert_vectors4_near(v_rotated_around_axis, vector4f{1.3837f, -0.0864f, 0.5725f, v.w}); 111 | } 112 | -------------------------------------------------------------------------------- /lantern/include/camera.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_CAMERA_H 2 | #define LANTERN_CAMERA_H 3 | 4 | #include "vector3.h" 5 | 6 | namespace lantern 7 | { 8 | /** Class representing camera in 3D world */ 9 | class camera final 10 | { 11 | public: 12 | /** Constructs camera with specified params 13 | * @param position Initial camera position 14 | * @param forward Initial camera direction, might be not normalized 15 | * @param fake_up Fake up vector which will be used to construct the real up vector from the forward vector, might be not normalized 16 | * @param horizontal_fov Camera's horizontal field of view in radians 17 | * @param aspect_ratio Viewport aspect ratio, used to calculate vertical field of view 18 | * @param near_plane_z Near plane z-coordinate 19 | * @param far_plane_z Far plane z-coordinate 20 | */ 21 | camera( 22 | vector3f const& position, 23 | vector3f const& forward, 24 | vector3f const& fake_up, 25 | float const horizontal_fov, 26 | float const aspect_ratio, 27 | float const near_plane_z, 28 | float const far_plane_z); 29 | 30 | /** Gets camera position 31 | * @returns Current position 32 | */ 33 | vector3f get_position() const; 34 | 35 | /** Sets camera position 36 | * @param position New camera position 37 | */ 38 | void set_position(vector3f const& position); 39 | 40 | /** Gets camera's z-axis coordinates 41 | * @returns Z-axis (forward vector) 42 | */ 43 | vector3f get_forward() const; 44 | 45 | /** Gets camera's x-axis coordinates 46 | * @returns X-axis (right vector) 47 | */ 48 | vector3f get_right() const; 49 | 50 | /** Gets camera's y-axis coordinates 51 | * @returns Y-axis (up vector) 52 | */ 53 | vector3f get_up() const; 54 | 55 | /** Gets horizontal field of view 56 | * @returns Horizontal field of view in radians 57 | */ 58 | float get_horizontal_fov() const; 59 | 60 | /** Gets vertical field of view 61 | * @returns Vertical field of view in radians 62 | */ 63 | float get_vertical_fov() const; 64 | 65 | /** Gets aspect ratio 66 | * @returns Aspect ratio (height / width) 67 | */ 68 | float get_aspect_ratio() const; 69 | 70 | /** Gets near plane z-coordinate 71 | * @returns Near plane z-coordinate 72 | */ 73 | float get_near_plane_z() const; 74 | 75 | /** Gets far plane z-coordinate 76 | * @returns Far plane z-coordinate 77 | */ 78 | float get_far_plane_z() const; 79 | 80 | /** Moves camera along its right vector 81 | * @param distance Distance to move 82 | */ 83 | void move_right(float const distance); 84 | 85 | /** Moves camera along its -right vector 86 | * @param distance Distance to move 87 | */ 88 | void move_left(float const distance); 89 | 90 | /** Moves camera along its up vector 91 | * @param distance Distance to move 92 | */ 93 | void move_up(float const distance); 94 | 95 | /** Moves camera along its -up vector 96 | * @param distance Distance to move 97 | */ 98 | void move_down(float const distance); 99 | 100 | /** Moves camera along its forward vector 101 | * @param distance Distance to move 102 | */ 103 | void move_forward(float const distance); 104 | 105 | /** Moves camera along its -forward vector 106 | * @param distance Distance to move 107 | */ 108 | void move_backward(float const distance); 109 | 110 | /** Rotates camera around y-axis 111 | * @param radians Radians to rotate for 112 | */ 113 | void yaw(float const radians); 114 | 115 | /** Rotates camera around x-axis 116 | * @param radians Radians to rotate for 117 | */ 118 | void pitch(float const radians); 119 | 120 | private: 121 | /** Current position */ 122 | vector3f m_position; 123 | 124 | /** Current forward vector */ 125 | vector3f m_forward; 126 | 127 | /** Current right vector */ 128 | vector3f m_right; 129 | 130 | /** Current up vector */ 131 | vector3f m_up; 132 | 133 | /** Horizontal field of view */ 134 | float const m_horizontal_fov; 135 | 136 | /** Vertical field of view */ 137 | float const m_vertical_fov; 138 | 139 | /** Aspect ratio */ 140 | float const m_aspect_ratio; 141 | 142 | /** Near plane z-coordinate */ 143 | float const m_near_plane_z; 144 | 145 | /** Far plane z-coordiante */ 146 | float const m_far_plane_z; 147 | 148 | /** Creates coordinate system base on forward and fake up vector 149 | * @param fake_up Temp up vector 150 | */ 151 | void establish_coordinate_system(vector3f const& fake_up); 152 | }; 153 | } 154 | 155 | #endif // LANTERN_CAMERA_H 156 | -------------------------------------------------------------------------------- /examples/rasterized_triangle_app/UbuntuFontLicense.txt: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /tests/src/obj_import.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_utils.h" 2 | #include "obj_import.h" 3 | 4 | using namespace lantern; 5 | 6 | static void assert_obj_vertices(mesh const& mesh) 7 | { 8 | // Test positions 9 | // 10 | 11 | std::vector const correct_vertices 12 | { vector3f{0.0f, 0.0f, 0.0f}, 13 | vector3f{0.0f, 0.0f, 1.0f}, 14 | vector3f{0.0f, 1.0f, 0.0f}, 15 | vector3f(0.0f, 1.0f, 1.0f), 16 | vector3f{1.0f, 0.0f, 0.0f}, 17 | vector3f{1.0f, 0.0f, 1.0f}, 18 | vector3f{1.0f, 1.0f, 0.0f}, 19 | vector3f{1.0f, 1.0f, 1.0f} }; 20 | 21 | std::vector const& vertices = mesh.get_vertices(); 22 | 23 | ASSERT_EQ(vertices.size(), 8); 24 | for (size_t i{0}; i < 8; ++i) 25 | { 26 | assert_vectors3_near(correct_vertices[i], vertices[i]); 27 | } 28 | 29 | // Test indices 30 | // 31 | 32 | std::vector const& indices = mesh.get_indices(); 33 | 34 | std::vector const correct_indices 35 | { 0, 6, 4, 36 | 0, 2, 6, 37 | 0, 3, 2, 38 | 0, 1, 3, 39 | 2, 7, 6, 40 | 2, 3, 7, 41 | 4, 6, 7, 42 | 4, 7, 5, 43 | 0, 4, 5, 44 | 0, 5, 1, 45 | 1, 5, 7, 46 | 1, 7, 3 }; 47 | 48 | ASSERT_EQ(indices.size(), 36); 49 | for (size_t i{0}; i < 36; ++i) 50 | { 51 | ASSERT_EQ(correct_indices[i], indices[i]); 52 | } 53 | } 54 | 55 | static void assert_obj_texcoords(mesh const& mesh) 56 | { 57 | mesh_attribute_info const* texcoords_info{nullptr}; 58 | for (size_t i{0}; i < mesh.get_vector2f_attributes().size(); ++i) 59 | { 60 | mesh_attribute_info const& attribute_info = mesh.get_vector2f_attributes()[i]; 61 | if (attribute_info.get_id() == TEXCOORD_ATTR_ID) 62 | { 63 | texcoords_info = &attribute_info; 64 | } 65 | } 66 | 67 | ASSERT_NE(texcoords_info, nullptr); 68 | 69 | std::vector const correct_texcoords 70 | { vector2f{0.0f, 0.0f}, 71 | vector2f{1.0f, 0.0f}, 72 | vector2f{1.0f, 1.0f}, 73 | vector2f(0.0f, 1.0f) }; 74 | 75 | ASSERT_EQ(texcoords_info->get_data().size(), 4); 76 | for (size_t i{0}; i < 4; ++i) 77 | { 78 | assert_vectors2_near(correct_texcoords[i], texcoords_info->get_data()[i]); 79 | } 80 | 81 | std::vector const correct_indices 82 | { 0, 2, 1, 83 | 0, 3, 2, 84 | 0, 2, 3, 85 | 0, 1, 2, 86 | 0, 2, 3, 87 | 0, 1, 2, 88 | 1, 2, 3, 89 | 1, 3, 0, 90 | 0, 1, 2, 91 | 0, 2, 3, 92 | 0, 1, 2, 93 | 0, 2, 3 }; 94 | 95 | ASSERT_EQ(texcoords_info->get_indices().size(), 36); 96 | for (size_t i{0}; i < 36; ++i) 97 | { 98 | ASSERT_EQ(correct_indices[i], texcoords_info->get_indices()[i]); 99 | } 100 | } 101 | 102 | void assert_obj_normals(mesh const& mesh) 103 | { 104 | mesh_attribute_info const* normals_info{nullptr}; 105 | for (size_t i{0}; i < mesh.get_vector3f_attributes().size(); ++i) 106 | { 107 | mesh_attribute_info const& attribute_info = mesh.get_vector3f_attributes()[i]; 108 | if (attribute_info.get_id() == NORMAL_ATTR_ID) 109 | { 110 | normals_info = &attribute_info; 111 | } 112 | } 113 | ASSERT_NE(normals_info, nullptr); 114 | 115 | std::vector const correct_normals 116 | { vector3f{0.0f, 0.0f, 1.0f}, 117 | vector3f{0.0f, 0.0f, -1.0f}, 118 | vector3f{0.0f, 1.0f, 0.0f}, 119 | vector3f{0.0f, -1.0f, 0.0f}, 120 | vector3f{1.0f, 0.0f, 0.0f}, 121 | vector3f{-1.0f, 0.0f, 0.0f} }; 122 | 123 | ASSERT_EQ(normals_info->get_data().size(), 6); 124 | for (size_t i{0}; i < 6; ++i) 125 | { 126 | assert_vectors3_near(correct_normals[i], normals_info->get_data()[i]); 127 | } 128 | 129 | std::vector const correct_indices 130 | { 1, 1, 1, 131 | 1, 1, 1, 132 | 5, 5, 5, 133 | 5, 5, 5, 134 | 2, 2, 2, 135 | 2, 2, 2, 136 | 4, 4, 4, 137 | 4, 4, 4, 138 | 3, 3, 3, 139 | 3, 3, 3, 140 | 0, 0, 0, 141 | 0, 0, 0 }; 142 | ASSERT_EQ(normals_info->get_indices().size(), 36); 143 | for (size_t i{0}; i < 36; ++i) 144 | { 145 | ASSERT_EQ(correct_indices[i], normals_info->get_indices()[i]); 146 | } 147 | } 148 | 149 | TEST(obj_import, import_obj_mesh) 150 | { 151 | mesh const& unit_cube_mesh_pos{load_mesh_from_obj("resources/unit_cube_pos.obj", false, false)}; 152 | assert_obj_vertices(unit_cube_mesh_pos); 153 | 154 | mesh const& unit_cube_mesh_pos_texcoord{load_mesh_from_obj("resources/unit_cube_pos_texcoord.obj", true, false)}; 155 | assert_obj_vertices(unit_cube_mesh_pos_texcoord); 156 | assert_obj_texcoords(unit_cube_mesh_pos_texcoord); 157 | 158 | mesh const& unit_cube_mesh_pos_normal{load_mesh_from_obj("resources/unit_cube_pos_normal.obj", false, true)}; 159 | assert_obj_vertices(unit_cube_mesh_pos_normal); 160 | assert_obj_normals(unit_cube_mesh_pos_normal); 161 | 162 | mesh const& unit_cube_mesh_pos_texcoord_normal{load_mesh_from_obj("resources/unit_cube_pos_texcoord_normal.obj", true, true)}; 163 | assert_obj_vertices(unit_cube_mesh_pos_texcoord_normal); 164 | assert_obj_texcoords(unit_cube_mesh_pos_texcoord_normal); 165 | assert_obj_normals(unit_cube_mesh_pos_texcoord_normal); 166 | } 167 | -------------------------------------------------------------------------------- /lantern/src/matrix4x4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "matrix4x4.h" 3 | 4 | using namespace lantern; 5 | 6 | matrix4x4f const matrix4x4f::IDENTITY = matrix4x4f{ 7 | 1.0f, 0.0f, 0.0f, 0.0f, 8 | 0.0f, 1.0f, 1.0f, 0.0f, 9 | 0.0f, 0.0f, 1.0f, 0.0f, 10 | 0.0f, 0.0f, 0.0f, 1.0f}; 11 | 12 | matrix4x4f::matrix4x4f() 13 | { 14 | 15 | } 16 | 17 | matrix4x4f::matrix4x4f(float const m[4][4]) 18 | { 19 | for (int i{0}; i < 4; ++i) 20 | { 21 | for (int j{0}; j < 4; ++j) 22 | { 23 | this->values[i][j] = m[i][j]; 24 | } 25 | } 26 | } 27 | 28 | matrix4x4f::matrix4x4f( 29 | float const m00, float const m01, float const m02, float const m03, 30 | float const m10, float const m11, float const m12, float const m13, 31 | float const m20, float const m21, float const m22, float const m23, 32 | float const m30, float const m31, float const m32, float const m33) 33 | { 34 | this->values[0][0] = m00; 35 | this->values[0][1] = m01; 36 | this->values[0][2] = m02; 37 | this->values[0][3] = m03; 38 | 39 | this->values[1][0] = m10; 40 | this->values[1][1] = m11; 41 | this->values[1][2] = m12; 42 | this->values[1][3] = m13; 43 | 44 | this->values[2][0] = m20; 45 | this->values[2][1] = m21; 46 | this->values[2][2] = m22; 47 | this->values[2][3] = m23; 48 | 49 | this->values[3][0] = m30; 50 | this->values[3][1] = m31; 51 | this->values[3][2] = m32; 52 | this->values[3][3] = m33; 53 | } 54 | 55 | matrix4x4f matrix4x4f::operator*(matrix4x4f const& m) const 56 | { 57 | matrix4x4f result; 58 | 59 | for (size_t i{0}; i < 4; ++i) 60 | { 61 | for (size_t j{0}; j < 4; ++j) 62 | { 63 | result.values[i][j] = 0.0f; 64 | for (size_t k{0}; k < 4; ++k) 65 | { 66 | result.values[i][j] += this->values[i][k] * m.values[k][j]; 67 | } 68 | } 69 | } 70 | 71 | return result; 72 | } 73 | 74 | matrix4x4f matrix4x4f::translation(float const x, float const y, float const z) 75 | { 76 | return matrix4x4f{ 77 | 1.0f, 0.0f, 0.0f, 0.0f, 78 | 0.0f, 1.0f, 0.0f, 0.0f, 79 | 0.0f, 0.0f, 1.0f, 0.0f, 80 | x, y, z, 1.0f}; 81 | } 82 | 83 | matrix4x4f matrix4x4f::scale(float const x, float const y, float const z) 84 | { 85 | return matrix4x4f{ 86 | x, 0.0f, 0.0f, 0.0f, 87 | 0.0f, y, 0.0f, 0.0f, 88 | 0.0f, 0.0f, z, 0.0f, 89 | 0.0f, 0.0f, 0.0f, 1.0f}; 90 | } 91 | 92 | matrix4x4f matrix4x4f::uniform_scale(float const s) 93 | { 94 | return matrix4x4f{ 95 | s, 0.0f, 0.0f, 0.0f, 96 | 0.0f, s, 0.0f, 0.0f, 97 | 0.0f, 0.0f, s, 0.0f, 98 | 0.0f, 0.0f, 0.0f, 1.0f}; 99 | } 100 | 101 | matrix4x4f matrix4x4f::rotation_around_x_axis(float const radians) 102 | { 103 | return matrix4x4f{ 104 | 1.0f, 0.0f, 0.0f, 0.0f, 105 | 0.0f, std::cos(radians), std::sin(radians), 0.0f, 106 | 0.0f, -std::sin(radians), std::cos(radians), 0.0f, 107 | 0.0f, 0.0f, 0.0f, 1.0f}; 108 | } 109 | 110 | matrix4x4f matrix4x4f::rotation_around_y_axis(float const radians) 111 | { 112 | return matrix4x4f{ 113 | std::cos(radians), 0.0f, -std::sin(radians), 0.0f, 114 | 0.0f, 1.0f, 0.0f, 0.0f, 115 | std::sin(radians), 0.0f, std::cos(radians), 0.0f, 116 | 0.0f, 0.0f, 0.0f, 1.0f}; 117 | } 118 | 119 | matrix4x4f matrix4x4f::rotation_around_z_axis(float const radians) 120 | { 121 | return matrix4x4f{ 122 | std::cos(radians), std::sin(radians), 0.0f, 0.0f, 123 | -std::sin(radians), std::cos(radians), 0.0f, 0.0f, 124 | 0.0f, 0.0f, 1.0f, 0.0f, 125 | 0.0f, 0.0f, 0.0f, 1.0f}; 126 | } 127 | 128 | matrix4x4f matrix4x4f::rotation_around_axis(vector3f const& axis, float const radians) 129 | { 130 | vector3f axis_normalized{axis.normalized()}; 131 | 132 | float const cos_value{std::cos(radians)}; 133 | float const sin_value{std::sin(radians)}; 134 | 135 | return matrix4x4f{ 136 | axis_normalized.x * axis_normalized.x * (1.0f - cos_value) + cos_value, 137 | axis_normalized.x * axis_normalized.y * (1.0f - cos_value) + axis_normalized.z * sin_value, 138 | axis_normalized.x * axis_normalized.z * (1.0f - cos_value) - axis_normalized.y * sin_value, 139 | 0.0f, 140 | 141 | axis_normalized.x * axis_normalized.y * (1.0f - cos_value) - axis_normalized.z * sin_value, 142 | axis_normalized.y * axis_normalized.y * (1.0f - cos_value) + cos_value, 143 | axis_normalized.y * axis_normalized.z * (1.0f - cos_value) + axis_normalized.x * sin_value, 144 | 0.0f, 145 | 146 | axis_normalized.x * axis_normalized.z * (1.0f - cos_value) + axis_normalized.y * sin_value, 147 | axis_normalized.y * axis_normalized.z * (1.0f - cos_value) - axis_normalized.x * sin_value, 148 | axis_normalized.z * axis_normalized.z * (1.0f - cos_value) + cos_value, 149 | 0.0f, 150 | 151 | 0.0f, 152 | 0.0f, 153 | 0.0f, 154 | 1.0f}; 155 | } 156 | 157 | matrix4x4f matrix4x4f::clip_space(float const hfov, float const vfov, float const near, float const far) 158 | { 159 | float const projection_plane_z{1.0f}; 160 | 161 | float const right{std::tan(hfov / 2.0f) * projection_plane_z}; 162 | float const left{-right}; 163 | float const top{std::tan(vfov / 2.0f) * projection_plane_z}; 164 | float const bottom{-top}; 165 | 166 | return matrix4x4f{ 167 | 2.0f * projection_plane_z / (right - left), 0.0f, 0.0f, 0.0f, 168 | 0.0f, 2.0f * projection_plane_z / (top - bottom), 0.0f, 0.0f, 169 | (left + right) / (left - right), (bottom + top) / (bottom - top), (far + near) / (far - near), 1.0f, 170 | 0.0f, 0.0f, -2.0f * near * far / (far - near), 0.0f}; 171 | } 172 | -------------------------------------------------------------------------------- /lantern/src/matrix3x3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "matrix3x3.h" 3 | 4 | using namespace lantern; 5 | 6 | matrix3x3f const matrix3x3f::IDENTITY = matrix3x3f{ 7 | 1.0f, 0.0f, 0.0f, 8 | 0.0f, 1.0f, 1.0f, 9 | 0.0f, 0.0f, 1.0f}; 10 | 11 | matrix3x3f::matrix3x3f() 12 | { 13 | 14 | } 15 | 16 | matrix3x3f::matrix3x3f(float const array[3][3]) 17 | { 18 | for (size_t i{0}; i < 3; ++i) 19 | { 20 | for (size_t j{0}; j < 3; ++j) 21 | { 22 | this->values[i][j] = array[i][j]; 23 | } 24 | } 25 | } 26 | 27 | matrix3x3f::matrix3x3f( 28 | float const m00, float const m01, float const m02, 29 | float const m10, float const m11, float const m12, 30 | float const m20, float const m21, float const m22) 31 | { 32 | this->values[0][0] = m00; 33 | this->values[0][1] = m01; 34 | this->values[0][2] = m02; 35 | 36 | this->values[1][0] = m10; 37 | this->values[1][1] = m11; 38 | this->values[1][2] = m12; 39 | 40 | this->values[2][0] = m20; 41 | this->values[2][1] = m21; 42 | this->values[2][2] = m22; 43 | } 44 | 45 | matrix3x3f matrix3x3f::operator*(matrix3x3f const& m) const 46 | { 47 | matrix3x3f result; 48 | 49 | for (size_t i{0}; i < 3; ++i) 50 | { 51 | for (size_t j{0}; j < 3; ++j) 52 | { 53 | result.values[i][j] = 0.0f; 54 | for (size_t k{0}; k < 3; ++k) 55 | { 56 | result.values[i][j] += this->values[i][k] * m.values[k][j]; 57 | } 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | 64 | float matrix3x3f::det() const 65 | { 66 | return 67 | this->values[0][0] * (this->values[1][1] * this->values[2][2] - this->values[1][2] * this->values[2][1]) - 68 | this->values[0][1] * (this->values[1][0] * this->values[2][2] - this->values[1][2] * this->values[2][0]) + 69 | this->values[0][2] * (this->values[1][0] * this->values[2][1] - this->values[1][1] * this->values[2][0]); 70 | } 71 | 72 | matrix3x3f matrix3x3f::inversed() const 73 | { 74 | return inversed_precalc_det(det()); 75 | } 76 | 77 | matrix3x3f matrix3x3f::inversed_precalc_det(float const det) const 78 | { 79 | matrix3x3f result; 80 | 81 | result.values[0][0] = this->values[1][1] * this->values[2][2] - this->values[1][2] * this->values[2][1]; 82 | result.values[1][0] = this->values[1][2] * this->values[2][0] - this->values[1][0] * this->values[2][2]; 83 | result.values[2][0] = this->values[1][0] * this->values[2][1] - this->values[1][1] * this->values[2][0]; 84 | 85 | result.values[0][1] = this->values[0][2] * this->values[2][1] - this->values[0][1] * this->values[2][2]; 86 | result.values[1][1] = this->values[0][0] * this->values[2][2] - this->values[0][2] * this->values[2][0]; 87 | result.values[2][1] = this->values[0][1] * this->values[2][0] - this->values[0][0] * this->values[2][1]; 88 | 89 | result.values[0][2] = this->values[0][1] * this->values[1][2] - this->values[0][2] * this->values[1][1]; 90 | result.values[1][2] = this->values[0][2] * this->values[1][0] - this->values[0][0] * this->values[1][2]; 91 | result.values[2][2] = this->values[0][0] * this->values[1][1] - this->values[0][1] * this->values[1][0]; 92 | 93 | float const det_ivnersed = 1.0f / det; 94 | for (size_t j{0}; j < 3; ++j) 95 | { 96 | for (size_t i{0}; i < 3; ++i) 97 | { 98 | result.values[i][j] *= det_ivnersed; 99 | } 100 | } 101 | 102 | return result; 103 | } 104 | 105 | matrix3x3f matrix3x3f::scale(float const x, float const y, float const z) 106 | { 107 | return matrix3x3f{ 108 | x, 0.0f, 0.0f, 109 | 0.0f, y, 0.0f, 110 | 0.0f, 0.0f, z}; 111 | } 112 | 113 | matrix3x3f matrix3x3f::uniform_scale(float const s) 114 | { 115 | return matrix3x3f{ 116 | s, 0.0f, 0.0f, 117 | 0.0f, s, 0.0f, 118 | 0.0f, 0.0f, s}; 119 | } 120 | 121 | matrix3x3f matrix3x3f::rotation_around_x_axis(float const radians) 122 | { 123 | return matrix3x3f{ 124 | 1.0f, 0.0f, 0.0f, 125 | 0.0f, std::cos(radians), std::sin(radians), 126 | 0.0f, -std::sin(radians), std::cos(radians)}; 127 | } 128 | 129 | matrix3x3f matrix3x3f::rotation_around_y_axis(float const radians) 130 | { 131 | return matrix3x3f{ 132 | std::cos(radians), 0.0f, -std::sin(radians), 133 | 0.0f, 1.0f, 0.0f, 134 | std::sin(radians), 0.0f, std::cos(radians)}; 135 | } 136 | 137 | matrix3x3f matrix3x3f::rotation_around_z_axis(float const radians) 138 | { 139 | return matrix3x3f{ 140 | std::cos(radians), std::sin(radians), 0.0f, 141 | -std::sin(radians), std::cos(radians), 0.0f, 142 | 0.0f, 0.0f, 1.0f}; 143 | } 144 | 145 | matrix3x3f matrix3x3f::rotation_around_axis(vector3f const& axis, float const radians) 146 | { 147 | vector3f axis_normalized{axis.normalized()}; 148 | 149 | float const cos_value{std::cos(radians)}; 150 | float const sin_value{std::sin(radians)}; 151 | 152 | return matrix3x3f{ 153 | axis_normalized.x * axis_normalized.x * (1.0f - cos_value) + cos_value, 154 | axis_normalized.x * axis_normalized.y * (1.0f - cos_value) + axis_normalized.z * sin_value, 155 | axis_normalized.x * axis_normalized.z * (1.0f - cos_value) - axis_normalized.y * sin_value, 156 | 157 | axis_normalized.x * axis_normalized.y * (1.0f - cos_value) - axis_normalized.z * sin_value, 158 | axis_normalized.y * axis_normalized.y * (1.0f - cos_value) + cos_value, 159 | axis_normalized.y * axis_normalized.z * (1.0f - cos_value) + axis_normalized.x * sin_value, 160 | 161 | axis_normalized.x * axis_normalized.z * (1.0f - cos_value) + axis_normalized.y * sin_value, 162 | axis_normalized.y * axis_normalized.z * (1.0f - cos_value) - axis_normalized.x * sin_value, 163 | axis_normalized.z * axis_normalized.z * (1.0f - cos_value) + cos_value}; 164 | } 165 | -------------------------------------------------------------------------------- /lantern/include/geometry_stage.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_GEOMETRY_STAGE_H 2 | #define LANTERN_GEOMETRY_STAGE_H 3 | 4 | #include "mesh.h" 5 | #include "texture.h" 6 | #include "matrix4x4.h" 7 | 8 | namespace lantern 9 | { 10 | /** This rendering stage is responsible for transforming geometry and invoking a vertex shader 11 | * @ingroup Rendering 12 | */ 13 | class geometry_stage final 14 | { 15 | public: 16 | /** Constructs geometry stage with default settings */ 17 | geometry_stage(); 18 | 19 | /** Invokes stage 20 | * @param mesh Mesh to process 21 | * @param shader Shader to use for vertex processing 22 | * @param target_texture Texture mesh will be rendered to 23 | * @param delegate Object to pass results to for futher processing 24 | */ 25 | template 26 | void invoke( 27 | mesh const& mesh, 28 | TShader& shader, 29 | bool const do_homogeneous_division, 30 | texture& target_texture, 31 | TDelegate& delegate); 32 | 33 | private: 34 | /** Storage for transformed vertices */ 35 | std::vector m_transformed_vertices_storage; 36 | 37 | /* Storage for clip flags */ 38 | std::vector m_transformed_vertices_clip_flags_storage; 39 | }; 40 | 41 | template 42 | void geometry_stage::invoke( 43 | mesh const& mesh, 44 | TShader& shader, 45 | bool const do_homogeneous_division, 46 | texture& target_texture, 47 | TDelegate& delegate) 48 | { 49 | // Change transformed vertices storages capacity if needed and clear it 50 | // 51 | 52 | std::vector const& vertices = mesh.get_vertices(); 53 | size_t vertices_count{vertices.size()}; 54 | 55 | if (m_transformed_vertices_storage.capacity() < vertices_count) 56 | { 57 | m_transformed_vertices_storage.reserve(vertices_count); 58 | } 59 | m_transformed_vertices_storage.clear(); 60 | 61 | if (m_transformed_vertices_clip_flags_storage.capacity() < vertices_count) 62 | { 63 | m_transformed_vertices_clip_flags_storage.reserve(vertices_count); 64 | } 65 | m_transformed_vertices_clip_flags_storage.clear(); 66 | 67 | // Process vertices and clip 68 | // 69 | for (size_t i{0}; i < vertices_count; ++i) 70 | { 71 | vector3f const& v{vertices.at(i)}; 72 | vector4f v_transformed{shader.process_vertex(vector4f{v.x, v.y, v.z, 1.0f})}; 73 | 74 | bool clipped{false}; 75 | if ((v_transformed.x > v_transformed.w) || (v_transformed.x < -v_transformed.w)) 76 | { 77 | clipped = true; 78 | } 79 | else if ((v_transformed.y > v_transformed.w) || (v_transformed.y < -v_transformed.w)) 80 | { 81 | clipped = true; 82 | } 83 | else if ((v_transformed.z > v_transformed.w) || (v_transformed.z < -v_transformed.w)) 84 | { 85 | clipped = true; 86 | } 87 | 88 | m_transformed_vertices_storage.push_back(v_transformed); 89 | m_transformed_vertices_clip_flags_storage.push_back(clipped); 90 | } 91 | 92 | // Transform vertices to screen coordinates 93 | // 94 | 95 | float const width{static_cast(target_texture.get_width())}; 96 | float const height{static_cast(target_texture.get_height())}; 97 | 98 | if (do_homogeneous_division) 99 | { 100 | matrix4x4f const ndc_to_screen{ 101 | (width) / 2.0f, 0.0f, 0.0f, 0.0f, 102 | 0.0f, -(height) / 2.0f, 0.0f, 0.0f, 103 | 0.0f, 0.0f, 1.0f, 0.0f, 104 | (width) / 2.0f, (height) / 2.0f, 0.0f, 1.0f}; 105 | 106 | for (size_t i{0}; i < vertices_count; ++i) 107 | { 108 | vector4f& v = m_transformed_vertices_storage.at(i); 109 | v = v * ndc_to_screen; 110 | } 111 | } 112 | else 113 | { 114 | for (size_t i{0}; i < vertices_count; ++i) 115 | { 116 | if (m_transformed_vertices_clip_flags_storage.at(i) == true) 117 | { 118 | continue; 119 | } 120 | 121 | vector4f& v = m_transformed_vertices_storage.at(i); 122 | 123 | float const w_inversed{1.0f / v.w}; 124 | 125 | v.x *= w_inversed; 126 | v.y *= w_inversed; 127 | v.z *= w_inversed; 128 | 129 | v.w = w_inversed; 130 | 131 | // NDC to screen 132 | // 133 | v.x = v.x * width / 2.0f + width / 2.0f; 134 | v.y = -v.y * height / 2.0f + height / 2.0f; 135 | } 136 | } 137 | 138 | // Process results 139 | // 140 | std::vector const& indices = mesh.get_indices(); 141 | 142 | size_t indices_count{indices.size()}; 143 | for (size_t i{0}; i < indices_count; i += 3) 144 | { 145 | unsigned int const index0{indices.at(i + 0)}; 146 | unsigned int const index1{indices.at(i + 1)}; 147 | unsigned int const index2{indices.at(i + 2)}; 148 | 149 | vector4f const& v0 = m_transformed_vertices_storage.at(index0); 150 | vector4f const& v1 = m_transformed_vertices_storage.at(index1); 151 | vector4f const& v2 = m_transformed_vertices_storage.at(index2); 152 | 153 | bool const v0_clipped{m_transformed_vertices_clip_flags_storage.at(index0)}; 154 | bool const v1_clipped{m_transformed_vertices_clip_flags_storage.at(index1)}; 155 | bool const v2_clipped{m_transformed_vertices_clip_flags_storage.at(index2)}; 156 | 157 | // Clip triangle if at least one vertex is outside of a screen as a workaround 158 | // TODO: clipping 159 | // 160 | if (v0_clipped || v1_clipped || v2_clipped) 161 | { 162 | continue; 163 | } 164 | 165 | delegate.process_geometry_stage_result( 166 | v0, v1, v2, 167 | index0, index1, index2, 168 | shader, 169 | target_texture); 170 | } 171 | } 172 | } 173 | 174 | #endif // LANTERN_GEOMETRY_STAGE_H 175 | -------------------------------------------------------------------------------- /lantern/include/obj_import.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_OBJ_IMPORT_H 2 | #define LANTERN_OBJ_IMPORT_H 3 | 4 | #include 5 | #include "mesh.h" 6 | 7 | namespace lantern 8 | { 9 | /** Wavefront .obj file reader. 10 | * Derived classes can override on_* virtual functions to process definitions 11 | */ 12 | class obj_reader 13 | { 14 | public: 15 | /** Parses .obj file 16 | * @param path Path to obj file 17 | */ 18 | void read(std::string const& path); 19 | 20 | typedef void (obj_reader::*parse_face_function)(std::istringstream&); 21 | 22 | protected: 23 | /** Gets called when parsing starts */ 24 | virtual void on_reading_started(); 25 | 26 | /** Gets called when parsing ends */ 27 | virtual void on_reading_ended(); 28 | 29 | /** Gets called when vertex definition found 30 | * @param x Vertex x-coordinate 31 | * @param y Vertex y-coordinate 32 | * @param z Vertex z-coordinate 33 | */ 34 | virtual void on_vertex_def(float const x, float const y, float const z); 35 | 36 | /** Gets called when texture coordinates definition found 37 | * @param x U-coordinate 38 | * @param y V-coordinate 39 | */ 40 | virtual void on_texcoord_def(float const x, float const y); 41 | 42 | /** Gets called when normal definition found 43 | * @param x Normal x-coordinate 44 | * @param y Normal y-coordinate 45 | * @param z Normal z-coordinate 46 | */ 47 | virtual void on_normal_def(float const x, float const y, float const z); 48 | 49 | /** Gets called when face definition parsing starts */ 50 | virtual void on_face_def_started(); 51 | 52 | /** Gets called when face definition parsing ends */ 53 | virtual void on_face_def_ended(); 54 | 55 | /** Gets called when face indices are ready 56 | * @param vertex_index0 Index of first vertex 57 | * @param vertex_index1 Index of second vertex 58 | * @param vertex_index2 Index of third vertex 59 | */ 60 | virtual void on_face_pos_def(unsigned int vertex_index0, unsigned int vertex_index1, unsigned int vertex_index2); 61 | 62 | /** Gets called when face texcoords are ready 63 | * @param texcoord_index0 Index of first texcoord 64 | * @param texcoord_index1 Index of second texcoord 65 | * @param texcoord_index2 Index of third texcoord 66 | */ 67 | virtual void on_face_texcoord_def(unsigned int texcoord_index0, unsigned int texcoord_index1, unsigned int texcoord_index2); 68 | 69 | /** Gets called when face normals are ready 70 | * @param normal_index0 Index of first normal 71 | * @param normal_index1 Index of second normal 72 | * @param normal_index2 Index of third normal 73 | */ 74 | virtual void on_face_normal_def(unsigned int normal_index0, unsigned int normal_index1, unsigned int normal_index2); 75 | 76 | private: 77 | /** Gets function suitable to parse specified definition 78 | * @param face_def Face definition string 79 | * @returns Function to parse the definition 80 | */ 81 | parse_face_function get_parse_function(std::string face_def); 82 | 83 | /** Parses vertex only face defintiion 84 | * @param stream Face definition string stream 85 | */ 86 | void parse_face_vertex(std::istringstream& stream); 87 | 88 | /** Parses vertex and normal face defintiion 89 | * @param stream Face definition string stream 90 | */ 91 | void parse_face_vertex_normal(std::istringstream& stream); 92 | 93 | /** Parses vertex and texcoord face defintiion 94 | * @param stream Face definition string stream 95 | */ 96 | void parse_face_vertex_texcoord(std::istringstream& stream); 97 | 98 | /** Parses vertex, texcoord and normal face defintiion 99 | * @param stream Face definition string stream 100 | */ 101 | void parse_face_vertex_texcoord_normal(std::istringstream& stream); 102 | }; 103 | 104 | /** Simple importer of mesh object from .obj file 105 | */ 106 | class obj_mesh_importer final : public obj_reader 107 | { 108 | public: 109 | /** Constructs mesh importer with specified params 110 | * @param read_texcoords Indicates if mesh will contain texcoords from .obj file 111 | * @param read_normals Indicates if mesh will contain normals from .obj file 112 | */ 113 | obj_mesh_importer(bool const read_texcoords, bool const read_normals); 114 | 115 | /** Gets result mesh 116 | * @returns Mesh from .obj file 117 | */ 118 | mesh get_mesh() const; 119 | 120 | virtual void on_reading_started() override; 121 | 122 | virtual void on_vertex_def(float const x, float const y, float const z) override; 123 | virtual void on_texcoord_def(float const x, float const y) override; 124 | virtual void on_normal_def(float const x, float const y, float const z) override; 125 | 126 | virtual void on_face_pos_def(unsigned int vertex_index0, unsigned int vertex_index1, unsigned int vertex_index2) override; 127 | virtual void on_face_texcoord_def(unsigned int texcoord_index0, unsigned int texcoord_index1, unsigned int texcoord_index2) override; 128 | virtual void on_face_normal_def(unsigned int normal_index0, unsigned int normal_index1, unsigned int normal_index2) override; 129 | 130 | private: 131 | /** Is object reading texcoords */ 132 | bool const m_read_texcoords; 133 | 134 | /** Is object reading normals */ 135 | bool const m_read_normals; 136 | 137 | /** Mesh vertices */ 138 | std::vector m_vertices; 139 | 140 | /** Mesh indices */ 141 | std::vector m_indices; 142 | 143 | /** Mesh texcoords */ 144 | std::vector m_texcoords; 145 | 146 | /** Mesh texcoords indices */ 147 | std::vector m_texcoords_indices; 148 | 149 | /** Mesh normals */ 150 | std::vector m_normals; 151 | 152 | /** Mesh normals indices */ 153 | std::vector m_normals_indices; 154 | }; 155 | 156 | /** Reads mesh from .obj file. Helper function to avoid creating temporary parser objects 157 | * @param path .obj file path 158 | * @param read_texcoords Indicates if mesh will contain texcoords from .obj file 159 | * @param read_normals Indicates if mesh will contain normals from .obj file 160 | */ 161 | mesh load_mesh_from_obj(std::string const& path, bool const read_texcoords, bool const read_normals); 162 | } 163 | 164 | #endif // LANTERN_OBJ_IMPORT_H 165 | -------------------------------------------------------------------------------- /lantern/src/app.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "app.h" 5 | 6 | using namespace lantern; 7 | 8 | app* app::_instance = nullptr; 9 | 10 | app::app(unsigned int const width, unsigned int const height) 11 | : m_freetype_library{nullptr}, 12 | m_window{nullptr}, 13 | m_sdl_renderer{nullptr}, 14 | m_sdl_target_texture{nullptr}, 15 | m_target_texture{width, height}, 16 | m_target_framerate_delay{0}, 17 | m_last_fps{0}, 18 | #ifdef _WIN32 19 | m_path_separator{'\\'} 20 | #else 21 | m_path_separator{'/'} 22 | #endif 23 | { 24 | if (_instance != nullptr) 25 | { 26 | throw std::runtime_error("Another app isntance has already been created"); 27 | } 28 | 29 | _instance = this; 30 | 31 | // Initialize FreeType library 32 | // 33 | if (FT_Init_FreeType(&m_freetype_library)) 34 | { 35 | throw std::runtime_error("Couldn't initialize FreeType library"); 36 | } 37 | 38 | // Initialize SDL library and according objects 39 | // 40 | 41 | if (SDL_Init(SDL_INIT_VIDEO) < 0) 42 | { 43 | throw std::runtime_error(SDL_GetError()); 44 | } 45 | 46 | int flags = IMG_INIT_PNG; 47 | int inited = IMG_Init(flags); 48 | if ((inited & flags) != flags) 49 | { 50 | throw std::runtime_error(IMG_GetError()); 51 | } 52 | 53 | m_window = SDL_CreateWindow( 54 | "lantern", 55 | SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 56 | width, height, 57 | SDL_WINDOW_SHOWN); 58 | if (m_window == nullptr) 59 | { 60 | throw std::runtime_error(SDL_GetError()); 61 | } 62 | 63 | m_sdl_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED); 64 | if (m_sdl_renderer == nullptr) 65 | { 66 | throw std::runtime_error(SDL_GetError()); 67 | } 68 | 69 | m_sdl_target_texture = SDL_CreateTexture( 70 | m_sdl_renderer, 71 | SDL_PIXELFORMAT_ARGB8888, 72 | SDL_TEXTUREACCESS_STREAMING, 73 | width, height); 74 | if (m_sdl_target_texture == nullptr) 75 | { 76 | throw std::runtime_error(SDL_GetError()); 77 | } 78 | 79 | char* base_path{SDL_GetBasePath()}; 80 | if (base_path != nullptr) 81 | { 82 | m_resources_path = std::string(base_path) + "resources" + m_path_separator; 83 | SDL_free(base_path); 84 | } 85 | else 86 | { 87 | throw std::runtime_error(SDL_GetError()); 88 | } 89 | 90 | set_target_framerate(60); 91 | } 92 | 93 | app::~app() 94 | { 95 | // Clean up FreeType library 96 | // 97 | 98 | if (m_freetype_library != nullptr) 99 | { 100 | FT_Done_FreeType(m_freetype_library); 101 | m_freetype_library = nullptr; 102 | } 103 | 104 | // Clean up SDL library 105 | // 106 | 107 | if (m_sdl_target_texture != nullptr) 108 | { 109 | SDL_DestroyTexture(m_sdl_target_texture); 110 | m_sdl_target_texture = nullptr; 111 | } 112 | 113 | if (m_sdl_renderer != nullptr) 114 | { 115 | SDL_DestroyRenderer(m_sdl_renderer); 116 | m_sdl_renderer = nullptr; 117 | } 118 | 119 | if (m_window != nullptr) 120 | { 121 | SDL_DestroyWindow(m_window); 122 | m_window = nullptr; 123 | } 124 | 125 | IMG_Quit(); 126 | 127 | SDL_Quit(); 128 | } 129 | 130 | int app::start() 131 | { 132 | bool running{true}; 133 | SDL_Event event; 134 | 135 | // Time when last frame was executed 136 | Uint32 last_frame_time{0}; 137 | 138 | Uint32 time_accumulator{0}; 139 | unsigned int frames_accumulator{0}; 140 | 141 | while(running) 142 | { 143 | // Calculate time since last frame 144 | // 145 | Uint32 current_time{SDL_GetTicks()}; 146 | Uint32 delta_since_last_frame{current_time - last_frame_time}; 147 | 148 | // Save last frame time 149 | last_frame_time = SDL_GetTicks(); 150 | 151 | // Process events 152 | // 153 | while (SDL_PollEvent(&event)) 154 | { 155 | if (event.type == SDL_QUIT) 156 | { 157 | running = false; 158 | } 159 | else if (event.type == SDL_KEYDOWN) 160 | { 161 | on_key_down(event.key.keysym); 162 | } 163 | } 164 | 165 | // Clear texture with black 166 | m_target_texture.clear(0); 167 | 168 | // Execute frame 169 | frame(delta_since_last_frame / 1000.0f); 170 | 171 | // Sum up passed time 172 | time_accumulator += delta_since_last_frame; 173 | 174 | // Present texture on a screen 175 | // 176 | SDL_UpdateTexture(m_sdl_target_texture, nullptr, m_target_texture.get_data(), m_target_texture.get_pitch()); 177 | SDL_RenderCopy(m_sdl_renderer, m_sdl_target_texture, nullptr, nullptr); 178 | SDL_RenderPresent(m_sdl_renderer); 179 | 180 | // Sum up passed frames 181 | ++frames_accumulator; 182 | 183 | if (m_target_framerate_delay > 0) 184 | { 185 | // Calculate delay we must have to stick to the target framerate 186 | // 187 | Uint32 time_required_for_frame = SDL_GetTicks() - last_frame_time; 188 | int time_to_wait = m_target_framerate_delay - time_required_for_frame; 189 | 190 | if (time_to_wait > 0) 191 | { 192 | SDL_Delay(static_cast(time_to_wait)); 193 | } 194 | } 195 | 196 | // Drop frames and seconds counters to zero every second 197 | // 198 | if (time_accumulator >= 1000) 199 | { 200 | m_last_fps = frames_accumulator; 201 | time_accumulator = 0; 202 | frames_accumulator = 0; 203 | } 204 | } 205 | 206 | return 0; 207 | } 208 | 209 | FT_Library app::get_freetype_library() const 210 | { 211 | return m_freetype_library; 212 | } 213 | 214 | unsigned int app::get_last_fps() const 215 | { 216 | return m_last_fps; 217 | } 218 | 219 | std::string app::get_resources_path() const 220 | { 221 | return m_resources_path; 222 | } 223 | 224 | char app::get_path_separator() const 225 | { 226 | return m_path_separator; 227 | } 228 | 229 | app const* app::get_instance() 230 | { 231 | return _instance; 232 | } 233 | 234 | texture& app::get_target_texture() 235 | { 236 | return m_target_texture; 237 | } 238 | 239 | renderer& app::get_renderer() 240 | { 241 | return m_renderer; 242 | } 243 | 244 | void app::on_key_down(SDL_Keysym const) 245 | { 246 | // Does not handle any key by default 247 | } 248 | 249 | void app::set_target_framerate(unsigned int const fps) 250 | { 251 | if (fps == 0) 252 | { 253 | m_target_framerate_delay = 0; 254 | } 255 | else 256 | { 257 | m_target_framerate_delay = 1000 / fps; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /lantern/include/vector2.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_VECTOR2_H 2 | #define LANTERN_VECTOR2_H 3 | 4 | #include "math_common.h" 5 | 6 | namespace lantern 7 | { 8 | /** 2-dimensional vector template class */ 9 | template 10 | class vector2 final 11 | { 12 | public: 13 | /** X-coordinate of vector */ 14 | T x; 15 | 16 | /** Y-coordinate of vector */ 17 | T y; 18 | 19 | /** Default constructor (initializes with default values) */ 20 | vector2(); 21 | 22 | /** Constructs vector with specified values 23 | * @param x X-coordinate of a vector 24 | * @param y Y-coordinate of a vector 25 | */ 26 | vector2(T const x, T const y); 27 | 28 | // Operators 29 | // 30 | 31 | bool operator==(vector2 const& v) const; 32 | vector2 operator+(vector2 const& v) const; 33 | vector2& operator+=(vector2 const& v); 34 | vector2 operator-(vector2 const& v) const; 35 | vector2& operator-=(vector2 const& v); 36 | vector2 operator-() const; 37 | vector2 operator*(float const f) const; 38 | vector2& operator*=(float const f); 39 | vector2 operator/(float const f) const; 40 | vector2& operator/=(float const f); 41 | 42 | /** Calculates vector's length 43 | * @returns Length 44 | */ 45 | float length() const; 46 | 47 | /** Calculates vector's squared length, thus avoiding square root operation 48 | * @returns Squared length 49 | */ 50 | float length_sqr() const; 51 | 52 | /** Normalizes vector (changing length to 1) */ 53 | void normalize(); 54 | 55 | /** Gets normalized vector, leaving this vector untouched 56 | * @returns Normalized version of this vector 57 | */ 58 | vector2 normalized() const; 59 | 60 | /** Calculates dot product with v vector 61 | * @returns Dot product 62 | */ 63 | float dot(vector2 const& v) const; 64 | 65 | /* Calculates distance from one point to v 66 | * @param p v point 67 | * @returns Distance between two points */ 68 | float distance_to(vector2 const& v) const; 69 | 70 | /** Calculates angle with v vector 71 | * @returns Angle in radians 72 | */ 73 | float angle_with(vector2 const& v) const; 74 | 75 | /** Calculates projection of this vector on v vector 76 | * @returns Projection vector 77 | */ 78 | vector2 projection_on(vector2 const& v) const; 79 | 80 | /** Calculates perpendicular from this vector to v vector 81 | * @returns Perpendicular 82 | */ 83 | vector2 perpendicular_on(vector2 const& v) const; 84 | }; 85 | 86 | // vector2 implementation 87 | // 88 | 89 | template 90 | inline vector2::vector2() 91 | : x{}, y{} 92 | { 93 | } 94 | 95 | template 96 | inline vector2::vector2(T const x, T const y) 97 | : x{x}, y{y} 98 | { 99 | 100 | } 101 | 102 | template 103 | inline bool vector2::operator==(vector2 const& v) const 104 | { 105 | return equals(this->x, v.x) && equals(this->y, v.y); 106 | } 107 | 108 | template 109 | inline vector2 vector2::operator+(vector2 const& v) const 110 | { 111 | return vector2{this->x + v.x, this->y + v.y}; 112 | } 113 | 114 | template 115 | inline vector2& vector2::operator+=(vector2 const& v) 116 | { 117 | this->x += v.x; 118 | this->y += v.y; 119 | 120 | return *this; 121 | } 122 | 123 | template 124 | inline vector2 vector2::operator-(vector2 const& v) const 125 | { 126 | return vector2{this->x - v.x, this->y - v.y}; 127 | } 128 | 129 | template 130 | inline vector2& vector2::operator-=(vector2 const& v) 131 | { 132 | this->x -= v.x; 133 | this->y -= v.y; 134 | 135 | return *this; 136 | } 137 | 138 | template 139 | inline vector2 vector2::operator-() const 140 | { 141 | return vector2{-this->x, -this->y}; 142 | } 143 | 144 | template 145 | inline vector2 vector2::operator*(float const f) const 146 | { 147 | return vector2{this->x * f, this->y * f}; 148 | } 149 | 150 | template 151 | inline vector2& vector2::operator*=(float const f) 152 | { 153 | this->x *= f; 154 | this->y *= f; 155 | 156 | return *this; 157 | } 158 | 159 | template 160 | inline vector2 vector2::operator/(float const f) const 161 | { 162 | return vector2{this->x / f, this->y / f}; 163 | } 164 | 165 | template 166 | inline vector2& vector2::operator/=(float const f) 167 | { 168 | this->x /= f; 169 | this->y /= f; 170 | 171 | return *this; 172 | } 173 | 174 | template 175 | inline float vector2::length() const 176 | { 177 | return std::sqrt(this->x * this->x + this->y * this->y); 178 | } 179 | 180 | template 181 | inline float vector2::length_sqr() const 182 | { 183 | return this->x * this->x + this->y * this->y; 184 | } 185 | 186 | template 187 | inline void vector2::normalize() 188 | { 189 | float const length_reciprocal{1.0f / this->length()}; 190 | 191 | x *= length_reciprocal; 192 | y *= length_reciprocal; 193 | } 194 | 195 | template 196 | inline vector2 vector2::normalized() const 197 | { 198 | float const length_reciprocal{1.0f / this->length()}; 199 | 200 | return vector2{this->x * length_reciprocal, this->y * length_reciprocal}; 201 | } 202 | 203 | template 204 | inline float vector2::dot(vector2 const& v) const 205 | { 206 | return this->x * v.x + this->y * v.y; 207 | } 208 | 209 | template 210 | inline float vector2::distance_to(vector2 const& v) const 211 | { 212 | float const dist_x{v.x - this->x}; 213 | float const dist_y{v.y - this->y}; 214 | 215 | return std::sqrt(dist_x * dist_x + dist_y * dist_y); 216 | } 217 | 218 | template 219 | inline float vector2::angle_with(vector2 const& v) const 220 | { 221 | return std::acos(this->dot(v) / (this->length() * v.length())); 222 | } 223 | 224 | template 225 | inline vector2 vector2::projection_on(vector2 const& v) const 226 | { 227 | return v.normalized() * (this->dot(v) / v.length()); 228 | } 229 | 230 | template 231 | inline vector2 vector2::perpendicular_on(vector2 const& v) const 232 | { 233 | return (*this) - this->projection_on(v); 234 | } 235 | 236 | // Typedefs 237 | // 238 | 239 | using vector2f = vector2; 240 | using vector2ui = vector2; 241 | } 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /lantern/include/renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_RENDERER_H 2 | #define LANTERN_RENDERER_H 3 | 4 | #include 5 | #include "shader_bind_point_info.h" 6 | #include "geometry_stage.h" 7 | #include "rasterizing_stage.h" 8 | #include "merging_stage.h" 9 | 10 | namespace lantern 11 | { 12 | /** @defgroup Rendering */ 13 | 14 | /** Renderer is the root object for rendering in a texture. 15 | * It manages all the stages and passes data between them. 16 | * There are three stages for now, in order of invoking: 17 | * - \ref geometry_stage is reponsible for transforming geometry. 18 | * - \ref rasterizing_stage is responsible for calculating polygon's coverage. 19 | * - \ref merging_stage is responsible for merging results into a texture 20 | * @ingroup Rendering 21 | */ 22 | class renderer final 23 | { 24 | friend class geometry_stage; 25 | friend class rasterizing_stage; 26 | friend class merging_stage; 27 | 28 | public: 29 | /** Constructs renderer with default settings for each stage */ 30 | renderer(); 31 | 32 | /** Gets geometry stage 33 | * @returns Geometry stage instance 34 | */ 35 | geometry_stage& get_geometry_stage(); 36 | 37 | /** Gets rasterizing stage 38 | * @returns Rasterizing stage instance 39 | */ 40 | rasterizing_stage& get_rasterizing_stage(); 41 | 42 | /** Gets merging stage 43 | * @returns Merging stage instance 44 | */ 45 | merging_stage& get_merging_stage(); 46 | 47 | /** Renders a mesh in a texture using specified shader 48 | * @param mesh Mesh to render 49 | * @param shader Shader to use for rendering 50 | * @param target_texture Texture to render image into 51 | */ 52 | template 53 | void render_mesh(mesh const& mesh, TShader& shader, texture& target_texture); 54 | 55 | private: 56 | /** Passes geometry stage result to the rasterizer stage 57 | * @param vertex0 First triangle vertex 58 | * @param vertex1 Second triangle vertex 59 | * @param vertex2 Third triangle vertex 60 | * @param index0 First vertex attribute index 61 | * @param index1 Second vertex attribute index 62 | * @param index2 Third vertex attribute index 63 | * @param shader Shader to use 64 | * @param target_texture Texture polygon will drawn into 65 | */ 66 | template 67 | void process_geometry_stage_result( 68 | vector4f const& vertex0, vector4f const& vertex1, vector4f const& vertex2, 69 | unsigned int const index0, unsigned int const index1, unsigned int const index2, 70 | TShader& shader, 71 | texture& target_texture); 72 | 73 | /** Passes rasterizing stage result to the merging stage 74 | * @param pixel_coordinates Coordinates of a pixel that should be filled 75 | * @param sample_point Sample point coordinates 76 | * @param shader Shader to use 77 | * @param target_texture Texture polygon will drawn into 78 | */ 79 | template 80 | void process_rasterizing_stage_result( 81 | vector2ui const& pixel_coordinates, vector3f sample_point, TShader& shader, texture& target_texture); 82 | 83 | /** Binds mesh attributes to shader bind points 84 | * @param required_bind_points Shader bind points 85 | * @param available_attributes Mesh attributes 86 | * @param binded_attributes_storage Storage for bindings 87 | */ 88 | template 89 | void bind_attributes( 90 | std::vector> const& required_bind_points, 91 | std::vector> const& available_attributes, 92 | std::vector>& binded_attributes_storage); 93 | 94 | /* Geometry stage instance */ 95 | geometry_stage m_geometry_stage; 96 | 97 | /* Rasterizing stage instance */ 98 | rasterizing_stage m_rasterizing_stage; 99 | 100 | /* Merging stage instance */ 101 | merging_stage m_merging_stage; 102 | 103 | /** Binded attributes */ 104 | binded_mesh_attributes m_binded_mesh_attributes; 105 | }; 106 | 107 | template 108 | inline void renderer::render_mesh(mesh const& mesh, TShader& shader, texture& target_texture) 109 | { 110 | // Prepare bind points for all available types 111 | // 112 | bind_attributes(shader.get_color_bind_points(), mesh.get_color_attributes(), m_binded_mesh_attributes.color_attributes); 113 | bind_attributes(shader.get_float_bind_points(), mesh.get_float_attributes(), m_binded_mesh_attributes.float_attributes); 114 | bind_attributes(shader.get_vector2f_bind_points(), mesh.get_vector2f_attributes(), m_binded_mesh_attributes.vector2f_attributes); 115 | bind_attributes(shader.get_vector3f_bind_points(), mesh.get_vector3f_attributes(), m_binded_mesh_attributes.vector3f_attributes); 116 | 117 | // Pass data to the first stage 118 | // 119 | bool const do_homogeneous_division{m_rasterizing_stage.get_rasterization_algorithm() == rasterization_algorithm_option::homogeneous}; 120 | m_geometry_stage.invoke(mesh, shader, do_homogeneous_division, target_texture, *this); 121 | } 122 | 123 | template 124 | inline void renderer::process_geometry_stage_result( 125 | vector4f const& vertex0, vector4f const& vertex1, vector4f const& vertex2, 126 | unsigned int const index0, unsigned int const index1, unsigned int const index2, 127 | TShader& shader, 128 | texture& target_texture) 129 | { 130 | m_rasterizing_stage.invoke( 131 | vertex0, vertex1, vertex2, 132 | index0, index1, index2, 133 | shader, 134 | m_binded_mesh_attributes, 135 | target_texture, 136 | *this); 137 | } 138 | 139 | template 140 | inline void renderer::process_rasterizing_stage_result( 141 | vector2ui const& pixel_coordinates, vector3f sample_point, TShader& shader, texture& target_texture) 142 | { 143 | m_merging_stage.invoke(pixel_coordinates, sample_point, shader, target_texture, *this); 144 | } 145 | 146 | template 147 | void renderer::bind_attributes( 148 | std::vector> const& required_bind_points, 149 | std::vector> const& available_attributes, 150 | std::vector>& binded_attributes_storage) 151 | { 152 | // Clear the storage 153 | binded_attributes_storage.clear(); 154 | 155 | size_t const bind_points_size{required_bind_points.size()}; 156 | size_t const available_attributes_size{available_attributes.size()}; 157 | 158 | for (size_t i{0}; i < bind_points_size; ++i) 159 | { 160 | shader_bind_point_info const& bind_point_info = required_bind_points.at(i); 161 | 162 | bool binded{false}; 163 | 164 | // Find mesh attribute we can bind to the point 165 | // 166 | for (size_t j{0}; j < available_attributes_size; ++j) 167 | { 168 | mesh_attribute_info const& attr_info = available_attributes.at(j); 169 | if (attr_info.get_id() == bind_point_info.attribute_id) 170 | { 171 | binded_attributes_storage.push_back( 172 | binded_mesh_attribute_info{attr_info, bind_point_info.bind_point}); 173 | 174 | binded = true; 175 | break; 176 | } 177 | } 178 | 179 | if (!binded) 180 | { 181 | throw std::runtime_error("Mesh doesn't contain attribute required by shader"); 182 | } 183 | } 184 | }; 185 | } 186 | 187 | #endif // LANTERN_RENDERER_H 188 | -------------------------------------------------------------------------------- /lantern/include/vector3.h: -------------------------------------------------------------------------------- 1 | #ifndef LANTERN_VECTOR3_H 2 | #define LANTERN_VECTOR3_H 3 | 4 | #include "math_common.h" 5 | 6 | namespace lantern 7 | { 8 | /** 3-dimensional vector template class */ 9 | template 10 | class vector3 final 11 | { 12 | public: 13 | /** X-coordinate of vector */ 14 | T x; 15 | 16 | /** Y-coordinate of vector */ 17 | T y; 18 | 19 | /** Z-coordinate of vector */ 20 | T z; 21 | 22 | /** Default constructor (initializes with default values) */ 23 | vector3(); 24 | 25 | /** Constructs vector with specified values 26 | * @param x X-coordinate of a vector 27 | * @param y Y-coordinate of a vector 28 | * @param z Z-coordinate of a vector 29 | */ 30 | vector3(T const x, T const y, T const z); 31 | 32 | // Operators 33 | // 34 | 35 | bool operator==(vector3 const& v) const; 36 | vector3 operator+(vector3 const& v) const; 37 | vector3& operator+=(vector3 const& v); 38 | vector3 operator-(vector3 const& v) const; 39 | vector3& operator-=(vector3 const& v); 40 | vector3 operator-() const; 41 | vector3 operator*(float const f) const; 42 | vector3& operator*=(float const f); 43 | vector3 operator/(float const f) const; 44 | vector3& operator/=(float const f); 45 | 46 | /** Calculates vector's length 47 | * @returns Length 48 | */ 49 | float length() const; 50 | 51 | /** Calculates vector's squared length, thus avoiding square root operation 52 | * @returns Squared length 53 | */ 54 | float length_sqr() const; 55 | 56 | /** Normalizes vector (changing length to 1) */ 57 | void normalize(); 58 | 59 | /** Gets normalized vector, leaving this vector untouched 60 | * @returns Normalized version of this vector 61 | */ 62 | vector3 normalized() const; 63 | 64 | /** Calculates dot product with another vector 65 | * @returns Dot product 66 | */ 67 | float dot(vector3 const& v) const; 68 | 69 | /** Calculates cross product with another vector 70 | * @returns Cross product 71 | */ 72 | vector3 cross(vector3 const& v) const; 73 | 74 | /** Calculates distance to another point 75 | * @returns Distance 76 | */ 77 | float distance_to(vector3 const& p) const; 78 | 79 | /** Calculates angle with another vector 80 | * @returns Angle in radians 81 | */ 82 | float angle_with(vector3 const& v) const; 83 | 84 | /** Calculates projection of this vector on another vector 85 | * @returns Projection vector 86 | */ 87 | vector3 projection_on(vector3 const& v) const; 88 | 89 | /** Calculates perpendicular from this vector to another vector 90 | * @returns Perpendicular 91 | */ 92 | vector3 perpendicular_on(vector3 const& v) const; 93 | }; 94 | 95 | // vector3 implementation 96 | // 97 | 98 | template 99 | inline vector3::vector3() 100 | : x{}, y{}, z{} 101 | { 102 | 103 | } 104 | 105 | template 106 | inline vector3::vector3(T const x, T const y, T const z) 107 | : x(x), y(y), z(z) 108 | { 109 | 110 | } 111 | 112 | template 113 | inline bool vector3::operator==(vector3 const& v) const 114 | { 115 | return equals(this->x, v.x) && equals(this->y, v.y) && equals(this->z, v.z); 116 | } 117 | 118 | template 119 | inline vector3 vector3::operator+(vector3 const& v) const 120 | { 121 | return vector3{this->x + v.x, this->y + v.y, this->z + v.z}; 122 | } 123 | 124 | template 125 | inline vector3& vector3::operator+=(vector3 const& v) 126 | { 127 | this->x += v.x; 128 | this->y += v.y; 129 | this->z += v.z; 130 | 131 | return *this; 132 | } 133 | 134 | template 135 | inline vector3 vector3::operator-(vector3 const& v) const 136 | { 137 | return vector3{this->x - v.x, this->y - v.y, this->z - v.z}; 138 | } 139 | 140 | template 141 | inline vector3& vector3::operator-=(vector3 const& v) 142 | { 143 | this->x -= v.x; 144 | this->y -= v.y; 145 | this->z -= v.z; 146 | 147 | return *this; 148 | } 149 | 150 | template 151 | inline vector3 vector3::operator-() const 152 | { 153 | return vector3{-this->x, -this->y, -this->z}; 154 | } 155 | 156 | template 157 | inline vector3 vector3::operator*(float const f) const 158 | { 159 | return vector3{this->x * f, this->y * f, this->z * f}; 160 | } 161 | 162 | template 163 | inline vector3& vector3::operator*=(float const f) 164 | { 165 | this->x *= f; 166 | this->y *= f; 167 | this->z *= f; 168 | 169 | return *this; 170 | } 171 | 172 | template 173 | inline vector3 vector3::operator/(float const f) const 174 | { 175 | return vector3{this->x / f, this->y / f, this->z / f}; 176 | } 177 | 178 | template 179 | inline vector3& vector3::operator/=(float const f) 180 | { 181 | this->x /= f; 182 | this->y /= f; 183 | this->z /= f; 184 | 185 | return *this; 186 | } 187 | 188 | template 189 | inline float vector3::length() const 190 | { 191 | return std::sqrt(this->x * this->x + this->y * this->y + this->z * this->z); 192 | } 193 | 194 | template 195 | inline float vector3::length_sqr() const 196 | { 197 | return this->x * this->x + this->y * this->y + this->z * this->z; 198 | } 199 | 200 | template 201 | inline void vector3::normalize() 202 | { 203 | float const length_reciprocal{1.0f / this->length()}; 204 | 205 | this->x *= length_reciprocal; 206 | this->y *= length_reciprocal; 207 | this->z *= length_reciprocal; 208 | } 209 | 210 | template 211 | inline vector3 vector3::normalized() const 212 | { 213 | float const length_reciprocal{1.0f / this->length()}; 214 | 215 | return vector3{this->x * length_reciprocal, this->y * length_reciprocal, this->z * length_reciprocal}; 216 | } 217 | 218 | template 219 | inline float vector3::distance_to(vector3 const& v) const 220 | { 221 | float const dist_x{v.x - this->x}; 222 | float const dist_y{v.y - this->y}; 223 | float const dist_z{v.z - this->z}; 224 | 225 | return std::sqrt(dist_x * dist_x + dist_y * dist_y + dist_z * dist_z); 226 | } 227 | 228 | template 229 | inline float vector3::angle_with(vector3 const& v) const 230 | { 231 | return std::acos(this->dot(v) / (this->length() * v.length())); 232 | } 233 | 234 | template 235 | inline vector3 vector3::projection_on(vector3 const& v) const 236 | { 237 | return v.normalized() * (this->dot(v) / v.length()); 238 | } 239 | 240 | template 241 | inline vector3 vector3::perpendicular_on(vector3 const& v) const 242 | { 243 | return (*this) - this->projection_on(v); 244 | } 245 | 246 | template 247 | inline float vector3::dot(vector3 const& v) const 248 | { 249 | return this->x * v.x + this->y * v.y + this->z * v.z; 250 | } 251 | 252 | template 253 | inline vector3 vector3::cross(vector3 const& v) const 254 | { 255 | return vector3{this->y * v.z - this->z * v.y, this->z * v.x - this->x * v.z, this->x * v.y - this->y * v.x}; 256 | } 257 | 258 | template 259 | inline vector3 operator*(float const f, vector3 const& v) 260 | { 261 | return vector3{v.x * f, v.y * f, v.z * f}; 262 | } 263 | 264 | // Typedefs 265 | // 266 | 267 | using vector3f = vector3; 268 | } 269 | 270 | #endif // LANTERN_VECTOR3_H 271 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | # Project name 4 | project(lantern) 5 | 6 | # Add testing tool 7 | include(CTest) 8 | 9 | # SDL2 look up ============================== 10 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") 11 | find_package(SDL2 REQUIRED) 12 | find_package(SDL2IMAGE REQUIRED) 13 | find_package(Freetype REQUIRED) 14 | # =========================================== 15 | 16 | # Compiler setup ============================ 17 | if (MSVC) 18 | set(COMPILER_FLAGS 19 | "") 20 | 21 | add_definitions(-D_USE_MATH_DEFINES) 22 | add_definitions(-DNOMINMAX) 23 | else() 24 | set(COMPILER_FLAGS 25 | "-std=gnu++0x -Wall -Wno-comment") 26 | endif() 27 | 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}") 29 | # =========================================== 30 | 31 | # Library target ============================ 32 | file(GLOB LANTERN_HEADERS "lantern/include/*.h") 33 | 34 | file(GLOB LANTERN_SOURCES "lantern/src/*.cpp") 35 | 36 | set(LANTERN_INCLUDE_FOLDERS 37 | lantern/include 38 | lantern/include/math 39 | lantern/include/rendering 40 | lantern/include/rendering/shaders 41 | lantern/include/rendering/rasterizing 42 | lantern/include/rendering/geometry 43 | lantern/include/rendering/ui) 44 | 45 | add_library( 46 | lantern STATIC 47 | ${LANTERN_SOURCES} 48 | ${LANTERN_HEADERS}) 49 | 50 | target_include_directories(lantern PUBLIC ${LANTERN_INCLUDE_FOLDERS}) 51 | target_include_directories(lantern PRIVATE ${SDL2_INCLUDE_DIR} ${SDL2IMAGE_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) 52 | 53 | set_target_properties( 54 | lantern PROPERTIES 55 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 56 | # =========================================== 57 | 58 | # Gooogle C++ testing framework target ====== 59 | if(DEFINED ENV{GTEST_ROOT}) 60 | 61 | string(REPLACE "\\" "/" GTEST_SOURCES "$ENV{GTEST_ROOT}/src/gtest-all.cc") 62 | 63 | add_library( 64 | gtest STATIC 65 | ${GTEST_SOURCES}) 66 | 67 | target_include_directories(gtest PRIVATE $ENV{GTEST_ROOT}/include $ENV{GTEST_ROOT}) 68 | 69 | if(UNIX) 70 | target_link_libraries(gtest pthread) 71 | endif() 72 | 73 | endif() 74 | # =========================================== 75 | 76 | # Tests target ============================== 77 | set(TESTS_SOURCES 78 | tests/src/camera.cpp 79 | tests/src/main.cpp 80 | tests/src/matrix3x3.cpp 81 | tests/src/matrix4x4.cpp 82 | tests/src/obj_import.cpp 83 | tests/src/pipeline.cpp 84 | tests/src/vector3.cpp 85 | tests/src/vector4.cpp) 86 | 87 | set(TESTS_HEADERS 88 | tests/include/assert_utils.h) 89 | 90 | add_executable( 91 | tests 92 | ${TESTS_SOURCES} 93 | ${TESTS_HEADERS} 94 | ${LANTERN_HEADERS}) 95 | 96 | target_include_directories(tests PRIVATE lantern/include tests/include) 97 | 98 | if (DEFINED ENV{GTEST_ROOT}) 99 | target_include_directories(tests PRIVATE $ENV{GTEST_ROOT}/include) 100 | endif() 101 | 102 | set_target_properties( 103 | tests PROPERTIES 104 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests") 105 | 106 | target_link_libraries(tests lantern gtest) 107 | target_link_libraries(tests lantern ${SDL2_LIBRARY} ${SDL2IMAGE_LIBRARY}) 108 | 109 | add_custom_command( 110 | TARGET tests POST_BUILD 111 | COMMAND ${CMAKE_COMMAND} -E copy_directory 112 | "${PROJECT_SOURCE_DIR}/tests/resources" 113 | $/resources) 114 | 115 | if (WIN32) 116 | add_custom_command( 117 | TARGET tests 118 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 119 | "${SDL2_DLL}" 120 | $) 121 | 122 | add_custom_command( 123 | TARGET tests 124 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 125 | "${SDL2IMAGE_DLL}" 126 | $) 127 | 128 | add_custom_command( 129 | TARGET tests 130 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 131 | "${SDL2IMAGE_LIBPNG_DLL}" 132 | $) 133 | 134 | add_custom_command( 135 | TARGET tests 136 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 137 | "${SDL2IMAGE_ZLIB_DLL}" 138 | $) 139 | endif() 140 | # =========================================== 141 | 142 | # Empty app target ========================== 143 | add_executable( 144 | empty_app WIN32 145 | examples/empty_app/main.cpp) 146 | 147 | set_target_properties( 148 | empty_app PROPERTIES 149 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/examples/empty_app") 150 | 151 | target_include_directories(empty_app PRIVATE lantern/include) 152 | target_include_directories(empty_app PRIVATE ${SDL2_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) 153 | 154 | target_link_libraries(empty_app lantern ${SDL2_LIBRARY} ${SDL2IMAGE_LIBRARY} ${FREETYPE_LIBRARIES}) 155 | 156 | if (WIN32) 157 | add_custom_command( 158 | TARGET empty_app 159 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 160 | "${SDL2_DLL}" 161 | $) 162 | 163 | add_custom_command( 164 | TARGET empty_app 165 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 166 | "${SDL2IMAGE_DLL}" 167 | $) 168 | 169 | add_custom_command( 170 | TARGET empty_app 171 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 172 | "${SDL2IMAGE_LIBPNG_DLL}" 173 | $) 174 | 175 | add_custom_command( 176 | TARGET empty_app 177 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 178 | "${SDL2IMAGE_ZLIB_DLL}" 179 | $) 180 | endif() 181 | # =========================================== 182 | 183 | # Rasterized triangle app target ====== 184 | add_executable( 185 | rasterized_triangle_app WIN32 186 | examples/rasterized_triangle_app/main.cpp) 187 | 188 | set_target_properties( 189 | rasterized_triangle_app PROPERTIES 190 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/examples/rasterized_triangle_app") 191 | 192 | target_include_directories(rasterized_triangle_app PRIVATE lantern/include) 193 | target_include_directories(rasterized_triangle_app PRIVATE ${SDL2_INCLUDE_DIR} ${SDL2IMAGE_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) 194 | 195 | target_link_libraries(rasterized_triangle_app lantern ${SDL2_LIBRARY} ${SDL2IMAGE_LIBRARY} ${FREETYPE_LIBRARIES}) 196 | 197 | if (WIN32) 198 | add_custom_command( 199 | TARGET rasterized_triangle_app 200 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 201 | "${SDL2_DLL}" 202 | $) 203 | 204 | add_custom_command( 205 | TARGET rasterized_triangle_app 206 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 207 | "${SDL2IMAGE_DLL}" 208 | $) 209 | 210 | add_custom_command( 211 | TARGET rasterized_triangle_app 212 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 213 | "${SDL2IMAGE_LIBPNG_DLL}" 214 | $) 215 | 216 | add_custom_command( 217 | TARGET rasterized_triangle_app 218 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 219 | "${SDL2IMAGE_ZLIB_DLL}" 220 | $) 221 | endif() 222 | 223 | add_custom_command( 224 | TARGET rasterized_triangle_app POST_BUILD 225 | COMMAND ${CMAKE_COMMAND} -E copy_directory 226 | "${PROJECT_SOURCE_DIR}/examples/rasterized_triangle_app/resources" 227 | $/resources) 228 | # =========================================== -------------------------------------------------------------------------------- /cmake/FindSDL2.cmake: -------------------------------------------------------------------------------- 1 | # Simple FindSDL.cmake modification for SDL2 2 | # Also adds SDL2_DLL variable 3 | # 4 | # - Locate SDL2 library 5 | # This module defines 6 | # SDL2_LIBRARY, the name of the library to link against 7 | # SDL2_FOUND, if false, do not try to link to SDL2 8 | # SDL2_INCLUDE_DIR, where to find SDL.h 9 | # SDL2_VERSION_STRING, human-readable string containing the version of SDL2 10 | # SDL2_DLL, where to find SDL2.dll 11 | # 12 | # This module responds to the the flag: 13 | # SDL2_BUILDING_LIBRARY 14 | # If this is defined, then no SDL2main will be linked in because 15 | # only applications need main(). 16 | # Otherwise, it is assumed you are building an application and this 17 | # module will attempt to locate and set the the proper link flags 18 | # as part of the returned SDL_LIBRARY variable. 19 | # 20 | # Don't forget to include SDLmain.h and SDLmain.m your project for the 21 | # OS X framework based version. (Other versions link to -lSDLmain which 22 | # this module will try to find on your behalf.) Also for OS X, this 23 | # module will automatically add the -framework Cocoa on your behalf. 24 | # 25 | # 26 | # Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration 27 | # and no SDL2_LIBRARY, it means CMake did not find your SDL2 library 28 | # (SDL2.dll, libsdl2.so, SDL2.framework, etc). 29 | # Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. 30 | # Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value 31 | # as appropriate. These values are used to generate the final SDL2_LIBRARY 32 | # variable, but when these values are unset, SDL2_LIBRARY does not get created. 33 | # 34 | # 35 | # $SDL2DIR is an environment variable that would 36 | # correspond to the ./configure --prefix=$SDL2DIR 37 | # used in building SDL2. 38 | # 39 | # On OSX, this will prefer the Framework version (if found) over others. 40 | # People will have to manually change the cache values of 41 | # SDL2_LIBRARY to override this selection or set the CMake environment 42 | # CMAKE_INCLUDE_PATH to modify the search paths. 43 | # 44 | # Note that the header path has changed from SDL2/SDL.h to just SDL.h 45 | # This needed to change because "proper" SDL convention 46 | # is #include "SDL.h", not . This is done for portability 47 | # reasons because not all systems place things in SDL2/ (see FreeBSD). 48 | 49 | #============================================================================= 50 | # Copyright 2003-2009 Kitware, Inc. 51 | # Copyright 2012 Benjamin Eikel 52 | # 53 | # Distributed under the OSI-approved BSD License (the "License"); 54 | # see accompanying file Copyright.txt for details. 55 | # 56 | # This software is distributed WITHOUT ANY WARRANTY; without even the 57 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 58 | # See the License for more information. 59 | #============================================================================= 60 | 61 | find_path(SDL2_INCLUDE_DIR 62 | NAMES 63 | SDL.h 64 | HINTS 65 | ENV SDL2DIR 66 | PATH_SUFFIXES 67 | include/ 68 | include/SDL2 69 | ) 70 | 71 | find_library(SDL2_LIBRARY_TEMP 72 | NAMES 73 | SDL2 74 | HINTS 75 | ENV SDL2DIR 76 | PATH_SUFFIXES 77 | lib 78 | lib/win32 79 | lib/x86 80 | ) 81 | 82 | find_file(SDL2_DLL 83 | NAMES 84 | SDL2.dll 85 | HINTS 86 | ENV SDL2DIR 87 | PATH_SUFFIXES 88 | bin 89 | lib/win32 90 | lib/x86 91 | ) 92 | 93 | if(NOT SDL2_BUILDING_LIBRARY) 94 | if(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") 95 | # Non-OS X framework versions expect you to also dynamically link to 96 | # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms 97 | # seem to provide SDL2main for compatibility even though they don't 98 | # necessarily need it. 99 | find_library(SDL2MAIN_LIBRARY 100 | NAMES 101 | SDL2main 102 | HINTS 103 | ENV SDL2DIR 104 | PATH_SUFFIXES 105 | lib 106 | lib/win32 107 | lib/x86 108 | ) 109 | endif() 110 | endif() 111 | 112 | # SDL2 may require threads on your system. 113 | # The Apple build may not need an explicit flag because one of the 114 | # frameworks may already provide it. 115 | # But for non-OSX systems, I will use the CMake Threads package. 116 | if(NOT APPLE) 117 | find_package(Threads) 118 | endif() 119 | 120 | # MinGW needs an additional library, mwindows 121 | # It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows 122 | # (Actually on second look, I think it only needs one of the m* libraries.) 123 | if(MINGW) 124 | set(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") 125 | endif() 126 | 127 | if(SDL2_LIBRARY_TEMP) 128 | # For SDLmain 129 | if(SDL2MAIN_LIBRARY AND NOT SDL2_BUILDING_LIBRARY) 130 | list(FIND SDL2_LIBRARY_TEMP "${SDL2MAIN_LIBRARY}" _SDL2_MAIN_INDEX) 131 | if(_SDL2_MAIN_INDEX EQUAL -1) 132 | set(SDL2_LIBRARY_TEMP "${SDL2MAIN_LIBRARY}" ${SDL2_LIBRARY_TEMP}) 133 | endif() 134 | unset(_SDL2_MAIN_INDEX) 135 | endif() 136 | 137 | # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. 138 | # CMake doesn't display the -framework Cocoa string in the UI even 139 | # though it actually is there if I modify a pre-used variable. 140 | # I think it has something to do with the CACHE STRING. 141 | # So I use a temporary variable until the end so I can set the 142 | # "real" variable in one-shot. 143 | if(APPLE) 144 | set(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") 145 | endif() 146 | 147 | # For threads, as mentioned Apple doesn't need this. 148 | # In fact, there seems to be a problem if I used the Threads package 149 | # and try using this line, so I'm just skipping it entirely for OS X. 150 | if(NOT APPLE) 151 | set(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) 152 | endif() 153 | 154 | # For MinGW library 155 | if(MINGW) 156 | set(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) 157 | endif() 158 | 159 | # Set the final string here so the GUI reflects the final state. 160 | set(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") 161 | # Set the temp variable to INTERNAL so it is not seen in the CMake GUI 162 | set(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") 163 | endif() 164 | 165 | if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h") 166 | file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") 167 | file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") 168 | file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") 169 | string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") 170 | string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") 171 | string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") 172 | set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}) 173 | unset(SDL2_VERSION_MAJOR_LINE) 174 | unset(SDL2_VERSION_MINOR_LINE) 175 | unset(SDL2_VERSION_PATCH_LINE) 176 | unset(SDL2_VERSION_MAJOR) 177 | unset(SDL2_VERSION_MINOR) 178 | unset(SDL2_VERSION_PATCH) 179 | endif() 180 | 181 | include(FindPackageHandleStandardArgs) 182 | 183 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 184 | REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR 185 | VERSION_VAR SDL2_VERSION_STRING) 186 | -------------------------------------------------------------------------------- /examples/rasterized_triangle_app/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "app.h" 3 | #include "obj_import.h" 4 | #include "camera.h" 5 | #include "color_shader.h" 6 | #include "texture_shader.h" 7 | #include "ui_label.h" 8 | 9 | using namespace lantern; 10 | 11 | enum class shader_option 12 | { 13 | color, 14 | texture 15 | }; 16 | 17 | /** Draws simple triangle with interpolated color attribute */ 18 | class rasterized_color_triangle_app : public app 19 | { 20 | public: 21 | rasterized_color_triangle_app(unsigned int const width, unsigned int const height); 22 | 23 | protected: 24 | void frame(float const delta_since_last_frame) override; 25 | void on_key_down(SDL_Keysym const key) override; 26 | 27 | private: 28 | /** Updates view-model-projection matrix and gives it to the shader */ 29 | void update_shader_mvp(); 30 | 31 | vector3f const m_triangle_position; 32 | vector3f const m_triangle_rotation; 33 | mesh m_triangle_mesh; 34 | 35 | camera m_camera; 36 | 37 | color_shader m_color_shader; 38 | texture_shader m_texture_shader; 39 | shader_option m_shader_option; 40 | 41 | texture m_texture; 42 | 43 | font m_ui_font; 44 | ui_label m_fps_label; 45 | ui_label m_controls_description_label; 46 | ui_label m_modes_description_label; 47 | 48 | unsigned int m_last_fps; 49 | }; 50 | 51 | rasterized_color_triangle_app::rasterized_color_triangle_app(unsigned int const width, unsigned int const height) 52 | : app(width, height), 53 | m_triangle_position{0.0f, 0.0f, 1.5f}, 54 | m_triangle_rotation{vector3f{0.0f, 0.0f, 0.0f}}, 55 | m_triangle_mesh{load_mesh_from_obj(get_resources_path() + "triangle.obj", false, false)}, 56 | m_camera{ 57 | vector3f{0.0f, 0.0f, 0.0f}, 58 | vector3f{0.0f, 0.0f, 1.0f}, 59 | vector3f{0.0f, 1.0f, 0.0f}, 60 | static_cast(M_PI) / 2.0f, 61 | static_cast(height) / static_cast(width), 62 | 0.01f, 63 | 20.0f}, 64 | m_shader_option{shader_option::color}, 65 | m_texture{texture::load_from_file(get_resources_path() + "chess.png")}, 66 | m_ui_font{get_resources_path() + "Ubuntu-L.ttf", 15}, 67 | m_fps_label{m_ui_font, get_target_texture()}, 68 | m_controls_description_label{m_ui_font, get_target_texture()}, 69 | m_modes_description_label{m_ui_font, get_target_texture()} 70 | 71 | { 72 | // Update model-view-projection matrix for the first time 73 | update_shader_mvp(); 74 | 75 | std::vector const indices{0, 1, 2}; 76 | 77 | // Add color attribute to triangle mesh 78 | // 79 | std::vector const colors{color::GREEN.with_alpha(0.0f), color::RED.with_alpha(0.0f), color::BLUE.with_alpha(1.0f)}; 80 | mesh_attribute_info const color_info{COLOR_ATTR_ID, colors, indices, attribute_interpolation_option::linear}; 81 | m_triangle_mesh.get_color_attributes().push_back(color_info); 82 | 83 | // Add uv attribute to triangle mesh 84 | // 85 | std::vector uvs{vector2f{0.5f, 0.0f}, vector2f{0.0f, 1.0f}, vector2f{1.0f, 1.0f}}; 86 | mesh_attribute_info uv_info{TEXCOORD_ATTR_ID, uvs, indices, attribute_interpolation_option::perspective_correct}; 87 | m_triangle_mesh.get_vector2f_attributes().push_back(uv_info); 88 | 89 | // Setup texture shader 90 | // 91 | m_texture_shader.set_texture(&m_texture); 92 | 93 | // Setup UI labels 94 | // 95 | 96 | m_fps_label.set_position(vector2f{-0.95f, 0.9f}); 97 | m_controls_description_label.set_position(vector2f{-0.95f, -0.85f}); 98 | m_modes_description_label.set_position(vector2f{-0.95f, -0.95f}); 99 | 100 | m_controls_description_label.set_text("Controls: WASD. R or F to move along Y-axis"); 101 | m_modes_description_label.set_text("Shaders: 1 - color shader, 2 - texture mapping"); 102 | 103 | m_controls_description_label.set_color(color{0.5f, 0.5f, 0.5f}); 104 | m_modes_description_label.set_color(color{0.5f, 0.5f, 0.5f}); 105 | 106 | // This enables alpha blending 107 | // 108 | // get_pipeline().get_merger().set_alpha_blending_enabled(true); 109 | }; 110 | 111 | void rasterized_color_triangle_app::frame(float const delta_since_last_frame) 112 | { 113 | if (m_last_fps != get_last_fps()) 114 | { 115 | m_last_fps = get_last_fps(); 116 | 117 | std::ostringstream output_stream; 118 | output_stream << "Framerate is: " << m_last_fps; 119 | m_fps_label.set_text(output_stream.str()); 120 | } 121 | 122 | // Draw the triangle 123 | // 124 | 125 | if (m_shader_option == shader_option::color) 126 | { 127 | get_renderer().render_mesh(m_triangle_mesh, m_color_shader, get_target_texture()); 128 | } 129 | else if (m_shader_option == shader_option::texture) 130 | { 131 | get_renderer().render_mesh(m_triangle_mesh, m_texture_shader, get_target_texture()); 132 | } 133 | 134 | m_fps_label.draw(get_renderer(), get_target_texture()); 135 | m_controls_description_label.draw(get_renderer(), get_target_texture()); 136 | m_modes_description_label.draw(get_renderer(), get_target_texture()); 137 | } 138 | 139 | void rasterized_color_triangle_app::on_key_down(SDL_Keysym const key) 140 | { 141 | app::on_key_down(key); 142 | 143 | float const moving_speed{0.01f}; 144 | float const rotation_speed{0.05f}; 145 | 146 | if (key.sym == SDLK_a) 147 | { 148 | m_camera.move_left(moving_speed); 149 | } 150 | else if (key.sym == SDLK_d) 151 | { 152 | m_camera.move_right(moving_speed); 153 | } 154 | else if (key.sym == SDLK_w) 155 | { 156 | m_camera.move_forward(moving_speed); 157 | } 158 | else if (key.sym == SDLK_s) 159 | { 160 | m_camera.move_backward(moving_speed); 161 | } 162 | else if (key.sym == SDLK_r) 163 | { 164 | m_camera.move_up(moving_speed); 165 | } 166 | else if (key.sym == SDLK_f) 167 | { 168 | m_camera.move_down(moving_speed); 169 | } 170 | else if (key.sym == SDLK_q) 171 | { 172 | m_camera.yaw(-rotation_speed); 173 | } 174 | else if (key.sym == SDLK_e) 175 | { 176 | m_camera.yaw(rotation_speed); 177 | } 178 | else if (key.sym == SDLK_1) 179 | { 180 | m_shader_option = shader_option::color; 181 | } 182 | else if (key.sym == SDLK_2) 183 | { 184 | m_shader_option = shader_option::texture; 185 | } 186 | 187 | // Update model-view-projection according to camera changes 188 | update_shader_mvp(); 189 | } 190 | 191 | void rasterized_color_triangle_app::update_shader_mvp() 192 | { 193 | matrix4x4f const local_to_world_transform{ 194 | matrix4x4f::rotation_around_x_axis(m_triangle_rotation.x) * 195 | matrix4x4f::rotation_around_y_axis(m_triangle_rotation.y) * 196 | matrix4x4f::rotation_around_z_axis(m_triangle_rotation.z) * 197 | matrix4x4f::translation(m_triangle_position.x, m_triangle_position.y, m_triangle_position.z)}; 198 | 199 | matrix4x4f const camera_rotation{ 200 | m_camera.get_right().x, m_camera.get_up().x, m_camera.get_forward().x, 0.0f, 201 | m_camera.get_right().y, m_camera.get_up().y, m_camera.get_forward().y, 0.0f, 202 | m_camera.get_right().z, m_camera.get_up().z, m_camera.get_forward().z, 0.0f, 203 | 0.0f, 0.0f, 0.0f, 1.0f}; 204 | 205 | matrix4x4f const camera_translation{ 206 | 1.0f, 0.0f, 0.0f, 0.0f, 207 | 0.0f, 1.0f, 0.0f, 0.0f, 208 | 0.0f, 0.0f, 1.0f, 0.0f, 209 | -m_camera.get_position().x, -m_camera.get_position().y, -m_camera.get_position().z, 1.0f}; 210 | 211 | matrix4x4f const world_to_camera_transform{camera_translation * camera_rotation}; 212 | 213 | matrix4x4f const camera_to_clip_transform{ 214 | matrix4x4f::clip_space( 215 | m_camera.get_horizontal_fov(), 216 | m_camera.get_vertical_fov(), 217 | m_camera.get_near_plane_z(), 218 | m_camera.get_far_plane_z())}; 219 | 220 | matrix4x4f const local_to_clip_transform{ 221 | local_to_world_transform * world_to_camera_transform * camera_to_clip_transform}; 222 | 223 | if (m_shader_option == shader_option::color) 224 | { 225 | m_color_shader.set_mvp_matrix(local_to_clip_transform); 226 | } 227 | else 228 | { 229 | m_texture_shader.set_mvp_matrix(local_to_clip_transform); 230 | } 231 | } 232 | 233 | int main(int argc, char* argv[]) 234 | { 235 | return rasterized_color_triangle_app{640, 480}.start(); 236 | } 237 | 238 | -------------------------------------------------------------------------------- /tests/src/pipeline.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "assert_utils.h" 3 | #include "pipeline.h" 4 | 5 | using namespace lantern; 6 | 7 | class test_shader final 8 | { 9 | public: 10 | test_shader(color const& c, texture const* target_texture) 11 | : m_color(c), m_target_texture{target_texture} 12 | { 13 | 14 | } 15 | 16 | std::vector> get_color_bind_points() 17 | { 18 | return std::vector>{}; 19 | } 20 | 21 | std::vector> get_float_bind_points() 22 | { 23 | return std::vector>{}; 24 | } 25 | std::vector> get_vector2f_bind_points() 26 | { 27 | return std::vector>{}; 28 | } 29 | 30 | std::vector> get_vector3f_bind_points() 31 | { 32 | return std::vector>{}; 33 | } 34 | 35 | vector4f process_vertex(vector4f const& vertex) 36 | { 37 | return vertex; 38 | } 39 | 40 | color process_pixel(vector2ui const& pixel) 41 | { 42 | color const current_color = m_target_texture->get_pixel_color(pixel); 43 | 44 | if (current_color != color::BLACK) 45 | { 46 | return color::RED; 47 | } 48 | else 49 | { 50 | return m_color; 51 | } 52 | } 53 | 54 | private: 55 | color const m_color; 56 | texture const* m_target_texture; 57 | }; 58 | 59 | static void assert_pixel_centers_are_lit_no_ambiguities(pipeline& p) 60 | { 61 | // Rasterizes triangle that doesn't have any pixel centers on edge 62 | // Picture: rasterization_no_ambiguities_test_case.png 63 | // 64 | 65 | texture texture{5, 5}; 66 | test_shader shader_white{color::WHITE, &texture}; 67 | 68 | std::vector triangle_vertices{ 69 | vector3f{0.05f, 0.9f, 0.0f}, vector3f{-0.9f, 0.5f, 0.0f}, vector3f{0.15f, -0.8f, 0.0f}}; 70 | std::vector const triangle_indices{0, 1, 2}; 71 | mesh triangle_mesh{triangle_vertices, triangle_indices}; 72 | texture.clear(0); 73 | p.draw(triangle_mesh, shader_white, texture); 74 | assert_pixels_two_colors( 75 | texture, 76 | std::vector{ 77 | vector2ui{0, 1}, 78 | vector2ui{1, 1}, vector2ui{1, 2}, 79 | vector2ui{2, 0}, vector2ui{2, 1}, vector2ui{2, 2}, vector2ui{2, 3}}, 80 | color::WHITE, 81 | color::BLACK); 82 | } 83 | 84 | static void assert_pixel_centers_are_lit_top_left_rule(pipeline& p) 85 | { 86 | // Rasterizes triangle with pixel centers at the edges to check top-left filling rule 87 | // 88 | 89 | texture texture{5, 5}; 90 | test_shader shader_white{color::WHITE, &texture}; 91 | 92 | std::vector const triangle_indices{0, 1, 2}; 93 | 94 | // Picture: rasterization_top_left_rule_test_case_1.png 95 | // 96 | std::vector triangle_vertices{ 97 | vector3f{-0.8f, 0.8f, 0.0f}, vector3f{-0.8f, -0.8f, 0.0f}, vector3f{0.8f, 0.8f, 0.0f}}; 98 | mesh triangle_mesh_1{triangle_vertices, triangle_indices}; 99 | texture.clear(0); 100 | p.draw(triangle_mesh_1, shader_white, texture); 101 | assert_pixels_two_colors( 102 | texture, 103 | std::vector{ 104 | vector2ui{0, 0}, vector2ui{0, 1}, vector2ui{0, 2}, vector2ui{0, 3}, 105 | vector2ui{1, 0}, vector2ui{1, 1}, vector2ui{1, 2}, 106 | vector2ui{2, 0}, vector2ui{2, 1}, 107 | vector2ui{3, 0}}, 108 | color::WHITE, 109 | color::BLACK); 110 | 111 | // Picture: rasterization_top_left_rule_test_case_2.png 112 | // 113 | triangle_vertices = std::vector{ 114 | vector3f{0.8f, 0.8f, 0.0f}, vector3f{-0.8f, 0.8f, 0.0f}, vector3f{0.8f, -0.8f, 0.0f}}; 115 | mesh triangle_mesh_2{triangle_vertices, triangle_indices}; 116 | texture.clear(0); 117 | p.draw(triangle_mesh_2, shader_white, texture); 118 | assert_pixels_two_colors( 119 | texture, 120 | std::vector{ 121 | vector2ui{0, 0}, 122 | vector2ui{1, 0}, vector2ui{1, 1}, 123 | vector2ui{2, 0}, vector2ui{2, 1}, vector2ui{2, 2}, 124 | vector2ui{3, 0}, vector2ui{3, 1}, vector2ui{3, 2}, vector2ui{3, 3}}, 125 | color::WHITE, 126 | color::BLACK); 127 | 128 | // Picture: rasterization_top_left_rule_test_case_3.png 129 | // 130 | triangle_vertices = std::vector{ 131 | vector3f{0.8f, 0.8f, 0.0f}, vector3f{-0.8f, -0.8f, 0.0f}, vector3f{0.8f, -0.8f, 0.0f}}; 132 | mesh triangle_mesh_3{triangle_vertices, triangle_indices}; 133 | texture.clear(0); 134 | p.draw(triangle_mesh_3, shader_white, texture); 135 | assert_pixels_two_colors( 136 | texture, 137 | std::vector{ 138 | vector2ui{1, 3}, 139 | vector2ui{2, 2}, vector2ui{2, 3}, 140 | vector2ui{3, 1}, vector2ui{3, 2}, vector2ui{3, 3}}, 141 | color::WHITE, 142 | color::BLACK); 143 | 144 | // Picture: rasterization_top_left_rule_test_case_4.png 145 | // 146 | triangle_vertices = std::vector{ 147 | vector3f{-0.8f, 0.8f, 0.0f}, vector3f{-0.8f, -0.8f, 0.0f}, vector3f{0.8f, -0.8f, 0.0f}}; 148 | mesh triangle_mesh_4{triangle_vertices, triangle_indices}; 149 | texture.clear(0); 150 | p.draw(triangle_mesh_4, shader_white, texture); 151 | assert_pixels_two_colors( 152 | texture, 153 | std::vector{ 154 | vector2ui{0, 1}, vector2ui{0, 2}, vector2ui{0, 3}, 155 | vector2ui{1, 2}, vector2ui{1, 3}, 156 | vector2ui{2, 3}}, 157 | color::WHITE, 158 | color::BLACK); 159 | 160 | // Picture: rasterization_top_left_rule_test_case_5.png 161 | // 162 | std::vector rect_vertices{ 163 | vector3 < float > {0.8f, 0.8f, 0.0f}, vector3 < float > {-0.8f, 0.8f, 0.0f}, vector3 < float > {-0.8f, -0.8f, 0.0f}, vector3 < float > {0.8f, -0.8f, 0.0f}, vector3f{0.0f, 0.0f, 0.0f}}; 164 | std::vector rect_indices{ 165 | 0, 1, 4, 166 | 1, 2, 4, 167 | 2, 3, 4, 168 | 3, 0, 4}; 169 | mesh rect_mesh{rect_vertices, rect_indices}; 170 | texture.clear(0); 171 | p.draw(rect_mesh, shader_white, texture); 172 | assert_pixels_two_colors( 173 | texture, 174 | std::vector{ 175 | vector2ui{0, 0}, vector2ui{0, 1}, vector2ui{0, 2}, vector2ui{0, 3}, 176 | vector2ui{1, 0}, vector2ui{1, 1}, vector2ui{1, 2}, vector2ui{1, 3}, 177 | vector2ui{2, 0}, vector2ui{2, 1}, vector2ui{2, 2}, vector2ui{2, 3}, 178 | vector2ui{3, 0}, vector2ui{3, 1}, vector2ui{3, 2}, vector2ui{3, 3}}, 179 | color::WHITE, 180 | color::BLACK); 181 | 182 | // Picture: rasterization_top_left_rule_test_case_6.png 183 | // 184 | 185 | test_shader shader_blue{color::BLUE, &texture}; 186 | 187 | texture.clear(0); 188 | p.draw(triangle_mesh_4, shader_white, texture); 189 | p.draw(triangle_mesh_2, shader_blue, texture); 190 | assert_pixels_colors( 191 | texture, 192 | std::vector{ 193 | vector2ui{0, 0}, 194 | vector2ui{1, 0}, vector2ui{1, 1}, 195 | vector2ui{2, 0}, vector2ui{2, 1}, vector2ui{2, 2}, 196 | vector2ui{3, 0}, vector2ui{3, 1}, vector2ui{3, 2}, vector2ui{3, 3}}, 197 | color::BLUE); 198 | assert_pixels_colors( 199 | texture, 200 | std::vector{ 201 | vector2ui{0, 1}, vector2ui{0, 2}, vector2ui{0, 3}, 202 | vector2ui{1, 2}, vector2ui{1, 3}, 203 | vector2ui{2, 3}}, 204 | color::WHITE); 205 | } 206 | 207 | TEST(pipeline, mesh_rasterization_traversal_aabb) 208 | { 209 | pipeline p; 210 | 211 | p.set_face_culling(face_culling_option::counter_clockwise); 212 | p.set_fill_mode(fill_mode_option::solid); 213 | 214 | p.set_rasterization_algorithm(rasterization_algorithm_option::traversal_aabb); 215 | assert_pixel_centers_are_lit_no_ambiguities(p); 216 | assert_pixel_centers_are_lit_top_left_rule(p); 217 | } 218 | 219 | TEST(pipeline, mesh_rasterization_traversal_backtracking) 220 | { 221 | pipeline p; 222 | 223 | p.set_face_culling(face_culling_option::counter_clockwise); 224 | p.set_fill_mode(fill_mode_option::solid); 225 | 226 | p.set_rasterization_algorithm(rasterization_algorithm_option::traversal_backtracking); 227 | assert_pixel_centers_are_lit_no_ambiguities(p); 228 | assert_pixel_centers_are_lit_top_left_rule(p); 229 | } 230 | 231 | TEST(pipeline, mesh_rasterization_traversal_zigzag) 232 | { 233 | pipeline p; 234 | 235 | p.set_face_culling(face_culling_option::counter_clockwise); 236 | p.set_fill_mode(fill_mode_option::solid); 237 | 238 | p.set_rasterization_algorithm(rasterization_algorithm_option::traversal_zigzag); 239 | assert_pixel_centers_are_lit_no_ambiguities(p); 240 | assert_pixel_centers_are_lit_top_left_rule(p); 241 | } 242 | 243 | TEST(pipeline, mesh_rasterization_inversed_slope) 244 | { 245 | pipeline p; 246 | 247 | p.set_face_culling(face_culling_option::counter_clockwise); 248 | p.set_fill_mode(fill_mode_option::solid); 249 | 250 | p.set_rasterization_algorithm(rasterization_algorithm_option::inversed_slope); 251 | assert_pixel_centers_are_lit_no_ambiguities(p); 252 | assert_pixel_centers_are_lit_top_left_rule(p); 253 | } 254 | 255 | TEST(pipeline, mesh_rasterization_homogeneous) 256 | { 257 | pipeline p; 258 | 259 | p.set_face_culling(face_culling_option::counter_clockwise); 260 | p.set_fill_mode(fill_mode_option::solid); 261 | 262 | p.set_rasterization_algorithm(rasterization_algorithm_option::homogeneous); 263 | assert_pixel_centers_are_lit_no_ambiguities(p); 264 | assert_pixel_centers_are_lit_top_left_rule(p); 265 | } -------------------------------------------------------------------------------- /lantern/src/obj_import.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "obj_import.h" 5 | 6 | using namespace lantern; 7 | 8 | void obj_reader::read(std::string const& path) 9 | { 10 | // Open file 11 | // 12 | std::ifstream file_stream{path}; 13 | if (!file_stream.is_open()) 14 | { 15 | throw std::runtime_error("Could not open file: " + path); 16 | } 17 | 18 | on_reading_started(); 19 | 20 | // Read line-by-line 21 | // 22 | std::string line; 23 | parse_face_function parse_face{nullptr}; 24 | 25 | while(std::getline(file_stream, line)) 26 | { 27 | // Skip comments 28 | // 29 | if (line[0] == '#') 30 | { 31 | continue; 32 | } 33 | 34 | // Get line definition type 35 | // 36 | 37 | std::istringstream line_stream{line}; 38 | 39 | std::string definition_type; 40 | line_stream >> definition_type; 41 | 42 | // Process each definition 43 | // 44 | 45 | if (definition_type == "v") 46 | { 47 | float x, y, z; 48 | line_stream >> x >> y >> z; 49 | on_vertex_def(x, y, z); 50 | } 51 | else if (definition_type == "vn") 52 | { 53 | float x, y, z; 54 | line_stream >> x >> y >> z; 55 | on_normal_def(x, y, z); 56 | } 57 | else if (definition_type == "vt") 58 | { 59 | float x, y; 60 | line_stream >> x >> y; 61 | on_texcoord_def(x, y); 62 | } 63 | else if (definition_type == "f") 64 | { 65 | if (parse_face == nullptr) 66 | { 67 | parse_face = get_parse_function(line); 68 | } 69 | 70 | on_face_def_started(); 71 | 72 | (this->*parse_face)(line_stream); 73 | 74 | on_face_def_ended(); 75 | } 76 | 77 | file_stream >> std::ws; 78 | } 79 | 80 | on_reading_ended(); 81 | } 82 | 83 | obj_reader::parse_face_function obj_reader::get_parse_function(std::string face_definition) 84 | { 85 | std::istringstream stream{face_definition}; 86 | 87 | char c; 88 | unsigned int ui; 89 | 90 | // Skip "f" 91 | stream >> c; 92 | 93 | // First number is vertex index anyway, skip it together with preceding whitespaces 94 | stream >> std::skipws >> ui >> std::noskipws; 95 | 96 | stream >> c; 97 | if (c == ' ') 98 | { 99 | // Whitespace immediately after vertex index => no uv, no normals 100 | return &obj_reader::parse_face_vertex; 101 | } 102 | 103 | // Otherwise, it's '/' 104 | 105 | stream >> ui; 106 | if (stream.fail()) 107 | { 108 | // Definition us "v//n" 109 | return &obj_reader::parse_face_vertex_normal; 110 | } 111 | 112 | stream >> c; 113 | if (c == '/') 114 | { 115 | // Definition is "v/uv/n" 116 | return &obj_reader::parse_face_vertex_texcoord_normal; 117 | } 118 | 119 | // Definition is "v/uv" 120 | return &obj_reader::parse_face_vertex_texcoord; 121 | } 122 | 123 | void obj_reader::parse_face_vertex(std::istringstream& stream) 124 | { 125 | unsigned int index0, index1, index2; 126 | stream >> index0 >> index1 >> index2; 127 | 128 | on_face_pos_def(index0 - 1, index1 - 1, index2 - 1); 129 | } 130 | 131 | void obj_reader::parse_face_vertex_normal(std::istringstream& stream) 132 | { 133 | unsigned int vertex_index0, vertex_index1, vertex_index2; 134 | unsigned int normal_index0, normal_index1, normal_index2; 135 | 136 | stream >> vertex_index0; 137 | stream.ignore(2); 138 | stream >> normal_index0; 139 | 140 | stream >> vertex_index1; 141 | stream.ignore(2); 142 | stream >> normal_index1; 143 | 144 | stream >> vertex_index2; 145 | stream.ignore(2); 146 | stream >> normal_index2; 147 | 148 | on_face_pos_def(vertex_index0 - 1, vertex_index1 - 1, vertex_index2 - 1); 149 | on_face_normal_def(normal_index0 - 1, normal_index1 - 1, normal_index2 - 1); 150 | } 151 | 152 | void obj_reader::parse_face_vertex_texcoord(std::istringstream& stream) 153 | { 154 | unsigned int vertex_index0, vertex_index1, vertex_index2; 155 | unsigned int texcoord_index0, texcoord_index1, texcoord_index2; 156 | 157 | stream >> vertex_index0; 158 | stream.ignore(); 159 | stream >> texcoord_index0; 160 | 161 | stream >> vertex_index1; 162 | stream.ignore(); 163 | stream >> texcoord_index1; 164 | 165 | stream >> vertex_index2; 166 | stream.ignore(); 167 | stream >> texcoord_index2; 168 | 169 | on_face_pos_def(vertex_index0 - 1, vertex_index1 - 1, vertex_index2 - 1); 170 | on_face_texcoord_def(texcoord_index0 - 1, texcoord_index1 - 1, texcoord_index2 - 1); 171 | } 172 | 173 | void obj_reader::parse_face_vertex_texcoord_normal(std::istringstream& stream) 174 | { 175 | unsigned int vertex_index0, vertex_index1, vertex_index2; 176 | unsigned int normal_index0, normal_index1, normal_index2; 177 | unsigned int texcoord_index0, texcoord_index1, texcoord_index2; 178 | 179 | stream >> vertex_index0; 180 | stream.ignore(); 181 | stream >> texcoord_index0; 182 | stream.ignore(); 183 | stream >> normal_index0; 184 | 185 | stream >> vertex_index1; 186 | stream.ignore(); 187 | stream >> texcoord_index1; 188 | stream.ignore(); 189 | stream >> normal_index1; 190 | 191 | stream >> vertex_index2; 192 | stream.ignore(); 193 | stream >> texcoord_index2; 194 | stream.ignore(); 195 | stream >> normal_index2; 196 | 197 | on_face_pos_def(vertex_index0 - 1, vertex_index1 - 1, vertex_index2 - 1); 198 | on_face_texcoord_def(texcoord_index0 - 1, texcoord_index1 - 1, texcoord_index2 - 1); 199 | on_face_normal_def(normal_index0 - 1, normal_index1 - 1, normal_index2 - 1); 200 | } 201 | 202 | void obj_reader::on_reading_started() 203 | { 204 | 205 | } 206 | 207 | void obj_reader::on_reading_ended() 208 | { 209 | 210 | } 211 | 212 | void obj_reader::on_vertex_def(float const x, float const y, float const z) 213 | { 214 | 215 | } 216 | 217 | void obj_reader::on_texcoord_def(float const x, float const y) 218 | { 219 | 220 | } 221 | 222 | void obj_reader::on_normal_def(float const x, float const y, float const z) 223 | { 224 | 225 | } 226 | 227 | void obj_reader::on_face_def_started() 228 | { 229 | 230 | } 231 | 232 | void obj_reader::on_face_def_ended() 233 | { 234 | 235 | } 236 | 237 | void obj_reader::on_face_pos_def(unsigned int vertex_index0, unsigned int vertex_index1, unsigned int vertex_index2) 238 | { 239 | 240 | } 241 | 242 | void obj_reader::on_face_texcoord_def(unsigned int texcoord_index0, unsigned int texcoord_index1, unsigned int texcoord_index2) 243 | { 244 | 245 | } 246 | 247 | void obj_reader::on_face_normal_def(unsigned int normal_index0, unsigned int normal_index1, unsigned int normal_index2) 248 | { 249 | 250 | } 251 | 252 | // obj_mesh_importer 253 | // 254 | 255 | obj_mesh_importer::obj_mesh_importer(bool const read_texcoords, bool const read_normals) 256 | : m_read_texcoords(read_texcoords), m_read_normals(read_normals) 257 | { 258 | 259 | } 260 | 261 | mesh obj_mesh_importer::get_mesh() const 262 | { 263 | mesh m{m_vertices, m_indices}; 264 | 265 | if (m_read_texcoords) 266 | { 267 | mesh_attribute_info texcoords_info{TEXCOORD_ATTR_ID, m_texcoords, m_texcoords_indices, attribute_interpolation_option::perspective_correct}; 268 | m.get_vector2f_attributes().push_back(texcoords_info); 269 | }; 270 | 271 | if (m_read_normals) 272 | { 273 | mesh_attribute_info normals_info{NORMAL_ATTR_ID, m_normals, m_normals_indices, attribute_interpolation_option::linear}; 274 | m.get_vector3f_attributes().push_back(normals_info); 275 | } 276 | 277 | return m; 278 | } 279 | 280 | void obj_mesh_importer::on_reading_started() 281 | { 282 | m_vertices.clear(); 283 | m_indices.clear(); 284 | 285 | m_texcoords.clear(); 286 | m_texcoords_indices.clear(); 287 | 288 | m_normals.clear(); 289 | m_normals_indices.clear(); 290 | } 291 | 292 | void obj_mesh_importer::on_vertex_def(float const x, float const y, float const z) 293 | { 294 | m_vertices.push_back(vector3f{x, y, z}); 295 | } 296 | 297 | void obj_mesh_importer::on_texcoord_def(float const x, float const y) 298 | { 299 | if (m_read_texcoords) 300 | { 301 | m_texcoords.push_back(vector2f{x, y}); 302 | } 303 | } 304 | 305 | void obj_mesh_importer::on_normal_def(float const x, float const y, float const z) 306 | { 307 | if (m_read_normals) 308 | { 309 | m_normals.push_back(vector3f{x, y, z}); 310 | } 311 | } 312 | 313 | void obj_mesh_importer::on_face_pos_def(unsigned int vertex_index0, unsigned int vertex_index1, unsigned int vertex_index2) 314 | { 315 | m_indices.push_back(vertex_index0); 316 | m_indices.push_back(vertex_index1); 317 | m_indices.push_back(vertex_index2); 318 | } 319 | 320 | void obj_mesh_importer::on_face_texcoord_def(unsigned int texcoord_index0, unsigned int texcoord_index1, unsigned int texcoord_index2) 321 | { 322 | if (m_read_texcoords) 323 | { 324 | m_texcoords_indices.push_back(texcoord_index0); 325 | m_texcoords_indices.push_back(texcoord_index1); 326 | m_texcoords_indices.push_back(texcoord_index2); 327 | } 328 | } 329 | 330 | void obj_mesh_importer::on_face_normal_def(unsigned int normal_index0, unsigned int normal_index1, unsigned int normal_index2) 331 | { 332 | if (m_read_normals) 333 | { 334 | m_normals_indices.push_back(normal_index0); 335 | m_normals_indices.push_back(normal_index1); 336 | m_normals_indices.push_back(normal_index2); 337 | } 338 | } 339 | 340 | // Helper functions 341 | // 342 | 343 | mesh lantern::load_mesh_from_obj(std::string const& path, bool const read_texcoords, bool const read_normals) 344 | { 345 | obj_mesh_importer importer{read_texcoords, read_normals}; 346 | importer.read(path); 347 | 348 | return importer.get_mesh(); 349 | } 350 | --------------------------------------------------------------------------------