diff --git a/.gitmodules b/.gitmodules
new file mode 100755
index 0000000..eeed3b5
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "libraries/include/stb"]
+ path = libraries/include/stb
+ url = https://github.com/nothings/stb
+[submodule "libraries/include/glm"]
+ path = libraries/include/glm
+ url = https://github.com/g-truc/glm
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..c882834
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+z3d
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..00465ab
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..374fd27
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,91 @@
+{
+ "files.associations": {
+ "*.ipp": "cpp",
+ "*.tcc": "cpp",
+ "cctype": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "csignal": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "cstring": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "any": "cpp",
+ "array": "cpp",
+ "atomic": "cpp",
+ "strstream": "cpp",
+ "bit": "cpp",
+ "bitset": "cpp",
+ "cfenv": "cpp",
+ "charconv": "cpp",
+ "chrono": "cpp",
+ "cinttypes": "cpp",
+ "codecvt": "cpp",
+ "compare": "cpp",
+ "complex": "cpp",
+ "concepts": "cpp",
+ "condition_variable": "cpp",
+ "coroutine": "cpp",
+ "cstdint": "cpp",
+ "deque": "cpp",
+ "forward_list": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "set": "cpp",
+ "string": "cpp",
+ "unordered_map": "cpp",
+ "unordered_set": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "regex": "cpp",
+ "source_location": "cpp",
+ "string_view": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "utility": "cpp",
+ "hash_map": "cpp",
+ "hash_set": "cpp",
+ "format": "cpp",
+ "fstream": "cpp",
+ "future": "cpp",
+ "initializer_list": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "mutex": "cpp",
+ "new": "cpp",
+ "numbers": "cpp",
+ "ostream": "cpp",
+ "ranges": "cpp",
+ "semaphore": "cpp",
+ "shared_mutex": "cpp",
+ "span": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "stdfloat": "cpp",
+ "stop_token": "cpp",
+ "streambuf": "cpp",
+ "text_encoding": "cpp",
+ "thread": "cpp",
+ "typeindex": "cpp",
+ "typeinfo": "cpp",
+ "valarray": "cpp",
+ "variant": "cpp"
+ }
+}
diff --git a/data/images/logo.png b/data/images/logo.png
new file mode 100644
index 0000000..c3f292d
Binary files /dev/null and b/data/images/logo.png differ
diff --git a/data/images/spinner.png b/data/images/spinner.png
new file mode 100644
index 0000000..c58b5f0
Binary files /dev/null and b/data/images/spinner.png differ
diff --git a/include/assets/data_loaders/generic/generic_3dtk_loader.hpp b/include/assets/data_loaders/generic/generic_3dtk_loader.hpp
new file mode 100644
index 0000000..9b457d1
--- /dev/null
+++ b/include/assets/data_loaders/generic/generic_3dtk_loader.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "glm/mat4x4.hpp"
+#include "util/result.hpp"
+#include "util/string_list.hpp"
+#include "assets/prefetch_queue.hpp"
+
+#include "assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp"
+#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp"
+#include "assets/prefetch_lookup.hpp"
+
+
+
+template
+struct generic_3dtk_loader
+{
+ [[nodiscard]] static std::error_code prefetch(
+ const ztu::string_list& filenames,
+ prefetch_queue& queue
+ );
+
+ [[nodiscard]] static std::error_code load(
+ dynamic_point_cloud_buffer& buffer,
+ const file_dir_list& paths,
+ prefetch_lookup& asset_lookup,
+ dynamic_point_cloud_store& store,
+ bool pedantic = false
+ );
+
+protected:
+
+ [[nodiscard]] static ztu::result parse_index(
+ std::string_view filename
+ );
+
+ ztu::result> analyze_component_format(
+ std::string_view line
+ );
+
+ void transform_point_cloud(
+ std::span points,
+ const glm::mat4& pose
+ );
+
+private:
+ std::error_code read_point_file(
+ const std::filesystem::path& filename,
+ dynamic_point_cloud_buffer& point_cloud
+ );
+};
+
+#define INCLUDE_GENERIC_3DTK_LOADER_IMPLEMENTATION
+#include "assets/data_loaders/generic/generic_3dtk_loader.ipp"
+#undef INCLUDE_GENERIC_3DTK_LOADER_IMPLEMENTATION
diff --git a/include/geometry/aabb.hpp b/include/geometry/aabb.hpp
new file mode 100755
index 0000000..de3ee64
--- /dev/null
+++ b/include/geometry/aabb.hpp
@@ -0,0 +1,91 @@
+#pragma once
+
+#include "glm/glm.hpp"
+
+#include
+#include
+#include
+#include
+
+struct aabb
+{
+ using vector_type = glm::vec3;
+ using scalar_type = vector_type::value_type;
+ using index_type = vector_type::length_type;
+
+ static constexpr auto default_min = std::numeric_limits::max();
+ static constexpr auto default_max =-std::numeric_limits::max();
+
+ vector_type min { default_min, default_min, default_min };
+ vector_type max { default_max, default_max, default_max };
+
+ [[nodiscard]] vector_type size() const
+ {
+ return max - min;
+ }
+
+ [[nodiscard]] vector_type center() const
+ {
+ return min + 0.5f * size();
+ }
+
+ [[nodiscard]] vector_type closest_point_inside(const vector_type& point) const {
+ return {
+ std::clamp(point.x, min.x, max.x),
+ std::clamp(point.y, min.y, max.y),
+ std::clamp(point.z, min.z, max.z)
+ };
+ }
+
+ aabb& add_aabb(const aabb& other)
+ {
+ for (index_type i{}; i != min.length(); ++i)
+ {
+ min[i] = std::min(min[i], other.min[i]);
+ max[i] = std::max(max[i], other.max[i]);
+ }
+ return *this;
+ }
+
+ aabb& add_point(const auto& point)
+ {
+ for (index_type i{}; i != min.length(); ++i)
+ {
+ min[i] = std::min(min[i], point[i]);
+ max[i] = std::max(max[i], point[i]);
+ }
+ return *this;
+ }
+
+ template
+ aabb& add_points(std::span points)
+ {
+ for (const auto& point : points)
+ {
+ add_point(point);
+ }
+ return *this;
+ }
+
+ aabb& join(const aabb& other)
+ {
+ min = glm::min(min, other.min);
+ max = glm::max(max, other.max);
+ return *this;
+ }
+
+ [[nodiscard]] aabb transformed(const glm::mat4x4& matrix) const
+ {
+ const auto vertices = std::array{
+ vector_type{ matrix * glm::vec4{ min.x, min.y, min.z, 1 } },
+ vector_type{ matrix * glm::vec4{ min.x, min.y, max.z, 1 } },
+ vector_type{ matrix * glm::vec4{ min.x, max.y, min.z, 1 } },
+ vector_type{ matrix * glm::vec4{ min.x, max.y, max.z, 1 } },
+ vector_type{ matrix * glm::vec4{ max.x, min.y, min.z, 1 } },
+ vector_type{ matrix * glm::vec4{ max.x, min.y, max.z, 1 } },
+ vector_type{ matrix * glm::vec4{ max.x, max.y, min.z, 1 } },
+ vector_type{ matrix * glm::vec4{ max.x, max.y, max.z, 1 } }
+ };
+ return aabb{}.add_points(vertices);
+ }
+};
diff --git a/include/geometry/normal_estimation.hpp b/include/geometry/normal_estimation.hpp
new file mode 100644
index 0000000..4d8bbb0
--- /dev/null
+++ b/include/geometry/normal_estimation.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "util/uix.hpp"
+#include "assets/components/mesh_vertex_components.hpp"
+
+void estimate_normals(
+ std::span vertices,
+ std::span> triangles,
+ std::vector& normals
+);
\ No newline at end of file
diff --git a/include/opengl/data/shader_program_data.hpp b/include/opengl/data/shader_program_data.hpp
new file mode 100755
index 0000000..e94f79d
--- /dev/null
+++ b/include/opengl/data/shader_program_data.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+
+#include "GL/glew.h"
+#include
+
+#include "opengl/handles/shader_handle.hpp"
+#include "opengl/handles/shader_program_handle.hpp"
+
+namespace zgl
+{
+class shader_program_data
+{
+private:
+ explicit shader_program_data(GLuint program_id);
+
+public:
+
+ shader_program_data() = default;
+
+ [[nodiscard]] static std::error_code build_from(
+ const shader_handle& vertex_shader,
+ const shader_handle& geometry_shader,
+ const shader_handle& fragment_shader,
+ shader_program_data& data
+ );
+
+ shader_program_data(const shader_program_data& other) = delete;
+ shader_program_data& operator=(const shader_program_data& other) = delete;
+
+ shader_program_data(shader_program_data&& other) noexcept ;
+ shader_program_data& operator=(shader_program_data&& other) noexcept;
+
+ [[nodiscard]] shader_program_handle handle() const;
+
+ ~shader_program_data();
+
+private:
+ shader_program_handle m_handle{};
+};
+}
+
+#define INCLUDE_SHADER_PROGRAM_DATA_IMPLEMENTATION
+#include "opengl/data/shader_program_data.ipp"
+#undef INCLUDE_SHADER_PROGRAM_DATA_IMPLEMENTATION
diff --git a/include/opengl/error.hpp b/include/opengl/error.hpp
new file mode 100644
index 0000000..8b2b620
--- /dev/null
+++ b/include/opengl/error.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include
+#include "GL/glew.h"
+
+namespace zgl
+{
+namespace error
+{
+
+enum class codes : GLenum {
+ ok = GL_NO_ERROR,
+ invalid_enum = GL_INVALID_ENUM,
+ invalid_value = GL_INVALID_VALUE,
+ invalid_operation = GL_INVALID_OPERATION,
+ invalid_framebuffer_operation = GL_INVALID_FRAMEBUFFER_OPERATION,
+ out_of_memory = GL_OUT_OF_MEMORY,
+ stack_overflow = GL_STACK_OVERFLOW,
+ stack_underflow = GL_STACK_UNDERFLOW,
+ context_lost = GL_CONTEXT_LOST
+};
+
+struct category : std::error_category
+{
+ [[nodiscard]] const char* name() const noexcept override
+ {
+ return "opengl";
+ }
+
+ [[nodiscard]] std::string message(int ev) const override
+ {
+ switch (static_cast(ev)) {
+ using enum codes;
+ case ok:
+ return "No error has been recorded.";
+ case invalid_enum:
+ return "An unacceptable value is specified for an enumerated argument.";
+ case invalid_value:
+ return "A numeric argument is out of range.";
+ case invalid_operation:
+ return "The specified operation is not allowed in the current state.";
+ case invalid_framebuffer_operation:
+ return "The framebuffer object is not complete.";
+ case out_of_memory:
+ return "There is not enough memory left to execute the command.";
+ case stack_overflow:
+ return "An attempt has been made to perform an operation that would cause an internal stack to underflow.";
+ case stack_underflow:
+ return "An attempt has been made to perform an operation that would cause an internal stack to overflow.";
+ case context_lost:
+ return "The OpenGL context has been lost.";
+ default:
+ return "";
+ }
+ }
+};
+
+} // namespace error
+
+[[nodiscard]] inline std::error_category& error_category() noexcept
+{
+ static error::category category;
+ return category;
+}
+
+[[nodiscard]] inline std::error_code make_error_code(const GLenum e) noexcept
+{
+ return { static_cast(e), error_category() };
+}
+
+} // namespace zgl
+
+template<>
+struct std::is_error_code_enum : std::true_type {};
diff --git a/include/opengl/handles/alpha_handle.hpp b/include/opengl/handles/alpha_handle.hpp
new file mode 100644
index 0000000..0a49f9e
--- /dev/null
+++ b/include/opengl/handles/alpha_handle.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace zgl
+{
+
+using alpha_handle = float;
+
+} // namespace zgl
diff --git a/include/opengl/handles/material_handle.hpp b/include/opengl/handles/material_handle.hpp
new file mode 100644
index 0000000..ce420df
--- /dev/null
+++ b/include/opengl/handles/material_handle.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include
+#include "opengl/handles/texture_handle.hpp"
+#include "opengl/handles/surface_properties_handle.hpp"
+#include "opengl/handles/alpha_handle.hpp"
+
+namespace zgl
+{
+struct material_handle
+{
+ std::optional texture{ std::nullopt };
+ std::optional surface_properties{ std::nullopt };
+ std::optional alpha{ std::nullopt };
+};
+}
diff --git a/include/opengl/handles/matrix_handles.hpp b/include/opengl/handles/matrix_handles.hpp
new file mode 100644
index 0000000..ba646fb
--- /dev/null
+++ b/include/opengl/handles/matrix_handles.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "glm/glm.hpp"
+
+namespace zgl
+{
+using model_matrix_handle = glm::mat4x4;
+
+using view_matrix_handle = glm::mat4x4;
+
+using projection_matrix_handle = glm::mat4x4;
+}
\ No newline at end of file
diff --git a/include/opengl/handles/mesh_handle.hpp b/include/opengl/handles/mesh_handle.hpp
new file mode 100755
index 0000000..8f5088c
--- /dev/null
+++ b/include/opengl/handles/mesh_handle.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "GL/glew.h"
+#include "util/uix.hpp"
+
+namespace zgl
+{
+struct mesh_handle
+{
+ inline void bind() const;
+ inline static void unbind();
+
+ GLuint vao_id{ 0 };
+ GLsizei index_count{ 0 };
+};
+}
+
+#define INCLUDE_MESH_INSTANCE_IMPLEMENTATION
+#include "opengl/handles/mesh_handle.ipp"
+#undef INCLUDE_MESH_INSTANCE_IMPLEMENTATION
\ No newline at end of file
diff --git a/include/opengl/handles/point_cloud_handle.hpp b/include/opengl/handles/point_cloud_handle.hpp
new file mode 100755
index 0000000..49dd4af
--- /dev/null
+++ b/include/opengl/handles/point_cloud_handle.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "GL/glew.h"
+#include "util/uix.hpp"
+
+namespace zgl
+{
+struct point_cloud_handle
+{
+ inline void bind() const;
+ inline static void unbind();
+
+ GLuint vao_id{ 0 };
+ GLsizei point_count{ 0 };
+};
+}
+
+#define INCLUDE_POINT_CLOUD_INSTANCE_IMPLEMENTATION
+#include "opengl/handles/point_cloud_handle.ipp"
+#undef INCLUDE_POINT_CLOUD_INSTANCE_IMPLEMENTATION
\ No newline at end of file
diff --git a/include/opengl/handles/shader_program_handle.hpp b/include/opengl/handles/shader_program_handle.hpp
new file mode 100644
index 0000000..59fa5fc
--- /dev/null
+++ b/include/opengl/handles/shader_program_handle.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "GL/glew.h"
+#include "opengl/shader_program_variable.hpp"
+
+#include "opengl/shader_program_variable.hpp"
+#include "util/uix.hpp"
+#include
+
+namespace zgl
+{
+struct shader_program_handle
+{
+ using attribute_support_type = ztu::u32;
+ using uniform_support_type = ztu::u32;
+
+ inline void bind() const;
+ static void unbind();
+
+ template
+ void set_uniform(const T& value) const;
+
+ [[nodiscard]] attribute_support_type check_attribute_support(std::span attributes) const;
+
+ [[nodiscard]] uniform_support_type check_uniform_support(std::span uniforms) const;
+
+ GLuint program_id{ 0 };
+};
+}
+
+#define INCLUDE_GL_SHADER_PROGRAM_INSTANCE_IMPLEMENTATION
+#include "opengl/handles/shader_program_handle.ipp"
+#undef INCLUDE_GL_SHADER_PROGRAM_INSTANCE_IMPLEMENTATION
diff --git a/include/opengl/handles/surface_properties_handle.hpp b/include/opengl/handles/surface_properties_handle.hpp
new file mode 100644
index 0000000..8780217
--- /dev/null
+++ b/include/opengl/handles/surface_properties_handle.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "glm/glm.hpp"
+
+namespace zgl
+{
+struct surface_properties_handle
+{
+ glm::mat3 filters{
+ 0.1986f, 0.0000f, 0.0000f,
+ 0.5922f, 0.0166f, 0.0000f,
+ 0.5974f, 0.2084f, 0.2084f
+ };
+ float shininess{ 100.2237f };
+};
+}
diff --git a/include/opengl/handles/texture_handle.hpp b/include/opengl/handles/texture_handle.hpp
new file mode 100644
index 0000000..084efc2
--- /dev/null
+++ b/include/opengl/handles/texture_handle.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "GL/glew.h"
+
+namespace zgl {
+struct texture_handle
+{
+ inline void bind() const;
+ inline static void unbind();
+
+ GLuint texture_id{ 0 };
+};
+}
+
+#define INCLUDE_TEXTURE_INSTANCE_IMPLEMENTATION
+#include "opengl/handles/texture_handle.ipp"
+#undef INCLUDE_TEXTURE_INSTANCE_IMPLEMENTATION
diff --git a/include/opengl/shader_program_variable.hpp b/include/opengl/shader_program_variable.hpp
new file mode 100644
index 0000000..63def71
--- /dev/null
+++ b/include/opengl/shader_program_variable.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "GL/glew.h"
+
+namespace zgl
+{
+struct shader_program_variable
+{
+ struct info_type
+ {
+ GLenum type;
+ GLint location;
+ } info;
+ const char* name;
+};
+}
diff --git a/include/opengl/type_utils.hpp b/include/opengl/type_utils.hpp
new file mode 100755
index 0000000..9956830
--- /dev/null
+++ b/include/opengl/type_utils.hpp
@@ -0,0 +1,71 @@
+#pragma once
+
+#include
+#include "GL/glew.h"
+#include "util/uix.hpp"
+
+namespace zgl::type_utils
+{
+constexpr bool is_valid_type(GLenum type) {
+ switch (type) {
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ case GL_INT:
+ case GL_UNSIGNED_INT:
+ case GL_FLOAT:
+ case GL_DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template
+consteval GLenum to_gl_type() {
+ if constexpr (std::same_as) {
+ return GL_BYTE;
+ } else if constexpr (std::same_as) {
+ return GL_UNSIGNED_BYTE;
+ } else if constexpr (std::same_as) {
+ return GL_SHORT;
+ } else if constexpr (std::same_as) {
+ return GL_UNSIGNED_SHORT;
+ } else if constexpr (std::same_as) {
+ return GL_INT;
+ } else if constexpr (std::same_as) {
+ return GL_UNSIGNED_INT;
+ } else if constexpr (std::same_as) {
+ return GL_FLOAT;
+ } else if constexpr (std::same_as) {
+ return GL_DOUBLE;
+ } else {
+ T::___unknown_type;
+ return GL_INVALID_ENUM;
+ }
+}
+
+constexpr GLsizei size_of(GLenum type) {
+ switch (type) {
+ case GL_BYTE:
+ return sizeof(ztu::i8);
+ case GL_UNSIGNED_BYTE:
+ return sizeof(ztu::u8);
+ case GL_SHORT:
+ return sizeof(ztu::i16);
+ case GL_UNSIGNED_SHORT:
+ return sizeof(ztu::u16);
+ case GL_INT:
+ return sizeof(ztu::i32);
+ case GL_UNSIGNED_INT:
+ return sizeof(ztu::u32);
+ case GL_FLOAT:
+ return sizeof(float);
+ case GL_DOUBLE:
+ return sizeof(double);
+ default:
+ return 0;
+ }
+}
+} // namespace zgl::type_utils
diff --git a/include/scene/camera_view.hpp b/include/scene/camera_view.hpp
new file mode 100644
index 0000000..c31b5ab
--- /dev/null
+++ b/include/scene/camera_view.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "glm/glm.hpp"
+#include "glm/gtc/matrix_transform.hpp"
+#include
+
+struct camera_view
+{
+ glm::vec3 position;
+ glm::vec3 front;
+ glm::vec3 right;
+ glm::vec3 up;
+ float aspect_ratio;
+ float fov;
+
+ [[nodiscard]] glm::mat4 create_view_matrix() const {
+ return glm::lookAt(position, position + front, up);
+ }
+
+ [[nodiscard]] glm::mat4 create_projection_matrix() const {
+ return glm::perspective(
+ fov,
+ aspect_ratio,
+ 0.1f, 1000.0f
+ );
+ }
+};
diff --git a/libraries/include/glm b/libraries/include/glm
new file mode 160000
index 0000000..33b4a62
--- /dev/null
+++ b/libraries/include/glm
@@ -0,0 +1 @@
+Subproject commit 33b4a621a697a305bc3a7610d290677b96beb181
diff --git a/shaders/mesh/fragment_face.glsl b/shaders/mesh/fragment_face.glsl
new file mode 100644
index 0000000..4d7fedc
--- /dev/null
+++ b/shaders/mesh/fragment_face.glsl
@@ -0,0 +1,64 @@
+#ifdef TEXTURE
+#ifdef U_COLOR
+#error Texture and color attribute are mutually exclusive.
+#endif
+#endif
+
+out vec4 pixel_color;
+
+#ifdef TEXTURE
+layout (location = 3) uniform sampler2D tex;
+layout (location = 2) in vec2 frag_tex_coord;
+#endif
+
+#ifdef U_COLOR
+layout (location = 3) uniform vec4 color;
+#endif
+
+#ifdef LIGHTING
+layout (location = 4) uniform vec3 view_pos;
+layout (location = 5) uniform vec3 point_light_direction;
+layout (location = 6) uniform vec3 point_light_color;
+layout (location = 7) uniform vec3 ambient_light_color;
+layout (location = 8) uniform vec3 ambient_filter;
+layout (location = 9) uniform vec3 diffuse_filter;
+layout (location = 10) uniform vec3 specular_filter;
+layout (location = 11) uniform float shininess;
+layout (location = 0) in vec3 frag_position;
+layout (location = 1) in vec3 frag_normal;
+#endif
+
+#ifdef U_ALPHA
+layout (location = 12) uniform float alpha;
+#endif
+
+void main() {
+
+ pixel_color = vec4(1);
+
+#ifdef TEXTURE
+ pixel_color *= texture(tex, frag_tex_coord);
+#endif
+
+#ifdef U_COLOR
+ pixel_color *= color;
+#endif
+
+#ifdef U_ALPHA
+ pixel_color.w *= alpha;
+#endif
+
+#ifdef LIGHTING
+ vec3 ambient = pixel_color.rgb * ambient_filter * ambient_light_color;
+
+ float point_light_alignment = max(dot(frag_normal, point_light_direction), 0.0);
+ vec3 diffuse = pixel_color.rgb * diffuse_filter * point_light_color * point_light_alignment;
+
+ vec3 reflection_dir = reflect(-point_light_direction, frag_normal);
+ vec3 view_dir = normalize(frag_position - view_pos);
+ float specular_strength = pow(max(dot(view_dir, reflection_dir), 0.0), shininess);
+ vec3 specular = specular_filter * point_light_color * specular_strength;
+
+ pixel_color.rgb *= ambient + diffuse + specular;
+#endif
+}
diff --git a/shaders/mesh/fragment_point.glsl b/shaders/mesh/fragment_point.glsl
new file mode 100644
index 0000000..fc4fbfd
--- /dev/null
+++ b/shaders/mesh/fragment_point.glsl
@@ -0,0 +1,7 @@
+layout (location = 0) in vec4 frag_color;
+
+out vec4 pixel_color;
+
+void main() {
+ pixel_color = frag_color;
+}
diff --git a/shaders/mesh/vertex_face.glsl b/shaders/mesh/vertex_face.glsl
new file mode 100644
index 0000000..cbae90f
--- /dev/null
+++ b/shaders/mesh/vertex_face.glsl
@@ -0,0 +1,27 @@
+layout (location = 0) uniform mat4 mvp_matrix;
+layout (location = 0) in vec3 vertex_position;
+
+#ifdef LIGHTING
+layout (location = 1) uniform mat4 model_matrix;
+layout (location = 1) in vec3 vertex_normal;
+layout (location = 0) out vec3 frag_position;
+layout (location = 1) out vec3 frag_normal;
+#endif
+
+#ifdef TEXTURE
+layout (location = 2) in vec2 vertex_tex_coord;
+layout (location = 2) out vec2 frag_tex_coord;
+#endif
+
+void main() {
+ gl_Position = mvp_matrix * vec4(vertex_position, 1.0);
+
+#ifdef LIGHTING
+ frag_position = (model_matrix * vec4(vertex_position, 1.0)).xyz;
+ frag_normal = normalize(mat3(model_matrix) * vertex_normal);
+#endif
+
+#ifdef TEXTURE
+ frag_tex_coord = vertex_tex_coord;
+#endif
+}
diff --git a/shaders/mesh/vertex_point.glsl b/shaders/mesh/vertex_point.glsl
new file mode 100644
index 0000000..ce9a662
--- /dev/null
+++ b/shaders/mesh/vertex_point.glsl
@@ -0,0 +1,59 @@
+layout (location = 0) uniform mat4 mvp_matrix;
+layout (location = 2) uniform float point_size;
+layout (location = 0) in vec3 vertex_position;
+layout (location = 0) out vec4 frag_color;
+
+#ifdef TEXTURE
+layout (location = 3) uniform sampler2D tex;
+layout (location = 2) in vec2 vertex_tex_coord;
+#endif
+
+#ifdef LIGHTING
+layout (location = 1) uniform mat4 model_matrix;
+layout (location = 4) uniform vec3 view_pos;
+layout (location = 5) uniform vec3 point_light_direction;
+layout (location = 6) uniform vec3 point_light_color;
+layout (location = 7) uniform vec3 ambient_light_color;
+layout (location = 8) uniform vec3 ambient_filter;
+layout (location = 9) uniform vec3 diffuse_filter;
+layout (location = 10) uniform vec3 specular_filter;
+layout (location = 11) uniform float shininess;
+layout (location = 1) in vec3 vertex_normal;
+#endif
+
+#ifdef U_ALPHA
+layout (location = 12) uniform float alpha;
+#endif
+
+void main() {
+
+ gl_Position = mvp_matrix * vec4(vertex_position, 1.0);
+ gl_PointSize = point_size / gl_Position.w;
+
+ frag_color = vec4(1);
+
+#ifdef TEXTURE
+ frag_color *= texture(tex, vertex_tex_coord);
+#endif
+
+#ifdef U_ALPHA
+ frag_color.w *= alpha;
+#endif
+
+#ifdef LIGHTING
+ vec3 position = (model_matrix * vec4(vertex_position, 1.0)).xyz;
+ vec3 normal = normalize(mat3(model_matrix) * vertex_normal);
+
+ vec3 ambient = frag_color.rgb * ambient_filter * ambient_light_color;
+
+ float point_light_alignment = max(dot(normal, point_light_direction), 0.0);
+ vec3 diffuse = frag_color.rgb * diffuse_filter * point_light_color * point_light_alignment;
+
+ vec3 reflection_dir = reflect(-point_light_direction, normal);
+ vec3 view_dir = normalize(position - view_pos);
+ float specular_strength = pow(max(dot(view_dir, reflection_dir), 0.0), shininess);
+ vec3 specular = specular_filter * point_light_color * specular_strength;
+
+ frag_color.rgb *= ambient + diffuse + specular;
+#endif
+}
diff --git a/shaders/point_cloud/fragment.glsl b/shaders/point_cloud/fragment.glsl
new file mode 100644
index 0000000..fc4fbfd
--- /dev/null
+++ b/shaders/point_cloud/fragment.glsl
@@ -0,0 +1,7 @@
+layout (location = 0) in vec4 frag_color;
+
+out vec4 pixel_color;
+
+void main() {
+ pixel_color = frag_color;
+}
diff --git a/shaders/point_cloud/vertex.glsl b/shaders/point_cloud/vertex.glsl
new file mode 100644
index 0000000..7edaeb7
--- /dev/null
+++ b/shaders/point_cloud/vertex.glsl
@@ -0,0 +1,51 @@
+layout (location = 0) in vec3 vertex_position;
+layout (location = 0) uniform mat4 mvp_matrix;
+layout (location = 2) uniform float point_size;
+layout (location = 0) out vec4 frag_color;
+
+#ifdef LIGHTING
+layout (location = 4) uniform mat4 model_matrix;
+layout (location = 5) uniform vec3 camera_position;
+layout (location = 1) in vec3 vertex_normal;
+#endif
+
+#ifdef RAINBOW
+layout (location = 6) uniform float rainbow_offset_y;
+layout (location = 7) uniform float rainbow_scale_y;
+
+// taken from 'https://github.com/hughsk/glsl-hsv2rgb'
+vec3 hue2rgb(float hue)
+{
+ vec4 offsets = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+ vec3 p = abs(fract(vec3(hue) + offsets.xyz) * 6.0 - offsets.www);
+ return clamp(p - offsets.xxx, 0.0, 1.0);
+}
+#endif
+
+#ifdef REFLECTANCE
+layout (location = 2) in float vertex_reflectance;
+#endif
+
+void main()
+{
+ gl_Position = mvp_matrix * vec4(vertex_position, 1.0);
+ gl_PointSize = point_size / gl_Position.w;
+
+ frag_color = vec4(1);
+
+#ifdef LIGHTING
+ vec3 world_position = vec3(model_matrix * vec4(vertex_position, 1.0));
+ vec3 world_normal = normalize(mat3(model_matrix) * vertex_normal);
+ vec3 view_direction = normalize(camera_position - world_position);
+ frag_color.rgb *= max(dot(world_normal, view_direction), 0.0);
+#endif
+
+#ifdef RAINBOW
+ float rainbow_pos = rainbow_scale_y * (vertex_position.y + rainbow_offset_y);
+ frag_color.rgb *= hue2rgb(mod(rainbow_pos, 1.0f));
+#endif
+
+#ifdef REFLECTANCE
+ frag_color.rgb *= vertex_reflectance;
+#endif
+}
diff --git a/source/opengl/handles/mesh_handle.ipp b/source/opengl/handles/mesh_handle.ipp
new file mode 100644
index 0000000..7e7ae7d
--- /dev/null
+++ b/source/opengl/handles/mesh_handle.ipp
@@ -0,0 +1,16 @@
+#ifndef INCLUDE_MESH_INSTANCE_IMPLEMENTATION
+#error Never include this file directly include 'mesh_handle.hpp'
+#endif
+
+namespace zgl
+{
+inline void mesh_handle::bind() const
+{
+ glBindVertexArray(vao_id);
+}
+
+inline void mesh_handle::unbind()
+{
+ glBindVertexArray(0);
+}
+}
\ No newline at end of file
diff --git a/source/opengl/handles/point_cloud_handle.ipp b/source/opengl/handles/point_cloud_handle.ipp
new file mode 100644
index 0000000..a773a5f
--- /dev/null
+++ b/source/opengl/handles/point_cloud_handle.ipp
@@ -0,0 +1,16 @@
+#ifndef INCLUDE_POINT_CLOUD_INSTANCE_IMPLEMENTATION
+#error Never include this file directly include 'point_cloud_handle.hpp'
+#endif
+
+namespace zgl
+{
+inline void point_cloud_handle::bind() const
+{
+ glBindVertexArray(vao_id);
+}
+
+inline void point_cloud_handle::unbind()
+{
+ glBindVertexArray(0);
+}
+}
diff --git a/source/opengl/handles/shader_program_handle.ipp b/source/opengl/handles/shader_program_handle.ipp
new file mode 100755
index 0000000..537dbbd
--- /dev/null
+++ b/source/opengl/handles/shader_program_handle.ipp
@@ -0,0 +1,63 @@
+#ifndef INCLUDE_GL_SHADER_PROGRAM_INSTANCE_IMPLEMENTATION
+#error Never include this file directly include 'shader_program_handle.hpp'
+#endif
+
+#include "glm/glm.hpp"
+#include "glm/gtc/type_ptr.hpp"
+
+namespace zgl
+{
+inline void shader_program_handle::bind() const
+{
+ glUseProgram(program_id);
+}
+
+inline void shader_program_handle::unbind()
+{
+ glUseProgram(0);
+}
+
+template
+void shader_program_handle::set_uniform(const T& value) const
+{
+ if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_FLOAT_MAT4);
+ glUniformMatrix4fv(VariableInfo.location, 1, false, glm::value_ptr(value));
+ }
+ else if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_FLOAT_MAT3);
+ glUniformMatrix3fv(VariableInfo.location, 1, false, glm::value_ptr(value));
+ }
+ else if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_FLOAT_VEC4);
+ glUniform4fv(VariableInfo.location, 1, glm::value_ptr(value));
+ }
+ else if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_FLOAT_VEC3);
+ glUniform3fv(VariableInfo.location, 1, glm::value_ptr(value));
+ }
+ else if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_FLOAT_VEC2);
+ glUniform2fv(VariableInfo.location, 1, glm::value_ptr(value));
+ }
+ else if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_FLOAT);
+ glUniform1f(VariableInfo.location, value);
+ }
+ else if constexpr (std::same_as)
+ {
+ static_assert(VariableInfo.type == GL_INT or VariableInfo.type == GL_SAMPLER_2D);
+ glUniform1i(VariableInfo.location, value);
+ }
+ else
+ {
+ T::_unknown_shader_uniform_type_;
+ }
+}
+}
\ No newline at end of file
diff --git a/source/opengl/handles/texture_handle.ipp b/source/opengl/handles/texture_handle.ipp
new file mode 100644
index 0000000..0693a93
--- /dev/null
+++ b/source/opengl/handles/texture_handle.ipp
@@ -0,0 +1,16 @@
+#ifndef INCLUDE_TEXTURE_INSTANCE_IMPLEMENTATION
+#error Never include this file directly include 'texture_handle.hpp'
+#endif
+
+namespace zgl
+{
+inline void texture_handle::bind() const
+{
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+}
+
+inline void texture_handle::unbind()
+{
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+} // namespace zgl