diff --git a/CMakeLists.txt b/CMakeLists.txt index 690dec6d..2483076e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ target_link_libraries(DirectZ PRIVATE iostreams archive_static assimp + zlibstatic ) target_link_libraries(dzp PRIVATE DirectZ) diff --git a/Tests.cmake b/Tests.cmake index e2fccec8..e16c4f69 100644 --- a/Tests.cmake +++ b/Tests.cmake @@ -2,7 +2,7 @@ include(CTest) function(add_dz_test TEST_NAME TEST_SRC) add_executable(${TEST_NAME} ${TEST_SRC}) target_include_directories(${TEST_NAME} PRIVATE ${DIRECTZ_INCLUDE_DIRS}) - target_link_libraries(${TEST_NAME} PRIVATE DirectZ) + target_link_libraries(${TEST_NAME} PRIVATE DirectZ zlibstatic) if(ANDROID) target_link_libraries(${TEST_NAME} PRIVATE android log) elseif(IOS) @@ -38,5 +38,6 @@ add_dz_test(DZ_LineGrid tests/LineGrid.cpp) add_dz_test(DZ_D7Stream tests/D7Stream.cpp) add_dz_test(DZ_ImGuiTest tests/ImGui.cpp) add_dz_test(DZ_ECSTest tests/ECS.cpp) -file(COPY images/Suzuho-Ueda.bmp DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY images/hi.bmp DESTINATION ${CMAKE_BINARY_DIR}) \ No newline at end of file +file(COPY images/Suzuho-Ueda.bmp DESTINATION ${CMAKE_BINARY_DIR}/images) +file(COPY images/hi.bmp DESTINATION ${CMAKE_BINARY_DIR}/images) +file(COPY models/SaiyanOne.glb DESTINATION ${CMAKE_BINARY_DIR}/models) \ No newline at end of file diff --git a/include/dz/ECS.hpp b/include/dz/ECS.hpp index cdb5454e..4aebadb7 100644 --- a/include/dz/ECS.hpp +++ b/include/dz/ECS.hpp @@ -508,7 +508,14 @@ namespace dz { } template - void AddProviderSingle(int parent_id, size_t& id, const TData& data, std::vector>& reflectable_group_vector, int& out_index) { + void AddProviderSingle( + int parent_id, + size_t& id, + const TData& data, + std::vector>& reflectable_group_vector, + int& out_index, + const std::string& name = "" + ) { if (id) return; @@ -607,20 +614,26 @@ namespace dz { } template - size_t AddProvider(int parent_id, const TData& data, std::vector>& reflectable_group_vector, int& out_index) { + int AddProvider( + int parent_id, + const TData& data, + std::vector>& reflectable_group_vector, + int& out_index, + const std::string& name = "" + ) { size_t id = 0; - (AddProviderSingle(parent_id, id, data, reflectable_group_vector, out_index), ...); + (AddProviderSingle(parent_id, id, data, reflectable_group_vector, out_index, name), ...); if (!id) throw std::runtime_error("Unable to find Provider supporting TData"); return id; } template - size_t AddEntity(int parent_id, const TEntity& entity_data, const std::vector& mesh_indexes) { + int AddEntity(int parent_id, const TEntity& entity_data, const std::vector& mesh_indexes, const std::string& name = "") { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; auto entity_id = AddProvider(parent_id, entity_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); auto& entity_group = GetGroupByID(entity_id); for (auto& mesh_index : mesh_indexes) { auto& mesh_group = GetGroupByIndex(mesh_index); @@ -630,27 +643,27 @@ namespace dz { .mesh_index = mesh_index, .material_index = mesh_group.material_index }; - auto submesh_id = AddProvider(entity_id, submesh_data, entity_group.reflectable_children, submesh_index); + auto submesh_id = AddProvider(entity_id, submesh_data, entity_group.reflectable_children, submesh_index, mesh_group.name); } return entity_id; } template - size_t AddEntity(const TEntity& entity_data, const std::vector& mesh_indexes) { - return AddEntity(-1, entity_data, mesh_indexes); + int AddEntity(const TEntity& entity_data, const std::vector& mesh_indexes, const std::string& name = "") { + return AddEntity(-1, entity_data, mesh_indexes, name); } template - size_t AddScene(int parent_id, const TScene& scene_data) { + int AddScene(int parent_id, const TScene& scene_data, const std::string& name = "") { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; return AddProvider(parent_id, scene_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); } template - size_t AddScene(const TScene& scene_data) { - return AddScene(-1, scene_data); + int AddScene(const TScene& scene_data, const std::string& name = "") { + return AddScene(-1, scene_data, name); } SceneProviderT& GetScene(size_t scene_id) { @@ -658,7 +671,7 @@ namespace dz { } template - size_t AddMaterial(const TMaterial& material_data, int& out_index) { + int AddMaterial(const TMaterial& material_data, int& out_index) { return AddProvider(-1, material_data, material_group_vector, out_index); } @@ -678,13 +691,13 @@ namespace dz { UpdateAtlas(); } - size_t AddMesh( - size_t parent_id, + int AddMesh( const std::vector>& positions, const std::vector>& uv2s, const std::vector>& normals, int material_index, - int& out_index + int& out_index, + const std::string& name = "" ) { MeshProviderT mesh_data; auto position_index = buffer_group_get_buffer_element_count(buffer_group, VertexPositions_Str); @@ -700,9 +713,7 @@ namespace dz { if (!normals.empty()) mesh_data.normal_offset = normal_index; - auto parent_group_ptr = FindParentGroupPtr(parent_id); - auto mesh_id = AddProvider(parent_id, mesh_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : mesh_group_vector, out_index); + auto mesh_id = AddProvider(-1, mesh_data, mesh_group_vector, out_index, name); if (mesh_data.position_offset != -1) { buffer_group_set_buffer_element_count(buffer_group, VertexPositions_Str, mesh_data.position_offset + positions.size()); @@ -737,16 +748,6 @@ namespace dz { return mesh_id; } - size_t AddMesh( - const std::vector>& positions, - const std::vector>& uv2s, - const std::vector>& normals, - int material_index, - int& out_index - ) { - return AddMesh(-1, positions, uv2s, normals, material_index, out_index); - } - ReflectableGroup& GetGenericGroupByID(size_t id) { auto ptr = FindParentGroupPtr(id); if (ptr) @@ -836,11 +837,11 @@ namespace dz { } template - size_t AddCamera(size_t parent_id, const TCamera& camera_data, TCamera::ProjectionType projectionType) { + int AddCamera(size_t parent_id, const TCamera& camera_data, TCamera::ProjectionType projectionType, const std::string& name = "") { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; auto camera_id = AddProvider(parent_id, camera_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); auto& camera = GetCamera(camera_id); auto& camera_group = GetGroupByID(camera_id); @@ -869,8 +870,8 @@ namespace dz { } template - size_t AddCamera(const TCamera& camera_data, TCamera::ProjectionType projectionType) { - return AddCamera(-1, camera_data, projectionType); + int AddCamera(const TCamera& camera_data, TCamera::ProjectionType projectionType, const std::string& name = "") { + return AddCamera(-1, camera_data, projectionType, name); } CameraProviderT& GetCamera(size_t camera_id) { diff --git a/include/dz/ECS/Camera.hpp b/include/dz/ECS/Camera.hpp index 99950661..08a02006 100644 --- a/include/dz/ECS/Camera.hpp +++ b/include/dz/ECS/Camera.hpp @@ -134,16 +134,16 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"type", {0, 0}}, - {"is_active", {1, 0}} + {"Type", {0, 0}}, + {"Is Active", {1, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "type"}, - {1, "is_active"} + {0, "Type"}, + {1, "Is Active"} }; inline static std::vector prop_names = { - "type", - "is_active" + "Type", + "Is Active" }; inline static const std::vector typeinfos = { &typeid(Camera::ProjectionType), @@ -172,25 +172,25 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"position", {0, 0}}, - {"center", {1, 0}}, - {"up", {2, 0}}, - {"nearPlane", {3, 0}}, - {"farPlane", {4, 0}} + {"Position", {0, 0}}, + {"Center", {1, 0}}, + {"Up", {2, 0}}, + {"Near Plane", {3, 0}}, + {"Far Plane", {4, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "posiiton"}, - {1, "center"}, - {2, "up"}, - {3, "nearPlane"}, - {4, "farPlane"}, + {0, "Position"}, + {1, "Center"}, + {2, "Up"}, + {3, "Near Plane"}, + {4, "Far Plane"}, }; inline static std::vector prop_names = { - "position", - "center", - "up", - "nearPlane", - "farPlane" + "Position", + "Center", + "Up", + "Near Plane", + "Far Plane" }; inline static const std::vector typeinfos = { &typeid(vec), @@ -219,16 +219,16 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"aspect", {0, 0}}, - {"fov", {1, 0}} + {"Aspect", {0, 0}}, + {"FOV", {1, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "aspect"}, - {1, "fov"} + {0, "Aspect"}, + {1, "FOV"} }; inline static std::vector prop_names = { - "aspect", - "fov" + "Aspect", + "FOV" }; inline static const std::vector typeinfos = { &typeid(float), @@ -254,16 +254,16 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"orthoWidth", {0, 0}}, - {"orthoHeight", {1, 0}} + {"Ortho Width", {0, 0}}, + {"Ortho Height", {1, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "orthoWidth"}, - {1, "orthoHeight"} + {0, "Ortho Width"}, + {1, "Ortho Height"} }; inline static std::vector prop_names = { - "orthoWidth", - "orthoHeight" + "Ortho Width", + "Ortho Height" }; inline static const std::vector typeinfos = { &typeid(float), diff --git a/include/dz/ECS/Entity.hpp b/include/dz/ECS/Entity.hpp index 05896388..2ad75910 100644 --- a/include/dz/ECS/Entity.hpp +++ b/include/dz/ECS/Entity.hpp @@ -97,19 +97,19 @@ void GetEntityModel(int entity_index, out mat4 out_model, out int parent_index, int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"position", {0, 0}}, - {"rotation", {1, 0}}, - {"scale", {2, 0}} + {"Position", {0, 0}}, + {"Rotation", {1, 0}}, + {"Scale", {2, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "position"}, - {1, "rotation"}, - {2, "scale"} + {0, "Position"}, + {1, "Rotation"}, + {2, "Scale"} }; inline static std::vector prop_names = { - "position", - "rotation", - "scale" + "Position", + "Rotation", + "Scale" }; inline static const std::vector typeinfos = { &typeid(vec), diff --git a/include/dz/ECS/Material.hpp b/include/dz/ECS/Material.hpp index 93fc5ffd..6de2ed37 100644 --- a/include/dz/ECS/Material.hpp +++ b/include/dz/ECS/Material.hpp @@ -84,23 +84,15 @@ void EnsureMaterialFragColor(in SubMesh submesh, inout vec4 current_color) { int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"atlasImageSize", {0, 0}}, - {"atlasPackedRect", {1, 0}}, - {"albedo", {2, 0}} + {"Albedo Color", {0, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "atlasImageSize"}, - {1, "atlasPackedRect"}, - {2, "albedo"} + {0, "Albedo Color"} }; inline static std::vector prop_names = { - "atlasImageSize", - "atlasPackedRect", - "albedo" + "Albedo Color" }; inline static const std::vector typeinfos = { - &typeid(vec), - &typeid(vec), &typeid(color_vec) }; diff --git a/include/dz/ECS/Scene.hpp b/include/dz/ECS/Scene.hpp index 3f07ec90..c480aa6a 100644 --- a/include/dz/ECS/Scene.hpp +++ b/include/dz/ECS/Scene.hpp @@ -86,19 +86,19 @@ void GetSceneModel(int scene_index, out mat4 out_model, out int parent_index, ou int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"position", {0, 0}}, - {"rotation", {1, 0}}, - {"scale", {2, 0}} + {"Position", {0, 0}}, + {"Rotation", {1, 0}}, + {"Scale", {2, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "position"}, - {1, "rotation"}, - {2, "scale"} + {0, "Position"}, + {1, "Rotation"}, + {2, "Scale"} }; inline static std::vector prop_names = { - "position", - "rotation", - "scale" + "Position", + "Rotation", + "Scale" }; inline static const std::vector typeinfos = { &typeid(vec), diff --git a/include/dz/ECS/SubMesh.hpp b/include/dz/ECS/SubMesh.hpp index 35ec96b9..615461ba 100644 --- a/include/dz/ECS/SubMesh.hpp +++ b/include/dz/ECS/SubMesh.hpp @@ -39,15 +39,19 @@ struct SubMesh { int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"Material", {0, 0}} + {"Material Index", {0, 0}}, + {"Material", {1, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "Material"} + {0, "Material Index"}, + {1, "Material"} }; inline static std::vector prop_names = { + "Material Index", "Material" }; inline static const std::vector typeinfos = { + &typeid(int), &typeid(MaterialIndexReflectable) }; diff --git a/include/dz/Loaders/Assimp_Loader.hpp b/include/dz/Loaders/Assimp_Loader.hpp index 27f88507..bb892931 100644 --- a/include/dz/Loaders/Assimp_Loader.hpp +++ b/include/dz/Loaders/Assimp_Loader.hpp @@ -3,23 +3,58 @@ #include #include #include +#include +#include +#include +#include namespace dz::loaders { using MeshPair = std::pair; + using MaterialPair = std::pair; + using SceneID = size_t; + using EntityID = size_t; + using ParentID = int; + using MaterialIndex = int; + using TPosition = vec; + using TRotation = vec; + using TScale = vec; + using TUV2 = vec; + using TNormal = vec; + using AddSceneFunction = std::function; + using AddEntityFunction = std::function&, + TPosition, + TRotation, + TScale + )>; + using AddMaterialFunction = std::function; using AddMeshFunction = std::function>&, - const std::vector>&, - const std::vector>& + const std::string&, + MaterialIndex, + const std::vector&, + const std::vector&, + const std::vector& )>; struct Assimp_Info { + ParentID parent_id = -1; + AddSceneFunction add_scene_function; + AddEntityFunction add_entity_function; AddMeshFunction add_mesh_function; + AddMaterialFunction add_material_function; std::filesystem::path path; std::shared_ptr bytes; size_t bytes_length = 0; }; struct Assimp_Loader { - using value_type = MeshPair; + using value_type = SceneID; using info_type = Assimp_Info; static value_type Load(const info_type& info); }; diff --git a/include/dz/math.hpp b/include/dz/math.hpp index f872102a..3c35ce72 100644 --- a/include/dz/math.hpp +++ b/include/dz/math.hpp @@ -5,6 +5,9 @@ #pragma once #include #include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif #include #include #include @@ -421,6 +424,182 @@ namespace dz color_vec& operator=(color_vec&&) noexcept = default; }; + /** + * @brief A quaternion class for representing rotations in 3D space. + * + * This struct inherits from vec and represents a quaternion with four components: w, x, y, z. + * It provides operations including construction, conjugation, inversion, quaternion multiplication, + * and rotation of a 3D vector. + * + * @tparam T Underlying scalar type (e.g., float, double). + */ + template + struct quat : public vec + { + using Base = vec; + + using Base::data; + using Base::operator=; + using Base::operator+=; + using Base::operator-=; + using Base::operator*=; + using Base::operator/=; + using Base::operator+; + using Base::operator-; + using Base::operator*; + using Base::operator/; + using Base::operator[]; + using Base::length; + using Base::normalize; + + /** + * @brief Default constructor. Components are uninitialized. + */ + quat() = default; + + /** + * @brief Copy constructor. + * @param other The quaternion to copy. + */ + quat(const quat&) = default; + + /** + * @brief Copy assignment operator. + * @param other The quaternion to assign from. + * @return Reference to this quaternion. + */ + quat& operator=(const quat&) = default; + + /** + * @brief Move constructor. + * @param other The quaternion to move. + */ + quat(quat&&) noexcept = default; + + /** + * @brief Move assignment operator. + * @param other The quaternion to move from. + * @return Reference to this quaternion. + */ + quat& operator=(quat&&) noexcept = default; + + /** + * @brief Variadic constructor forwarding to vec. + * @tparam Args Variadic template arguments. + * @param args Values forwarded to base class constructor. + */ + template + quat(Args&&... args) : Base(std::forward(args)...) { } + + /** + * @brief Construct quaternion from explicit components. + * @param w Scalar component. + * @param x X vector component. + * @param y Y vector component. + * @param z Z vector component. + */ + quat(T w, T x, T y, T z) + { + data[0] = w; + data[1] = x; + data[2] = y; + data[3] = z; + } + + /** + * @brief Returns the conjugate of the quaternion. + * + * The conjugate of (w, x, y, z) is (w, -x, -y, -z). + * + * @return Conjugated quaternion. + */ + quat conjugate() const + { + return quat(data[0], -data[1], -data[2], -data[3]); + } + + /** + * @brief Returns the inverse of the quaternion. + * + * The inverse is the conjugate divided by the squared norm. + * + * @return Inverse quaternion. + */ + quat inverse() const + { + quat conj = conjugate(); + T norm_sq = T(0); + for (size_t i = 0; i < 4; ++i) + norm_sq += data[i] * data[i]; + return conj / norm_sq; + } + + /** + * @brief Rotates a 3D vector using this quaternion. + * + * @param v The 3D vector to rotate. + * @return Rotated vector. + */ + vec rotate(const vec& v) const + { + quat qv(0, v[0], v[1], v[2]); + quat result = (*this) * qv * inverse(); + return vec(result[1], result[2], result[3]); + } + + /** + * @brief Quaternion multiplication. + * + * Multiplies this quaternion by another. + * + * @param rhs The right-hand side quaternion. + * @return Result of multiplication. + */ + quat operator*(const quat& rhs) const + { + T w = data[0] * rhs[0] - data[1] * rhs[1] - data[2] * rhs[2] - data[3] * rhs[3]; + T x = data[0] * rhs[1] + data[1] * rhs[0] + data[2] * rhs[3] - data[3] * rhs[2]; + T y = data[0] * rhs[2] - data[1] * rhs[3] + data[2] * rhs[0] + data[3] * rhs[1]; + T z = data[0] * rhs[3] + data[1] * rhs[2] - data[2] * rhs[1] + data[3] * rhs[0]; + return quat(w, x, y, z); + } + + /** + * @brief Constructs a quaternion from an axis-angle representation. + * + * @param axis The normalized axis of rotation. + * @param angle The angle of rotation in radians. + * @return Quaternion representing the rotation. + */ + static quat from_axis_angle(const vec& axis, T angle) + { + T half_angle = angle * T(0.5); + T s = std::sin(half_angle); + return quat(std::cos(half_angle), axis[0] * s, axis[1] * s, axis[2] * s); + } + + /** + * @brief Converts the quaternion to axis-angle representation. + * + * @param out_axis Normalized axis of rotation (output). + * @param out_angle Angle in radians (output). + */ + void to_axis_angle(vec& out_axis, T& out_angle) const + { + quat norm_q = this->normalize(); + out_angle = T(2) * std::acos(norm_q[0]); + T s = std::sqrt(T(1) - norm_q[0] * norm_q[0]); + if (s < T(1e-6)) + { + out_axis = vec(1, 0, 0); + } + else + { + out_axis = vec(norm_q[1] / s, norm_q[2] / s, norm_q[3] / s); + } + } + }; + /** * @brief A generic fixed-size matrix template supporting common matrix operations. * @@ -896,6 +1075,74 @@ namespace dz } return result; } + + bool decompose(vec& position, quat& rotation, vec& scale) + { + const auto& m = *this; + + vec col0 = vec{ m[0][0], m[0][1], m[0][2] }; + vec col1 = vec{ m[1][0], m[1][1], m[1][2] }; + vec col2 = vec{ m[2][0], m[2][1], m[2][2] }; + + scale[0] = col0.length(); + scale[1] = col1.length(); + scale[2] = col2.length(); + scale[3] = 1.0f; + + if (scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f) + return false; + + vec norm_col0 = col0 / scale[0]; + vec norm_col1 = col1 / scale[1]; + vec norm_col2 = col2 / scale[2]; + + mat rot_matrix; + rot_matrix[0] = norm_col0; + rot_matrix[1] = norm_col1; + rot_matrix[2] = norm_col2; + + float trace = rot_matrix[0][0] + rot_matrix[1][1] + rot_matrix[2][2]; + if (trace > 0.0f) + { + float s = sqrtf(trace + 1.0f) * 2.0f; + rotation[0] = 0.25f * s; + rotation[1] = (rot_matrix[2][1] - rot_matrix[1][2]) / s; + rotation[2] = (rot_matrix[0][2] - rot_matrix[2][0]) / s; + rotation[3] = (rot_matrix[1][0] - rot_matrix[0][1]) / s; + } + else if (rot_matrix[0][0] > rot_matrix[1][1] && rot_matrix[0][0] > rot_matrix[2][2]) + { + float s = sqrtf(1.0f + rot_matrix[0][0] - rot_matrix[1][1] - rot_matrix[2][2]) * 2.0f; + rotation[0] = (rot_matrix[2][1] - rot_matrix[1][2]) / s; + rotation[1] = 0.25f * s; + rotation[2] = (rot_matrix[0][1] + rot_matrix[1][0]) / s; + rotation[3] = (rot_matrix[0][2] + rot_matrix[2][0]) / s; + } + else if (rot_matrix[1][1] > rot_matrix[2][2]) + { + float s = sqrtf(1.0f + rot_matrix[1][1] - rot_matrix[0][0] - rot_matrix[2][2]) * 2.0f; + rotation[0] = (rot_matrix[0][2] - rot_matrix[2][0]) / s; + rotation[1] = (rot_matrix[0][1] + rot_matrix[1][0]) / s; + rotation[2] = 0.25f * s; + rotation[3] = (rot_matrix[1][2] + rot_matrix[2][1]) / s; + } + else + { + float s = sqrtf(1.0f + rot_matrix[2][2] - rot_matrix[0][0] - rot_matrix[1][1]) * 2.0f; + rotation[0] = (rot_matrix[1][0] - rot_matrix[0][1]) / s; + rotation[1] = (rot_matrix[0][2] + rot_matrix[2][0]) / s; + rotation[2] = (rot_matrix[1][2] + rot_matrix[2][1]) / s; + rotation[3] = 0.25f * s; + } + + position[0] = m[3][0]; + position[1] = m[3][1]; + position[2] = m[3][2]; + position[3] = 1.0f; + + return true; + } + }; /** @@ -1445,217 +1692,43 @@ namespace dz {} }; + /** - * @brief A quaternion class for representing rotations in 3D space. - * - * This struct inherits from vec and represents a quaternion with four components: w, x, y, z. - * It provides operations including construction, conjugation, inversion, quaternion multiplication, - * and rotation of a 3D vector. + * @brief Converts the quaternion to a 4x4 rotation matrix. * - * @tparam T Underlying scalar type (e.g., float, double). + * @return 4x4 rotation matrix. */ - template - struct quat : public vec + template + mat quat_to_mat4(const quat& quat) { - using Base = vec; - - using Base::data; - using Base::operator=; - using Base::operator+=; - using Base::operator-=; - using Base::operator*=; - using Base::operator/=; - using Base::operator+; - using Base::operator-; - using Base::operator*; - using Base::operator/; - using Base::operator[]; - using Base::length; - using Base::normalize; - - /** - * @brief Default constructor. Components are uninitialized. - */ - quat() = default; - - /** - * @brief Copy constructor. - * @param other The quaternion to copy. - */ - quat(const quat&) = default; - - /** - * @brief Copy assignment operator. - * @param other The quaternion to assign from. - * @return Reference to this quaternion. - */ - quat& operator=(const quat&) = default; - - /** - * @brief Move constructor. - * @param other The quaternion to move. - */ - quat(quat&&) noexcept = default; - - /** - * @brief Move assignment operator. - * @param other The quaternion to move from. - * @return Reference to this quaternion. - */ - quat& operator=(quat&&) noexcept = default; - - /** - * @brief Variadic constructor forwarding to vec. - * @tparam Args Variadic template arguments. - * @param args Values forwarded to base class constructor. - */ - template - quat(Args&&... args) : Base(std::forward(args)...) { } - - /** - * @brief Construct quaternion from explicit components. - * @param w Scalar component. - * @param x X vector component. - * @param y Y vector component. - * @param z Z vector component. - */ - quat(T w, T x, T y, T z) - { - data[0] = w; - data[1] = x; - data[2] = y; - data[3] = z; - } - - /** - * @brief Returns the conjugate of the quaternion. - * - * The conjugate of (w, x, y, z) is (w, -x, -y, -z). - * - * @return Conjugated quaternion. - */ - quat conjugate() const - { - return quat(data[0], -data[1], -data[2], -data[3]); - } - - /** - * @brief Returns the inverse of the quaternion. - * - * The inverse is the conjugate divided by the squared norm. - * - * @return Inverse quaternion. - */ - quat inverse() const - { - quat conj = conjugate(); - T norm_sq = T(0); - for (size_t i = 0; i < 4; ++i) - norm_sq += data[i] * data[i]; - return conj / norm_sq; - } - - /** - * @brief Rotates a 3D vector using this quaternion. - * - * @param v The 3D vector to rotate. - * @return Rotated vector. - */ - vec rotate(const vec& v) const - { - quat qv(0, v[0], v[1], v[2]); - quat result = (*this) * qv * inverse(); - return vec(result[1], result[2], result[3]); - } - - /** - * @brief Quaternion multiplication. - * - * Multiplies this quaternion by another. - * - * @param rhs The right-hand side quaternion. - * @return Result of multiplication. - */ - quat operator*(const quat& rhs) const - { - T w = data[0] * rhs[0] - data[1] * rhs[1] - data[2] * rhs[2] - data[3] * rhs[3]; - T x = data[0] * rhs[1] + data[1] * rhs[0] + data[2] * rhs[3] - data[3] * rhs[2]; - T y = data[0] * rhs[2] - data[1] * rhs[3] + data[2] * rhs[0] + data[3] * rhs[1]; - T z = data[0] * rhs[3] + data[1] * rhs[2] - data[2] * rhs[1] + data[3] * rhs[0]; - return quat(w, x, y, z); - } - - /** - * @brief Constructs a quaternion from an axis-angle representation. - * - * @param axis The normalized axis of rotation. - * @param angle The angle of rotation in radians. - * @return Quaternion representing the rotation. - */ - static quat from_axis_angle(const vec& axis, T angle) - { - T half_angle = angle * T(0.5); - T s = std::sin(half_angle); - return quat(std::cos(half_angle), axis[0] * s, axis[1] * s, axis[2] * s); - } - - /** - * @brief Converts the quaternion to axis-angle representation. - * - * @param out_axis Normalized axis of rotation (output). - * @param out_angle Angle in radians (output). - */ - void to_axis_angle(vec& out_axis, T& out_angle) const - { - quat norm_q = this->normalize(); - out_angle = T(2) * std::acos(norm_q[0]); - T s = std::sqrt(T(1) - norm_q[0] * norm_q[0]); - if (s < T(1e-6)) - { - out_axis = vec(1, 0, 0); - } - else - { - out_axis = vec(norm_q[1] / s, norm_q[2] / s, norm_q[3] / s); - } - } - - /** - * @brief Converts the quaternion to a 4x4 rotation matrix. - * - * @return 4x4 rotation matrix. - */ - mat to_mat4() const - { - T w = data[0], x = data[1], y = data[2], z = data[3]; - T xx = x * x, yy = y * y, zz = z * z; - T xy = x * y, xz = x * z, yz = y * z; - T wx = w * x, wy = w * y, wz = w * z; - - mat m = {}; - m[0][0] = T(1) - T(2) * (yy + zz); - m[0][1] = T(2) * (xy - wz); - m[0][2] = T(2) * (xz + wy); - m[0][3] = T(0); - - m[1][0] = T(2) * (xy + wz); - m[1][1] = T(1) - T(2) * (xx + zz); - m[1][2] = T(2) * (yz - wx); - m[1][3] = T(0); - - m[2][0] = T(2) * (xz - wy); - m[2][1] = T(2) * (yz + wx); - m[2][2] = T(1) - T(2) * (xx + yy); - m[2][3] = T(0); - - m[3][0] = T(0); - m[3][1] = T(0); - m[3][2] = T(0); - m[3][3] = T(1); + T w = quat[0], x = quat[1], y = quat[2], z = quat[3]; + T xx = x * x, yy = y * y, zz = z * z; + T xy = x * y, xz = x * z, yz = y * z; + T wx = w * x, wy = w * y, wz = w * z; + + mat m = {}; + m[0][0] = T(1) - T(2) * (yy + zz); + m[0][1] = T(2) * (xy - wz); + m[0][2] = T(2) * (xz + wy); + m[0][3] = T(0); + + m[1][0] = T(2) * (xy + wz); + m[1][1] = T(1) - T(2) * (xx + zz); + m[1][2] = T(2) * (yz - wx); + m[1][3] = T(0); + + m[2][0] = T(2) * (xz - wy); + m[2][1] = T(2) * (yz + wx); + m[2][2] = T(1) - T(2) * (xx + yy); + m[2][3] = T(0); + + m[3][0] = T(0); + m[3][1] = T(0); + m[3][2] = T(0); + m[3][3] = T(1); - return m; - } - }; + return m; + } // Convert a unit quaternion to a 3x3 rotation matrix // Assumes quat is normalized @@ -1750,20 +1823,29 @@ namespace dz return quat(std::cos(half), axis[0] * s, axis[1] * s, axis[2] * s); } - /** - * @brief Converts a quaternion to a 4x4 rotation matrix. - * - * @param q A normalized quaternion. - * @return A 4x4 rotation matrix. - */ template - mat quat_to_mat4(const quat& q) + vec quat_to_euler_xyz(const quat& q) { - mat m(T(1)); - mat r = quat_to_mat3(q); - for (size_t i = 0; i < 3; ++i) - for (size_t j = 0; j < 3; ++j) - m[i][j] = r[i][j]; - return m; + T x = q.data[1]; + T y = q.data[2]; + T z = q.data[3]; + T w = q.data[0]; + + T sinr_cosp = static_cast(2) * (w * x + y * z); + T cosr_cosp = static_cast(1) - static_cast(2) * (x * x + y * y); + T roll = std::atan2(sinr_cosp, cosr_cosp); + + T sinp = static_cast(2) * (w * y - z * x); + T pitch; + if (std::abs(sinp) >= static_cast(1)) + pitch = std::copysign(static_cast(M_PI) / static_cast(2), sinp); + else + pitch = std::asin(sinp); + + T siny_cosp = static_cast(2) * (w * z + x * y); + T cosy_cosp = static_cast(1) - static_cast(2) * (y * y + z * z); + T yaw = std::atan2(siny_cosp, cosy_cosp); + + return vec(pitch, yaw, roll, T(1.0)); } } \ No newline at end of file diff --git a/models/SaiyanOne.glb b/models/SaiyanOne.glb index 559513b5..298e35d2 100644 Binary files a/models/SaiyanOne.glb and b/models/SaiyanOne.glb differ diff --git a/src/Assimp/Assimp.hpp b/src/Assimp/Assimp.hpp index 8d2c4718..5906da97 100644 --- a/src/Assimp/Assimp.hpp +++ b/src/Assimp/Assimp.hpp @@ -6,7 +6,7 @@ template R AssimpConvert(T val) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { if constexpr (std::is_same_v>) { mat m(0.0f); @@ -28,7 +28,8 @@ R AssimpConvert(T val) m[3][3] = val.d4; return m; } - if constexpr (std::is_same_v>) + } + else if constexpr (std::is_same_v>) { if constexpr (std::is_same_v) { return aiMatrix4x4(val[0][0], val[1][0], val[2][0], val[3][0], @@ -36,6 +37,28 @@ R AssimpConvert(T val) val[0][2], val[1][2], val[2][2], val[3][2], val[0][3], val[1][3], val[2][3], val[3][3]); } + } + else if constexpr (std::is_same_v) { + if constexpr (std::is_same_v>) { + return vec(val.x, val.y); + } + } + else if constexpr (std::is_same_v) { + if constexpr (std::is_same_v>) { + return vec(val.x, val.y); + } + else if constexpr (std::is_same_v>) { + return vec(val.x, val.y, val.z); + } + else if constexpr (std::is_same_v>) { + return vec(val.x, val.y, val.z, 1.0f); + } + } + else if constexpr (std::is_same_v) { + if constexpr (std::is_same_v>) { + return quat(val.w, val.x, val.y, val.z); + } + } R r; return r; } diff --git a/src/DirectZ.cpp b/src/DirectZ.cpp index cb824565..e64ec01f 100644 --- a/src/DirectZ.cpp +++ b/src/DirectZ.cpp @@ -87,6 +87,7 @@ namespace dz #include "ImagePack.cpp" #include "Loaders/STB_Image_Loader.cpp" +#include "Loaders/Assimp_Loader.cpp" #include "runtime.cpp" diff --git a/src/ECS/Material.cpp b/src/ECS/Material.cpp index 968636a1..e6591cdd 100644 --- a/src/ECS/Material.cpp +++ b/src/ECS/Material.cpp @@ -20,9 +20,7 @@ void* dz::ecs::Material::MaterialReflectable::GetVoidPropertyByIndex(int prop_in assert(material_ptr); auto& material = *material_ptr; switch (prop_index) { - case 0: return &material.atlas_pack[0]; - case 1: return &material.atlas_pack[2]; - case 2: return &material.albedo; + case 0: return &material.albedo; default: return nullptr; } } diff --git a/src/ECS/SubMesh.cpp b/src/ECS/SubMesh.cpp index d903f406..717cc4b2 100644 --- a/src/ECS/SubMesh.cpp +++ b/src/ECS/SubMesh.cpp @@ -21,6 +21,7 @@ void* dz::ecs::SubMesh::SubMeshReflectable::GetVoidPropertyByIndex(int prop_inde auto& mesh = *mesh_ptr; switch (prop_index) { case 0: return &mesh.material_index; + case 1: return &mesh.material_index; default: return nullptr; } } diff --git a/src/Image.cpp b/src/Image.cpp index 035cf2d5..fc02eb0a 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -3,10 +3,6 @@ #include "Image.cpp.hpp" namespace dz { - - void upload_image_data(Image* image); - - Image* image_create_internal(const ImageCreateInfoInternal& info); void image_pre_resize_2D_internal(Image* image_ptr, uint32_t width, uint32_t height) { auto& image = *image_ptr; @@ -203,38 +199,20 @@ namespace dz { vkBindImageMemory(dr.device, image.image, image.memory, 0); // Upload data if provided - if (image.data) + if (!image.data) { - upload_image_data(image_ptr); + init_empty_image_data(image_ptr); } + upload_image_data(image_ptr); + // Create ImageView VkImageViewCreateInfo viewInfo{}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image.image; viewInfo.viewType = image.view_type; viewInfo.format = image.format; - switch (image.format) { - case VK_FORMAT_R8_UNORM: - case VK_FORMAT_R8G8_UNORM: - case VK_FORMAT_R8G8B8_UNORM: - case VK_FORMAT_R8G8B8A8_UNORM: - case VK_FORMAT_R32G32B32A32_SFLOAT: - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - break; - case VK_FORMAT_D32_SFLOAT: - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - break; - case VK_FORMAT_D32_SFLOAT_S8_UINT: - viewInfo.subresourceRange.aspectMask = - VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - break; - case VK_FORMAT_R8_UINT: - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - break; - default: - break; - } + viewInfo.subresourceRange.aspectMask = image_get_aspect_mask(image_ptr); viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; @@ -266,11 +244,46 @@ namespace dz { vkCreateSampler(dr.device, &samplerInfo, nullptr, &image.sampler); } } + + void init_empty_image_data(Image* image_ptr) { + auto& image = *image_ptr; + auto channels = image_get_channels_size_of_t(image_ptr); + auto pixel_stride = image_get_sizeof_channels(channels); + auto image_size = image.width * image.height * image.depth * pixel_stride; + image.data = std::shared_ptr((char*)malloc(image_size), free); + memset(image.data.get(), 0, image_size); + } + + uint32_t image_get_aspect_mask(Image* image_ptr) { + switch (image_ptr->format) { + case VK_FORMAT_R8_UNORM: + case VK_FORMAT_R8G8_UNORM: + case VK_FORMAT_R8G8B8_UNORM: + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R32G32B32A32_SFLOAT: + return VK_IMAGE_ASPECT_COLOR_BIT; + break; + case VK_FORMAT_D32_SFLOAT: + return VK_IMAGE_ASPECT_DEPTH_BIT; + break; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + break; + case VK_FORMAT_R8_UINT: + return VK_IMAGE_ASPECT_STENCIL_BIT; + break; + default: + break; + } + return 0; + } void upload_image_data(Image* image_ptr) { auto& image = *image_ptr; - VkDeviceSize image_size = image.width * image.height * image.depth * 4; // Assuming 4 bytes per texel (e.g., RGBA8) + auto channels = image_get_channels_size_of_t(image_ptr); + auto pixel_stride = image_get_sizeof_channels(channels); + VkDeviceSize image_size = image.width * image.height * image.depth * pixel_stride; VkBuffer staging_buffer; VkDeviceMemory staging_buffer_memory; @@ -310,7 +323,7 @@ namespace dz { barrier_to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier_to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier_to_transfer.image = image.image; - barrier_to_transfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier_to_transfer.subresourceRange.aspectMask = image_get_aspect_mask(image_ptr); barrier_to_transfer.subresourceRange.baseMipLevel = 0; barrier_to_transfer.subresourceRange.levelCount = 1; barrier_to_transfer.subresourceRange.baseArrayLayer = 0; @@ -334,7 +347,7 @@ namespace dz { region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.aspectMask = image_get_aspect_mask(image_ptr); region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; diff --git a/src/Image.cpp.hpp b/src/Image.cpp.hpp index 6bec75f8..96ae330b 100644 --- a/src/Image.cpp.hpp +++ b/src/Image.cpp.hpp @@ -37,4 +37,10 @@ namespace dz { VkSampleCountFlagBits multisampling = VK_SAMPLE_COUNT_1_BIT; void* data = nullptr; }; + + void upload_image_data(Image*); + void init_empty_image_data(Image*); + uint32_t image_get_aspect_mask(Image*); + + Image* image_create_internal(const ImageCreateInfoInternal& info); } \ No newline at end of file diff --git a/src/ImagePack.cpp b/src/ImagePack.cpp index 15be508a..1999d083 100644 --- a/src/ImagePack.cpp +++ b/src/ImagePack.cpp @@ -1,4 +1,6 @@ #include +#include "Image.cpp.hpp" +#include "Directz.cpp.hpp" #include #include @@ -11,7 +13,7 @@ #include // using Format = zg::images::Image::Format; -bool ImagePack::is_dirty() +bool dz::ImagePack::is_dirty() { if (image_vec.size() != rect_vec.size()) return true; @@ -27,7 +29,7 @@ bool ImagePack::is_dirty() } return any_dirty; } -void ImagePack::repack() +void dz::ImagePack::repack() { constexpr int padding = 0; // padding in pixels around each image @@ -217,7 +219,7 @@ void ImagePack::repack() atlas = image_vec[0]; } } -size_t ImagePack::findImageIndex(Image* image) +size_t dz::ImagePack::findImageIndex(Image* image) { size_t index = 0; auto image_vec_size = image_vec.size(); @@ -232,7 +234,7 @@ size_t ImagePack::findImageIndex(Image* image) else throw std::runtime_error("Image not found"); } -void ImagePack::addImage(Image* image) +void dz::ImagePack::addImage(Image* image) { try { @@ -243,16 +245,23 @@ void ImagePack::addImage(Image* image) image_vec.push_back(image); } } -bool ImagePack::check() +bool dz::ImagePack::check() { auto isDirty = is_dirty(); - if (!isDirty) + if (!isDirty) { + if (!atlas) { + atlas = image_create({ + .width = 1, + .height = 1 + }); + } return false; + } repack(); return true; } -Image* ImagePack::getAtlas() { return atlas; } -vec ImagePack::getUV(vec original_uv, Image* image) +Image* dz::ImagePack::getAtlas() { return atlas; } +vec dz::ImagePack::getUV(vec original_uv, Image* image) { return {}; // auto& image_ref = *image; @@ -264,7 +273,7 @@ vec ImagePack::getUV(vec original_uv, Image* image) // float atlas_v = (packed_rect.y + pixel_v) / float(atlas_size.y); // return {atlas_u, atlas_v}; } -std::vector> ImagePack::getUVs(const std::vector>& original_uvs, Image* image) +std::vector> dz::ImagePack::getUVs(const std::vector>& original_uvs, Image* image) { auto& image_ref = *image; auto& packed_rect = findPackedRect(image); @@ -281,9 +290,9 @@ std::vector> ImagePack::getUVs(const std::vector>& o // } return new_uvs; } -ImagePack::rect_type& ImagePack::findPackedRect(Image* image) +dz::ImagePack::rect_type& dz::ImagePack::findPackedRect(Image* image) { return rect_vec[findImageIndex(image)]; } -size_t ImagePack::size() const { return image_vec.size(); } -bool ImagePack::empty() const { return image_vec.empty(); } +size_t dz::ImagePack::size() const { return image_vec.size(); } +bool dz::ImagePack::empty() const { return image_vec.empty(); } diff --git a/src/Loaders/Assimp_Loader.cpp b/src/Loaders/Assimp_Loader.cpp index ff2cc030..77495638 100644 --- a/src/Loaders/Assimp_Loader.cpp +++ b/src/Loaders/Assimp_Loader.cpp @@ -1,17 +1,23 @@ #include #include "../Assimp/Assimp.hpp" +#include #include +#include namespace dz::loaders::assimp_loader { Assimp::Importer importer; struct AssimpContext { const aiScene* scene_ptr = 0; size_t totalNodes = 0; + std::unordered_map mesh_index_pair_map; }; + + #define ASSIMP_FLAGS aiProcess_Triangulate | aiProcessPreset_TargetRealtime_Fast | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_ConvertToLeftHanded + void InitContext(AssimpContext& context, const Assimp_Info& info) { if (!info.path.empty()) { auto info_path_string = info.path.string(); - const aiScene *scene_ptr = importer.ReadFile(info_path_string.c_str(), aiProcess_Triangulate | aiProcessPreset_TargetRealtime_Fast | aiProcess_FlipUVs | aiProcess_CalcTangentSpace); + const aiScene *scene_ptr = importer.ReadFile(info_path_string.c_str(), ASSIMP_FLAGS); if (!scene_ptr || scene_ptr->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene_ptr->mRootNode) { std::cerr << importer.GetErrorString() << std::endl; @@ -20,7 +26,7 @@ namespace dz::loaders::assimp_loader { context.scene_ptr = scene_ptr; } else if (info.bytes && info.bytes_length) { - const aiScene *scene_ptr = importer.ReadFileFromMemory(info.bytes.get(), info.bytes_length, aiProcess_Triangulate | aiProcessPreset_TargetRealtime_Fast | aiProcess_FlipUVs | aiProcess_CalcTangentSpace, nullptr); + const aiScene *scene_ptr = importer.ReadFileFromMemory(info.bytes.get(), info.bytes_length, ASSIMP_FLAGS, nullptr); if (!scene_ptr || scene_ptr->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene_ptr->mRootNode) { std::cerr << importer.GetErrorString() << std::endl; @@ -40,19 +46,141 @@ namespace dz::loaders::assimp_loader { } } - dz::loaders::MeshPair ProcessNode(AssimpContext& context, aiNode* node) { + std::vector LoadMaterialImages( + const aiScene *aiscene, + aiMaterial *material, + const aiTextureType &type, + const std::string &typeName + ) + { + std::vector images; + for (uint32_t i = 0; i < material->GetTextureCount(type); i++) + { + aiString str; + material->GetTexture(type, i, &str); + Image* image_ptr = nullptr; + int32_t imageWidth = 0, imageHeight = 0; + // Check if the image is embedded + if (str.data[0] == '*') + { + uint32_t imageIndex = atoi(&str.data[1]); + aiTexture *aiTex = aiscene->mTextures[imageIndex]; + if (aiTex->mHeight == 0) + { + // The embedded image is compressed (e.g., PNG or JPG in memory) + image_ptr = STB_Image_Loader::Load({ + .bytes = std::shared_ptr((char*)aiTex->pcData, [](auto p) {}), + .bytes_length = aiTex->mWidth + }); + } + else + { + // The embedded image is uncompressed raw data + throw std::runtime_error("A embedded image is uncompressed raw data, we currently only support compressed images such as PNG or JPG"); + } + } + else + { + // External image + throw std::runtime_error("A image is an external image, we currently only support compressed embedded images such as PNG or JPG"); + } + images.push_back(image_ptr); + } + return images; + } + + void AddNode(AssimpContext& context, const Assimp_Info& info, aiNode* node, size_t parent_id) { mat transformation = AssimpConvert>(node->mTransformation); + vec position; + quat rotation_quat; + vec scale; + transformation.decompose(position, rotation_quat, scale); + auto rotation = quat_to_euler_xyz(rotation_quat); + std::vector mesh_indexes; + for (uint32_t i = 0; i < node->mNumMeshes; i++) + { + uint32_t aiMeshIndex = node->mMeshes[i]; + auto pair_it = context.mesh_index_pair_map.find(aiMeshIndex); + if (pair_it != context.mesh_index_pair_map.end()) { + mesh_indexes.push_back(pair_it->second.second); + } + auto& ai_mesh = context.scene_ptr->mMeshes[aiMeshIndex]; + // image data + std::vector> keyed_images; + if (ai_mesh->mMaterialIndex >= 0) + { + aiMaterial *material = context.scene_ptr->mMaterials[ai_mesh->mMaterialIndex]; + std::string material_name(material->GetName().C_Str()); + // Load base color (albedo) image + std::vector baseColorMaps = LoadMaterialImages(context.scene_ptr, material, aiTextureType_BASE_COLOR, "baseColor"); + for (auto& image_ptr : baseColorMaps) + { + keyed_images.push_back({"ColorTexture", material_name, image_ptr}); + } + // Load diffuse images + std::vector diffuseMaps; + if (!baseColorMaps.size()) + { + diffuseMaps = LoadMaterialImages(context.scene_ptr, material, aiTextureType_DIFFUSE, "diffuse"); + for (auto& image_ptr : diffuseMaps) + { + keyed_images.push_back({"ColorTexture", material_name, image_ptr}); + } + } + } + std::vector material_indexes; + material_indexes.reserve(keyed_images.size()); + for (auto& keyed_tuple : keyed_images) { + auto material_pair = info.add_material_function(std::get<1>(keyed_tuple), std::get<2>(keyed_tuple)); + material_indexes.push_back(material_pair.second); + } + // vertex data + std::vector positions; + positions.reserve(ai_mesh->mNumFaces * 3); + std::vector uv2s; + uv2s.reserve(ai_mesh->mNumFaces * 3); + std::vector normals; + normals.reserve(ai_mesh->mNumFaces * 3); + static constexpr auto i0 = 0; + static constexpr auto i1 = 1; + static constexpr auto i2 = 2; + for (auto fi = 0; fi < ai_mesh->mNumFaces; ++fi) { + auto& face = ai_mesh->mFaces[fi]; + if (face.mNumIndices == 3) { + auto i_0 = face.mIndices[i0]; + auto i_1 = face.mIndices[i1]; + auto i_2 = face.mIndices[i2]; + positions.push_back(AssimpConvert>(ai_mesh->mVertices[i_0])); + positions.push_back(AssimpConvert>(ai_mesh->mVertices[i_1])); + positions.push_back(AssimpConvert>(ai_mesh->mVertices[i_2])); + uv2s.push_back(AssimpConvert>(ai_mesh->mTextureCoords[0][i_0])); + uv2s.push_back(AssimpConvert>(ai_mesh->mTextureCoords[0][i_1])); + uv2s.push_back(AssimpConvert>(ai_mesh->mTextureCoords[0][i_2])); + normals.push_back(AssimpConvert>(ai_mesh->mNormals[i_0])); + normals.push_back(AssimpConvert>(ai_mesh->mNormals[i_1])); + normals.push_back(AssimpConvert>(ai_mesh->mNormals[i_2])); + } + else + throw std::runtime_error("expected number of indices in face to equal 3"); + } + auto material_index = material_indexes.empty() ? -1 : material_indexes[0]; + auto mesh_pair = info.add_mesh_function(ai_mesh->mName.C_Str(), material_index, positions, uv2s, normals); + context.mesh_index_pair_map[aiMeshIndex] = mesh_pair; + mesh_indexes.push_back(mesh_pair.second); + } + auto entity_id = info.add_entity_function(parent_id, node->mName.C_Str(), mesh_indexes, position, rotation, scale); } - dz::loaders::MeshPair AssimpLoad(AssimpContext& context, const Assimp_Info& info) { + dz::loaders::SceneID AssimpLoad(AssimpContext& context, const Assimp_Info& info) { CountNodes(context, context.scene_ptr->mRootNode); - return ProcessNode(context, context.scene_ptr->mRootNode); + auto scene_id = info.add_scene_function(info.parent_id, context.scene_ptr->mName.C_Str()); + AddNode(context, info, context.scene_ptr->mRootNode, scene_id); + return scene_id; } } -dz::loaders::MeshPair dz::loaders::Assimp_Loader::Load(const info_type& info) { +dz::loaders::SceneID dz::loaders::Assimp_Loader::Load(const info_type& info) { using namespace dz::loaders::assimp_loader; AssimpContext context; InitContext(context, info); - value_type value(0, -1); return AssimpLoad(context, info); } \ No newline at end of file diff --git a/src/Shader.cpp b/src/Shader.cpp index 38c83839..091ab910 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -1542,7 +1542,8 @@ namespace dz { {{VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}, {VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT}}, }; - void transition_image_layout(VkImage image, VkImageLayout old_layout, VkImageLayout new_layout) { + void transition_image_layout(Image* image_ptr, VkImageLayout old_layout, VkImageLayout new_layout) { + auto image = image_ptr->image; auto device = dr.device; auto command_buffer = begin_single_time_commands(); @@ -1553,7 +1554,7 @@ namespace dz { barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.aspectMask = image_get_aspect_mask(image_ptr); barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.baseArrayLayer = 0; @@ -1589,7 +1590,8 @@ namespace dz { if (runtime_ite == bound_buffer_group->runtime_images.end()) continue; - auto& image = *runtime_ite->second;; + auto image_ptr = runtime_ite->second.get(); + auto& image = *image_ptr; auto exp_lay_ite = shader_image.expected_layouts.find(shader); if (exp_lay_ite == shader_image.expected_layouts.end()) @@ -1599,7 +1601,7 @@ namespace dz { if (image.current_layout != required) { - transition_image_layout(image.image, image.current_layout, required); + transition_image_layout(image_ptr, image.current_layout, required); image.current_layout = required; } } diff --git a/tests/ECS.cpp b/tests/ECS.cpp index b10ea1d9..e3e949c7 100644 --- a/tests/ECS.cpp +++ b/tests/ECS.cpp @@ -59,11 +59,12 @@ auto set_pair_id_index(auto pair, auto& id, auto& index) { index = pair.second; } -using TL = TypeLoader; +using TL = TypeLoader; std::pair AddPyramidMesh(ExampleECS& ecs, int material_index); std::pair AddPlaneMesh(ExampleECS& ecs, int material_index); std::pair AddCubeMesh(ExampleECS& ecs, int material_index); +void RenderReflectables(ReflectableGroup& what_reflectable_group); int main() { ExampleECS::RegisterStateCID(); @@ -100,48 +101,73 @@ int main() { auto& ecs = *ecs_ptr; if (!state_loaded) { - int mat1_index = -1; - auto mat1_id = ecs.AddMaterial(Material{}, mat1_index); - auto im_1 = TL::Load({ - .path = "hi.bmp" - }); - ecs.SetMaterialImage(mat1_id, im_1); + // int mat1_index = -1; + // auto mat1_id = ecs.AddMaterial(Material{}, mat1_index); + // auto im_1 = TL::Load({ + // .path = "images/hi.bmp" + // }); + // ecs.SetMaterialImage(mat1_id, im_1); int blue_material = -1; auto mat2_id = ecs.AddMaterial(Material{ .albedo = {0.0f, 0.0f, 1.0f, 1.0f} }, blue_material); - auto [pyramid_mesh_id, pyramid_mesh_index] = AddPyramidMesh(ecs, blue_material); - auto [plane_mesh_id, plane_mesh_index] = AddPlaneMesh(ecs, blue_material); - auto [cube_mesh_id, cube_mesh_index] = AddCubeMesh(ecs, blue_material); + // auto [pyramid_mesh_id, pyramid_mesh_index] = AddPyramidMesh(ecs, blue_material); + // auto [plane_mesh_id, plane_mesh_index] = AddPlaneMesh(ecs, blue_material); + // auto [cube_mesh_id, cube_mesh_index] = AddCubeMesh(ecs, blue_material); - int mat3_index = -1; - auto mat3_id = ecs.AddMaterial(Material{}, mat3_index); - auto suzuho = TL::Load({ - .path = "Suzuho-Ueda.bmp" - }); - ecs.SetMaterialImage(mat3_id, suzuho); + // int mat3_index = -1; + // auto mat3_id = ecs.AddMaterial(Material{}, mat3_index); + // auto suzuho = TL::Load({ + // .path = "images/Suzuho-Ueda.bmp" + // }); + // ecs.SetMaterialImage(mat3_id, suzuho); - auto scene1_id = ecs.AddScene(Scene{}); + // auto scene1_id = ecs.AddScene(Scene{}); - auto cam1_id = ecs.AddCamera(scene1_id, Camera{}, Camera::Perspective); + // auto cam1_id = ecs.AddCamera(scene1_id, Camera{}, Camera::Perspective); - auto e1_id = ecs.AddEntity(scene1_id, Entity{}, {plane_mesh_index}); - auto e2_id = ecs.AddEntity(scene1_id, Entity{ - .position = {1.f, 1.f, 0.f, 1.f} - }, {plane_mesh_index}); + // auto e1_id = ecs.AddEntity(scene1_id, Entity{}, {plane_mesh_index}); + // auto e2_id = ecs.AddEntity(scene1_id, Entity{ + // .position = {1.f, 1.f, 0.f, 1.f} + // }, {plane_mesh_index}); - auto scene2_id = ecs.AddScene(Scene{}); + // auto scene2_id = ecs.AddScene(Scene{}); - auto cam2_id = ecs.AddCamera(scene2_id, Camera{}, Camera::Perspective); + // auto cam2_id = ecs.AddCamera(scene2_id, Camera{}, Camera::Perspective); - auto e3_id = ecs.AddEntity(scene2_id, Entity{}, {cube_mesh_index}); - auto e4_id = ecs.AddEntity(scene2_id, Entity{ - .position = {-2.f, 1.f, 0.f, 1.f} - }, {pyramid_mesh_index}); + // auto e3_id = ecs.AddEntity(scene2_id, Entity{}, {cube_mesh_index}); + // auto e4_id = ecs.AddEntity(scene2_id, Entity{ + // .position = {-2.f, 1.f, 0.f, 1.f} + // }, {pyramid_mesh_index}); auto cam3_id = ecs.AddCamera(Camera{}, Camera::Perspective); + + TL::Load(Assimp_Info{ + .add_scene_function = [&](auto parent_id, const auto& name) { + return ecs.AddScene(parent_id, Scene{}, name); + }, + .add_entity_function = [&](auto parent_id, const auto& name, const auto& mesh_indexes, auto position, auto rotation, auto scale) { + return ecs.AddEntity(parent_id, Entity{ + .position = position, + .rotation = rotation, + .scale = scale + }, mesh_indexes, name); + }, + .add_mesh_function = [&](const auto& name, auto material_index, const auto& positions, const auto& uv2s, const auto& normals) -> MeshPair { + int out_index = -1; + auto out_id = ecs.AddMesh(positions, uv2s, normals, material_index, out_index, name); + return {out_id, out_index}; + }, + .add_material_function = [&](const auto& name, auto image_ptr) -> MaterialPair { + int out_index = -1; + auto out_id = ecs.AddMaterial(Material{}, out_index); + ecs.SetMaterialImage(out_id, image_ptr); + return {out_id, out_index}; + }, + .path = "models/SaiyanOne.glb" + }); } ecs.MarkReady(); @@ -613,222 +639,7 @@ int main() { ImGui::TextDisabled("GID: 0x%08X", SelectedReflectableID); ImGui::Spacing(); - std::function render_reflectables; - render_reflectables = [&](auto& what_reflectable_group) { - auto& reflectables = what_reflectable_group.GetReflectables(); - - auto reflect_begin = reflectables.begin(); - auto reflect_it = reflect_begin; - auto reflect_end = reflectables.end(); - size_t reflect_dist = 0; - - auto update_iterators = [&]() { - reflect_begin = reflectables.begin(); - reflect_it = reflect_begin + reflect_dist; - reflect_end = reflectables.end(); - }; - - for (; reflect_it != reflect_end; reflect_it++) { - reflect_dist = std::distance(reflect_begin, reflect_it); - auto& reflectable = **reflect_it; - ImGui::PushID(&reflectable); - const auto& reflectable_name = reflectable.GetName(); - - if (ImGui::CollapsingHeader(reflectable_name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) - { - bool popped_style_var = false; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 4)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 8)); - - auto reflectable_type_hint = reflectable.GetTypeHint(); - - switch (reflectable_type_hint) { - case ReflectableTypeHint::VEC2: - { - auto data_ptr = reflectable.GetVoidPropertyByIndex(0); - if (ImGui::DragFloat2("##vec2", static_cast(data_ptr), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { - reflectable.NotifyChange(0); - update_iterators(); - } - break; - } - case ReflectableTypeHint::VEC3: - { - auto data_ptr = reflectable.GetVoidPropertyByIndex(0); - if (ImGui::DragFloat3("##vec3", static_cast(data_ptr), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { - reflectable.NotifyChange(0); - update_iterators(); - } - break; - } - case ReflectableTypeHint::VEC3_RGB: - { - auto data_ptr = reflectable.GetVoidPropertyByIndex(0); - if (ImGui::ColorEdit3("##rgb", static_cast(data_ptr), ImGuiColorEditFlags_Float)) { - reflectable.NotifyChange(0); - update_iterators(); - } - break; - } - case ReflectableTypeHint::VEC4: - { - auto data_ptr = reflectable.GetVoidPropertyByIndex(0); - if (ImGui::DragFloat4("##vec4", static_cast(data_ptr), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { - reflectable.NotifyChange(0); - update_iterators(); - } - break; - } - case ReflectableTypeHint::VEC4_RGBA: - { - auto data_ptr = reflectable.GetVoidPropertyByIndex(0); - if (ImGui::ColorEdit4("##rgba", static_cast(data_ptr), ImGuiColorEditFlags_Float)) { - reflectable.NotifyChange(0); - update_iterators(); - } - break; - } - default: - case ReflectableTypeHint::STRUCT: { - const auto& reflectable_typeinfos = reflectable.GetPropertyTypeinfos(); - const auto& reflectable_prop_names = reflectable.GetPropertyNames(); - const auto& disabled_properties = reflectable.GetDisabledProperties(); - size_t index = 0; - for (auto& prop_name : reflectable_prop_names) - { - ImGui::PushID(prop_name.c_str()); - auto prop_index = reflectable.GetPropertyIndexByName(prop_name); - auto type_info = reflectable_typeinfos[prop_index]; - bool is_disabled = (prop_index < disabled_properties.size()) ? disabled_properties[prop_index] : false; - - ImGui::BeginDisabled(is_disabled); - ImGui::Text("%s", prop_name.c_str()); - ImGui::SameLine(); - - if (*type_info == typeid(float)) - { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - ImGui::PushID(prop_index); - if (ImGui::InputFloat("##input", &value, 0.1f, 1.0f, "%.3f")) { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - ImGui::PopID(); - } - else if (*type_info == typeid(int)) { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - ImGui::PushID(prop_index); - if (ImGui::InputInt("##input", &value)) { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - ImGui::PopID(); - } - else if (*type_info == typeid(bool)) { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - ImGui::PushID(prop_index); - if (ImGui::Checkbox("##checkbox", &value)) - { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - ImGui::PopID(); - } - else if (*type_info == typeid(Light::LightType)) { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - static const char* projection_types[] = { "Directional", "Spot", "Point" }; - int current_index = static_cast(value); - if (ImGui::Combo("##lightType", ¤t_index, projection_types, IM_ARRAYSIZE(projection_types))) { - value = static_cast(current_index); - reflectable.NotifyChange(prop_index); - update_iterators(); - } - } - else if (*type_info == typeid(Camera::ProjectionType)) { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - static const char* projection_types[] = { "Perspective", "Orthographic" }; - int current_index = static_cast(value); - if (ImGui::Combo("##proj", ¤t_index, projection_types, IM_ARRAYSIZE(projection_types))) { - value = static_cast(current_index); - reflectable.NotifyChange(prop_index); - update_iterators(); - } - } - else if (*type_info == typeid(std::string)) { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - if (value.capacity() < 128) value.reserve(128); - ImGui::PushID(prop_index); - if (ImGui::InputText("##s", value.data(), value.capacity() + 1)) { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - ImGui::PopID(); - } - else if (*type_info == typeid(vec)) { - auto& value = reflectable.template GetPropertyByIndex>(prop_index); - if (ImGui::DragFloat2("##vec2", static_cast(&value[0]), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - } - else if (*type_info == typeid(vec)) { - auto& value = reflectable.template GetPropertyByIndex>(prop_index); - if (ImGui::DragFloat3("##vec3", static_cast(&value[0]), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - } - else if (*type_info == typeid(vec)) { - auto& value = reflectable.template GetPropertyByIndex>(prop_index); - if (ImGui::DragFloat4("##vec4", static_cast(&value[0]), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { - reflectable.NotifyChange(prop_index); - update_iterators(); - } - } - else if (*type_info == typeid(color_vec)) { - auto& value = reflectable.template GetPropertyByIndex>(prop_index); - if (ImGui::ColorEdit3("##rgb", static_cast(&value[0]), ImGuiColorEditFlags_Float)) { - reflectable.NotifyChange(0); - update_iterators(); - } - } - else if (*type_info == typeid(color_vec)) { - auto& value = reflectable.template GetPropertyByIndex>(prop_index); - if (ImGui::ColorEdit4("##rgba", static_cast(&value[0]), ImGuiColorEditFlags_Float)) { - reflectable.NotifyChange(0); - update_iterators(); - } - } - else if (*type_info == typeid(MaterialIndexReflectable)) { - auto& value = reflectable.template GetPropertyByIndex(prop_index); - auto& material_group = ecs.GetGroupByIndex(value.material_index); - ImGui::EndDisabled(); - ImGui::PopID(); - ImGui::PopStyleVar(2); - popped_style_var = true; - render_reflectables(material_group); - break; - } - else { - ImGui::TextDisabled(""); - } - - ImGui::EndDisabled(); - ImGui::PopID(); - } - break; - } - } - - if (!popped_style_var) - ImGui::PopStyleVar(2); - } - - ImGui::PopID(); - } - }; - - render_reflectables(reflectable_group); + RenderReflectables(reflectable_group); ImGui::PopID(); } @@ -845,6 +656,220 @@ int main() { } } +void RenderReflectables(ReflectableGroup& what_reflectable_group) { + auto& reflectables = what_reflectable_group.GetReflectables(); + + auto reflect_begin = reflectables.begin(); + auto reflect_it = reflect_begin; + auto reflect_end = reflectables.end(); + size_t reflect_dist = 0; + + auto update_iterators = [&]() { + reflect_begin = reflectables.begin(); + reflect_it = reflect_begin + reflect_dist; + reflect_end = reflectables.end(); + }; + + for (; reflect_it != reflect_end; reflect_it++) { + reflect_dist = std::distance(reflect_begin, reflect_it); + auto& reflectable = **reflect_it; + ImGui::PushID(&reflectable); + const auto& reflectable_name = reflectable.GetName(); + + if (ImGui::CollapsingHeader(reflectable_name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) + { + bool popped_style_var = false; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 4)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 8)); + + auto reflectable_type_hint = reflectable.GetTypeHint(); + + switch (reflectable_type_hint) { + case ReflectableTypeHint::VEC2: + { + auto data_ptr = reflectable.GetVoidPropertyByIndex(0); + if (ImGui::DragFloat2("##vec2", static_cast(data_ptr), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { + reflectable.NotifyChange(0); + update_iterators(); + } + break; + } + case ReflectableTypeHint::VEC3: + { + auto data_ptr = reflectable.GetVoidPropertyByIndex(0); + if (ImGui::DragFloat3("##vec3", static_cast(data_ptr), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { + reflectable.NotifyChange(0); + update_iterators(); + } + break; + } + case ReflectableTypeHint::VEC3_RGB: + { + auto data_ptr = reflectable.GetVoidPropertyByIndex(0); + if (ImGui::ColorEdit3("##rgb", static_cast(data_ptr), ImGuiColorEditFlags_Float)) { + reflectable.NotifyChange(0); + update_iterators(); + } + break; + } + case ReflectableTypeHint::VEC4: + { + auto data_ptr = reflectable.GetVoidPropertyByIndex(0); + if (ImGui::DragFloat4("##vec4", static_cast(data_ptr), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { + reflectable.NotifyChange(0); + update_iterators(); + } + break; + } + case ReflectableTypeHint::VEC4_RGBA: + { + auto data_ptr = reflectable.GetVoidPropertyByIndex(0); + if (ImGui::ColorEdit4("##rgba", static_cast(data_ptr), ImGuiColorEditFlags_Float)) { + reflectable.NotifyChange(0); + update_iterators(); + } + break; + } + default: + case ReflectableTypeHint::STRUCT: { + const auto& reflectable_typeinfos = reflectable.GetPropertyTypeinfos(); + const auto& reflectable_prop_names = reflectable.GetPropertyNames(); + const auto& disabled_properties = reflectable.GetDisabledProperties(); + size_t index = 0; + for (auto& prop_name : reflectable_prop_names) + { + ImGui::PushID(prop_name.c_str()); + auto prop_index = reflectable.GetPropertyIndexByName(prop_name); + auto type_info = reflectable_typeinfos[prop_index]; + bool is_disabled = (prop_index < disabled_properties.size()) ? disabled_properties[prop_index] : false; + + ImGui::BeginDisabled(is_disabled); + ImGui::Text("%s", prop_name.c_str()); + ImGui::SameLine(); + + if (*type_info == typeid(float)) + { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + ImGui::PushID(prop_index); + if (ImGui::InputFloat("##input", &value, 0.1f, 1.0f, "%.3f")) { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + ImGui::PopID(); + } + else if (*type_info == typeid(int)) { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + ImGui::PushID(prop_index); + if (ImGui::InputInt("##input", &value)) { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + ImGui::PopID(); + } + else if (*type_info == typeid(bool)) { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + ImGui::PushID(prop_index); + if (ImGui::Checkbox("##checkbox", &value)) + { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + ImGui::PopID(); + } + else if (*type_info == typeid(Light::LightType)) { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + static const char* projection_types[] = { "Directional", "Spot", "Point" }; + int current_index = static_cast(value); + if (ImGui::Combo("##lightType", ¤t_index, projection_types, IM_ARRAYSIZE(projection_types))) { + value = static_cast(current_index); + reflectable.NotifyChange(prop_index); + update_iterators(); + } + } + else if (*type_info == typeid(Camera::ProjectionType)) { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + static const char* projection_types[] = { "Perspective", "Orthographic" }; + int current_index = static_cast(value); + if (ImGui::Combo("##proj", ¤t_index, projection_types, IM_ARRAYSIZE(projection_types))) { + value = static_cast(current_index); + reflectable.NotifyChange(prop_index); + update_iterators(); + } + } + else if (*type_info == typeid(std::string)) { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + if (value.capacity() < 128) value.reserve(128); + ImGui::PushID(prop_index); + if (ImGui::InputText("##s", value.data(), value.capacity() + 1)) { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + ImGui::PopID(); + } + else if (*type_info == typeid(vec)) { + auto& value = reflectable.template GetPropertyByIndex>(prop_index); + if (ImGui::DragFloat2("##vec2", static_cast(&value[0]), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + } + else if (*type_info == typeid(vec)) { + auto& value = reflectable.template GetPropertyByIndex>(prop_index); + if (ImGui::DragFloat3("##vec3", static_cast(&value[0]), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + } + else if (*type_info == typeid(vec)) { + auto& value = reflectable.template GetPropertyByIndex>(prop_index); + if (ImGui::DragFloat4("##vec4", static_cast(&value[0]), 0.01f, -1000.0f, 1000.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp)) { + reflectable.NotifyChange(prop_index); + update_iterators(); + } + } + else if (*type_info == typeid(color_vec)) { + auto& value = reflectable.template GetPropertyByIndex>(prop_index); + if (ImGui::ColorEdit3("##rgb", static_cast(&value[0]), ImGuiColorEditFlags_Float)) { + reflectable.NotifyChange(0); + update_iterators(); + } + } + else if (*type_info == typeid(color_vec)) { + auto& value = reflectable.template GetPropertyByIndex>(prop_index); + if (ImGui::ColorEdit4("##rgba", static_cast(&value[0]), ImGuiColorEditFlags_Float)) { + reflectable.NotifyChange(0); + update_iterators(); + } + } + else if (*type_info == typeid(MaterialIndexReflectable)) { + auto& value = reflectable.template GetPropertyByIndex(prop_index); + auto& material_group = ecs_ptr->GetGroupByIndex(value.material_index); + ImGui::EndDisabled(); + ImGui::PopID(); + ImGui::PopStyleVar(2); + popped_style_var = true; + RenderReflectables(material_group); + break; + } + else { + ImGui::TextDisabled(""); + } + + ImGui::EndDisabled(); + ImGui::PopID(); + } + break; + } + } + + if (!popped_style_var) + ImGui::PopStyleVar(2); + } + + ImGui::PopID(); + } +}; + std::pair AddPyramidMesh(ExampleECS& ecs, int material_index) { std::vector> positions = {