diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 43a32e9d..4f8b700e 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,7 +21,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-latest, windows-latest] + os: [windows-latest] steps: - uses: actions/checkout@v2 diff --git a/Assets/Materials/base_mat.icm b/Assets/Materials/base_mat.material.json similarity index 100% rename from Assets/Materials/base_mat.icm rename to Assets/Materials/base_mat.material.json diff --git a/Assets/Models/cube.obj b/Assets/Meshes/cube.obj similarity index 100% rename from Assets/Models/cube.obj rename to Assets/Meshes/cube.obj diff --git a/Assets/Models/sphere.obj b/Assets/Meshes/sphere.obj similarity index 100% rename from Assets/Models/sphere.obj rename to Assets/Meshes/sphere.obj diff --git a/Assets/Shaders/frag_uniforms.glsl b/Assets/Shaders/glsl/frag_uniforms.glsl similarity index 100% rename from Assets/Shaders/frag_uniforms.glsl rename to Assets/Shaders/glsl/frag_uniforms.glsl diff --git a/Assets/Shaders/lastpass.fs b/Assets/Shaders/glsl/lastpass.fs similarity index 100% rename from Assets/Shaders/lastpass.fs rename to Assets/Shaders/glsl/lastpass.fs diff --git a/Assets/Shaders/lastpass.vs b/Assets/Shaders/glsl/lastpass.vs similarity index 100% rename from Assets/Shaders/lastpass.vs rename to Assets/Shaders/glsl/lastpass.vs diff --git a/Assets/Shaders/normal.fs b/Assets/Shaders/glsl/normal.fs similarity index 100% rename from Assets/Shaders/normal.fs rename to Assets/Shaders/glsl/normal.fs diff --git a/Assets/Shaders/pbr.fs b/Assets/Shaders/glsl/pbr.fs similarity index 100% rename from Assets/Shaders/pbr.fs rename to Assets/Shaders/glsl/pbr.fs diff --git a/Assets/Shaders/phong.fs b/Assets/Shaders/glsl/phong.fs similarity index 100% rename from Assets/Shaders/phong.fs rename to Assets/Shaders/glsl/phong.fs diff --git a/Assets/Shaders/picking.fs b/Assets/Shaders/glsl/picking.fs similarity index 100% rename from Assets/Shaders/picking.fs rename to Assets/Shaders/glsl/picking.fs diff --git a/Assets/Shaders/skinning.vs b/Assets/Shaders/glsl/skinning.vs similarity index 93% rename from Assets/Shaders/skinning.vs rename to Assets/Shaders/glsl/skinning.vs index 085b09cb..7245dda7 100644 --- a/Assets/Shaders/skinning.vs +++ b/Assets/Shaders/glsl/skinning.vs @@ -14,7 +14,6 @@ layout (location = 6) in vec4 bone_weights; uniform mat4 model; uniform mat4 bonesTransformMatrices[MAX_BONES]; -uniform mat4 bonesOffsetMatrices[MAX_BONES]; out vec3 fnormal; out vec3 ftangent; @@ -46,7 +45,7 @@ void main() { break; } - mat4 finalBonesMatrix = bonesTransformMatrices[bone_ids[i]] * bonesOffsetMatrices[bone_ids[i]]; + mat4 finalBonesMatrix = bonesTransformMatrices[bone_ids[i]]; totalPosition += finalBonesMatrix * vec4(vertex, 1.0f) * bone_weights[i]; diff --git a/Assets/Shaders/skybox.fs b/Assets/Shaders/glsl/skybox.fs similarity index 100% rename from Assets/Shaders/skybox.fs rename to Assets/Shaders/glsl/skybox.fs diff --git a/Assets/Shaders/skybox.vs b/Assets/Shaders/glsl/skybox.vs similarity index 100% rename from Assets/Shaders/skybox.vs rename to Assets/Shaders/glsl/skybox.vs diff --git a/Assets/Shaders/solid.fs b/Assets/Shaders/glsl/solid.fs similarity index 100% rename from Assets/Shaders/solid.fs rename to Assets/Shaders/glsl/solid.fs diff --git a/Assets/Shaders/vert_uniforms.glsl b/Assets/Shaders/glsl/vert_uniforms.glsl similarity index 100% rename from Assets/Shaders/vert_uniforms.glsl rename to Assets/Shaders/glsl/vert_uniforms.glsl diff --git a/Assets/Shaders/lastpass.shader.json b/Assets/Shaders/lastpass.shader.json new file mode 100644 index 00000000..fa584148 --- /dev/null +++ b/Assets/Shaders/lastpass.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/lastpass.vs" + }, + { + "stage": "fragment", + "source": "glsl/lastpass.fs" + } +] \ No newline at end of file diff --git a/Assets/Shaders/normal.shader.json b/Assets/Shaders/normal.shader.json new file mode 100644 index 00000000..d3cc80d5 --- /dev/null +++ b/Assets/Shaders/normal.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/skinning.vs" + }, + { + "stage": "fragment", + "source": "glsl/normal.fs" + } +] \ No newline at end of file diff --git a/Assets/Shaders/pbr.shader.json b/Assets/Shaders/pbr.shader.json new file mode 100644 index 00000000..259a1796 --- /dev/null +++ b/Assets/Shaders/pbr.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/skinning.vs" + }, + { + "stage": "fragment", + "source": "glsl/pbr.fs" + } +] \ No newline at end of file diff --git a/Assets/Shaders/phong.shader.json b/Assets/Shaders/phong.shader.json new file mode 100644 index 00000000..158f01e4 --- /dev/null +++ b/Assets/Shaders/phong.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/skinning.vs" + }, + { + "stage": "fragment", + "source": "glsl/phong.fs" + } +] \ No newline at end of file diff --git a/Assets/Shaders/picking.shader.json b/Assets/Shaders/picking.shader.json new file mode 100644 index 00000000..606a78aa --- /dev/null +++ b/Assets/Shaders/picking.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/skinning.vs" + }, + { + "stage": "fragment", + "source": "glsl/picking.fs" + } +] \ No newline at end of file diff --git a/Assets/Shaders/skybox.shader.json b/Assets/Shaders/skybox.shader.json new file mode 100644 index 00000000..a5634f61 --- /dev/null +++ b/Assets/Shaders/skybox.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/skybox.vs" + }, + { + "stage": "fragment", + "source": "glsl/skybox.fs" + } +] \ No newline at end of file diff --git a/Assets/Shaders/solid.shader.json b/Assets/Shaders/solid.shader.json new file mode 100644 index 00000000..33324d75 --- /dev/null +++ b/Assets/Shaders/solid.shader.json @@ -0,0 +1,10 @@ +[ + { + "stage": "vertex", + "source": "glsl/skinning.vs" + }, + { + "stage": "fragment", + "source": "glsl/solid.fs" + } +] \ No newline at end of file diff --git a/Assets/Textures/Editor/folder.png b/Assets/Textures/Editor/folder.png new file mode 100644 index 00000000..b5908bc5 Binary files /dev/null and b/Assets/Textures/Editor/folder.png differ diff --git a/Assets/Textures/Editor/shader.png b/Assets/Textures/Editor/shader.png new file mode 100644 index 00000000..7212768a Binary files /dev/null and b/Assets/Textures/Editor/shader.png differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 358b7855..5088feec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,10 +22,6 @@ include_directories(includes) set(IMGUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/includes/ImGUI/imgui_impl_glfw.cpp ${CMAKE_CURRENT_SOURCE_DIR}/includes/ImGUI/imgui_impl_opengl3.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/includes/ImGUI/imgui.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/includes/ImGUI/imgui_widgets.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/includes/ImGUI/imgui_draw.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/includes/ImGUI/imgui_tables.cpp ) set(IMGUIZMO_SRC diff --git a/ICE/Assets/CMakeLists.txt b/ICE/Assets/CMakeLists.txt index a3352f0e..77bb5578 100644 --- a/ICE/Assets/CMakeLists.txt +++ b/ICE/Assets/CMakeLists.txt @@ -7,7 +7,12 @@ add_library(${PROJECT_NAME} STATIC) target_sources(${PROJECT_NAME} PRIVATE src/AssetBank.cpp - src/AssetPath.cpp) + src/AssetPath.cpp + src/Shader.cpp + src/Model.cpp + src/Material.cpp + src/GPURegistry.cpp + src/Texture.cpp) target_link_libraries(${PROJECT_NAME} PUBLIC diff --git a/ICE/Assets/include/Asset.h b/ICE/Assets/include/Asset.h index 1dae6038..4ef9e3ca 100644 --- a/ICE/Assets/include/Asset.h +++ b/ICE/Assets/include/Asset.h @@ -14,7 +14,7 @@ namespace ICE { typedef unsigned long long AssetUID; -enum class AssetType { ETex2D, ETexCube, EShader, EMesh, EModel, EMaterial }; +enum class AssetType { EModel, EMesh, EMaterial, ETex2D, ETexCube, EShader, EOther }; class Asset : public Resource { public: diff --git a/ICE/Assets/include/AssetBank.h b/ICE/Assets/include/AssetBank.h index 54600e16..f9492ca7 100644 --- a/ICE/Assets/include/AssetBank.h +++ b/ICE/Assets/include/AssetBank.h @@ -30,7 +30,7 @@ struct AssetBankEntry { class AssetBank { public: - AssetBank(const std::shared_ptr& factory); + AssetBank(); template std::shared_ptr getAsset(AssetUID uid) { @@ -167,6 +167,5 @@ class AssetBank { std::unordered_map nameMapping; std::unordered_map resources; AssetLoader loader; - std::shared_ptr graphics_factory; }; } // namespace ICE diff --git a/ICE/Assets/include/AssetLoader.h b/ICE/Assets/include/AssetLoader.h index 75d25cc2..d3a6d6d0 100644 --- a/ICE/Assets/include/AssetLoader.h +++ b/ICE/Assets/include/AssetLoader.h @@ -35,8 +35,8 @@ class AssetLoader { private: std::unordered_map< std::type_index, - std::variant>, std::shared_ptr>, std::shared_ptr>, - std::shared_ptr>, std::shared_ptr>>> + std::variant>, std::shared_ptr>, std::shared_ptr>, + std::shared_ptr>, std::shared_ptr>, std::shared_ptr>>> loaders; }; } // namespace ICE diff --git a/ICE/Assets/include/GPURegistry.h b/ICE/Assets/include/GPURegistry.h new file mode 100644 index 00000000..50ddf27c --- /dev/null +++ b/ICE/Assets/include/GPURegistry.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "Asset.h" +#include "AssetBank.h" +#include "Mesh.h" +#include "Shader.h" +#include "Texture.h" + +namespace ICE { +class GPURegistry { + public: + GPURegistry(const std::shared_ptr &factory, const std::shared_ptr &bank); + + AssetUID getUID(const AssetPath &path) const { return m_asset_bank->getUID(path); } + std::shared_ptr getMaterial(const AssetPath &path) { return m_asset_bank->getAsset(getUID(path)); } + std::shared_ptr getMaterial(AssetUID id) { return m_asset_bank->getAsset(id); } + std::shared_ptr getShader(AssetUID id); + std::shared_ptr getShader(const AssetPath &path) { return getShader(getUID(path)); } + AABB getMeshAABB(AssetUID id) { return m_asset_bank->getAsset(id)->getBoundingBox(); } + const SkinningData &getMeshSkinningData(AssetUID id) { return m_asset_bank->getAsset(id)->getSkinningData(); } + std::shared_ptr getMesh(AssetUID id); + std::shared_ptr getMesh(const AssetPath &path) { return getMesh(getUID(path)); } + std::shared_ptr getTexture2D(AssetUID id); + std::shared_ptr getTexture2D(const AssetPath &path) { return getTexture2D(getUID(path)); } + std::shared_ptr getCubemap(AssetUID id); + + private: + std::unordered_map> m_shader_programs; + std::unordered_map> m_gpu_meshes; + std::unordered_map> m_gpu_tex2d; + std::unordered_map> m_gpu_cubemaps; + + std::shared_ptr m_graphics_factory; + std::shared_ptr m_asset_bank; +}; +} // namespace ICE diff --git a/ICE/Graphics/include/Material.h b/ICE/Assets/include/Material.h similarity index 90% rename from ICE/Graphics/include/Material.h rename to ICE/Assets/include/Material.h index e4ade123..ea491459 100644 --- a/ICE/Graphics/include/Material.h +++ b/ICE/Assets/include/Material.h @@ -36,6 +36,9 @@ class Material : public Asset { } } + void renameUniform(const std::string& previous_name, const std::string& new_name); + void removeUniform(const std::string& name); + std::unordered_map getAllUniforms() const; AssetUID getShader() const; void setShader(AssetUID shader_id); diff --git a/ICE/Graphics/include/Mesh.h b/ICE/Assets/include/Mesh.h similarity index 68% rename from ICE/Graphics/include/Mesh.h rename to ICE/Assets/include/Mesh.h index da0ae0de..aa268f97 100644 --- a/ICE/Graphics/include/Mesh.h +++ b/ICE/Assets/include/Mesh.h @@ -1,70 +1,64 @@ -// -// Created by Thomas Ibanez on 16.11.20. -// - -#pragma once - -#include -#include - -#include -#include - -#include "VertexArray.h" - -namespace ICE { - -constexpr int MAX_BONES_PER_VERTEX = 4; -constexpr int MAX_BONES = 100; -constexpr int INVALID_BONE_ID = -1; - -struct MeshData { - std::vector vertices; - std::vector normals; - std::vector uvCoords; - std::vector tangents; - std::vector bitangents; - std::vector boneIDs; - std::vector boneWeights; - std::vector indices; -}; - -struct SkinningData { - std::unordered_map boneOffsetMatrices; -}; - -class Mesh : public Asset { - public: - Mesh(const MeshData &data); - Mesh(MeshData &&data); - - const std::vector &getVertices() const; - - const std::vector &getNormals() const; - - const std::vector &getUVCoords() const; - - const std::vector &getIndices() const; - - const std::shared_ptr getVertexArray() const; - void setVertexArray(const std::shared_ptr &vao); - - const AABB &getBoundingBox() const; - - std::string getTypeName() const override; - AssetType getType() const override; - bool usesBones() const { return m_has_bones; } - void setSkinningData(const SkinningData &skinningData) { - m_skinningData = skinningData; - m_has_bones = true; - } - - SkinningData getSkinningData() const { return m_skinningData; } - private: - MeshData m_data; - SkinningData m_skinningData; - bool m_has_bones = false; - std::shared_ptr vertexArray; - AABB boundingBox; -}; +// +// Created by Thomas Ibanez on 16.11.20. +// + +#pragma once + +#include +#include + +#include +#include + +#include "VertexArray.h" + +namespace ICE { + +constexpr int MAX_BONES_PER_VERTEX = 4; +constexpr int MAX_BONES = 100; +constexpr int INVALID_BONE_ID = -1; + +struct MeshData { + std::vector vertices; + std::vector normals; + std::vector uvCoords; + std::vector tangents; + std::vector bitangents; + std::vector boneIDs; + std::vector boneWeights; + std::vector indices; +}; + +struct SkinningData { + std::unordered_map inverseBindMatrices; //BoneID -> Inverse Bind Matrix +}; + +class Mesh : public Asset { + public: + Mesh(const MeshData &data); + Mesh(MeshData &&data); + + const MeshData &getMeshData() const { return m_data; } + + const std::vector &getVertices() const; + + const std::vector &getNormals() const; + + const std::vector &getUVCoords() const; + + const std::vector &getIndices() const; + + const AABB &getBoundingBox() const; + + const SkinningData &getSkinningData() const { return m_skinningData; } + void setIBM(int boneID, const Eigen::Matrix4f &ibm) { m_skinningData.inverseBindMatrices[boneID] = ibm; } + + std::string getTypeName() const override; + AssetType getType() const override; + + private: + MeshData m_data; + SkinningData m_skinningData; + AABB boundingBox; +}; } // namespace ICE \ No newline at end of file diff --git a/ICE/Graphics/include/Model.h b/ICE/Assets/include/Model.h similarity index 59% rename from ICE/Graphics/include/Model.h rename to ICE/Assets/include/Model.h index 28558c4a..c8a6e50a 100644 --- a/ICE/Graphics/include/Model.h +++ b/ICE/Assets/include/Model.h @@ -9,41 +9,38 @@ class Model : public Asset { public: struct Node { std::string name; - Eigen::Matrix4f localTransform; // default transform - Eigen::Matrix4f animatedTransform; // For mesh-only animation (no bones) - std::vector meshIndices; // Meshes used by this node - std::vector children; // Node indices - }; - - struct BoneInfo { - Eigen::Matrix4f finalTransformation; + Eigen::Matrix4f localTransform; // default transform + std::vector meshIndices; // Meshes used by this node + std::vector children; // Node indices }; struct Skeleton { std::unordered_map boneMapping; - std::vector bones; Eigen::Matrix4f globalInverseTransform; }; - Model(const std::vector &nodes, const std::vector> &meshes, const std::vector &materials); + Model(const std::vector &nodes, const std::vector &meshes, const std::vector &materials); std::vector getNodes() const { return m_nodes; } std::vector &getNodes() { return m_nodes; } - std::vector> getMeshes() const { return m_meshes; } - std::vector getMaterialsIDs() const { return m_materials; } + std::vector getMeshes() const { return m_meshes; } + std::vector getMaterialsIDs() const { return m_materials; } AABB getBoundingBox() const { return m_boundingbox; } std::unordered_map getAnimations() const { return m_animations; } Skeleton &getSkeleton() { return m_skeleton; } void setSkeleton(const Skeleton &skeleton) { m_skeleton = skeleton; } void setAnimations(const std::unordered_map &animations) { m_animations = animations; } + void traverse(std::vector &meshes, std::vector &materials, std::vector &transforms, + const Eigen::Matrix4f &base_transform = Eigen::Matrix4f::Identity()); + AssetType getType() const override { return AssetType::EModel; } std::string getTypeName() const override { return "Model"; } private: std::vector m_nodes; - std::vector> m_meshes; - std::vector m_materials; + std::vector m_meshes; + std::vector m_materials; std::unordered_map m_animations; Skeleton m_skeleton; AABB m_boundingbox{{0, 0, 0}, {0, 0, 0}}; diff --git a/ICE/Assets/include/Shader.h b/ICE/Assets/include/Shader.h new file mode 100644 index 00000000..d8fa20d1 --- /dev/null +++ b/ICE/Assets/include/Shader.h @@ -0,0 +1,31 @@ +// +// Created by Thomas Ibanez on 20.11.20. +// + +#pragma once + +#include + +#include + +namespace ICE { +enum class ShaderStage { Vertex, Fragment, Geometry, TessControl, TessEval, Compute }; + +// Map of shader stages to their source code {filename, source} +using ShaderSource = std::unordered_map>; + +class Shader : public Asset { + public: + Shader() = default; + Shader(const ShaderSource& sources) : m_sources(sources) {} + + ShaderSource getSources() const { return m_sources; } + + std::string getTypeName() const override; + + AssetType getType() const override; + + private: + ShaderSource m_sources; +}; +} // namespace ICE \ No newline at end of file diff --git a/ICE/Graphics/include/Texture.h b/ICE/Assets/include/Texture.h similarity index 58% rename from ICE/Graphics/include/Texture.h rename to ICE/Assets/include/Texture.h index 6de9653d..1e085892 100644 --- a/ICE/Graphics/include/Texture.h +++ b/ICE/Assets/include/Texture.h @@ -2,12 +2,10 @@ // Created by Thomas Ibanez on 22.12.20. // -#ifndef ICE_TEXTURE_H -#define ICE_TEXTURE_H -#include - +#pragma once #include #include +#include #include #include @@ -21,31 +19,40 @@ enum class TextureType { Tex2D = 0, CubeMap = 1 }; class Texture : public Asset { public: - virtual void bind(uint32_t slot = 0) const = 0; - virtual void setData(void* data, uint32_t size) = 0; - - virtual TextureFormat getFormat() const = 0; - - virtual uint32_t getWidth() const = 0; - virtual uint32_t getHeight() const = 0; + virtual ~Texture() { + if (data_ != nullptr) { + stbi_image_free(data_); + data_ = nullptr; + } + } + const void* data() const { return data_; } - virtual void* getTexture() const = 0; + TextureFormat getFormat() const { return m_format; } + TextureWrap getWrap() const { return m_wrap; } - virtual TextureType getTextureType() const = 0; + uint32_t getWidth() const { return m_width; } + uint32_t getHeight() const { return m_height; } + protected: static void* getDataFromFile(const std::string file, int* width, int* height, int* channels, int force = STBI_default) { stbi_uc* data = stbi_load(file.c_str(), width, height, channels, force); - if(data == nullptr) { + if (data == nullptr) { Logger::Log(Logger::ERROR, "Graphics", "Texture %s could not load: %s", file.c_str(), stbi_failure_reason()); } return data; } + + void* data_ = nullptr; + TextureWrap m_wrap = TextureWrap::Repeat; + TextureFormat m_format = TextureFormat::None; + int m_width = 0; + int m_height = 0; }; class Texture2D : public Texture { public: - virtual TextureWrap getWrap() const = 0; - virtual TextureType getTextureType() const = 0; + Texture2D(const std::string& path); + Texture2D(void* data, int width, int height, TextureFormat fmt); virtual AssetType getType() const override { return AssetType::ETex2D; } virtual std::string getTypeName() const override { return "Texture2D"; } @@ -53,12 +60,10 @@ class Texture2D : public Texture { class TextureCube : public Texture { public: - virtual TextureWrap getWrap() const = 0; - virtual TextureType getTextureType() const = 0; + TextureCube(const std::string& path); + TextureCube(void* data, int width, int height, TextureFormat fmt); virtual AssetType getType() const override { return AssetType::ETexCube; } virtual std::string getTypeName() const override { return "TextureCube"; } }; -} // namespace ICE - -#endif //ICE_TEXTURE_H +} // namespace ICE \ No newline at end of file diff --git a/ICE/Assets/src/AssetBank.cpp b/ICE/Assets/src/AssetBank.cpp index 3ac81f18..3665be7e 100644 --- a/ICE/Assets/src/AssetBank.cpp +++ b/ICE/Assets/src/AssetBank.cpp @@ -8,18 +8,20 @@ #include #include "MaterialLoader.h" +#include "MeshLoader.h" #include "ModelLoader.h" #include "ShaderLoader.h" #include "TextureLoader.h" namespace ICE { -AssetBank::AssetBank(const std::shared_ptr &factory) : graphics_factory(factory) { - loader.AddLoader(std::make_shared(factory)); - loader.AddLoader(std::make_shared(factory)); - loader.AddLoader(std::make_shared(factory, *this)); - loader.AddLoader(std::make_shared(factory)); +AssetBank::AssetBank() { + loader.AddLoader(std::make_shared()); + loader.AddLoader(std::make_shared()); + loader.AddLoader(std::make_shared(*this)); + loader.AddLoader(std::make_shared()); loader.AddLoader(std::make_shared()); + loader.AddLoader(std::make_shared()); } bool AssetBank::nameInUse(const AssetPath &name) { diff --git a/ICE/Assets/src/AssetPath.cpp b/ICE/Assets/src/AssetPath.cpp index 2460372e..67b5dc6b 100644 --- a/ICE/Assets/src/AssetPath.cpp +++ b/ICE/Assets/src/AssetPath.cpp @@ -13,6 +13,7 @@ namespace ICE { std::unordered_map AssetPath::typenames = {{typeid(Texture2D), "Textures"}, {typeid(TextureCube), "CubeMaps"}, + {typeid(Mesh), "Meshes"}, {typeid(Model), "Models"}, {typeid(Material), "Materials"}, {typeid(Shader), "Shaders"}}; diff --git a/ICE/Assets/src/GPURegistry.cpp b/ICE/Assets/src/GPURegistry.cpp new file mode 100644 index 00000000..10513c71 --- /dev/null +++ b/ICE/Assets/src/GPURegistry.cpp @@ -0,0 +1,98 @@ +#include "GPURegistry.h" + +#include + +namespace ICE { +GPURegistry::GPURegistry(const std::shared_ptr &factory, const std::shared_ptr &bank) + : m_graphics_factory(factory), + m_asset_bank(bank) { +} + +std::shared_ptr GPURegistry::getShader(AssetUID id) { + if (m_shader_programs.contains(id)) { + return m_shader_programs[id]; + } else { + auto shader_asset = m_asset_bank->getAsset(id); + if (!shader_asset) { + return nullptr; + } + auto program = m_graphics_factory->createShader(*shader_asset); + m_shader_programs[id] = program; + return program; + } +} + +std::shared_ptr GPURegistry::getMesh(AssetUID id) { + if (m_gpu_meshes.contains(id)) { + return m_gpu_meshes[id]; + } else { + auto mesh_asset = m_asset_bank->getAsset(id); + if (!mesh_asset) { + return nullptr; + } + + auto vertexArray = m_graphics_factory->createVertexArray(); + + auto vertexBuffer = m_graphics_factory->createVertexBuffer(); + auto normalsBuffer = m_graphics_factory->createVertexBuffer(); + auto uvBuffer = m_graphics_factory->createVertexBuffer(); + auto tangentBuffer = m_graphics_factory->createVertexBuffer(); + auto biTangentBuffer = m_graphics_factory->createVertexBuffer(); + auto boneIDBuffer = m_graphics_factory->createVertexBuffer(); + auto boneWeightBuffer = m_graphics_factory->createVertexBuffer(); + auto indexBuffer = m_graphics_factory->createIndexBuffer(); + + auto data = mesh_asset->getMeshData(); + vertexBuffer->putData(BufferUtils::CreateFloatBuffer(data.vertices), 3 * data.vertices.size() * sizeof(float)); + normalsBuffer->putData(BufferUtils::CreateFloatBuffer(data.normals), 3 * data.normals.size() * sizeof(float)); + tangentBuffer->putData(BufferUtils::CreateFloatBuffer(data.tangents), 3 * data.tangents.size() * sizeof(float)); + biTangentBuffer->putData(BufferUtils::CreateFloatBuffer(data.bitangents), 3 * data.bitangents.size() * sizeof(float)); + boneIDBuffer->putData(BufferUtils::CreateIntBuffer(data.boneIDs), 4 * data.boneIDs.size() * sizeof(int)); + boneWeightBuffer->putData(BufferUtils::CreateFloatBuffer(data.boneWeights), 4 * data.boneWeights.size() * sizeof(float)); + uvBuffer->putData(BufferUtils::CreateFloatBuffer(data.uvCoords), 2 * data.uvCoords.size() * sizeof(float)); + indexBuffer->putData(BufferUtils::CreateIntBuffer(data.indices), 3 * data.indices.size() * sizeof(int)); + + vertexArray->pushVertexBuffer(vertexBuffer, 0, 3); + vertexArray->pushVertexBuffer(normalsBuffer, 1, 3); + vertexArray->pushVertexBuffer(uvBuffer, 2, 2); + vertexArray->pushVertexBuffer(tangentBuffer, 3, 3); + vertexArray->pushVertexBuffer(biTangentBuffer, 4, 3); + vertexArray->pushVertexBuffer(boneIDBuffer, 5, 4); + vertexArray->pushVertexBuffer(boneWeightBuffer, 6, 4); + vertexArray->setIndexBuffer(indexBuffer); + + std::shared_ptr gpu_mesh = std::make_shared(vertexArray); + m_gpu_meshes[id] = gpu_mesh; + return gpu_mesh; + } +} + +std::shared_ptr GPURegistry::getTexture2D(AssetUID id) { + if (m_gpu_tex2d.contains(id)) { + return m_gpu_tex2d[id]; + } else { + auto tex_asset = m_asset_bank->getAsset(id); + if (!tex_asset) { + return nullptr; + } + + auto gpu_texture = m_graphics_factory->createTexture2D(*tex_asset); + m_gpu_tex2d[id] = gpu_texture; + return gpu_texture; + } +} + +std::shared_ptr GPURegistry::getCubemap(AssetUID id) { + if (m_gpu_cubemaps.contains(id)) { + return m_gpu_cubemaps[id]; + } else { + auto tex_asset = m_asset_bank->getAsset(id); + if (!tex_asset) { + return nullptr; + } + auto gpu_texture = m_graphics_factory->createTextureCube(*tex_asset); + m_gpu_cubemaps[id] = gpu_texture; + return gpu_texture; + } +} +} // namespace ICE diff --git a/ICE/Graphics/src/Material.cpp b/ICE/Assets/src/Material.cpp similarity index 60% rename from ICE/Graphics/src/Material.cpp rename to ICE/Assets/src/Material.cpp index 19a5cc84..2ceb8559 100644 --- a/ICE/Graphics/src/Material.cpp +++ b/ICE/Assets/src/Material.cpp @@ -21,6 +21,20 @@ bool Material::isTransparent() const { return m_transparent; } +void Material::renameUniform(const std::string& previous_name, const std::string& new_name) { + if (m_uniforms.contains(previous_name)) { + auto& val = m_uniforms[previous_name]; + m_uniforms.try_emplace(new_name, val); + m_uniforms.erase(previous_name); + } +} + +void Material::removeUniform(const std::string& name) { + if (m_uniforms.contains(name)) { + m_uniforms.erase(name); + } +} + std::unordered_map Material::getAllUniforms() const { return m_uniforms; } diff --git a/ICE/Assets/src/Model.cpp b/ICE/Assets/src/Model.cpp new file mode 100644 index 00000000..c492cd76 --- /dev/null +++ b/ICE/Assets/src/Model.cpp @@ -0,0 +1,40 @@ +#include "Model.h" + +namespace ICE { +Model::Model(const std::vector &nodes, const std::vector &meshes, const std::vector &materials) + : m_nodes(nodes), + m_meshes(meshes), + m_materials(materials) { + /* for (const auto &mesh : meshes) { + m_boundingbox = m_boundingbox.unionWith(mesh->getBoundingBox()); + }*/ +} + +void Model::traverse(std::vector &meshes, std::vector &materials, std::vector &transforms, + const Eigen::Matrix4f &base_transform) { + std::function recursive_traversal = [&](int node_idx, const Eigen::Matrix4f &transform) { + auto &node = m_nodes.at(node_idx); + for (const auto &i : node.meshIndices) { + if (i >= m_meshes.size()) { + continue; + } + auto mtl_id = m_materials.at(i); + auto mesh = m_meshes.at(i); + + Eigen::Matrix4f node_transform; + node_transform = transform * node.localTransform; + + meshes.push_back(mesh); + materials.push_back(mtl_id); + transforms.push_back(node_transform); + } + + for (const auto &child_idx : node.children) { + recursive_traversal(child_idx, transform); + } + }; + + recursive_traversal(0, base_transform); +} + +} // namespace ICE \ No newline at end of file diff --git a/ICE/Assets/src/Shader.cpp b/ICE/Assets/src/Shader.cpp new file mode 100644 index 00000000..1cb05bfe --- /dev/null +++ b/ICE/Assets/src/Shader.cpp @@ -0,0 +1,13 @@ +#include "Shader.h" + +namespace ICE { + +std::string Shader::getTypeName() const { + return "Shader"; +}; + +AssetType Shader::getType() const { + return AssetType::EShader; +}; + +} // namespace ICE diff --git a/ICE/Assets/src/Texture.cpp b/ICE/Assets/src/Texture.cpp new file mode 100644 index 00000000..36b9e101 --- /dev/null +++ b/ICE/Assets/src/Texture.cpp @@ -0,0 +1,33 @@ +#include "Texture.h" + +namespace ICE { + +Texture2D::Texture2D(const std::string& path) { + int channels = 0; + data_ = getDataFromFile(path, &m_width, &m_height, &channels); + if (channels == 3) { + m_format = TextureFormat::RGB8; + } else if (channels == 4) { + m_format = TextureFormat::RGBA8; + } else if (channels == 1) { + m_format = TextureFormat::MONO8; + } else { + m_format = TextureFormat::None; + } +} +Texture2D::Texture2D(void* data, int width, int height, TextureFormat fmt) { + data_ = data; + m_width = width; + m_height = height; + m_format = fmt; +} + +TextureCube::TextureCube(const std::string& path) { +} +TextureCube::TextureCube(void* data, int width, int height, TextureFormat fmt) { + data_ = data; + m_width = width; + m_height = height; + m_format = fmt; +} +} // namespace ICE diff --git a/ICE/Assets/test/AssetBankTest.cpp b/ICE/Assets/test/AssetBankTest.cpp index 7c7ccd11..83d47857 100644 --- a/ICE/Assets/test/AssetBankTest.cpp +++ b/ICE/Assets/test/AssetBankTest.cpp @@ -5,14 +5,11 @@ #include #include "AssetBank.h" -#include "NoneGraphicsFactory.h" using namespace ICE; - TEST(AssetBankTest, AddedAssetsCanBeRetrieved) { - NoneGraphicsFactory g_fac; - AssetBank ab(std::make_shared(g_fac)); + AssetBank ab; auto mtl = std::make_shared(); ab.addAsset("a_ice_test_mtl", mtl); ASSERT_EQ(ab.getAsset("a_ice_test_mtl"), mtl); @@ -23,19 +20,19 @@ TEST(AssetBankTest, AddedAssetsCanBeRetrieved) { ASSERT_EQ(ab.getAsset("a_ice_test_mesh"), mesh); ASSERT_EQ(ab.getAsset("lel"), nullptr); - auto tex = g_fac.createTexture2D("Not needed for this"); + auto tex = std::make_shared(nullptr, 0, 0, ICE::TextureFormat::SRGB8); ab.addAsset("a_ice_test_tex", tex); ASSERT_EQ(ab.getAsset("a_ice_test_tex"), tex); ASSERT_EQ(ab.getAsset("lil"), nullptr); - auto shader = g_fac.createShader("", ""); + auto shader = std::make_shared(); ab.addAsset("a_ice_test_shader", shader); ASSERT_EQ(ab.getAsset("a_ice_test_shader"), shader); ASSERT_EQ(ab.getAsset("lul"), nullptr); } TEST(AssetBankTest, AssetsCanBeRenamed) { - AssetBank ab(std::make_shared()); + AssetBank ab; auto mtl = std::make_shared(); ab.addAsset("a_ice_test_mtl", mtl); ASSERT_EQ(ab.getAsset("a_ice_test_mtl"), mtl); @@ -47,7 +44,7 @@ TEST(AssetBankTest, AssetsCanBeRenamed) { } TEST(AssetBankTest, GetNameReturnsCorrectName) { - AssetBank ab(std::make_shared()); + AssetBank ab; auto mtl = std::make_shared(); ab.addAsset("a_ice_test_mtl", mtl); ASSERT_EQ(AssetPath("Materials/a_ice_test_mtl"), ab.getName(ab.getUID(AssetPath("Materials/a_ice_test_mtl")))); @@ -55,7 +52,7 @@ TEST(AssetBankTest, GetNameReturnsCorrectName) { } TEST(AssetBankTest, NameInUseBehavesCorrectly) { - AssetBank ab(std::make_shared()); + AssetBank ab; auto mtl = std::make_shared(); ab.addAsset("a_ice_test_mtl", mtl); ASSERT_TRUE(ab.nameInUse(AssetPath::WithTypePrefix("a_ice_test_mtl"))); diff --git a/ICE/Components/include/RenderComponent.h b/ICE/Components/include/RenderComponent.h index f44bb4b9..e5e44d36 100644 --- a/ICE/Components/include/RenderComponent.h +++ b/ICE/Components/include/RenderComponent.h @@ -11,8 +11,9 @@ namespace ICE { struct RenderComponent : public Component { - RenderComponent(AssetUID model_id) : model(model_id) {} - AssetUID model; + RenderComponent(AssetUID mesh_id, AssetUID material_id) : mesh(mesh_id), material(material_id) {} + AssetUID mesh; + AssetUID material; }; } // namespace ICE diff --git a/ICE/Components/include/SkeletonPoseComponent.h b/ICE/Components/include/SkeletonPoseComponent.h new file mode 100644 index 00000000..2185f326 --- /dev/null +++ b/ICE/Components/include/SkeletonPoseComponent.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace ICE { +struct SkeletonPoseComponent : public Component { + AssetUID skeletonModel = NO_ASSET_ID; + std::vector bone_transform; + std::unordered_map bone_entity; +}; +} // namespace ICE \ No newline at end of file diff --git a/ICE/Components/include/SkinningComponent.h b/ICE/Components/include/SkinningComponent.h new file mode 100644 index 00000000..b7ee5c16 --- /dev/null +++ b/ICE/Components/include/SkinningComponent.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace ICE { +struct SkinningComponent : public Component { + Entity skeleton_entity = 0; +}; +} // namespace ICE \ No newline at end of file diff --git a/ICE/Components/include/TransformComponent.h b/ICE/Components/include/TransformComponent.h index 1ac2de52..1b87c697 100644 --- a/ICE/Components/include/TransformComponent.h +++ b/ICE/Components/include/TransformComponent.h @@ -1,7 +1,3 @@ -// -// Created by Thomas Ibanez on 16.11.20. -// - #pragma once #include @@ -10,53 +6,127 @@ #include "ICEMath.h" namespace ICE { + struct TransformComponent : public Component { - TransformComponent(const Eigen::Vector3f &pos, const Eigen::Vector3f &rot, const Eigen::Vector3f &sca) + TransformComponent(const Eigen::Vector3f& pos = Eigen::Vector3f::Zero(), const Eigen::Quaternionf& rot = Eigen::Quaternionf::Identity(), + const Eigen::Vector3f& sca = Eigen::Vector3f::Ones()) : m_position(pos), m_rotation(rot), m_scale(sca) {} + TransformComponent(const Eigen::Vector3f& pos = Eigen::Vector3f::Zero(), const Eigen::Vector3f& rot = Eigen::Vector3f::Zero(), + const Eigen::Vector3f& sca = Eigen::Vector3f::Ones()) + : m_position(pos), + m_scale(sca) { + setRotationEulerDeg(rot); + } + + TransformComponent(const Eigen::Matrix4f& matrix) { + m_position = matrix.block<3, 1>(0, 3); + + Eigen::Vector3f vX = matrix.block<3, 1>(0, 0); + Eigen::Vector3f vY = matrix.block<3, 1>(0, 1); + Eigen::Vector3f vZ = matrix.block<3, 1>(0, 2); + + m_scale << vX.norm(), vY.norm(), vZ.norm(); + + if (matrix.block<3, 3>(0, 0).determinant() < 0) { + m_scale.x() *= -1.0f; + vX *= -1.0f; // Flip the axis for rotation calculation + } + + vX.normalize(); + vY.normalize(); + vZ.normalize(); + + Eigen::Matrix3f rot_matrix; + rot_matrix << vX, vY, vZ; + + m_rotation = Eigen::Quaternionf(rot_matrix); + } + Eigen::Vector3f getPosition() const { return m_position; } - Eigen::Vector3f getRotation() const { return m_rotation; } + Eigen::Quaternionf getRotation() const { return m_rotation; } Eigen::Vector3f getScale() const { return m_scale; } + Eigen::Vector3f getRotationEulerDeg() const { return m_rotation.toRotationMatrix().eulerAngles(0, 1, 2) * 180.0 / M_PI; } + Eigen::Matrix4f getModelMatrix() const { if (m_dirty) { - m_model_matrix = transformationMatrix(m_position, m_rotation, m_scale); + m_model_matrix = Eigen::Matrix4f::Identity(); + + // Translation + m_model_matrix.block<3, 1>(0, 3) = m_position; + + // Rotation + m_model_matrix.block<3, 3>(0, 0) = m_rotation.toRotationMatrix(); + + // Scale + m_model_matrix.block<3, 3>(0, 0) *= m_scale.asDiagonal(); + m_dirty = false; } return m_model_matrix; } - Eigen::Vector3f &position() { + Eigen::Matrix4f getWorldMatrix() const { + if (m_dirty) { + m_world_matrix = m_parent_matrix * getModelMatrix(); + } + return m_world_matrix; + } + + void updateParentMatrix(const Eigen::Matrix4f& parent_matrix) { + m_parent_matrix = parent_matrix; + m_world_matrix = parent_matrix * getModelMatrix(); + } + + Eigen::Vector3f& position() { m_dirty = true; return m_position; } - Eigen::Vector3f &rotation() { + + Eigen::Quaternionf& rotation() { m_dirty = true; return m_rotation; } - Eigen::Vector3f &scale() { + + Eigen::Vector3f& scale() { m_dirty = true; return m_scale; } - void setPosition(const Eigen::Vector3f &_position) { + void setPosition(const Eigen::Vector3f& position) { + m_dirty = true; + m_position = position; + } + + void setRotation(const Eigen::Quaternionf& rotation) { m_dirty = true; - m_position = _position; + m_rotation = rotation.normalized(); } - void setRotation(const Eigen::Vector3f &_rotation) { + + void setRotationEulerDeg(const Eigen::Vector3f& eulerDeg) { m_dirty = true; - m_rotation = _rotation; + Eigen::Vector3f rad = eulerDeg * M_PI / 180.0; + m_rotation = Eigen::AngleAxisf(rad.x(), Eigen::Vector3f::UnitX()) * Eigen::AngleAxisf(rad.y(), Eigen::Vector3f::UnitY()) + * Eigen::AngleAxisf(rad.z(), Eigen::Vector3f::UnitZ()); } - void setScale(const Eigen::Vector3f &_scale) { + + void setScale(const Eigen::Vector3f& scale) { m_dirty = true; - m_scale = _scale; + m_scale = scale; } private: - Eigen::Vector3f m_position, m_rotation, m_scale; - mutable Eigen::Matrix4f m_model_matrix; + Eigen::Vector3f m_position; + Eigen::Quaternionf m_rotation; + Eigen::Vector3f m_scale; + + mutable Eigen::Matrix4f m_model_matrix = Eigen::Matrix4f::Identity(); + mutable Eigen::Matrix4f m_parent_matrix = Eigen::Matrix4f::Identity(); + mutable Eigen::Matrix4f m_world_matrix = Eigen::Matrix4f::Identity(); mutable bool m_dirty = true; }; + } // namespace ICE diff --git a/ICE/Core/include/ICEEngine.h b/ICE/Core/include/ICEEngine.h index a2f71f54..c7482fad 100644 --- a/ICE/Core/include/ICEEngine.h +++ b/ICE/Core/include/ICEEngine.h @@ -29,6 +29,7 @@ class ICEEngine { std::shared_ptr getCamera(); std::shared_ptr getAssetBank(); + std::shared_ptr getGPURegistry(); Entity getSelected() const; @@ -49,6 +50,8 @@ class ICEEngine { std::shared_ptr getInternalFramebuffer() const; void setRenderFramebufferInternal(bool use_internal); + std::shared_ptr getWindow() const; + private: std::shared_ptr m_graphics_factory; std::shared_ptr ctx; @@ -63,6 +66,5 @@ class ICEEngine { std::chrono::steady_clock::time_point lastFrameTime; EngineConfig config; - Registry registry; }; } // namespace ICE diff --git a/ICE/Core/src/ICEEngine.cpp b/ICE/Core/src/ICEEngine.cpp index 9b226ed2..1d54da41 100644 --- a/ICE/Core/src/ICEEngine.cpp +++ b/ICE/Core/src/ICEEngine.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace ICE { ICEEngine::ICEEngine() : camera(std::make_shared(60, 16.f / 9.f, 0.1f, 100000)), config(EngineConfig::LoadFromFile()) { @@ -42,12 +44,16 @@ void ICEEngine::step() { } void ICEEngine::setupScene(const std::shared_ptr &camera_) { - auto renderer = std::make_shared(api, m_graphics_factory, project->getCurrentScene()->getRegistry(), project->getAssetBank()); - auto rs = std::make_shared(); + auto renderer = std::make_shared(api, m_graphics_factory); + auto rs = std::make_shared(api, m_graphics_factory, project->getCurrentScene()->getRegistry(), project->getGPURegistry()); + auto as = std::make_shared(project->getCurrentScene()->getRegistry(), project->getAssetBank()); + auto sgs = std::make_shared(project->getCurrentScene()); rs->setCamera(camera_); rs->setRenderer(renderer); project->getCurrentScene()->getRegistry()->addSystem(rs); - + project->getCurrentScene()->getRegistry()->addSystem(as); + project->getCurrentScene()->getRegistry()->addSystem(sgs); + camera = camera_; auto [w, h] = m_window->getSize(); renderer->resize(w, h); } @@ -60,6 +66,11 @@ std::shared_ptr ICEEngine::getAssetBank() { return project->getAssetBank(); } +std::shared_ptr ICEEngine::getGPURegistry() { + return project->getGPURegistry(); +} + + std::shared_ptr ICEEngine::getApi() const { return api; } @@ -80,6 +91,10 @@ void ICEEngine::setRenderFramebufferInternal(bool use_internal) { } } +std::shared_ptr ICEEngine::getWindow() const { + return m_window; +} + void ICEEngine::setProject(const std::shared_ptr &project) { this->project = project; this->camera->getPosition() = project->getCameraPosition(); diff --git a/ICE/Graphics/CMakeLists.txt b/ICE/Graphics/CMakeLists.txt index 86100b0d..726804a5 100644 --- a/ICE/Graphics/CMakeLists.txt +++ b/ICE/Graphics/CMakeLists.txt @@ -9,10 +9,8 @@ target_sources(${PROJECT_NAME} PRIVATE src/PerspectiveCamera.cpp src/OrthographicCamera.cpp src/ForwardRenderer.cpp - src/Material.cpp src/Mesh.cpp - src/Model.cpp - src/GeometryPass.cpp) + src/GeometryPass.cpp "include/GPUMesh.h" "include/GPUTexture.h") target_link_libraries(${PROJECT_NAME} PUBLIC diff --git a/ICE/Graphics/include/ForwardRenderer.h b/ICE/Graphics/include/ForwardRenderer.h index 28f604aa..5fb71c58 100644 --- a/ICE/Graphics/include/ForwardRenderer.h +++ b/ICE/Graphics/include/ForwardRenderer.h @@ -22,46 +22,36 @@ namespace ICE { class ForwardRenderer : public Renderer { public: - ForwardRenderer(const std::shared_ptr& api, const std::shared_ptr& factory, - const std::shared_ptr& registry, const std::shared_ptr& assetBank); + ForwardRenderer(const std::shared_ptr& api, const std::shared_ptr& factory); - void submit(Entity e) override; - void remove(Entity e) override; + void submitSkybox(const Skybox& e) override; + void submitDrawable(const Drawable& e) override; + void submitLight(const Light& e) override; void prepareFrame(Camera& camera) override; - void render() override; + std::shared_ptr render() override; void endFrame() override; - void setTarget(const std::shared_ptr& fb) override; - void resize(uint32_t width, uint32_t height) override; void setClearColor(Eigen::Vector4f clearColor) override; void setViewport(int x, int y, int w, int h) override; private: - void submitModel(int node_idx, const std::vector& nodes, const std::vector>& meshes, - const std::vector& materials, const Eigen::Matrix4f& transform); - std::shared_ptr m_api; - std::shared_ptr m_registry; - std::shared_ptr m_asset_bank; - - std::shared_ptr target = nullptr; std::vector m_render_commands; - std::vector m_render_queue; - std::vector m_lights; - AssetUID m_skybox = NO_ASSET_ID; GeometryPass m_geometry_pass; - std::shared_ptr m_quad_vao; - std::shared_ptr m_camera_ubo; std::shared_ptr m_light_ubo; + std::optional m_skybox; + std::vector m_drawables; + std::vector m_lights; + RendererConfig config; }; } // namespace ICE \ No newline at end of file diff --git a/ICE/Graphics/include/GPUMesh.h b/ICE/Graphics/include/GPUMesh.h new file mode 100644 index 00000000..f97a786d --- /dev/null +++ b/ICE/Graphics/include/GPUMesh.h @@ -0,0 +1,14 @@ +#pragma once + +#include "VertexArray.h" + +namespace ICE { +class GPUMesh { + public: + explicit GPUMesh(const std::shared_ptr &vao) : m_vao(vao) {} + const std::shared_ptr getVertexArray() const { return m_vao; } + + private: + std::shared_ptr m_vao; +}; +} // namespace ICE diff --git a/ICE/Graphics/include/GPUTexture.h b/ICE/Graphics/include/GPUTexture.h new file mode 100644 index 00000000..b928e8c4 --- /dev/null +++ b/ICE/Graphics/include/GPUTexture.h @@ -0,0 +1,11 @@ +#pragma once + +namespace ICE { +class GPUTexture { + public: + virtual void bind(uint32_t slot) const = 0; + virtual int id() const = 0; + void* ptr() const { return reinterpret_cast(static_cast(id())); } + virtual ~GPUTexture() = default; +}; +} // namespace ICE diff --git a/ICE/Graphics/include/GraphicsFactory.h b/ICE/Graphics/include/GraphicsFactory.h index dac8290b..d21c36b8 100644 --- a/ICE/Graphics/include/GraphicsFactory.h +++ b/ICE/Graphics/include/GraphicsFactory.h @@ -9,6 +9,8 @@ #include "Framebuffer.h" #include "GraphicsAPI.h" #include "Shader.h" +#include "ShaderProgram.h" +#include "GPUTexture.h" #include "Texture.h" #include "VertexArray.h" @@ -29,14 +31,10 @@ class GraphicsFactory { virtual std::shared_ptr createUniformBuffer(size_t size, size_t binding) const = 0; - virtual std::shared_ptr createShader(const std::string& vertexFile, const std::string& fragmentFile) const = 0; + virtual std::shared_ptr createShader(const Shader& shader) const = 0; - virtual std::shared_ptr createShader(const std::string& vertexFile, const std::string& geometryFile, - const std::string& fragmentFile) const = 0; + virtual std::shared_ptr createTexture2D(const Texture2D &texture) const = 0; - virtual std::shared_ptr createTexture2D(const std::string& file) const = 0; - virtual std::shared_ptr createTexture2D(const void* data, size_t w, size_t h, TextureFormat fmt) const = 0; - - virtual std::shared_ptr createTextureCube(const std::string& file) const = 0; + virtual std::shared_ptr createTextureCube(const TextureCube& texture) const = 0; }; } // namespace ICE \ No newline at end of file diff --git a/ICE/Graphics/include/RenderCommand.h b/ICE/Graphics/include/RenderCommand.h index e04383dc..bdcaec14 100644 --- a/ICE/Graphics/include/RenderCommand.h +++ b/ICE/Graphics/include/RenderCommand.h @@ -7,18 +7,21 @@ #include #include +#include "GPUMesh.h" #include "Material.h" -#include "Mesh.h" -#include "Shader.h" +#include "Model.h" +#include "ShaderProgram.h" namespace ICE { struct RenderCommand { - std::shared_ptr mesh; + std::shared_ptr mesh; std::shared_ptr material; - std::shared_ptr shader; - std::unordered_map> textures; + std::shared_ptr shader; + std::unordered_map> textures; Eigen::Matrix4f model_matrix; + std::unordered_map bones; + bool faceCulling = true; bool depthTest = true; }; diff --git a/ICE/Graphics/include/Renderer.h b/ICE/Graphics/include/Renderer.h index 48c00aa3..177e9668 100644 --- a/ICE/Graphics/include/Renderer.h +++ b/ICE/Graphics/include/Renderer.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "Camera.h" #include "Context.h" @@ -42,14 +43,37 @@ struct alignas(16) CameraUBO { Eigen::Matrix4f view; }; +struct Skybox { + std::shared_ptr cube_mesh; + std::shared_ptr shader; + std::unordered_map> textures; +}; + +struct Drawable { + std::shared_ptr mesh; + std::shared_ptr material; + std::shared_ptr shader; + std::unordered_map> textures; + Eigen::Matrix4f model_matrix; + std::unordered_map bone_matrices; +}; + +struct Light { + Eigen::Vector3f position; + Eigen::Vector3f rotation; + Eigen::Vector3f color; + float distance_dropoff; + LightType type; +}; + class Renderer { public: - virtual void submit(Entity e) = 0; - virtual void remove(Entity e) = 0; - virtual void prepareFrame(Camera &camera) = 0; - virtual void render() = 0; + virtual void submitSkybox(const Skybox& e) = 0; + virtual void submitDrawable(const Drawable& e) = 0; + virtual void submitLight(const Light& e) = 0; + virtual void prepareFrame(Camera& camera) = 0; + virtual std::shared_ptr render() = 0; virtual void endFrame() = 0; - virtual void setTarget(const std::shared_ptr &target) = 0; virtual void resize(uint32_t width, uint32_t height) = 0; virtual void setClearColor(Eigen::Vector4f clearColor) = 0; virtual void setViewport(int x, int y, int w, int h) = 0; diff --git a/ICE/Graphics/include/Shader.h b/ICE/Graphics/include/ShaderProgram.h similarity index 67% rename from ICE/Graphics/include/Shader.h rename to ICE/Graphics/include/ShaderProgram.h index bc839067..5d2521fc 100644 --- a/ICE/Graphics/include/Shader.h +++ b/ICE/Graphics/include/ShaderProgram.h @@ -1,17 +1,10 @@ -// -// Created by Thomas Ibanez on 20.11.20. -// - -#ifndef ICE_SHADER_H -#define ICE_SHADER_H - -#include +#pragma once #include #include namespace ICE { -class Shader : public Asset { +class ShaderProgram { public: virtual void bind() const = 0; virtual void unbind() const = 0; @@ -25,11 +18,5 @@ class Shader : public Asset { virtual void loadFloat4(const std::string &name, Eigen::Vector4f vec) = 0; virtual void loadMat4(const std::string &name, Eigen::Matrix4f mat) = 0; - - std::string getTypeName() const override { return "Shader"; }; - - AssetType getType() const override { return AssetType::EShader; }; }; -} // namespace ICE - -#endif //ICE_SHADER_H +} // namespace ICE \ No newline at end of file diff --git a/ICE/Graphics/src/ForwardRenderer.cpp b/ICE/Graphics/src/ForwardRenderer.cpp index da8fd7f3..03000e6c 100644 --- a/ICE/Graphics/src/ForwardRenderer.cpp +++ b/ICE/Graphics/src/ForwardRenderer.cpp @@ -14,84 +14,29 @@ #include -#include "RenderData.h" - namespace ICE { -ForwardRenderer::ForwardRenderer(const std::shared_ptr& api, const std::shared_ptr& factory, - const std::shared_ptr& registry, const std::shared_ptr& assetBank) +ForwardRenderer::ForwardRenderer(const std::shared_ptr& api, const std::shared_ptr& factory) : m_api(api), - m_registry(registry), - m_asset_bank(assetBank), m_geometry_pass(api, factory, {1, 1, 1}) { - m_quad_vao = factory->createVertexArray(); - auto quad_vertex_vbo = factory->createVertexBuffer(); - quad_vertex_vbo->putData(full_quad_v.data(), full_quad_v.size() * sizeof(float)); - m_quad_vao->pushVertexBuffer(quad_vertex_vbo, 3); - auto quad_uv_vbo = factory->createVertexBuffer(); - quad_uv_vbo->putData(full_quad_tx.data(), full_quad_tx.size() * sizeof(float)); - m_quad_vao->pushVertexBuffer(quad_uv_vbo, 2); - auto quad_ibo = factory->createIndexBuffer(); - quad_ibo->putData(full_quad_idx.data(), full_quad_idx.size() * sizeof(int)); - m_quad_vao->setIndexBuffer(quad_ibo); - m_camera_ubo = factory->createUniformBuffer(sizeof(CameraUBO), 0); m_light_ubo = factory->createUniformBuffer(sizeof(SceneLightsUBO), 1); } -void ForwardRenderer::submit(Entity e) { - remove(e); - if (m_registry->entityHasComponent(e)) { - m_render_queue.emplace_back(e); - } - if (m_registry->entityHasComponent(e)) { - m_lights.emplace_back(e); - } - if (m_registry->entityHasComponent(e)) { - m_skybox = e; - } +void ForwardRenderer::submitSkybox(const Skybox& e) { + m_skybox.emplace(e); } - -void ForwardRenderer::remove(Entity e) { - auto queue_pos = std::ranges::find(m_render_queue, e); - if (queue_pos != m_render_queue.end()) { - m_render_queue.erase(queue_pos); - } - auto light_pos = std::ranges::find(m_lights, e); - if (light_pos != m_lights.end()) { - m_lights.erase(light_pos); - } - if (e == m_skybox) { - m_skybox = NO_ASSET_ID; - } +void ForwardRenderer::submitDrawable(const Drawable& e) { + m_drawables.push_back(e); +} +void ForwardRenderer::submitLight(const Light& e) { + m_lights.push_back(e); } void ForwardRenderer::prepareFrame(Camera& camera) { //TODO: Sort entities, make shader list, batch, make instances, set uniforms, etc.. - if (m_skybox != NO_ASSET_ID) { - auto shader = m_asset_bank->getAsset("__ice_skybox_shader"); - shader->bind(); - shader->loadMat4("projection", camera.getProjection()); - Eigen::Matrix4f view = camera.lookThrough(); - view(0, 3) = 0; - view(1, 3) = 0; - view(2, 3) = 0; - shader->loadMat4("view", view); - shader->loadInt("skybox", 0); - - auto skybox = m_registry->getComponent(m_skybox); - auto mesh = m_asset_bank->getAsset("cube")->getMeshes().at(0); - auto tex = m_asset_bank->getAsset(skybox->texture); - m_render_commands.push_back(RenderCommand{.mesh = mesh, - .material = nullptr, - .shader = shader, - .textures = {{skybox->texture, tex}}, - .faceCulling = false, - .depthTest = false}); - } - auto view_mat = camera.lookThrough(); auto proj_mat = camera.getProjection(); @@ -103,51 +48,35 @@ void ForwardRenderer::prepareFrame(Camera& camera) { light_ubo_data.ambient_light = Eigen::Vector4f(0.1f, 0.1f, 0.1f, 1.0f); for (int i = 0; i < m_lights.size(); i++) { auto light = m_lights[i]; - auto lc = m_registry->getComponent(light); - auto tc = m_registry->getComponent(light); - if (i >= MAX_LIGHTS) - break; - light_ubo_data.lights[i].position = tc->getPosition(); - light_ubo_data.lights[i].rotation = tc->getRotation(); - light_ubo_data.lights[i].color = lc->color; - light_ubo_data.lights[i].distance_dropoff = lc->distance_dropoff; - light_ubo_data.lights[i].type = static_cast(lc->type); + light_ubo_data.lights[i].position = light.position; + light_ubo_data.lights[i].rotation = light.rotation; + light_ubo_data.lights[i].color = light.color; + light_ubo_data.lights[i].distance_dropoff = light.distance_dropoff; + light_ubo_data.lights[i].type = static_cast(light.type); } m_light_ubo->putData(&light_ubo_data, sizeof(SceneLightsUBO)); - auto frustum = extractFrustumPlanes(proj_mat * view_mat); - for (const auto& e : m_render_queue) { - auto rc = m_registry->getComponent(e); - auto tc = m_registry->getComponent(e); - auto model = m_asset_bank->getAsset(rc->model); - if (!model) - continue; - - auto aabb = model->getBoundingBox(); - Eigen::Vector3f min = (tc->getModelMatrix() * Eigen::Vector4f(aabb.getMin().x(), aabb.getMin().y(), aabb.getMin().z(), 1.0)).head<3>(); - Eigen::Vector3f max = (tc->getModelMatrix() * Eigen::Vector4f(aabb.getMax().x(), aabb.getMax().y(), aabb.getMax().z(), 1.0)).head<3>(); - aabb = AABB(std::vector{min, max}); - if (!isAABBInFrustum(frustum, aabb)) { - continue; - } - auto meshes = model->getMeshes(); - auto materials = model->getMaterialsIDs(); - auto nodes = model->getNodes(); - - for (const auto& mtl : materials) { - auto material = m_asset_bank->getAsset(mtl); - auto shader = m_asset_bank->getAsset(material->getShader()); - - if (!shader) { - continue; - } - shader->bind(); - for (int i = 0; i < model->getSkeleton().bones.size(); i++) { - shader->loadMat4("bonesTransformMatrices[" + std::to_string(i) + "]", model->getSkeleton().bones[i].finalTransformation); - } - } + if (m_skybox.has_value()) { + RenderCommand skybox_cmd; + skybox_cmd.mesh = m_skybox->cube_mesh; + skybox_cmd.material = nullptr; + skybox_cmd.shader = m_skybox->shader; + skybox_cmd.textures = m_skybox->textures; + skybox_cmd.model_matrix = Eigen::Matrix4f::Identity(); + m_render_commands.push_back(skybox_cmd); + } - submitModel(0, nodes, meshes, materials, tc->getModelMatrix()); + for (const auto& drawable : m_drawables) { + RenderCommand cmd; + cmd.mesh = drawable.mesh; + cmd.material = drawable.material; + cmd.shader = drawable.shader; + cmd.textures = drawable.textures; + cmd.model_matrix = drawable.model_matrix; + cmd.depthTest = true; + cmd.faceCulling = true; + cmd.bones = drawable.bone_matrices; + m_render_commands.push_back(cmd); } std::sort(m_render_commands.begin(), m_render_commands.end(), [this](const RenderCommand& a, const RenderCommand& b) { @@ -160,99 +89,25 @@ void ForwardRenderer::prepareFrame(Camera& camera) { return false; } }); - m_geometry_pass.submit(&m_render_commands); -} -void ForwardRenderer::submitModel(int node_idx, const std::vector& nodes, const std::vector>& meshes, - const std::vector& materials, const Eigen::Matrix4f& transform) { - auto node = nodes.at(node_idx); - for (const auto& i : node.meshIndices) { - if (i >= meshes.size()) { - continue; - } - auto mtl_id = materials.at(i); - auto mesh = meshes.at(i); - auto material = m_asset_bank->getAsset(mtl_id); - if (!material) { - continue; - } - auto shader = m_asset_bank->getAsset(material->getShader()); - if (!shader) { - continue; - } - - if (!mesh) { - continue; - } - - Eigen::Matrix4f node_transform; - if (mesh->usesBones()) { - node_transform = transform; - } else { - node_transform = transform * node.animatedTransform; - } - - std::unordered_map> texs; - for (const auto& [name, value] : material->getAllUniforms()) { - if (std::holds_alternative(value)) { - auto v = std::get(value); - if (auto tex = m_asset_bank->getAsset(v); tex) { - texs.try_emplace(v, tex); - } - } - } - - m_render_commands.push_back(RenderCommand{.mesh = mesh, - .material = material, - .shader = shader, - .textures = texs, - .model_matrix = node_transform, - .faceCulling = true, - .depthTest = true}); - } - - for (const auto& child_idx : node.children) { - submitModel(child_idx, nodes, meshes, materials, transform); - } + m_geometry_pass.submit(&m_render_commands); } -void ForwardRenderer::render() { +std::shared_ptr ForwardRenderer::render() { m_geometry_pass.execute(); auto result = m_geometry_pass.getResult(); - - //Final pass, render the last result to the screen - if (!this->target) { - m_api->bindDefaultFramebuffer(); - } else { - this->target->bind(); - } - m_api->clear(); - auto shader = m_asset_bank->getAsset(AssetPath::WithTypePrefix("lastpass")); - - shader->bind(); - result->bindAttachment(0); - shader->loadInt("uTexture", 0); - m_quad_vao->bind(); - m_quad_vao->getIndexBuffer()->bind(); - m_api->renderVertexArray(m_quad_vao); + return result; } void ForwardRenderer::endFrame() { + m_skybox.reset(); + m_drawables.clear(); + m_lights.clear(); m_api->checkAndLogErrors(); m_render_commands.clear(); - if (this->target != nullptr) - this->target->unbind(); -} - -void ForwardRenderer::setTarget(const std::shared_ptr& fb) { - this->target = fb; } void ForwardRenderer::resize(uint32_t width, uint32_t height) { - if (target) { - target->bind(); - target->resize(width, height); - } m_api->setViewport(0, 0, width, height); m_geometry_pass.resize(width, height); } diff --git a/ICE/Graphics/src/GeometryPass.cpp b/ICE/Graphics/src/GeometryPass.cpp index ce00e7f2..85e2dfbb 100644 --- a/ICE/Graphics/src/GeometryPass.cpp +++ b/ICE/Graphics/src/GeometryPass.cpp @@ -10,9 +10,9 @@ void GeometryPass::execute() { m_framebuffer->bind(); m_api->setViewport(0, 0, m_framebuffer->getFormat().width, m_framebuffer->getFormat().height); m_api->clear(); - std::shared_ptr current_shader; + std::shared_ptr current_shader; std::shared_ptr current_material; - std::shared_ptr current_mesh; + std::shared_ptr current_mesh; for (const auto& command : *m_render_queue) { auto& shader = command.shader; @@ -27,9 +27,9 @@ void GeometryPass::execute() { current_shader = shader; } - if (mesh->usesBones()) { - for (const auto& [boneID, offsetMatrix] : mesh->getSkinningData().boneOffsetMatrices) { - current_shader->loadMat4("bonesOffsetMatrices[" + std::to_string(boneID) + "]", offsetMatrix); + if (!command.bones.empty()) { + for (const auto& [id, matrix] : command.bones) { + current_shader->loadMat4("bonesTransformMatrices[" + std::to_string(id) + "]", matrix); } } @@ -50,10 +50,12 @@ void GeometryPass::execute() { auto v = std::get(value); if (textures.contains(v)) { auto& tex = textures.at(v); - tex->bind(texture_count); - shader->loadInt(name, texture_count); + if (tex) { + tex->bind(texture_count); + shader->loadInt(name, texture_count); + texture_count++; + } } - texture_count++; } else if (std::holds_alternative(value)) { auto& v = std::get(value); shader->loadFloat2(name, v); diff --git a/ICE/Graphics/src/Mesh.cpp b/ICE/Graphics/src/Mesh.cpp index 44f689b2..57614160 100644 --- a/ICE/Graphics/src/Mesh.cpp +++ b/ICE/Graphics/src/Mesh.cpp @@ -30,14 +30,6 @@ const std::vector &Mesh::getIndices() const { return m_data.indices; } -const std::shared_ptr Mesh::getVertexArray() const { - return vertexArray; -} - -void Mesh::setVertexArray(const std::shared_ptr &vao) { - this->vertexArray = vao; -} - const AABB &Mesh::getBoundingBox() const { return boundingBox; } diff --git a/ICE/Graphics/src/Model.cpp b/ICE/Graphics/src/Model.cpp deleted file mode 100644 index 2ecaa30f..00000000 --- a/ICE/Graphics/src/Model.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "Model.h" - -namespace ICE { -Model::Model(const std::vector &nodes, const std::vector> &meshes, const std::vector &materials) - : m_nodes(nodes), - m_meshes(meshes), - m_materials(materials) { - for (const auto &mesh : meshes) { - m_boundingbox = m_boundingbox.unionWith(mesh->getBoundingBox()); - } -} - -} // namespace ICE \ No newline at end of file diff --git a/ICE/GraphicsAPI/CMakeLists.txt b/ICE/GraphicsAPI/CMakeLists.txt index 6ad92fb7..74fdf4fb 100644 --- a/ICE/GraphicsAPI/CMakeLists.txt +++ b/ICE/GraphicsAPI/CMakeLists.txt @@ -3,7 +3,6 @@ project(graphics_api) message(STATUS "Building ${PROJECT_NAME} module") -add_subdirectory(None) add_subdirectory(OpenGL) add_library(${PROJECT_NAME} INTERFACE) @@ -12,7 +11,6 @@ target_link_libraries(${PROJECT_NAME} INTERFACE graphics graphics_api_OpenGL - graphics_api_None ) target_include_directories(${PROJECT_NAME} INTERFACE diff --git a/ICE/GraphicsAPI/None/CMakeLists.txt b/ICE/GraphicsAPI/None/CMakeLists.txt deleted file mode 100644 index a8c3579f..00000000 --- a/ICE/GraphicsAPI/None/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.19) -project(graphics_api_None) - -message(STATUS "Building ${PROJECT_NAME} module") - -add_library(${PROJECT_NAME} INTERFACE) - -#target_link_libraries(${PROJECT_NAME}) - -target_include_directories(${PROJECT_NAME} INTERFACE - $ - $) - -enable_testing() -#add_subdirectory(test) diff --git a/ICE/GraphicsAPI/None/NoneGraphics.h b/ICE/GraphicsAPI/None/NoneGraphics.h deleted file mode 100644 index 15b74f94..00000000 --- a/ICE/GraphicsAPI/None/NoneGraphics.h +++ /dev/null @@ -1,125 +0,0 @@ -// -// Created by Thomas Ibanez on 24.02.21. -// - -#ifndef ICE_NONEGRAPHICS_H -#define ICE_NONEGRAPHICS_H - -#include - -#include - -namespace ICE { - -class NoneVertexBuffer : public VertexBuffer { - public: - void bind() const override {} - void unbind() const override {} - void putData(const void *data, uint32_t size) override {} - uint32_t getSize() const override { return 0; } -}; - -class NoneIndexBuffer : public IndexBuffer { - public: - void bind() const override {} - void unbind() const override {} - uint32_t getSize() const override { return 0; } - void putData(const void *data, uint32_t size) override {} -}; - -class NoneUniformBuffer : public UniformBuffer { - public: - void bind() const override {} - void unbind() const override {} - uint32_t getSize() const override { return 0; } - void putData(const void *data, uint32_t size, uint32_t offset) override {} -}; - -class NoneContext : public Context { - public: - void swapBuffers() override {} - void wireframeMode() override {} - void fillMode() override {} - void initialize() override {} -}; - -class NoneFramebuffer : public Framebuffer { - public: - NoneFramebuffer() : Framebuffer({0,0,0}) {} - void bind() override {} - void unbind() override {} - void resize(int width, int height) override {} - int getTexture() override { return 0; } - void bindAttachment(int slot) const override{}; - Eigen::Vector4i readPixel(int x, int y) override { return Eigen::Vector4i(); } -}; - -class NoneRendererAPI : public RendererAPI { - public: - void initialize() const override {} - void setViewport(int x, int y, int width, int height) const override {} - void setClearColor(float r, float g, float b, float a) const override {} - void clear() const override {} - void renderVertexArray(const std::shared_ptr &va) const override {} - void flush() const override {} - void finish() const override {} - void bindDefaultFramebuffer() const override {} - void setDepthTest(bool enable) const override {} - void setDepthMask(bool enable) const override {} - void setBackfaceCulling(bool enable) const override {} - void checkAndLogErrors() const override {} -}; - -class NoneShader : public Shader { - public: - void bind() const override {} - void unbind() const override {} - void loadInt(const std::string &name, int v) override {} - void loadInts(const std::string &name, int *array, uint32_t size) override {} - void loadFloat(const std::string &name, float v) override {} - void loadFloat2(const std::string &name, Eigen::Vector2f vec) override {} - void loadFloat3(const std::string &name, Eigen::Vector3f vec) override {} - void loadFloat4(const std::string &name, Eigen::Vector4f vec) override {} - void loadMat4(const std::string &name, Eigen::Matrix4f mat) override {} - virtual AssetType getType() const override { return AssetType::EShader; } - virtual std::string getTypeName() const override { return "Shader"; } -}; - -class NoneTexture2D : public Texture2D { - public: - void bind(uint32_t slot) const override {} - TextureFormat getFormat() const override { return {}; } - uint32_t getWidth() const override { return 0; } - uint32_t getHeight() const override { return 0; } - TextureWrap getWrap() const override { return {}; } - void setData(void *data, uint32_t size) override {} - void *getTexture() const override { return nullptr; } - TextureType getTextureType() const override { return TextureType::Tex2D; } -}; - -class NoneTextureCube : public TextureCube { - public: - void bind(uint32_t slot) const override {} - TextureFormat getFormat() const override { return {}; } - uint32_t getWidth() const override { return 0; } - uint32_t getHeight() const override { return 0; } - TextureWrap getWrap() const override { return {}; } - void setData(void *data, uint32_t size) override {} - void *getTexture() const override { return nullptr; } - TextureType getTextureType() const override { return TextureType::CubeMap; } -}; - -class NoneVertexArray : public VertexArray { - public: - void bind() const override {} - void unbind() const override {} - void pushVertexBuffer(const std::shared_ptr &buffer, int size) override {} - void pushVertexBuffer(const std::shared_ptr &buffer, int position, int size) override {} - void setIndexBuffer(const std::shared_ptr &buffer) override {} - int getIndexCount() const override { return 0; } - uint32_t getID() const override { return 0; } - std::shared_ptr getIndexBuffer() const override { return nullptr; } -}; -} // namespace ICE - -#endif //ICE_NONEGRAPHICS_H diff --git a/ICE/GraphicsAPI/None/NoneGraphicsFactory.h b/ICE/GraphicsAPI/None/NoneGraphicsFactory.h deleted file mode 100644 index 5dd4eb69..00000000 --- a/ICE/GraphicsAPI/None/NoneGraphicsFactory.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -#include "Context.h" -#include "Framebuffer.h" -#include "GraphicsAPI.h" -#include "GraphicsFactory.h" -#include "NoneGraphics.h" -#include "Shader.h" -#include "Texture.h" -#include "VertexArray.h" - -namespace ICE { -class NoneGraphicsFactory : public GraphicsFactory { - public: - std::shared_ptr createContext(const std::shared_ptr& window) const override { return std::make_shared(); } - - std::shared_ptr createFramebuffer(const FrameBufferFormat& format) const override { return std::make_shared(); } - - std::shared_ptr createRendererAPI() const override { return std::make_shared(); } - - std::shared_ptr createVertexArray() const override { return std::make_shared(); } - - std::shared_ptr createVertexBuffer() const override { return std::make_shared(); } - - std::shared_ptr createIndexBuffer() const override { return std::make_shared(); } - - std::shared_ptr createUniformBuffer(size_t size, size_t binding) const override { return std::make_shared(); } - - std::shared_ptr createShader(const std::string& vertexFile, const std::string& fragmentFile) const override { - return createShader(vertexFile, "", fragmentFile); - } - - std::shared_ptr createShader(const std::string& vertexFile, const std::string& geometryFile, - const std::string& fragmentFile) const override { - return std::make_shared(); - } - - std::shared_ptr createTexture2D(const std::string& file) const override { return std::make_shared(); } - std::shared_ptr createTexture2D(const void* data, size_t w, size_t h, TextureFormat fmt) const override { - return std::make_shared(); - } - - std::shared_ptr createTextureCube(const std::string& file) const override { return std::make_shared(); } -}; -} // namespace ICE \ No newline at end of file diff --git a/ICE/GraphicsAPI/OpenGL/include/OpenGLFactory.h b/ICE/GraphicsAPI/OpenGL/include/OpenGLFactory.h index a23d74f8..f1d65194 100644 --- a/ICE/GraphicsAPI/OpenGL/include/OpenGLFactory.h +++ b/ICE/GraphicsAPI/OpenGL/include/OpenGLFactory.h @@ -38,20 +38,10 @@ class OpenGLFactory : public GraphicsFactory { std::shared_ptr createIndexBuffer() const override { return std::make_shared(); } - std::shared_ptr createShader(const std::string& vertex_src, const std::string& fragment_src) const override { - return createShader(vertex_src, "", fragment_src); - } - - std::shared_ptr createShader(const std::string& vertex_src, const std::string& geometry_src, - const std::string& fragment_src) const override { - return std::make_shared(vertex_src, geometry_src, fragment_src); - } + std::shared_ptr createShader(const Shader& shader) const override { return std::make_shared(shader); } - std::shared_ptr createTexture2D(const std::string& file) const override { return std::make_shared(file); } - std::shared_ptr createTexture2D(const void* data, size_t w, size_t h, TextureFormat fmt) const override { - return std::make_shared(data, w, h, fmt); - } + std::shared_ptr createTexture2D(const Texture2D& texture) const override { return std::make_shared(texture); } - std::shared_ptr createTextureCube(const std::string& file) const override { return std::make_shared(file); } + std::shared_ptr createTextureCube(const TextureCube& texture) const override { return std::make_shared(texture); } }; } // namespace ICE \ No newline at end of file diff --git a/ICE/GraphicsAPI/OpenGL/include/OpenGLShader.h b/ICE/GraphicsAPI/OpenGL/include/OpenGLShader.h index 476605d0..2b68236b 100644 --- a/ICE/GraphicsAPI/OpenGL/include/OpenGLShader.h +++ b/ICE/GraphicsAPI/OpenGL/include/OpenGLShader.h @@ -8,42 +8,44 @@ #include #include #include -#include +#include + #include +#include namespace ICE { - class OpenGLShader : public Shader { - public: - void bind() const override; - - void unbind() const override; +class OpenGLShader : public ShaderProgram { + public: + explicit OpenGLShader(const Shader &shader_asset); - void loadInt(const std::string &name, int v) override; + void bind() const override; - void loadInts(const std::string &name, int *array, uint32_t size) override; + void unbind() const override; - void loadFloat(const std::string &name, float v) override; + void loadInt(const std::string &name, int v) override; - void loadFloat2(const std::string &name, Eigen::Vector2f vec) override; + void loadInts(const std::string &name, int *array, uint32_t size) override; - void loadFloat3(const std::string &name, Eigen::Vector3f vec) override; + void loadFloat(const std::string &name, float v) override; - void loadFloat4(const std::string &name, Eigen::Vector4f vec) override; + void loadFloat2(const std::string &name, Eigen::Vector2f vec) override; - void loadMat4(const std::string &name, Eigen::Matrix4f mat) override; + void loadFloat3(const std::string &name, Eigen::Vector3f vec) override; - OpenGLShader(const std::string &vertexFile, const std::string &geoFile, const std::string &fragmentFile); + void loadFloat4(const std::string &name, Eigen::Vector4f vec) override; - OpenGLShader(const std::string &vertexFile, const std::string &fragmentFile); + void loadMat4(const std::string &name, Eigen::Matrix4f mat) override; - private: + private: + GLint getLocation(const std::string &name); - GLint getLocation(const std::string &name); + void compileAndAttachStage(ShaderStage stage, const std::string &source); - uint32_t programID; - std::unordered_map locations; - }; -} + constexpr GLenum stageToGLStage(ShaderStage stage); + uint32_t m_programID; + std::unordered_map m_locations; +}; +} // namespace ICE -#endif //ICE_OPENGLSHADER_H +#endif //ICE_OPENGLSHADER_H diff --git a/ICE/GraphicsAPI/OpenGL/include/OpenGLTexture.h b/ICE/GraphicsAPI/OpenGL/include/OpenGLTexture.h index a7063c71..ba3eb877 100644 --- a/ICE/GraphicsAPI/OpenGL/include/OpenGLTexture.h +++ b/ICE/GraphicsAPI/OpenGL/include/OpenGLTexture.h @@ -6,6 +6,7 @@ #define ICE_OPENGLTEXTURE_H #include +#include #include #include @@ -63,65 +64,26 @@ constexpr int textureFormatToChannels(TextureFormat format) { } } -class OpenGLTexture2D : public Texture2D { +class OpenGLTexture2D : public GPUTexture { public: - OpenGLTexture2D(const std::string &file); - OpenGLTexture2D(const void *data, size_t w, size_t h, TextureFormat fmt); + OpenGLTexture2D(const Texture2D &tex); void bind(uint32_t slot) const override; - - TextureFormat getFormat() const override; - - uint32_t getWidth() const override; - uint32_t getHeight() const override; - - TextureWrap getWrap() const override; - - void setData(void *data, uint32_t size) override; - - void *getTexture() const override; - - TextureType getTextureType() const override; + int id() const override; private: - void loadData(const void *data, size_t w, size_t h, TextureFormat fmt); - - std::string file; - uint32_t id; - uint32_t width, height; - TextureFormat format; - TextureWrap wrap; - GLenum storageFormat; - GLenum dataFormat; + uint32_t m_id; }; -class OpenGLTextureCube : public TextureCube { +class OpenGLTextureCube : public GPUTexture { public: - OpenGLTextureCube(const std::string &file); + OpenGLTextureCube(const TextureCube &tex); void bind(uint32_t slot) const override; - - TextureFormat getFormat() const override; - - uint32_t getWidth() const override; - uint32_t getHeight() const override; - - TextureWrap getWrap() const override; - - void setData(void *data, uint32_t size) override; - - void *getTexture() const override; - - TextureType getTextureType() const override; + int id() const override; private: - std::string file; - uint32_t id; - uint32_t width, height; - TextureFormat format; - TextureWrap wrap; - GLenum storageFormat; - GLenum dataFormat; + uint32_t m_id; }; } // namespace ICE diff --git a/ICE/GraphicsAPI/OpenGL/src/OpenGLBuffers.cpp b/ICE/GraphicsAPI/OpenGL/src/OpenGLBuffers.cpp index 7759d889..d54faa2a 100644 --- a/ICE/GraphicsAPI/OpenGL/src/OpenGLBuffers.cpp +++ b/ICE/GraphicsAPI/OpenGL/src/OpenGLBuffers.cpp @@ -5,6 +5,7 @@ #include "OpenGLBuffers.h" #include +#include namespace ICE { @@ -82,12 +83,13 @@ uint32_t OpenGLUniformBuffer::getSize() const { void OpenGLUniformBuffer::unbind() const { glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBufferBase(GL_UNIFORM_BUFFER, binding, id); } void OpenGLUniformBuffer::putData(const void *data, uint32_t _size, uint32_t offset) { + assert(offset + _size <= size); bind(); - glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data); + glBufferSubData(GL_UNIFORM_BUFFER, offset, _size, data); unbind(); - size = _size; } } // namespace ICE diff --git a/ICE/GraphicsAPI/OpenGL/src/OpenGLShader.cpp b/ICE/GraphicsAPI/OpenGL/src/OpenGLShader.cpp index 6e81a594..a15e73b3 100644 --- a/ICE/GraphicsAPI/OpenGL/src/OpenGLShader.cpp +++ b/ICE/GraphicsAPI/OpenGL/src/OpenGLShader.cpp @@ -10,8 +10,31 @@ #include namespace ICE { + +OpenGLShader::OpenGLShader(const Shader &shader_asset) { + m_programID = glCreateProgram(); + Logger::Log(Logger::VERBOSE, "Graphics", "Compiling shader..."); + + for (const auto& [stage, source] : shader_asset.getSources()) { + compileAndAttachStage(stage, source.second); + } + + glLinkProgram(m_programID); + GLint linkStatus = 0; + glGetProgramiv(m_programID, GL_LINK_STATUS, &linkStatus); + if (linkStatus == GL_FALSE) { + Logger::Log(Logger::FATAL, "Graphics", "Error while linking shader"); + GLint maxLength = 0; + glGetProgramiv(m_programID, GL_INFO_LOG_LENGTH, &maxLength); + + std::vector errorLog(maxLength); + glGetProgramInfoLog(m_programID, maxLength, &maxLength, &errorLog[0]); + Logger::Log(Logger::FATAL, "Graphics", "Shader linking error: %s", errorLog.data()); + } +} + void OpenGLShader::bind() const { - glUseProgram(programID); + glUseProgram(m_programID); } void OpenGLShader::unbind() const { @@ -47,11 +70,11 @@ void OpenGLShader::loadMat4(const std::string &name, Eigen::Matrix4f mat) { } GLint OpenGLShader::getLocation(const std::string &name) { - if (this->locations.find(name) == this->locations.end()) { - GLint location = glGetUniformLocation(programID, name.c_str()); - locations[name] = static_cast(location); + if (!m_locations.contains(name)) { + GLint location = glGetUniformLocation(m_programID, name.c_str()); + m_locations[name] = static_cast(location); } - return locations[name]; + return m_locations[name]; } bool compileShader(GLenum type, const std::string &source, GLint *shader) { @@ -77,48 +100,31 @@ bool compileShader(GLenum type, const std::string &source, GLint *shader) { return compileStatus == GL_TRUE; } -//TODO: Another ctor for tesselation control and evaluation shaders -OpenGLShader::OpenGLShader(const std::string &vertex_src, const std::string &geo_src, const std::string &fragment_src) { - //TODO: Better tracing in case of errors, cleanup - this->programID = glCreateProgram(); - - GLint vertexShader; - Logger::Log(Logger::VERBOSE, "Graphics", "Compiling vertex shader..."); - if (!compileShader(GL_VERTEX_SHADER, vertex_src, &vertexShader)) { - Logger::Log(Logger::FATAL, "Graphics", "Error while compiling vertex shader"); +void OpenGLShader::compileAndAttachStage(ShaderStage stage, const std::string &source) { + GLint shader; + Logger::Log(Logger::VERBOSE, "Graphics", "\t + Compiling shader stage..."); + if (!compileShader(stageToGLStage(stage), source, &shader)) { + Logger::Log(Logger::FATAL, "Graphics", "Error while compiling shader stage"); } - glAttachShader(programID, vertexShader); + glAttachShader(m_programID, shader); +} - GLint fragmentShader; - Logger::Log(Logger::VERBOSE, "Graphics", "Compiling fragment shader..."); - if (!compileShader(GL_FRAGMENT_SHADER, fragment_src, &fragmentShader)) { - Logger::Log(Logger::FATAL, "Graphics", "Error while compiling fragment shader"); - } - glAttachShader(programID, fragmentShader); - - if (!geo_src.empty()) { - GLint geoShader; - Logger::Log(Logger::VERBOSE, "Graphics", "Compiling geometry shader..."); - if (!compileShader(GL_GEOMETRY_SHADER, geo_src, &geoShader)) { - Logger::Log(Logger::FATAL, "Graphics", "Error while compiling geometry shader"); - } - glAttachShader(programID, geoShader); - } - glLinkProgram(programID); - GLint linkStatus = 0; - glGetProgramiv(programID, GL_LINK_STATUS, &linkStatus); - if (linkStatus == GL_FALSE) { - Logger::Log(Logger::FATAL, "Graphics", "Error while linking shader"); - GLint maxLength = 0; - glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxLength); - - std::vector errorLog(maxLength); - glGetProgramInfoLog(programID, maxLength, &maxLength, &errorLog[0]); - Logger::Log(Logger::FATAL, "Graphics", "Shader linking error: %s", errorLog.data()); +constexpr GLenum OpenGLShader::stageToGLStage(ShaderStage stage) { + switch (stage) { + case ShaderStage::Vertex: + return GL_VERTEX_SHADER; + case ShaderStage::Fragment: + return GL_FRAGMENT_SHADER; + case ShaderStage::Geometry: + return GL_GEOMETRY_SHADER; + case ShaderStage::TessControl: + return GL_TESS_CONTROL_SHADER; + case ShaderStage::TessEval: + return GL_TESS_EVALUATION_SHADER; + case ShaderStage::Compute: + return GL_COMPUTE_SHADER; } } -OpenGLShader::OpenGLShader(const std::string &vertex_src, const std::string &fragment_src) : OpenGLShader(vertex_src, "", fragment_src) { -} } // namespace ICE \ No newline at end of file diff --git a/ICE/GraphicsAPI/OpenGL/src/OpenGLTexture2D.cpp b/ICE/GraphicsAPI/OpenGL/src/OpenGLTexture2D.cpp index cb5be8e4..bded6daa 100644 --- a/ICE/GraphicsAPI/OpenGL/src/OpenGLTexture2D.cpp +++ b/ICE/GraphicsAPI/OpenGL/src/OpenGLTexture2D.cpp @@ -9,68 +9,35 @@ namespace ICE { -OpenGLTexture2D::OpenGLTexture2D(const std::string &file) : file(file) { - int channels, w, h; - void *data = Texture::getDataFromFile(file, &w, &h, &channels); - loadData(data, w, h, channels == 4 ? TextureFormat::RGBA8 : TextureFormat::RGB8); - stbi_image_free(data); -} - -OpenGLTexture2D::OpenGLTexture2D(const void *data, size_t w, size_t h, TextureFormat fmt) { - loadData(data, w, h, fmt); -} - -void OpenGLTexture2D::loadData(const void *data, size_t w, size_t h, TextureFormat fmt) { - width = w; - height = h; +OpenGLTexture2D::OpenGLTexture2D(const Texture2D &tex) { + auto width = tex.getWidth(); + auto height = tex.getHeight(); - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); + glGenTextures(1, &m_id); + glBindTexture(GL_TEXTURE_2D, m_id); - storageFormat = textureFormatToGLInternalFormat(fmt); - dataFormat = (textureFormatToChannels(fmt) == 4) ? GL_RGBA : (textureFormatToChannels(fmt) == 3) ? GL_RGB : GL_RED; + auto fmt = tex.getFormat(); + auto storageFormat = textureFormatToGLInternalFormat(fmt); + auto dataFormat = (textureFormatToChannels(fmt) == 4) ? GL_RGBA : (textureFormatToChannels(fmt) == 3) ? GL_RGB : GL_RED; glPixelStorei(GL_UNPACK_ALIGNMENT, textureFormatToAlignment(fmt)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - wrap = TextureWrap::Repeat; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + auto wrap = tex.getWrap(); + auto glWrap = (wrap == TextureWrap::Clamp) ? GL_CLAMP_TO_EDGE : (wrap == TextureWrap::Repeat) ? GL_REPEAT : GL_REPEAT; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrap); - glTexImage2D(GL_TEXTURE_2D, 0, storageFormat, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, storageFormat, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, tex.data()); } -void OpenGLTexture2D::setData(void *data, uint32_t size) { - glTextureSubImage2D(id, 0, 0, 0, width, height, dataFormat, GL_UNSIGNED_BYTE, data); +int OpenGLTexture2D::id() const { + return m_id; } void OpenGLTexture2D::bind(uint32_t slot) const { glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, id); -} - -TextureWrap OpenGLTexture2D::getWrap() const { - return wrap; -} - -TextureFormat OpenGLTexture2D::getFormat() const { - return format; -} - -uint32_t OpenGLTexture2D::getWidth() const { - return width; -} - -uint32_t OpenGLTexture2D::getHeight() const { - return height; -} - -void *OpenGLTexture2D::getTexture() const { - return static_cast(0) + id; -} - -TextureType OpenGLTexture2D::getTextureType() const { - return TextureType::Tex2D; + glBindTexture(GL_TEXTURE_2D, m_id); } } // namespace ICE \ No newline at end of file diff --git a/ICE/GraphicsAPI/OpenGL/src/OpenGLTextureCube.cpp b/ICE/GraphicsAPI/OpenGL/src/OpenGLTextureCube.cpp index b7ef4a13..ee0c6fa6 100644 --- a/ICE/GraphicsAPI/OpenGL/src/OpenGLTextureCube.cpp +++ b/ICE/GraphicsAPI/OpenGL/src/OpenGLTextureCube.cpp @@ -9,16 +9,13 @@ namespace ICE { -OpenGLTextureCube::OpenGLTextureCube(const std::string &file) { - int channels, w, h; - void *data = Texture::getDataFromFile(file, &w, &h, &channels, STBI_rgb); - width = w; - height = h; +OpenGLTextureCube::OpenGLTextureCube(const TextureCube &texture_asset) { + + glGenTextures(1, &m_id); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_id); - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_CUBE_MAP, id); - - auto faces = equirectangularToCubemap((uint8_t *) data, width, height); + auto width = texture_asset.getWidth(); + auto faces = equirectangularToCubemap((uint8_t *) texture_asset.data(), width, texture_asset.getHeight()); for (int i = 0; i < 6; i++) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width / 4, width / 4, 0, GL_RGB, GL_UNSIGNED_BYTE, faces[i]); } @@ -29,39 +26,15 @@ OpenGLTextureCube::OpenGLTextureCube(const std::string &file) { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - stbi_image_free(data); -} - -void OpenGLTextureCube::bind(uint32_t slot) const { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_CUBE_MAP, id); -} - -TextureFormat OpenGLTextureCube::getFormat() const { - return TextureFormat::RGB8; -} - -uint32_t OpenGLTextureCube::getWidth() const { - return width; -} - -uint32_t OpenGLTextureCube::getHeight() const { - return height; -} - -TextureWrap OpenGLTextureCube::getWrap() const { - return TextureWrap::Clamp; } -void OpenGLTextureCube::setData(void *data, uint32_t size) { - //TODO -} -void *OpenGLTextureCube::getTexture() const { - return static_cast(0) + id; +int OpenGLTextureCube::id() const { + return m_id; } -TextureType OpenGLTextureCube::getTextureType() const { - return TextureType::CubeMap; +void OpenGLTextureCube::bind(uint32_t slot) const { + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_id); } } // namespace ICE \ No newline at end of file diff --git a/ICE/IO/CMakeLists.txt b/ICE/IO/CMakeLists.txt index f6fdb6e9..aef7310b 100644 --- a/ICE/IO/CMakeLists.txt +++ b/ICE/IO/CMakeLists.txt @@ -13,6 +13,8 @@ target_sources(${PROJECT_NAME} PRIVATE src/ModelLoader.cpp src/ShaderLoader.cpp src/MaterialLoader.cpp + src/ShaderExporter.cpp + src/MeshLoader.cpp ) target_link_libraries(${PROJECT_NAME} @@ -22,6 +24,7 @@ target_link_libraries(${PROJECT_NAME} graphics_api assimp platform + nlohmann_json ) target_include_directories(${PROJECT_NAME} PUBLIC diff --git a/ICE/IO/include/IAssetLoader.h b/ICE/IO/include/IAssetLoader.h index d9de1ac9..94039996 100644 --- a/ICE/IO/include/IAssetLoader.h +++ b/ICE/IO/include/IAssetLoader.h @@ -13,11 +13,8 @@ namespace ICE { template class IAssetLoader { public: - IAssetLoader(const std::shared_ptr &factory) : graphics_factory(factory) {} + IAssetLoader() = default; virtual std::shared_ptr load(const std::vector &files) = 0; - - protected: - std::shared_ptr graphics_factory; }; } // namespace ICE diff --git a/ICE/IO/include/MaterialLoader.h b/ICE/IO/include/MaterialLoader.h index 5e7f8303..c5a895ad 100644 --- a/ICE/IO/include/MaterialLoader.h +++ b/ICE/IO/include/MaterialLoader.h @@ -11,7 +11,7 @@ namespace ICE { class MaterialLoader : public IAssetLoader { public: - MaterialLoader() : IAssetLoader(nullptr) {} + MaterialLoader() = default; std::shared_ptr load(const std::vector &files) override; }; } // namespace ICE diff --git a/ICE/IO/include/MeshLoader.h b/ICE/IO/include/MeshLoader.h new file mode 100644 index 00000000..aa2a1b03 --- /dev/null +++ b/ICE/IO/include/MeshLoader.h @@ -0,0 +1,25 @@ +// +// Created by Thomas Ibanez on 31.07.21. +// + +#pragma once + +#include +#include + +#include + +#include "Asset.h" +#include "IAssetLoader.h" +#include "Mesh.h" + +namespace ICE { +class AssetBank; + +class MeshLoader : public IAssetLoader { + public: + MeshLoader() {} + + std::shared_ptr load(const std::vector &file) override; +}; +} // namespace ICE diff --git a/ICE/IO/include/ModelLoader.h b/ICE/IO/include/ModelLoader.h index 0b92c85c..c1b0135c 100644 --- a/ICE/IO/include/ModelLoader.h +++ b/ICE/IO/include/ModelLoader.h @@ -18,19 +18,16 @@ class AssetBank; class ModelLoader : public IAssetLoader { public: - ModelLoader(const std::shared_ptr &factory, AssetBank &bank) - : ref_bank(bank), - m_graphics_factory(factory), - IAssetLoader(factory) {} + ModelLoader(AssetBank &bank) : ref_bank(bank) {} std::shared_ptr load(const std::vector &file) override; int processNode(const aiNode *node, std::vector &nodes, Model::Skeleton &skeleton, std::unordered_set &used_names, const Eigen::Matrix4f &parent_transform); - std::shared_ptr extractMesh(const aiMesh *mesh, const std::string &model_name, const aiScene *scene, Model::Skeleton &skeleton); + AssetUID extractMesh(const aiMesh *mesh, const std::string &model_name, const aiScene *scene, Model::Skeleton &skeleton); AssetUID extractMaterial(const aiMaterial *material, const std::string &model_name, const aiScene *scene); AssetUID extractTexture(const aiMaterial *material, const std::string &tex_path, const aiScene *scene, aiTextureType type); - void extractBoneData(const aiMesh *mesh, const aiScene *scene, MeshData &data, SkinningData &skinning_data, Model::Skeleton &skeleton); + std::unordered_map extractBoneData(const aiMesh *mesh, MeshData &data, Model::Skeleton &skeleton); std::unordered_map extractAnimations(const aiScene *scene, Model::Skeleton &skeleton); private: @@ -42,6 +39,5 @@ class ModelLoader : public IAssetLoader { constexpr TextureFormat getTextureFormat(aiTextureType type, int channels); AssetBank &ref_bank; - std::shared_ptr m_graphics_factory; }; } // namespace ICE diff --git a/ICE/IO/include/Project.h b/ICE/IO/include/Project.h index 37d70ead..2a4bbb90 100644 --- a/ICE/IO/include/Project.h +++ b/ICE/IO/include/Project.h @@ -15,8 +15,9 @@ error "Missing the header." #endif #include #include +#include #include -#include +#include #include #include @@ -43,6 +44,7 @@ class Project { std::shared_ptr getAssetBank(); void setAssetBank(const std::shared_ptr& assetBank); + std::shared_ptr getGPURegistry(); void addScene(const Scene& scene); void setCurrentScene(const std::shared_ptr& scene); @@ -70,7 +72,7 @@ class Project { std::string source = entry; sources.push_back(m_base_directory / source); } - assetBank->addAssetWithSpecificUID(asset_path, sources, uid); + m_asset_bank->addAssetWithSpecificUID(asset_path, sources, uid); } } @@ -82,17 +84,19 @@ class Project { fs::path m_base_directory; fs::path m_scenes_directory; + fs::path m_models_directory; fs::path m_meshes_directory; fs::path m_materials_directory; fs::path m_shaders_directory; fs::path m_textures_directory; fs::path m_cubemaps_directory; - std::string name; + std::string m_name; - std::vector> scenes; + std::vector> m_scenes; std::shared_ptr m_current_scene; - std::shared_ptr assetBank; + std::shared_ptr m_asset_bank; + std::shared_ptr m_gpu_registry; Eigen::Vector3f cameraPosition, cameraRotation; }; diff --git a/ICE/IO/include/ShaderExporter.h b/ICE/IO/include/ShaderExporter.h new file mode 100644 index 00000000..05fe1ecf --- /dev/null +++ b/ICE/IO/include/ShaderExporter.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "AssetExporter.h" + +namespace ICE { +class ShaderExporter : public AssetExporter { + public: + void writeToJson(const std::filesystem::path &path, const Shader &object) override; + void writeToBin(const std::filesystem::path &path, const Shader &object) override; + + constexpr std::string stageToString(ShaderStage stage) { + switch (stage) { + case ShaderStage::Vertex: + return "vertex"; + case ShaderStage::Fragment: + return "fragment"; + case ShaderStage::Geometry: + return "geometry"; + case ShaderStage::TessControl: + return "tess_control"; + case ShaderStage::TessEval: + return "tess_eval"; + case ShaderStage::Compute: + return "compute"; + default: + return "unknown"; + } + } +}; +} // namespace ICE diff --git a/ICE/IO/include/ShaderLoader.h b/ICE/IO/include/ShaderLoader.h index 648f96e0..408552d1 100644 --- a/ICE/IO/include/ShaderLoader.h +++ b/ICE/IO/include/ShaderLoader.h @@ -13,8 +13,9 @@ namespace ICE { class ShaderLoader : public IAssetLoader { public: - ShaderLoader(const std::shared_ptr &factory) : IAssetLoader(factory) {} + ShaderLoader() = default; std::shared_ptr load(const std::vector &file) override; std::string readAndResolveIncludes(const std::filesystem::path &file); + constexpr ShaderStage stageFromString(const std::string &str); }; } // namespace ICE diff --git a/ICE/IO/include/TextureLoader.h b/ICE/IO/include/TextureLoader.h index 267b472d..7e868d0c 100644 --- a/ICE/IO/include/TextureLoader.h +++ b/ICE/IO/include/TextureLoader.h @@ -13,12 +13,12 @@ namespace ICE { class Texture2DLoader : public IAssetLoader { public: - Texture2DLoader(const std::shared_ptr &factory) : IAssetLoader(factory) {} + Texture2DLoader() = default; std::shared_ptr load(const std::vector &file) override; }; class TextureCubeLoader : public IAssetLoader { public: - TextureCubeLoader(const std::shared_ptr &factory) : IAssetLoader(factory) {} + TextureCubeLoader() = default; std::shared_ptr load(const std::vector &file) override; }; } // namespace ICE diff --git a/ICE/IO/src/EngineConfig.cpp b/ICE/IO/src/EngineConfig.cpp index 4317f2cb..960370d3 100644 --- a/ICE/IO/src/EngineConfig.cpp +++ b/ICE/IO/src/EngineConfig.cpp @@ -5,10 +5,10 @@ #include "EngineConfig.h" #include -#include #include #include +#include #include namespace ICE { diff --git a/ICE/IO/src/MaterialExporter.cpp b/ICE/IO/src/MaterialExporter.cpp index 884fd464..0e9a6674 100644 --- a/ICE/IO/src/MaterialExporter.cpp +++ b/ICE/IO/src/MaterialExporter.cpp @@ -1,62 +1,61 @@ #include "MaterialExporter.h" -#include #include -#include +#include #include +#include using json = nlohmann::json; namespace ICE { void MaterialExporter::writeToJson(const std::filesystem::path &path, const Material &material) { - std::ofstream outstream; - outstream.open(path); - json j; - j["shader_id"] = material.getShader(); + std::ofstream outstream; + outstream.open(path); + json j; + j["shader_id"] = material.getShader(); j["transparent"] = material.isTransparent(); - std::vector uniforms; - for(const auto &[name, value] : material.getAllUniforms()) { - json uniform; - uniform["name"] = name; - if (std::holds_alternative(value)) { - auto v = std::get(value); - uniform["type"] = "float"; - uniform["value"] = v; - } else if (std::holds_alternative(value)) { - auto v = std::get(value); - uniform["type"] = "int"; - uniform["value"] = v; - } else if (std::holds_alternative(value)) { - auto v = std::get(value); - uniform["type"] = "assetUID"; - uniform["value"] = v; - } else if (std::holds_alternative(value)) { - auto v = std::get(value); - uniform["type"] = "vec3"; - uniform["value"] = {v.x(), v.y(), v.z()}; - } else if (std::holds_alternative(value)) { - auto v = std::get(value); - uniform["type"] = "vec4"; - uniform["value"] = {v.x(), v.y(), v.z(), v.w()}; - } else if (std::holds_alternative(value)) { - auto v = std::get(value); - uniform["type"] = "mat4"; - uniform["value"] = std::vector(v.data(), v.data() + 16); - } else { - throw std::runtime_error("Uniform type not implemented"); - } - uniforms.push_back(std::move(uniform)); - } - - j["uniforms"] = uniforms; - outstream << j.dump(4); - outstream.close(); - + std::vector uniforms; + for (const auto &[name, value] : material.getAllUniforms()) { + json uniform; + uniform["name"] = name; + if (std::holds_alternative(value)) { + auto v = std::get(value); + uniform["type"] = "float"; + uniform["value"] = v; + } else if (std::holds_alternative(value)) { + auto v = std::get(value); + uniform["type"] = "int"; + uniform["value"] = v; + } else if (std::holds_alternative(value)) { + auto v = std::get(value); + uniform["type"] = "assetUID"; + uniform["value"] = v; + } else if (std::holds_alternative(value)) { + auto v = std::get(value); + uniform["type"] = "vec3"; + uniform["value"] = {v.x(), v.y(), v.z()}; + } else if (std::holds_alternative(value)) { + auto v = std::get(value); + uniform["type"] = "vec4"; + uniform["value"] = {v.x(), v.y(), v.z(), v.w()}; + } else if (std::holds_alternative(value)) { + auto v = std::get(value); + uniform["type"] = "mat4"; + uniform["value"] = std::vector(v.data(), v.data() + 16); + } else { + throw std::runtime_error("Uniform type not implemented"); + } + uniforms.push_back(std::move(uniform)); + } + + j["uniforms"] = uniforms; + outstream << j.dump(4); + outstream.close(); } -void MaterialExporter::writeToBin(const std::filesystem::path &path, const Material &object){ - throw std::runtime_error("Not implemented"); +void MaterialExporter::writeToBin(const std::filesystem::path &path, const Material &object) { + throw std::runtime_error("Not implemented"); } -} \ No newline at end of file +} // namespace ICE \ No newline at end of file diff --git a/ICE/IO/src/MaterialLoader.cpp b/ICE/IO/src/MaterialLoader.cpp index c1fde64a..fc73fd2f 100644 --- a/ICE/IO/src/MaterialLoader.cpp +++ b/ICE/IO/src/MaterialLoader.cpp @@ -5,7 +5,7 @@ #include "MaterialLoader.h" #include -#include +#include #include diff --git a/ICE/IO/src/MeshLoader.cpp b/ICE/IO/src/MeshLoader.cpp new file mode 100644 index 00000000..6f6b6a79 --- /dev/null +++ b/ICE/IO/src/MeshLoader.cpp @@ -0,0 +1,61 @@ +// +// Created by Thomas Ibanez on 31.07.21. +// + +#include "MeshLoader.h" + +#include +#include +#include +#include +#include + +#include + +namespace ICE { +std::shared_ptr MeshLoader::load(const std::vector &file) { + if (file.empty()) { + return nullptr; + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(file[0].string(), + aiProcess_FlipUVs | aiProcess_ValidateDataStructure | aiProcess_SortByPType | aiProcess_GenSmoothNormals + | aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_PreTransformVertices); + + if (scene->mNumMeshes < 1) { + return nullptr; + } + MeshData data; + auto mesh = scene->mMeshes[0]; + for (int i = 0; i < mesh->mNumVertices; i++) { + auto v = mesh->mVertices[i]; + auto n = mesh->HasNormals() ? mesh->mNormals[i] : aiVector3D{0, 0, 0}; + auto t = mesh->HasTangentsAndBitangents() ? mesh->mTangents[i] : aiVector3D{0, 0, 0}; + auto b = mesh->HasTangentsAndBitangents() ? mesh->mBitangents[i] : aiVector3D{0, 0, 0}; + Eigen::Vector2f uv(0, 0); + if (mesh->mTextureCoords[0] != nullptr) { + auto uv_file = mesh->mTextureCoords[0][i]; + uv.x() = uv_file.x; + uv.y() = uv_file.y; + } + data.vertices.emplace_back(v.x, v.y, v.z); + data.normals.emplace_back(n.x, n.y, n.z); + data.uvCoords.push_back(uv); + data.tangents.emplace_back(t.x, t.y, t.z); + data.bitangents.emplace_back(b.x, b.y, b.z); + data.boneIDs.emplace_back(Eigen::Vector4i::Constant(-1)); + data.boneWeights.emplace_back(Eigen::Vector4f::Zero()); + } + for (int i = 0; i < mesh->mNumFaces; i++) { + auto f = mesh->mFaces[i]; + assert(f.mNumIndices == 3); + data.indices.emplace_back(f.mIndices[0], f.mIndices[1], f.mIndices[2]); + } + + auto mesh_ = std::make_shared(data); + mesh_->setSources(file); + return mesh_; +} + +} // namespace ICE diff --git a/ICE/IO/src/ModelLoader.cpp b/ICE/IO/src/ModelLoader.cpp index 5320c7f6..09560e69 100644 --- a/ICE/IO/src/ModelLoader.cpp +++ b/ICE/IO/src/ModelLoader.cpp @@ -23,7 +23,7 @@ std::shared_ptr ModelLoader::load(const std::vector> meshes; + std::vector meshes; std::vector materials; std::vector nodes; Model::Skeleton skeleton; @@ -34,7 +34,6 @@ std::shared_ptr ModelLoader::load(const std::vectorsetSources(file); } std::unordered_set used_node_names; processNode(scene->mRootNode, nodes, skeleton, used_node_names, Eigen::Matrix4f::Identity()); @@ -62,7 +61,6 @@ int ModelLoader::processNode(const aiNode *ainode, std::vector &nod aiMatrix4x4 local = ainode->mTransformation; node.localTransform = aiMat4ToEigen(local); - node.animatedTransform = parent_transform * node.localTransform; for (unsigned int i = 0; i < ainode->mNumMeshes; ++i) { unsigned int mesh_idx = ainode->mMeshes[i]; @@ -70,22 +68,17 @@ int ModelLoader::processNode(const aiNode *ainode, std::vector &nod } auto insert_pos = nodes.size(); - if (skeleton.boneMapping.contains(name)) { - int boneID = skeleton.boneMapping.at(name); - skeleton.bones[boneID].finalTransformation = skeleton.globalInverseTransform * node.animatedTransform; - } - nodes.push_back(node); for (unsigned int c = 0; c < ainode->mNumChildren; ++c) { const aiNode *child = ainode->mChildren[c]; - int child_pos = processNode(child, nodes, skeleton, used_names, node.animatedTransform); + int child_pos = processNode(child, nodes, skeleton, used_names, parent_transform * node.localTransform); nodes.at(insert_pos).children.push_back(child_pos); } return insert_pos; } -std::shared_ptr ModelLoader::extractMesh(const aiMesh *mesh, const std::string &model_name, const aiScene *scene, Model::Skeleton &skeleton) { +AssetUID ModelLoader::extractMesh(const aiMesh *mesh, const std::string &model_name, const aiScene *scene, Model::Skeleton &skeleton) { MeshData data; for (int i = 0; i < mesh->mNumVertices; i++) { @@ -113,47 +106,26 @@ std::shared_ptr ModelLoader::extractMesh(const aiMesh *mesh, const std::st data.indices.emplace_back(f.mIndices[0], f.mIndices[1], f.mIndices[2]); } - SkinningData skinning_data; + std::unordered_map inverseBindMatrices; if (mesh->HasBones()) { - extractBoneData(mesh, scene, data, skinning_data, skeleton); + inverseBindMatrices = extractBoneData(mesh, data, skeleton); + } + auto mesh_ = std::make_shared(std::move(data)); + for (const auto &[boneID, ibm] : inverseBindMatrices) { + mesh_->setIBM(boneID, ibm); } - auto mesh_ = std::make_shared(data); - if (skinning_data.boneOffsetMatrices.size() > 0) { - mesh_->setSkinningData(skinning_data); + AssetUID mesh_id = 0; + AssetPath mesh_path = AssetPath::WithTypePrefix(model_name + "/" + mesh->mName.C_Str()); + if (mesh_id = ref_bank.getUID(mesh_path); mesh_id != 0) { + ref_bank.removeAsset(mesh_path); + ref_bank.addAssetWithSpecificUID(mesh_path, mesh_, mesh_id); + } else { + ref_bank.addAsset(mesh_path, mesh_); + mesh_id = ref_bank.getUID(mesh_path); } - auto vertexArray = graphics_factory->createVertexArray(); - - auto vertexBuffer = graphics_factory->createVertexBuffer(); - auto normalsBuffer = graphics_factory->createVertexBuffer(); - auto uvBuffer = graphics_factory->createVertexBuffer(); - auto tangentBuffer = graphics_factory->createVertexBuffer(); - auto biTangentBuffer = graphics_factory->createVertexBuffer(); - auto boneIDBuffer = graphics_factory->createVertexBuffer(); - auto boneWeightBuffer = graphics_factory->createVertexBuffer(); - auto indexBuffer = graphics_factory->createIndexBuffer(); - - vertexBuffer->putData(BufferUtils::CreateFloatBuffer(data.vertices), 3 * data.vertices.size() * sizeof(float)); - normalsBuffer->putData(BufferUtils::CreateFloatBuffer(data.normals), 3 * data.normals.size() * sizeof(float)); - tangentBuffer->putData(BufferUtils::CreateFloatBuffer(data.tangents), 3 * data.tangents.size() * sizeof(float)); - biTangentBuffer->putData(BufferUtils::CreateFloatBuffer(data.bitangents), 3 * data.bitangents.size() * sizeof(float)); - boneIDBuffer->putData(BufferUtils::CreateIntBuffer(data.boneIDs), 4 * data.boneIDs.size() * sizeof(int)); - boneWeightBuffer->putData(BufferUtils::CreateFloatBuffer(data.boneWeights), 4 * data.boneWeights.size() * sizeof(float)); - uvBuffer->putData(BufferUtils::CreateFloatBuffer(data.uvCoords), 2 * data.uvCoords.size() * sizeof(float)); - indexBuffer->putData(BufferUtils::CreateIntBuffer(data.indices), 3 * data.indices.size() * sizeof(int)); - - vertexArray->pushVertexBuffer(vertexBuffer, 0, 3); - vertexArray->pushVertexBuffer(normalsBuffer, 1, 3); - vertexArray->pushVertexBuffer(uvBuffer, 2, 2); - vertexArray->pushVertexBuffer(tangentBuffer, 3, 3); - vertexArray->pushVertexBuffer(biTangentBuffer, 4, 3); - vertexArray->pushVertexBuffer(boneIDBuffer, 5, 4); - vertexArray->pushVertexBuffer(boneWeightBuffer, 6, 4); - vertexArray->setIndexBuffer(indexBuffer); - - mesh_->setVertexArray(vertexArray); - return mesh_; + return mesh_id; } AssetUID ModelLoader::extractMaterial(const aiMaterial *material, const std::string &model_name, const aiScene *scene) { @@ -241,7 +213,7 @@ AssetUID ModelLoader::extractTexture(const aiMaterial *material, const std::stri } else { data2 = data; } - auto texture_ice = m_graphics_factory->createTexture2D(data2, width, height, getTextureFormat(type, channels)); + auto texture_ice = std::make_shared(data2, width, height, getTextureFormat(type, channels)); if (tex_id = ref_bank.getUID(AssetPath::WithTypePrefix(tex_path)); tex_id != 0) { ref_bank.removeAsset(AssetPath::WithTypePrefix(tex_path)); ref_bank.addAssetWithSpecificUID(AssetPath::WithTypePrefix(tex_path), texture_ice, tex_id); @@ -257,7 +229,8 @@ AssetUID ModelLoader::extractTexture(const aiMaterial *material, const std::stri return tex_id; } -void ModelLoader::extractBoneData(const aiMesh *mesh, const aiScene *scene, MeshData &data, SkinningData &skinning_data, Model::Skeleton &skeleton) { +std::unordered_map ModelLoader::extractBoneData(const aiMesh *mesh, MeshData &data, Model::Skeleton &skeleton) { + std::unordered_map inverseBindMatrices; for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) { std::string boneName = mesh->mBones[boneIndex]->mName.C_Str(); int boneID = -1; @@ -265,14 +238,12 @@ void ModelLoader::extractBoneData(const aiMesh *mesh, const aiScene *scene, Mesh if (!skeleton.boneMapping.contains(boneName)) { boneID = skeleton.boneMapping.size(); skeleton.boneMapping[boneName] = boneID; - Model::BoneInfo newBoneInfo = {.finalTransformation = Eigen::Matrix4f::Identity()}; - skeleton.bones.push_back(newBoneInfo); } else { //Bone Already Exists boneID = skeleton.boneMapping.at(boneName); } - skinning_data.boneOffsetMatrices.try_emplace(boneID, aiMat4ToEigen(mesh->mBones[boneIndex]->mOffsetMatrix)); + inverseBindMatrices.try_emplace(boneID, aiMat4ToEigen(mesh->mBones[boneIndex]->mOffsetMatrix)); aiVertexWeight *weights = mesh->mBones[boneIndex]->mWeights; unsigned int numWeights = mesh->mBones[boneIndex]->mNumWeights; @@ -290,6 +261,7 @@ void ModelLoader::extractBoneData(const aiMesh *mesh, const aiScene *scene, Mesh } } } + return inverseBindMatrices; } std::unordered_map ModelLoader::extractAnimations(const aiScene *scene, Model::Skeleton &skeleton) { @@ -322,7 +294,7 @@ std::unordered_map ModelLoader::extractAnimations(const a.tracks[boneName] = std::move(track); } std::string anim_name = anim->mName.C_Str(); - + out.try_emplace(anim_name.substr(anim_name.find_first_of('|') + 1), std::move(a)); } return out; diff --git a/ICE/IO/src/Project.cpp b/ICE/IO/src/Project.cpp index 5e96be8e..b90e0726 100644 --- a/ICE/IO/src/Project.cpp +++ b/ICE/IO/src/Project.cpp @@ -16,12 +16,14 @@ #include #include "MaterialExporter.h" +#include "ShaderExporter.h" namespace ICE { -Project::Project(const fs::path &base_directory, const std::string &name) - : m_base_directory(base_directory / name), - name(name), - assetBank(std::make_shared(std::make_shared())) { +Project::Project(const fs::path &base_directory, const std::string &m_name) + : m_base_directory(base_directory / m_name), + m_name(m_name), + m_asset_bank(std::make_shared()), + m_gpu_registry(std::make_shared(std::make_shared(), m_asset_bank)) { cameraPosition.setZero(); cameraRotation.setZero(); constexpr std::string_view assets_folder = "Assets"; @@ -29,31 +31,34 @@ Project::Project(const fs::path &base_directory, const std::string &name) m_shaders_directory = m_base_directory / assets_folder / "Shaders"; m_textures_directory = m_base_directory / assets_folder / "Textures"; m_cubemaps_directory = m_base_directory / assets_folder / "Cubemaps"; - m_meshes_directory = m_base_directory / assets_folder / "Models"; + m_models_directory = m_base_directory / assets_folder / "Models"; + m_meshes_directory = m_base_directory / assets_folder / "Meshes"; m_scenes_directory = m_base_directory / "Scenes"; } bool Project::CreateDirectories() { fs::create_directories(m_scenes_directory); try { - fs::create_directories(m_base_directory / "Assets"); fs::copy("Assets", m_base_directory / "Assets", fs::copy_options::recursive); } catch (std::filesystem::filesystem_error &e) { Logger::Log(Logger::FATAL, "IO", "Could not copy default assets: %s", e.what()); } - assetBank->addAsset("solid", {m_shaders_directory / "skinning.vs", m_shaders_directory / "solid.fs"}); - assetBank->addAsset("phong", {m_shaders_directory / "skinning.vs", m_shaders_directory / "phong.fs"}); - assetBank->addAsset("normal", {m_shaders_directory / "skinning.vs", m_shaders_directory / "normal.fs"}); - assetBank->addAsset("pbr", {m_shaders_directory / "skinning.vs", m_shaders_directory / "pbr.fs"}); - assetBank->addAsset("lastpass", {m_shaders_directory / "lastpass.vs", m_shaders_directory / "lastpass.fs"}); - assetBank->addAsset("__ice__picking_shader", {m_shaders_directory / "skinning.vs", m_shaders_directory / "picking.fs"}); + m_asset_bank->addAsset("solid", {m_shaders_directory / "solid.shader.json"}); + m_asset_bank->addAsset("phong", {m_shaders_directory / "phong.shader.json"}); + m_asset_bank->addAsset("normal", {m_shaders_directory / "normal.shader.json"}); + m_asset_bank->addAsset("pbr", {m_shaders_directory / "pbr.shader.json"}); + m_asset_bank->addAsset("lastpass", {m_shaders_directory / "lastpass.shader.json"}); + m_asset_bank->addAsset("__ice__picking_shader", {m_shaders_directory / "picking.shader.json"}); - assetBank->addAsset("base_mat", {m_materials_directory / "base_mat.icm"}); + m_asset_bank->addAsset("base_mat", {m_materials_directory / "base_mat.material.json"}); - assetBank->addAsset("cube", {m_meshes_directory / "cube.obj"}); - assetBank->addAsset("sphere", {m_meshes_directory / "sphere.obj"}); + m_asset_bank->addAsset("cube", {m_meshes_directory / "cube.obj"}); + m_asset_bank->addAsset("sphere", {m_meshes_directory / "sphere.obj"}); - scenes.push_back(std::make_shared("MainScene")); + m_asset_bank->addAsset("Editor/folder", {m_textures_directory / "Editor" / "folder.png"}); + m_asset_bank->addAsset("Editor/shader", {m_textures_directory / "Editor" / "shader.png"}); + + m_scenes.push_back(std::make_shared("MainScene")); setCurrentScene(getScenes()[0]); return true; } @@ -63,34 +68,40 @@ fs::path Project::getBaseDirectory() const { } std::string Project::getName() const { - return name; + return m_name; } void Project::writeToFile(const std::shared_ptr &editorCamera) { std::ofstream outstream; - outstream.open(m_base_directory / (name + ".ice")); + outstream.open(m_base_directory / (m_name + ".ice")); json j; j["camera_position"] = dumpVec3(editorCamera->getPosition()); j["camera_rotation"] = dumpVec3(editorCamera->getRotation()); std::vector vec; - for (const auto &s : scenes) { + for (const auto &s : m_scenes) { vec.push_back(s->getName()); } j["scenes"] = vec; vec.clear(); - for (const auto &[asset_id, mesh] : assetBank->getAll()) { + for (const auto &[asset_id, mesh] : m_asset_bank->getAll()) { vec.push_back(dumpAsset(asset_id, mesh)); } j["models"] = vec; vec.clear(); - for (const auto &[asset_id, material] : assetBank->getAll()) { - auto mtlName = assetBank->getName(asset_id).getName(); + for (const auto &[asset_id, mesh] : m_asset_bank->getAll()) { + vec.push_back(dumpAsset(asset_id, mesh)); + } + j["meshes"] = vec; + vec.clear(); + + for (const auto &[asset_id, material] : m_asset_bank->getAll()) { + auto mtlName = m_asset_bank->getName(asset_id).getName(); - fs::path path = m_materials_directory.parent_path() / (assetBank->getName(asset_id).prefix() + mtlName + ".icm"); + fs::path path = m_materials_directory.parent_path() / (m_asset_bank->getName(asset_id).prefix() + mtlName + ".material.json"); fs::create_directories(path.parent_path()); MaterialExporter().writeToJson(path, *material); @@ -101,19 +112,27 @@ void Project::writeToFile(const std::shared_ptr &editorCamera) { j["materials"] = vec; vec.clear(); - for (const auto &[asset_id, shader] : assetBank->getAll()) { + for (const auto &[asset_id, shader] : m_asset_bank->getAll()) { + auto mtlName = m_asset_bank->getName(asset_id).getName(); + + fs::path path = m_shaders_directory.parent_path() / (m_asset_bank->getName(asset_id).prefix() + mtlName + ".shader.json"); + fs::create_directories(path.parent_path()); + ShaderExporter().writeToJson(path, *shader); + + shader->setSources({path}); + vec.push_back(dumpAsset(asset_id, shader)); } j["shaders"] = vec; vec.clear(); - for (const auto &[asset_id, texture] : assetBank->getAll()) { + for (const auto &[asset_id, texture] : m_asset_bank->getAll()) { vec.push_back(dumpAsset(asset_id, texture)); } j["textures2D"] = vec; vec.clear(); - for (const auto &[asset_id, texture] : assetBank->getAll()) { + for (const auto &[asset_id, texture] : m_asset_bank->getAll()) { vec.push_back(dumpAsset(asset_id, texture)); } j["cubeMaps"] = vec; @@ -121,29 +140,30 @@ void Project::writeToFile(const std::shared_ptr &editorCamera) { outstream << j.dump(4); outstream.close(); - for (const auto &s : scenes) { + for (const auto &s : m_scenes) { outstream.open(m_scenes_directory / (s->getName() + ".ics")); j.clear(); - j["name"] = s->getName(); + j["m_name"] = s->getName(); json entities = json::array(); for (auto e : s->getRegistry()->getEntities()) { json entity; entity["id"] = e; - entity["name"] = s->getAlias(e); + entity["m_name"] = s->getAlias(e); entity["parent"] = s->getGraph()->getParentID(e); if (s->getRegistry()->entityHasComponent(e)) { RenderComponent rc = *s->getRegistry()->getComponent(e); json renderjson; - renderjson["model"] = rc.model; + renderjson["mesh"] = rc.mesh; + renderjson["material"] = rc.material; entity["renderComponent"] = renderjson; } if (s->getRegistry()->entityHasComponent(e)) { TransformComponent tc = *s->getRegistry()->getComponent(e); json transformjson; transformjson["position"] = dumpVec3(tc.getPosition()); - transformjson["rotation"] = dumpVec3(tc.getRotation()); + transformjson["rotation"] = dumpVec3(tc.getRotationEulerDeg()); transformjson["scale"] = dumpVec3(tc.getScale()); entity["transformComponent"] = transformjson; } @@ -154,6 +174,34 @@ void Project::writeToFile(const std::shared_ptr &editorCamera) { lightjson["type"] = lc.type; entity["lightComponent"] = lightjson; } + if (s->getRegistry()->entityHasComponent(e)) { + AnimationComponent ac = *s->getRegistry()->getComponent(e); + json animjson; + animjson["currentAnimation"] = ac.currentAnimation; + animjson["currentTime"] = ac.currentTime; + animjson["speed"] = ac.speed; + animjson["playing"] = ac.playing; + animjson["loop"] = ac.loop; + entity["animationComponent"] = animjson; + } + if (s->getRegistry()->entityHasComponent(e)) { + SkeletonPoseComponent sc = *s->getRegistry()->getComponent(e); + json spjson; + spjson["skeletonModel"] = sc.skeletonModel; + spjson["bone_entity"] = sc.bone_entity; + std::vector bone_transforms; + for (const auto& tr : sc.bone_transform) { + bone_transforms.push_back(JsonParser::dumpMat4(tr)); + } + spjson["bone_transforms"] = bone_transforms; + entity["skeletonPoseComponent"] = spjson; + } + if (s->getRegistry()->entityHasComponent(e)) { + SkinningComponent sc = *s->getRegistry()->getComponent(e); + json scjson; + scjson["skeleton_entity"] = sc.skeleton_entity; + entity["skinningComponent"] = scjson; + } entities.push_back(entity); } j["entities"] = entities; @@ -163,7 +211,7 @@ void Project::writeToFile(const std::shared_ptr &editorCamera) { } json Project::dumpAsset(AssetUID uid, const std::shared_ptr &asset) { - auto asset_path = assetBank->getName(uid); + auto asset_path = m_asset_bank->getName(uid); json tmp; auto paths = asset->getSources(); std::vector sources(paths.size()); @@ -178,13 +226,14 @@ json Project::dumpAsset(AssetUID uid, const std::shared_ptr &asset) { } void Project::loadFromFile() { - std::ifstream infile = std::ifstream(m_base_directory / (name + ".ice")); + std::ifstream infile = std::ifstream(m_base_directory / (m_name + ".ice")); json j; infile >> j; infile.close(); std::vector sceneNames = j["scenes"]; - json meshes = j["models"]; + json models = j["models"]; + json meshes = j["meshes"]; json material = j["materials"]; json shader = j["shaders"]; json texture = j["textures2D"]; @@ -197,7 +246,8 @@ void Project::loadFromFile() { loadAssetsOfType(texture); loadAssetsOfType(cubeMap); loadAssetsOfType(material); - loadAssetsOfType(meshes); + loadAssetsOfType(meshes); + loadAssetsOfType(models); for (const auto &s : sceneNames) { infile = std::ifstream(m_scenes_directory / (s + ".ics")); @@ -205,12 +255,12 @@ void Project::loadFromFile() { infile >> scenejson; infile.close(); - Scene scene = Scene(scenejson["name"]); + Scene scene = Scene(scenejson["m_name"]); for (json jentity : scenejson["entities"]) { Entity e = jentity["id"]; Entity parent = jentity["parent"]; - std::string alias = jentity["name"]; + std::string alias = jentity["m_name"]; scene.addEntity(e, alias, 0); @@ -222,7 +272,7 @@ void Project::loadFromFile() { } if (!jentity["renderComponent"].is_null()) { json rj = jentity["renderComponent"]; - RenderComponent rc(rj["model"]); + RenderComponent rc(rj["mesh"], rj["material"]); scene.getRegistry()->addComponent(e, rc); } if (!jentity["lightComponent"].is_null()) { @@ -230,11 +280,39 @@ void Project::loadFromFile() { LightComponent lc(static_cast((int) lj["type"]), JsonParser::parseVec3(lj["color"])); scene.getRegistry()->addComponent(e, lc); } + if (!jentity["animationComponent"].is_null()) { + json aj = jentity["animationComponent"]; + AnimationComponent ac; + ac.currentAnimation = aj["currentAnimation"]; + ac.currentTime = aj["currentTime"]; + ac.speed = aj["speed"]; + ac.playing = aj["playing"]; + ac.loop = aj["loop"]; + scene.getRegistry()->addComponent(e, ac); + } + if (!jentity["skeletonPoseComponent"].is_null()) { + json sj = jentity["skeletonPoseComponent"]; + SkeletonPoseComponent sc; + sc.skeletonModel = sj["skeletonModel"]; + sc.bone_entity = sj["bone_entity"].get>(); + std::vector bone_transforms; + for (const auto& jt : sj["bone_transforms"]) { + bone_transforms.push_back(JsonParser().readMat4(jt)); + } + sc.bone_transform = bone_transforms; + scene.getRegistry()->addComponent(e, sc); + } + if (!jentity["skinningComponent"].is_null()) { + json skj = jentity["skinningComponent"]; + SkinningComponent sc; + sc.skeleton_entity = skj["skeleton_entity"]; + scene.getRegistry()->addComponent(e, sc); + } } for (json jentity : scenejson["entities"]) { Entity e = jentity["id"]; Entity parent = jentity["parent"]; - scene.getGraph()->setParent(e, parent, true); + scene.getGraph()->setParent(e, parent); } addScene(scene); //TODO: it would be better to save the current scene index @@ -260,7 +338,7 @@ bool Project::renameAsset(const AssetPath &oldName, const AssetPath &newName) { if (newName.getName() == "" || newName.prefix() != oldName.prefix()) { return false; } - if (assetBank->renameAsset(oldName, newName)) { + if (m_asset_bank->renameAsset(oldName, newName)) { auto path = m_base_directory / "Assets"; for (const auto &file : getFilesInDir(path / oldName.prefix())) { if (file.substr(0, file.find_last_of(".")) == oldName.getName()) { @@ -286,23 +364,27 @@ std::vector Project::getFilesInDir(const fs::path &folder) { } std::vector> Project::getScenes() { - return scenes; + return m_scenes; } void Project::setScenes(const std::vector> &scenes) { - Project::scenes = scenes; + m_scenes = scenes; +} + +std::shared_ptr Project::getGPURegistry() { + return m_gpu_registry; } std::shared_ptr Project::getAssetBank() { - return assetBank; + return m_asset_bank; } -void Project::setAssetBank(const std::shared_ptr &assetBank) { - Project::assetBank = assetBank; +void Project::setAssetBank(const std::shared_ptr &asset_bank) { + m_asset_bank = asset_bank; } void Project::addScene(const Scene &scene) { - scenes.push_back(std::make_shared(scene)); + m_scenes.push_back(std::make_shared(scene)); } void Project::setCurrentScene(const std::shared_ptr &scene) { diff --git a/ICE/IO/src/ShaderExporter.cpp b/ICE/IO/src/ShaderExporter.cpp new file mode 100644 index 00000000..d6205cdc --- /dev/null +++ b/ICE/IO/src/ShaderExporter.cpp @@ -0,0 +1,28 @@ +#include "ShaderExporter.h" + +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace ICE { + +void ShaderExporter::writeToJson(const std::filesystem::path &path, const Shader &shader) { + std::ofstream outstream; + outstream.open(path); + json j = json::array(); + for (const auto& [stage, source] : shader.getSources()) { + j.push_back({ + {"stage", stageToString(stage)}, + {"source", source.first}}); + } + outstream << j.dump(4); + outstream.close(); +} + +void ShaderExporter::writeToBin(const std::filesystem::path &path, const Shader &object) { + throw std::runtime_error("Not implemented"); +} +} // namespace ICE \ No newline at end of file diff --git a/ICE/IO/src/ShaderLoader.cpp b/ICE/IO/src/ShaderLoader.cpp index 8668c50d..c8f1ab36 100644 --- a/ICE/IO/src/ShaderLoader.cpp +++ b/ICE/IO/src/ShaderLoader.cpp @@ -8,27 +8,53 @@ #include #include +#include #include namespace ICE { std::shared_ptr ShaderLoader::load(const std::vector &files) { - if (files.size() < 2) { - throw ICEException("Shaders must have at least 2 files"); + if (files.empty()) { + throw ICEException("No files provided for shader"); } - std::shared_ptr shader; - if (files.size() == 2) { - shader = graphics_factory->createShader(readAndResolveIncludes(files[0]), readAndResolveIncludes(files[1])); - } else if (files.size() == 3) { - shader = graphics_factory->createShader(readAndResolveIncludes(files[0]), readAndResolveIncludes(files[1]), readAndResolveIncludes(files[2])); - } else { - throw ICEException("Too many files for shader"); + nlohmann::json json; + std::ifstream infile = std::ifstream(files[0]); + infile >> json; + infile.close(); + + ShaderSource shader_sources; + for (const auto &stage_source : json) { + auto stage = stageFromString(stage_source["stage"]); + auto file = stage_source["source"].get(); + auto file_path = (files[0].parent_path() / file).string(); + auto source_code = readAndResolveIncludes(file_path); + shader_sources[stage] = {file, source_code}; } + auto shader = std::make_shared(shader_sources); + shader->setSources(files); return shader; } +constexpr ShaderStage ShaderLoader::stageFromString(const std::string &str) { + if (str == "vertex") { + return ShaderStage::Vertex; + } else if (str == "fragment") { + return ShaderStage::Fragment; + } else if (str == "geometry") { + return ShaderStage::Geometry; + } else if (str == "tess_control") { + return ShaderStage::TessControl; + } else if (str == "tess_eval") { + return ShaderStage::TessEval; + } else if (str == "compute") { + return ShaderStage::Compute; + } else { + throw ICEException("Unknown shader stage: " + str); + } +} + std::string ShaderLoader::readAndResolveIncludes(const std::filesystem::path &file_path) { std::ifstream file(file_path); if (!file.is_open()) { diff --git a/ICE/IO/src/TextureLoader.cpp b/ICE/IO/src/TextureLoader.cpp index c8296322..e24a0b76 100644 --- a/ICE/IO/src/TextureLoader.cpp +++ b/ICE/IO/src/TextureLoader.cpp @@ -13,14 +13,14 @@ std::shared_ptr Texture2DLoader::load(const std::vectorcreateTexture2D(file[0].string()); + auto texture = std::make_shared(file[0].string()); texture->setSources(file); return texture; } std::shared_ptr TextureCubeLoader::load(const std::vector &file) { Logger::Log(Logger::VERBOSE, "IO", "Loading cubemap..."); - auto texture = graphics_factory->createTextureCube(file[0].string()); + auto texture = std::make_shared(file[0].string()); texture->setSources(file); return texture; } diff --git a/ICE/IO/test/ModelLoaderTest.cpp b/ICE/IO/test/ModelLoaderTest.cpp index 6316aa42..db406ab0 100644 --- a/ICE/IO/test/ModelLoaderTest.cpp +++ b/ICE/IO/test/ModelLoaderTest.cpp @@ -1,17 +1,14 @@ #define STB_IMAGE_IMPLEMENTATION #include -#include #include -#include "ModelLoader.h" +#include "MeshLoader.h" using namespace ICE; TEST(ModelLoaderTest, LoadFromObj) { - auto gr_f = std::make_shared(); - AssetBank bank(gr_f); - auto mesh = ModelLoader(gr_f, bank).load({"cube.obj"}); - EXPECT_EQ(mesh->getMeshes().at(0)->getVertices().size(), 36); - EXPECT_EQ(mesh->getMeshes().at(0)->getIndices().size(), 12); + auto mesh = MeshLoader().load({"cube.obj"}); + EXPECT_EQ(mesh->getVertices().size(), 36); + EXPECT_EQ(mesh->getIndices().size(), 12); } \ No newline at end of file diff --git a/ICE/Math/src/AABB.cpp b/ICE/Math/src/AABB.cpp index c2c93f3e..03547ed2 100644 --- a/ICE/Math/src/AABB.cpp +++ b/ICE/Math/src/AABB.cpp @@ -9,7 +9,7 @@ namespace ICE { AABB::AABB(const Eigen::Vector3f &min, const Eigen::Vector3f &max) : min(min), max(max) { } AABB::AABB(const std::vector &points) : AABB(points[0], points[0]) { - for (auto v : points) { + for (const auto &v : points) { min = min.cwiseMin(v); max = max.cwiseMax(v); } diff --git a/ICE/Platform/FileUtils.cpp b/ICE/Platform/FileUtils.cpp index 13056864..0144d606 100644 --- a/ICE/Platform/FileUtils.cpp +++ b/ICE/Platform/FileUtils.cpp @@ -8,11 +8,10 @@ #include -#include "dialog.h" namespace ICE { -const std::string FileUtils::openFileDialog(const std::string &filter) { - const std::string file = open_native_dialog(filter); +const std::string FileUtils::openFileDialog(const std::vector &filters) { + const std::string file = open_native_dialog(filters); Logger::Log(Logger::DEBUG, "Platform", "User selected file: %s", file.c_str()); return file; } diff --git a/ICE/Platform/FileUtils.h b/ICE/Platform/FileUtils.h index 06f8f053..1ff210db 100644 --- a/ICE/Platform/FileUtils.h +++ b/ICE/Platform/FileUtils.h @@ -5,18 +5,16 @@ #ifndef ICE_FILEUTILS_H #define ICE_FILEUTILS_H - -#include +#include "dialog.h" namespace ICE { - class FileUtils { - - public: - static const std::string openFileDialog(const std::string& filter); - static const std::string openFolderDialog(); - static const std::string readFile(const std::string &path); - }; -} +class FileUtils { + public: + static const std::string openFileDialog(const std::vector &filters); + static const std::string openFolderDialog(); + static const std::string readFile(const std::string &path); +}; +} // namespace ICE -#endif //ICE_FILEUTILS_H +#endif //ICE_FILEUTILS_H diff --git a/ICE/Platform/Linux/dialog.cpp b/ICE/Platform/Linux/dialog.cpp index ae61b79f..816a8ae1 100644 --- a/ICE/Platform/Linux/dialog.cpp +++ b/ICE/Platform/Linux/dialog.cpp @@ -5,7 +5,7 @@ #include #include -const std::string open_native_dialog(std::string const& filter) { +const std::string open_native_dialog(const std::vector &filters) { GtkWidget *dialog; diff --git a/ICE/Platform/OSX/dialog.mm b/ICE/Platform/OSX/dialog.mm index 59f1e30c..7c755ba2 100644 --- a/ICE/Platform/OSX/dialog.mm +++ b/ICE/Platform/OSX/dialog.mm @@ -1,7 +1,7 @@ #include "dialog.h" #import -const std::string open_native_dialog(std::string const& filter) { +const std::string open_native_dialog(const std::vector& filters) { // Create the File Open Dialog class. NSOpenPanel* openDlg = [NSOpenPanel openPanel]; diff --git a/ICE/Platform/Win32/dialog.cpp b/ICE/Platform/Win32/dialog.cpp index e6ca42dd..508af06f 100644 --- a/ICE/Platform/Win32/dialog.cpp +++ b/ICE/Platform/Win32/dialog.cpp @@ -11,28 +11,42 @@ #include #include -const std::string open_native_dialog(std::string const& filter) { +const std::string open_native_dialog(const std::vector& filters) +{ + char filename[MAX_PATH] = { 0 }; - char filename[MAX_PATH]; + // Build filter string: "Description1 (*.ext1;*.ext2)\0*.ext1;*.ext2\0Description2 (*.ext3)\0*.ext3\0\0" + std::string filterStr; + for (const auto& filter : filters) + { + // Show both description and extension pattern in the filter name + filterStr += filter.description; + filterStr += " ("; + filterStr += filter.extension; + filterStr += ")"; + filterStr += '\0'; + filterStr += filter.extension; + filterStr += '\0'; + } + filterStr += '\0'; // Double null-terminated - OPENFILENAME ofn; - ZeroMemory(&filename, sizeof(filename)); - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = NULL; // If you have a window to center over, put its HANDLE here - //"Filter\0*.PDF\0"; - std::string mfilter("Filter\0*.", 8); - ofn.lpstrFilter = (mfilter + filter + std::string("\0\0\0", 2)).c_str(); - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.lpstrTitle = _T("Select a file"); - ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST; + OPENFILENAMEA ofn; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL; + ofn.lpstrFilter = filters.empty() ? NULL : filterStr.c_str(); + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrTitle = "Select a file"; + ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST; - if (GetOpenFileName(&ofn)) - { - return std::string(filename); - } - return std::string(); + if (GetOpenFileNameA(&ofn)) + { + // Ensure null-termination + filename[MAX_PATH - 1] = '\0'; + return std::string(filename); + } + return std::string(); } const std::string open_native_folder_dialog() { diff --git a/ICE/Platform/dialog.h b/ICE/Platform/dialog.h index b12fe94b..c9eeee4f 100644 --- a/ICE/Platform/dialog.h +++ b/ICE/Platform/dialog.h @@ -6,8 +6,14 @@ #define ICE_PLATEFORM_DIALOG_H #include +#include -const std::string open_native_dialog(const std::string& filter); +struct FileFilter { + std::string description; + std::string extension; +}; + +const std::string open_native_dialog(const std::vector& filters); const std::string open_native_folder_dialog(); #endif \ No newline at end of file diff --git a/ICE/Scene/include/Scene.h b/ICE/Scene/include/Scene.h index d1a0d3ac..ef2b6735 100644 --- a/ICE/Scene/include/Scene.h +++ b/ICE/Scene/include/Scene.h @@ -28,6 +28,8 @@ class Scene { std::shared_ptr getRegistry() const; Entity createEntity(); + Entity spawnTree(AssetUID model_id, const std::shared_ptr& bank); + void addEntity(Entity e, const std::string& alias, Entity parent); void removeEntity(Entity e); bool hasEntity(Entity e); diff --git a/ICE/Scene/include/SceneGraph.h b/ICE/Scene/include/SceneGraph.h index a4a1bc51..a51c44df 100644 --- a/ICE/Scene/include/SceneGraph.h +++ b/ICE/Scene/include/SceneGraph.h @@ -1,8 +1,10 @@ #pragma once - #include +#include #include +#include +#include namespace ICE { @@ -11,80 +13,92 @@ class SceneGraph { struct SceneNode { Entity entity{0}; std::vector> children{}; + + // Use weak_ptr to avoid cyclic references (Parent owns Child, Child observes Parent) + std::weak_ptr parent; }; SceneGraph() : root(std::make_shared()) { idToNode.try_emplace(0, root); } void addEntity(Entity e) { - auto sn = createSceneNode(e); + // Prevent adding duplicate entities + if (idToNode.find(e) != idToNode.end()) + return; + + auto sn = std::make_shared(); + sn->entity = e; + sn->parent = root; // Default parent is root + root->children.push_back(sn); - idToNode.insert({e, sn}); + idToNode[e] = sn; } void removeEntity(Entity e) { + if (e == 0 || idToNode.find(e) == idToNode.end()) + return; + auto sn = idToNode[e]; - auto parent = findParent(e, root); - auto it = std::find(parent->children.begin(), parent->children.end(), idToNode[e]); - parent->children.erase(it); - for (size_t i = 0; i < sn->children.size(); i++) { - parent->children.push_back(sn->children[i]); + auto parentPtr = sn->parent.lock(); + + if (parentPtr) { + // Remove 'sn' from its parent's list + auto& siblings = parentPtr->children; + siblings.erase(std::remove(siblings.begin(), siblings.end(), sn), siblings.end()); + + // Orphan handling: Reparent children to their grandparent + for (auto& child : sn->children) { + child->parent = parentPtr; // Update child's parent pointer + parentPtr->children.push_back(child); + } } - sn->children.clear(); + idToNode.erase(e); } - void setParent(Entity e, Entity newParent, bool recurse) { - auto parent = findParent(e, root); + void setParent(Entity e, Entity newParentID) { + if (idToNode.find(e) == idToNode.end() || idToNode.find(newParentID) == idToNode.end()) + return; + if (e == newParentID) + return; // Can't parent to self + auto sn = idToNode[e]; - auto newparent_node = idToNode[newParent]; - auto it = std::find(parent->children.begin(), parent->children.end(), sn); - parent->children.erase(it); - newparent_node->children.push_back(sn); - if (!recurse) { - for (size_t i = 0; i < sn->children.size(); i++) { - parent->children.push_back(sn->children[i]); - } + auto newParent = idToNode[newParentID]; + auto oldParent = sn->parent.lock(); + + if (oldParent) { + auto& siblings = oldParent->children; + siblings.erase(std::remove(siblings.begin(), siblings.end(), sn), siblings.end()); } + + sn->parent = newParent; + newParent->children.push_back(sn); } Entity getParentID(Entity e) { - auto it = std::find(root->children.begin(), root->children.end(), idToNode[e]); - if (it != root->children.end()) { - return root->entity; - } - for (size_t i = 0; i < root->children.size(); i++) { - auto subtreeSearch = findParent(e, root->children[i]); - if (subtreeSearch != nullptr) { - return subtreeSearch->entity; - } + if (idToNode.find(e) == idToNode.end()) + return 0; + + auto parentPtr = idToNode[e]->parent.lock(); + if (parentPtr) { + return parentPtr->entity; } return 0; } - std::shared_ptr root; - - private: - std::shared_ptr findParent(Entity e, const std::shared_ptr &root) { - auto it = std::find(root->children.begin(), root->children.end(), idToNode[e]); - if (it != root->children.end()) { - return root; - } - for (size_t i = 0; i < root->children.size(); i++) { - auto subtreeSearch = findParent(e, root->children[i]); - if (subtreeSearch != nullptr) { - return subtreeSearch; + const std::vector getChildren(Entity e) { + std::vector childrenIDs; + if (idToNode.find(e) != idToNode.end()) { + for (auto& child : idToNode[e]->children) { + childrenIDs.push_back(child->entity); } } - return nullptr; + return childrenIDs; } - std::shared_ptr createSceneNode(Entity e) { - auto sn = std::make_shared(); - sn->entity = e; - return sn; - } + std::shared_ptr getRoot() const { return root; } - std::unordered_map m_entity_names; + private: + std::shared_ptr root; std::unordered_map> idToNode; }; } // namespace ICE \ No newline at end of file diff --git a/ICE/Scene/src/Scene.cpp b/ICE/Scene/src/Scene.cpp index 7e8c5d3f..0e5c3a30 100644 --- a/ICE/Scene/src/Scene.cpp +++ b/ICE/Scene/src/Scene.cpp @@ -45,9 +45,70 @@ Entity Scene::createEntity() { return e; } +Entity Scene::spawnTree(AssetUID model_id, const std::shared_ptr &bank) { + auto model = bank->getAsset(model_id); + auto nodes = model->getNodes(); + auto meshes = model->getMeshes(); + auto materialIDs = model->getMaterialsIDs(); + std::unordered_map bone_entity; + + std::function spawnNode = [&](int nodeIndex, Entity parent, Entity skeleton) -> Entity { + auto &node = nodes[nodeIndex]; + + Entity nodeEntity = createEntity(); + if (skeleton == -1) { + skeleton = nodeEntity; + } + setAlias(nodeEntity, node.name); + if (model->getSkeleton().boneMapping.contains(node.name)) { + bone_entity[node.name] = nodeEntity; + } + m_graph->setParent(nodeEntity, parent); + registry->addComponent(nodeEntity, TransformComponent(node.localTransform)); + if (parent != 0) { + auto parent_tc = registry->getComponent(parent); + registry->getComponent(nodeEntity)->updateParentMatrix(parent_tc->getWorldMatrix()); + } + for (int meshIdx : node.meshIndices) { + Entity meshEntity = createEntity(); + setAlias(meshEntity, node.name + "_mesh_" + std::to_string(meshIdx)); + + m_graph->setParent(meshEntity, nodeEntity); + registry->addComponent(meshEntity, TransformComponent(Eigen::Matrix4f::Identity().eval())); + registry->addComponent(meshEntity, SkinningComponent{.skeleton_entity = skeleton}); + registry->addComponent(meshEntity, RenderComponent(meshes[meshIdx], materialIDs[meshIdx])); + } + + for (int childIndex : node.children) { + spawnNode(childIndex, nodeEntity, skeleton); + } + + return nodeEntity; + }; + + auto root = spawnNode(0, 0, -1); + + if (!model->getSkeleton().boneMapping.empty()) { + registry->addComponent(root, + SkeletonPoseComponent{.skeletonModel = model_id, + .bone_transform = std::vector( + model->getSkeleton().boneMapping.size(), Eigen::Matrix4f::Identity()), + .bone_entity = bone_entity}); + auto pose = registry->getComponent(root); + for (const auto &[name, id] : model->getSkeleton().boneMapping) { + Entity boneEntity = pose->bone_entity.at(name); + + Eigen::Matrix4f boneWorld = registry->getComponent(boneEntity)->getWorldMatrix(); + pose->bone_transform[id] = boneWorld; + } + } + + return root; +} + void Scene::addEntity(Entity e, const std::string &alias, Entity parent) { m_graph->addEntity(e); - m_graph->setParent(e, parent, false); + m_graph->setParent(e, parent); registry->addEntity(e); aliases.try_emplace(e, alias); } diff --git a/ICE/Scene/test/SceneGraphTest.cpp b/ICE/Scene/test/SceneGraphTest.cpp index 2bbf3989..120b8b97 100644 --- a/ICE/Scene/test/SceneGraphTest.cpp +++ b/ICE/Scene/test/SceneGraphTest.cpp @@ -8,14 +8,14 @@ using namespace ICE; TEST(SceneGraphTest, SceneGraphCreated) { SceneGraph sg = SceneGraph(); - ASSERT_EQ(sg.root->entity, 0); + ASSERT_EQ(sg.getRoot()->entity, 0); } TEST(SceneGraphTest, SceneGraphAddEntity) { SceneGraph sg = SceneGraph(); sg.addEntity(1); - ASSERT_EQ(sg.root->children[0]->entity, 1); + ASSERT_EQ(sg.getRoot()->children[0]->entity, 1); } TEST(SceneGraphTest, SceneGraphSetParent) @@ -23,10 +23,10 @@ TEST(SceneGraphTest, SceneGraphSetParent) SceneGraph sg = SceneGraph(); sg.addEntity(1); sg.addEntity(2); - ASSERT_EQ(sg.root->children[1]->entity, 2); - sg.setParent(2, 1, true); - ASSERT_EQ(sg.root->children.size(), 1); - ASSERT_EQ(sg.root->children[0]->children[0]->entity, 2); + ASSERT_EQ(sg.getRoot()->children[1]->entity, 2); + sg.setParent(2, 1); + ASSERT_EQ(sg.getRoot()->children.size(), 1); + ASSERT_EQ(sg.getRoot()->children[0]->children[0]->entity, 2); } TEST(SceneGraphTest, SceneGraphRemoveEntity) @@ -34,10 +34,10 @@ TEST(SceneGraphTest, SceneGraphRemoveEntity) SceneGraph sg = SceneGraph(); sg.addEntity(1); sg.addEntity(2); - sg.setParent(2, 1, true); + sg.setParent(2, 1); sg.removeEntity(1); - ASSERT_EQ(sg.root->children.size(), 1); - ASSERT_EQ(sg.root->children[0]->entity, 2); + ASSERT_EQ(sg.getRoot()->children.size(), 1); + ASSERT_EQ(sg.getRoot()->children[0]->entity, 2); } #endif //ICE_SCENEGRAPHTEST_H diff --git a/ICE/Storage/CMakeLists.txt b/ICE/Storage/CMakeLists.txt index 8788d22d..46fb5335 100644 --- a/ICE/Storage/CMakeLists.txt +++ b/ICE/Storage/CMakeLists.txt @@ -9,7 +9,9 @@ target_sources(${PROJECT_NAME} PRIVATE src/Filesystem.cpp ) -#target_link_libraries(${PROJECT_NAME}) +target_link_libraries(${PROJECT_NAME} PUBLIC + nlohmann_json +) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/ICE/Storage/include/JsonParser.h b/ICE/Storage/include/JsonParser.h index 459112ec..115ba91f 100644 --- a/ICE/Storage/include/JsonParser.h +++ b/ICE/Storage/include/JsonParser.h @@ -7,7 +7,7 @@ #include -#include +#include using json = nlohmann::json; namespace ICE { @@ -20,6 +20,24 @@ namespace ICE { static Eigen::Vector4f parseVec4(const json &src) { return Eigen::Vector4f(src["x"],src["y"],src["z"],src["w"]); } + + static json dumpMat4(const Eigen::Matrix4f& mat) { + json j = json::array(); + for (int i = 0; i < 4; ++i) + for (int k = 0; k < 4; ++k) + j.push_back(mat(i, k)); + return j; + } + + inline Eigen::Matrix4f readMat4(const json& j) { + Eigen::Matrix4f mat; + if (!j.is_array() || j.size() != 16) + throw std::runtime_error("Invalid JSON for Eigen::Matrix4f"); + for (int i = 0; i < 4; ++i) + for (int k = 0; k < 4; ++k) + mat(i, k) = j[i * 4 + k].get(); + return mat; + } }; } diff --git a/ICE/System/CMakeLists.txt b/ICE/System/CMakeLists.txt index fe990d96..635a5a86 100644 --- a/ICE/System/CMakeLists.txt +++ b/ICE/System/CMakeLists.txt @@ -7,7 +7,9 @@ add_library(${PROJECT_NAME} STATIC) target_sources(${PROJECT_NAME} PRIVATE src/RenderSystem.cpp - src/AnimationSystem.cpp) + src/AnimationSystem.cpp + src/SceneGraphSystem.cpp +) target_link_libraries(${PROJECT_NAME} PUBLIC diff --git a/ICE/System/include/AnimationSystem.h b/ICE/System/include/AnimationSystem.h index ecd7d381..be29e55b 100644 --- a/ICE/System/include/AnimationSystem.h +++ b/ICE/System/include/AnimationSystem.h @@ -15,7 +15,7 @@ class AnimationSystem : public System { std::vector getSignatures(const ComponentManager& comp_manager) const override { Signature signature; signature.set(comp_manager.getComponentType()); - signature.set(comp_manager.getComponentType()); + signature.set(comp_manager.getComponentType()); return {signature}; } @@ -30,16 +30,16 @@ class AnimationSystem : public System { return keys.size() - 1; } - void updateSkeleton(const std::shared_ptr& model, double time, const Animation& anim); + void updateSkeleton(const std::shared_ptr& model, double time, SkeletonPoseComponent* pose, const Animation& anim); + void finalizePose(); + Eigen::Vector3f interpolatePosition(double timeInTicks, const BoneAnimation& track); - Eigen::Matrix4f interpolatePosition(double timeInTicks, const BoneAnimation& track); + Eigen::Vector3f interpolateScale(double timeInTicks, const BoneAnimation& track); - Eigen::Matrix4f interpolateScale(double timeInTicks, const BoneAnimation& track); + Eigen::Quaternionf interpolateRotation(double time, const BoneAnimation& track); - Eigen::Matrix4f interpolateRotation(double time, const BoneAnimation& track); - - void applyTransforms(Model::Node* node, const Eigen::Matrix4f& parentTransform, Model::Skeleton& skeleton, double time, const Animation& anim, - std::vector& allModelNodes); + void applyTransforms(const Model::Node* node, const Eigen::Matrix4f& parentTransform, const Model::Skeleton& skeleton, double time, + SkeletonPoseComponent* pose, const Animation& anim, const std::vector& allModelNodes); std::shared_ptr m_registry; std::shared_ptr m_asset_bank; diff --git a/ICE/System/include/Registry.h b/ICE/System/include/Registry.h index 45e28287..4d923d7e 100644 --- a/ICE/System/include/Registry.h +++ b/ICE/System/include/Registry.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +30,8 @@ class Registry { componentManager.registerComponent(); componentManager.registerComponent(); componentManager.registerComponent(); + componentManager.registerComponent(); + componentManager.registerComponent(); } ~Registry() = default; diff --git a/ICE/System/include/RenderSystem.h b/ICE/System/include/RenderSystem.h index 5efba999..e639b3af 100644 --- a/ICE/System/include/RenderSystem.h +++ b/ICE/System/include/RenderSystem.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -14,15 +15,19 @@ namespace ICE { class Scene; +class Registry; class RenderSystem : public System { public: - RenderSystem() {}; + RenderSystem(const std::shared_ptr &api, const std::shared_ptr &factory, const std::shared_ptr ®, + const std::shared_ptr &gpu_bank); void onEntityAdded(Entity e) override; void onEntityRemoved(Entity e) override; void update(double delta) override; + void submitModel(const std::shared_ptr &model, const Eigen::Matrix4f &transform); + std::shared_ptr getRenderer() const; void setRenderer(const std::shared_ptr &renderer); std::shared_ptr getCamera() const; @@ -46,5 +51,17 @@ class RenderSystem : public System { private: std::shared_ptr m_renderer; std::shared_ptr m_camera; + std::shared_ptr m_target; + + AssetUID m_skybox = NO_ASSET_ID; + std::vector m_render_queue; + std::vector m_lights; + + std::shared_ptr m_api; + std::shared_ptr m_factory; + std::shared_ptr m_registry; + std::shared_ptr m_gpu_bank; + + std::shared_ptr m_quad_vao; }; } // namespace ICE diff --git a/ICE/System/include/SceneGraphSystem.h b/ICE/System/include/SceneGraphSystem.h new file mode 100644 index 00000000..7ea85425 --- /dev/null +++ b/ICE/System/include/SceneGraphSystem.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "System.h" + +namespace ICE { + +class SceneGraphSystem : public System { + public: + SceneGraphSystem(const std::shared_ptr &scene); + + void onEntityAdded(Entity e) override; + void onEntityRemoved(Entity e) override; + void update(double delta) override; + + std::vector getSignatures(const ComponentManager &comp_manager) const override { + Signature signature0; + signature0.set(comp_manager.getComponentType()); + return {signature0}; + } + + private: + std::shared_ptr m_scene; +}; +} // namespace ICE diff --git a/ICE/System/src/AnimationSystem.cpp b/ICE/System/src/AnimationSystem.cpp index 374078ff..04f8cd47 100644 --- a/ICE/System/src/AnimationSystem.cpp +++ b/ICE/System/src/AnimationSystem.cpp @@ -9,13 +9,16 @@ AnimationSystem::AnimationSystem(const std::shared_ptr& reg, const std void AnimationSystem::update(double dt) { for (auto e : entities) { auto anim = m_registry->getComponent(e); - auto rc = m_registry->getComponent(e); + auto pose = m_registry->getComponent(e); if (!anim->playing) continue; anim->currentTime += dt * anim->speed; - auto model = m_asset_bank->getAsset(rc->model); + auto model = m_asset_bank->getAsset(pose->skeletonModel); + if (!model->getAnimations().contains(anim->currentAnimation)) { + continue; + } auto animation = model->getAnimations().at(anim->currentAnimation); if (anim->currentTime > animation.duration) { @@ -26,117 +29,108 @@ void AnimationSystem::update(double dt) { anim->playing = false; } } - updateSkeleton(model, anim->currentTime, animation); + updateSkeleton(model, anim->currentTime, pose, animation); + finalizePose(); } } -void AnimationSystem::updateSkeleton(const std::shared_ptr& model, double time, const Animation& anim) { - auto& all_nodes = model->getNodes(); - applyTransforms(&all_nodes[0], Eigen::Matrix4f::Identity(), model->getSkeleton(), time, anim, all_nodes); -} - -void AnimationSystem::applyTransforms(Model::Node* node, const Eigen::Matrix4f& parentTransform, Model::Skeleton& skeleton, double time, - const Animation& anim, std::vector& allModelNodes) { - Eigen::Matrix4f nodeLocalTransform = node->localTransform; - std::string nodeName = node->name; - Eigen::Matrix4f globalTransform; - if (anim.tracks.contains(nodeName)) { - const BoneAnimation& track = anim.tracks.at(nodeName); +void AnimationSystem::updateSkeleton(const std::shared_ptr& model, double time, SkeletonPoseComponent* pose, const Animation& anim) { + for (auto const& [nodeName, nodeEntity] : pose->bone_entity) { + if (anim.tracks.contains(nodeName)) { + auto transform = m_registry->getComponent(nodeEntity); + const auto& track = anim.tracks.at(nodeName); - Eigen::Matrix4f posMatrix = interpolatePosition(time, track); - Eigen::Matrix4f rotMatrix = interpolateRotation(time, track); - Eigen::Matrix4f scaleMatrix = interpolateScale(time, track); + auto pos = interpolatePosition(time, track); + auto rot = interpolateRotation(time, track); + auto scale = interpolateScale(time, track); - nodeLocalTransform = posMatrix * rotMatrix * scaleMatrix; + transform->setPosition(pos); + transform->setRotation(rot); + transform->setScale(scale); + } } +} - globalTransform = parentTransform * nodeLocalTransform; - node->animatedTransform = globalTransform; +void AnimationSystem::finalizePose() { + for (auto e : entities) { + auto pose = m_registry->getComponent(e); + auto model = m_asset_bank->getAsset(pose->skeletonModel); + auto& skeleton = model->getSkeleton(); - if (skeleton.boneMapping.contains(nodeName)) { - int boneID = skeleton.boneMapping.at(nodeName); - skeleton.bones[boneID].finalTransformation = skeleton.globalInverseTransform * globalTransform; - } + auto rootTransform = m_registry->getComponent(e); + Eigen::Matrix4f modelWorldInv = rootTransform->getWorldMatrix().inverse(); + + for (const auto& [name, id] : skeleton.boneMapping) { + Entity boneEntity = pose->bone_entity.at(name); - for (int childIndex : node->children) { - applyTransforms(&allModelNodes[childIndex], globalTransform, skeleton, time, anim, allModelNodes); + Eigen::Matrix4f boneWorld = m_registry->getComponent(boneEntity)->getWorldMatrix(); + pose->bone_transform[id] = modelWorldInv * boneWorld; + } } } -Eigen::Matrix4f AnimationSystem::interpolatePosition(double timeInTicks, const BoneAnimation& track) { +Eigen::Vector3f AnimationSystem::interpolatePosition(double timeInTicks, const BoneAnimation& track) { if (track.positions.empty()) { - return Eigen::Matrix4f::Identity(); + return Eigen::Vector3f::Zero(); } if (track.positions.size() == 1) { - Eigen::Matrix4f positionMatrix = Eigen::Matrix4f::Identity(); - positionMatrix.block<3, 1>(0, 3) = track.positions[0].position; - return positionMatrix; + return track.positions[0].position; } size_t startIndex = findKeyIndex(timeInTicks, track.positions); - size_t nextIndex = startIndex + 1; + size_t nextIndex = std::min(startIndex + 1, track.positions.size() - 1); const auto& startKey = track.positions[startIndex]; const auto& nextKey = track.positions[nextIndex]; double totalTime = nextKey.timeStamp - startKey.timeStamp; if (totalTime == 0.0) - return Eigen::Matrix4f::Identity(); + return Eigen::Vector3f::Zero(); double currentTime = timeInTicks - startKey.timeStamp; float factor = (float) (currentTime / totalTime); Eigen::Vector3f interpolatedPosition = startKey.position + factor * (nextKey.position - startKey.position); - Eigen::Matrix4f positionMatrix = Eigen::Matrix4f::Identity(); - positionMatrix.block<3, 1>(0, 3) = interpolatedPosition; - - return positionMatrix; + return interpolatedPosition; } -Eigen::Matrix4f AnimationSystem::interpolateScale(double timeInTicks, const BoneAnimation& track) { +Eigen::Vector3f AnimationSystem::interpolateScale(double timeInTicks, const BoneAnimation& track) { if (track.scales.empty()) { - return Eigen::Matrix4f::Identity(); + return Eigen::Vector3f::Ones(); } if (track.scales.size() == 1) { - Eigen::Matrix4f scaleMatrix = Eigen::Matrix4f::Identity(); - scaleMatrix.block<3, 3>(0, 0) = track.scales[0].scale.asDiagonal(); - return scaleMatrix; + return track.scales[0].scale; } size_t startIndex = findKeyIndex(timeInTicks, track.scales); - size_t nextIndex = startIndex + 1; + size_t nextIndex = std::min(startIndex + 1, track.scales.size() - 1); const auto& startKey = track.scales[startIndex]; const auto& nextKey = track.scales[nextIndex]; double totalTime = nextKey.timeStamp - startKey.timeStamp; if (totalTime == 0.0) - return Eigen::Matrix4f::Identity(); + return Eigen::Vector3f::Ones(); double currentTime = timeInTicks - startKey.timeStamp; float factor = (float) (currentTime / totalTime); Eigen::Vector3f interpolatedScale = startKey.scale + factor * (nextKey.scale - startKey.scale); - Eigen::Matrix4f scaleMatrix = Eigen::Matrix4f::Identity(); - scaleMatrix.block<3, 3>(0, 0) = interpolatedScale.asDiagonal(); - return scaleMatrix; + return interpolatedScale; } -Eigen::Matrix4f AnimationSystem::interpolateRotation(double time, const BoneAnimation& track) { +Eigen::Quaternionf AnimationSystem::interpolateRotation(double time, const BoneAnimation& track) { if (track.rotations.size() == 1) { - Eigen::Matrix4f rotation_matrix = Eigen::Matrix4f::Identity(); - rotation_matrix.block<3, 3>(0, 0) = track.rotations[0].rotation.toRotationMatrix(); - - return rotation_matrix; + return track.rotations[0].rotation; } - size_t startIdx = findKeyIndex(time, track.rotations); - size_t nextIdx = startIdx + 1; + size_t startIndex = findKeyIndex(time, track.rotations); + size_t nextIndex = std::min(startIndex + 1, track.rotations.size() - 1); - const auto& startKey = track.rotations[startIdx]; - const auto& nextKey = track.rotations[nextIdx]; + const auto& startKey = track.rotations[startIndex]; + const auto& nextKey = track.rotations[nextIndex]; double totalTime = nextKey.timeStamp - startKey.timeStamp; double factor = (time - startKey.timeStamp) / totalTime; @@ -144,10 +138,7 @@ Eigen::Matrix4f AnimationSystem::interpolateRotation(double time, const BoneAnim Eigen::Quaternionf finalQuat = startKey.rotation.slerp((float) factor, nextKey.rotation); finalQuat.normalize(); - Eigen::Matrix4f rotation_matrix = Eigen::Matrix4f::Identity(); - rotation_matrix.block<3, 3>(0, 0) = finalQuat.toRotationMatrix(); - - return rotation_matrix; + return finalQuat; } } // namespace ICE \ No newline at end of file diff --git a/ICE/System/src/RenderSystem.cpp b/ICE/System/src/RenderSystem.cpp index b28ed8bb..3cabf2c6 100644 --- a/ICE/System/src/RenderSystem.cpp +++ b/ICE/System/src/RenderSystem.cpp @@ -4,22 +4,154 @@ #include "RenderSystem.h" +#include "Registry.h" +#include "RenderData.h" + namespace ICE { +RenderSystem::RenderSystem(const std::shared_ptr &api, const std::shared_ptr &factory, + const std::shared_ptr ®, const std::shared_ptr &gpu_bank) + : m_api(api), + m_factory(factory), + m_registry(reg), + m_gpu_bank(gpu_bank) { + m_quad_vao = factory->createVertexArray(); + auto quad_vertex_vbo = factory->createVertexBuffer(); + quad_vertex_vbo->putData(full_quad_v.data(), full_quad_v.size() * sizeof(float)); + m_quad_vao->pushVertexBuffer(quad_vertex_vbo, 3); + auto quad_uv_vbo = factory->createVertexBuffer(); + quad_uv_vbo->putData(full_quad_tx.data(), full_quad_tx.size() * sizeof(float)); + m_quad_vao->pushVertexBuffer(quad_uv_vbo, 2); + auto quad_ibo = factory->createIndexBuffer(); + quad_ibo->putData(full_quad_idx.data(), full_quad_idx.size() * sizeof(int)); + m_quad_vao->setIndexBuffer(quad_ibo); +} + void RenderSystem::update(double delta) { + + auto view_mat = m_camera->lookThrough(); + auto proj_mat = m_camera->getProjection(); + + if (m_skybox != NO_ASSET_ID) { + auto shader = m_gpu_bank->getShader(AssetPath::WithTypePrefix("__ice_skybox_shader")); + auto skybox = m_registry->getComponent(m_skybox); + auto mesh = m_gpu_bank->getMesh(AssetPath::WithTypePrefix("cube")); + auto tex = m_gpu_bank->getCubemap(skybox->texture); + m_renderer->submitSkybox(Skybox{ + .cube_mesh = mesh, + .shader = shader, + .textures = {{skybox->texture, tex}}, + }); + } + + auto frustum = extractFrustumPlanes(proj_mat * view_mat); + for (const auto &e : m_render_queue) { + auto rc = m_registry->getComponent(e); + auto tc = m_registry->getComponent(e); + auto mesh = m_gpu_bank->getMesh(rc->mesh); + auto material = m_gpu_bank->getMaterial(rc->material); + auto shader = m_gpu_bank->getShader(material->getShader()); + if (!mesh || !material || !shader) + continue; + + auto model_mat = tc->getWorldMatrix(); + + auto aabb = m_gpu_bank->getMeshAABB(rc->mesh); + Eigen::Vector3f min = (model_mat * Eigen::Vector4f(aabb.getMin().x(), aabb.getMin().y(), aabb.getMin().z(), 1.0)).head<3>(); + Eigen::Vector3f max = (model_mat * Eigen::Vector4f(aabb.getMax().x(), aabb.getMax().y(), aabb.getMax().z(), 1.0)).head<3>(); + aabb = AABB(std::vector{min, max}); + if (!isAABBInFrustum(frustum, aabb)) { + continue; + } + + std::unordered_map bone_matrices; + if (m_registry->entityHasComponent(e)) { + const auto &skinning = m_gpu_bank->getMeshSkinningData(rc->mesh); + auto skeleton_entity = m_registry->getComponent(e)->skeleton_entity; + auto pose = m_registry->getComponent(skeleton_entity); + for (const auto &[id, ibm] : skinning.inverseBindMatrices) { + bone_matrices.try_emplace(id, pose->bone_transform[id] * ibm); + } + + model_mat = m_registry->getComponent(skeleton_entity)->getWorldMatrix(); + } + + std::unordered_map> texs; + for (const auto &[name, value] : material->getAllUniforms()) { + if (std::holds_alternative(value)) { + auto v = std::get(value); + if (auto tex = m_gpu_bank->getTexture2D(v); tex) { + texs.try_emplace(v, tex); + } + } + } + m_renderer->submitDrawable(Drawable{.mesh = mesh, + .material = material, + .shader = shader, + .textures = texs, + .model_matrix = model_mat, + .bone_matrices = bone_matrices}); + } + + for (int i = 0; i < m_lights.size(); i++) { + if (i >= MAX_LIGHTS) + break; + auto light = m_lights[i]; + auto lc = m_registry->getComponent(light); + auto tc = m_registry->getComponent(light); + + m_renderer->submitLight(Light{.position = tc->getPosition(), + .rotation = tc->getRotationEulerDeg(), + .color = lc->color, + .distance_dropoff = lc->distance_dropoff, + .type = lc->type}); + } + m_renderer->prepareFrame(*m_camera); - m_renderer->render(); + auto rendered_fb = m_renderer->render(); m_renderer->endFrame(); + + //Final pass, render the last result to the screen + if (!m_target) { + m_api->bindDefaultFramebuffer(); + } else { + m_target->bind(); + } + + m_api->clear(); + auto shader = m_gpu_bank->getShader(AssetPath::WithTypePrefix("lastpass")); + + shader->bind(); + rendered_fb->bindAttachment(0); + shader->loadInt("uTexture", 0); + m_quad_vao->bind(); + m_quad_vao->getIndexBuffer()->bind(); + m_api->renderVertexArray(m_quad_vao); } void RenderSystem::onEntityAdded(Entity e) { - if (m_renderer) { - m_renderer->submit(e); + onEntityRemoved(e); + if (m_registry->entityHasComponent(e)) { + m_render_queue.emplace_back(e); + } + if (m_registry->entityHasComponent(e)) { + m_lights.emplace_back(e); + } + if (m_registry->entityHasComponent(e)) { + m_skybox = e; } } void RenderSystem::onEntityRemoved(Entity e) { - if (m_renderer) { - m_renderer->remove(e); + auto queue_pos = std::ranges::find(m_render_queue, e); + if (queue_pos != m_render_queue.end()) { + m_render_queue.erase(queue_pos); + } + auto light_pos = std::ranges::find(m_lights, e); + if (light_pos != m_lights.end()) { + m_lights.erase(light_pos); + } + if (e == m_skybox) { + m_skybox = NO_ASSET_ID; } } @@ -29,9 +161,6 @@ std::shared_ptr RenderSystem::getRenderer() const { void RenderSystem::setRenderer(const std::shared_ptr &renderer) { m_renderer = renderer; - for (const auto &e : entities) { - m_renderer->submit(e); - } } std::shared_ptr RenderSystem::getCamera() const { @@ -43,11 +172,13 @@ void RenderSystem::setCamera(const std::shared_ptr &camera) { } void RenderSystem::setTarget(const std::shared_ptr &fb) { - m_renderer->setTarget(fb); + m_target = fb; } void RenderSystem::setViewport(int x, int y, int w, int h) { if (w > 0 && h > 0) { + if (m_target) + m_target->resize(w, h); m_renderer->resize(w, h); } } diff --git a/ICE/System/src/SceneGraphSystem.cpp b/ICE/System/src/SceneGraphSystem.cpp new file mode 100644 index 00000000..618b5d09 --- /dev/null +++ b/ICE/System/src/SceneGraphSystem.cpp @@ -0,0 +1,29 @@ +#include "SceneGraphSystem.h" + +namespace ICE { +SceneGraphSystem::SceneGraphSystem(const std::shared_ptr &scene) : m_scene(scene) { + +} + +void SceneGraphSystem::onEntityAdded(Entity e) { +} +void SceneGraphSystem::onEntityRemoved(Entity e) { +} +void SceneGraphSystem::update(double delta) { + auto root = m_scene->getGraph()->getRoot(); + std::function &, const Eigen::Matrix4f &)> updateNode; + updateNode = [this, &updateNode](const std::shared_ptr &node, const Eigen::Matrix4f &parentMatrix) { + Eigen::Matrix4f newParentMatrix = parentMatrix; + if (node->entity != 0 && m_scene->getRegistry()->entityHasComponent(node->entity)) { + auto tc = m_scene->getRegistry()->getComponent(node->entity); + tc->updateParentMatrix(parentMatrix); + newParentMatrix = tc->getWorldMatrix(); + } + for (const auto &child : node->children) { + updateNode(child, newParentMatrix); + } + }; + updateNode(root, Eigen::Matrix4f::Identity()); +} + +} // namespace ICE \ No newline at end of file diff --git a/ICE/Util/include/GLFWWindow.h b/ICE/Util/include/GLFWWindow.h index 0f163936..ae5fdd04 100644 --- a/ICE/Util/include/GLFWWindow.h +++ b/ICE/Util/include/GLFWWindow.h @@ -13,6 +13,7 @@ class GLFWWindow : public Window { void* getHandle() const override; bool shouldClose() override; + void close() override; std::pair, std::shared_ptr> getInputHandlers() const override; void pollEvents() override; void swapBuffers() override; diff --git a/ICE/Util/include/Window.h b/ICE/Util/include/Window.h index 60a07346..39855f29 100644 --- a/ICE/Util/include/Window.h +++ b/ICE/Util/include/Window.h @@ -15,6 +15,7 @@ class Window { virtual void* getHandle() const = 0; virtual bool shouldClose() = 0; + virtual void close() = 0; virtual std::pair, std::shared_ptr> getInputHandlers() const = 0; virtual void pollEvents() = 0; virtual void swapBuffers() = 0; diff --git a/ICE/Util/src/GLFWWindow.cpp b/ICE/Util/src/GLFWWindow.cpp index 6cfa534a..193d29c6 100644 --- a/ICE/Util/src/GLFWWindow.cpp +++ b/ICE/Util/src/GLFWWindow.cpp @@ -53,6 +53,10 @@ bool GLFWWindow::shouldClose() { return glfwWindowShouldClose(m_handle); } +void GLFWWindow::close() { + glfwSetWindowShouldClose(m_handle, GLFW_TRUE); +} + void GLFWWindow::pollEvents() { glfwPollEvents(); } diff --git a/ICEBERG/CMakeLists.txt b/ICEBERG/CMakeLists.txt index 5e6c0cd4..802f7387 100644 --- a/ICEBERG/CMakeLists.txt +++ b/ICEBERG/CMakeLists.txt @@ -11,7 +11,13 @@ add_executable(${PROJECT_NAME} src/Inspector.cpp src/Assets.cpp src/Viewport.cpp - imgui_demo.cpp ${IMGUI_SRC} ${IMGUIZMO_SRC} ${GL3W_SRC} + src/AssetsRenderer.cpp + src/Dialog.cpp + src/MaterialEditor.cpp + src/ShaderEditor.cpp + ${IMGUI_SRC} + ${IMGUIZMO_SRC} + ${GL3W_SRC} ) target_include_directories(${PROJECT_NAME} PUBLIC @@ -23,6 +29,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC add_definitions(-DIMGUI_DEFINE_MATH_OPERATORS) -target_link_libraries(${PROJECT_NAME} PUBLIC ICE glfw) +target_link_libraries(${PROJECT_NAME} PUBLIC ICE DearImXML glfw) file(COPY ${ICE_ROOT_SOURCE_DIR}/Assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/XML DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/ICEBERG/Components/ComboBox.h b/ICEBERG/Components/ComboBox.h index a244b00c..fb8c6fec 100644 --- a/ICEBERG/Components/ComboBox.h +++ b/ICEBERG/Components/ComboBox.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/ICEBERG/Components/InputText.h b/ICEBERG/Components/InputText.h index b09eee71..29116be0 100644 --- a/ICEBERG/Components/InputText.h +++ b/ICEBERG/Components/InputText.h @@ -1,31 +1,32 @@ #pragma once -#include +#include #include #include class InputText { public: - InputText(const std::string &label, const std::string &default_text = "") : m_label(label) { setText(default_text); } - void onEdit(const std::function &f) { m_callback_edit = f; } + InputText(const std::string &label, const std::string &default_text = "") : m_label(label) { + setText(default_text); } + void onEdit(const std::function &f) { m_callback_edit = f; } void render() { - if (ImGui::InputText(m_label.c_str(), buffer, 512)) { - m_callback_edit(std::string(buffer)); + if (ImGui::InputText(m_label.c_str(), &m_text)) { + m_callback_edit(m_text_old, m_text); } - m_text = buffer; + m_text_old = m_text; } std::string getText() const { return m_text; } void setText(const std::string &text) { m_text = text; - memcpy(buffer, m_text.c_str(), m_text.size() + 1); + m_text_old = text; } private: std::string m_text; + std::string m_text_old; std::string m_label; - std::function m_callback_edit = [](const std::string &) { + std::function m_callback_edit = [](std::string, std::string) { }; - char buffer[512] = {0}; }; diff --git a/ICEBERG/Components/UniformInputs.h b/ICEBERG/Components/UniformInputs.h index 8bbd29a1..15b5a8e7 100644 --- a/ICEBERG/Components/UniformInputs.h +++ b/ICEBERG/Components/UniformInputs.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include @@ -22,6 +22,12 @@ class UniformInputs { void setValue(const ICE::UniformValue &value) { m_value = value; m_callback(value); + if (std::holds_alternative(m_value)) { + auto it = std::find(m_assets_ids.begin(), m_assets_ids.end(), std::get(m_value)); + if (it != m_assets_ids.end()) { + m_asset_combo.setSelected(std::distance(m_assets_ids.begin(), it)); + } + } } ICE::UniformValue getValue() const { return m_value; } @@ -34,9 +40,11 @@ class UniformInputs { m_asset_combo.setValues(path_with_none); m_assets_ids = {0}; m_assets_ids.insert(m_assets_ids.end(), ids.begin(), ids.end()); - auto it = std::find(m_assets_ids.begin(), m_assets_ids.end(), std::get(m_value)); - if (it != m_assets_ids.end()) { - m_asset_combo.setSelected(std::distance(m_assets_ids.begin(), it)); + if (std::holds_alternative(m_value)) { + auto it = std::find(m_assets_ids.begin(), m_assets_ids.end(), std::get(m_value)); + if (it != m_assets_ids.end()) { + m_asset_combo.setSelected(std::distance(m_assets_ids.begin(), it)); + } } m_asset_combo.onSelectionChanged( [cb = this->m_callback, id_list = this->m_assets_ids](const std::string &, int index) { cb(id_list[index]); }); @@ -52,7 +60,7 @@ class UniformInputs { } void render(ICE::AssetUID id) { m_asset_combo.render(); } void render(float &f) { - if (ImGui::InputFloat(m_label.c_str(), &f)) { + if (ImGui::InputFloat(m_label.c_str(), &f, 0.01f, 0.1f, "%.6f")) { m_callback(f); } } diff --git a/ICEBERG/UI/AddComponentPopup.h b/ICEBERG/UI/AddComponentPopup.h index 92b85627..61f0688a 100644 --- a/ICEBERG/UI/AddComponentPopup.h +++ b/ICEBERG/UI/AddComponentPopup.h @@ -1,69 +1,62 @@ #pragma once -#include #include +#include #include "Components/ComboBox.h" +#include "Dialog.h" -class AddComponentPopup { +class AddComponentPopup : public Dialog { public: - AddComponentPopup() : m_components_combo("##add_component_combo", {"Render", "Light"}) {} + AddComponentPopup() : m_components_combo("##add_component_combo", {"Render", "Light", "Animation"}) {} - void open(const std::shared_ptr ®istry, ICE::Entity entity) { + void setData(const std::shared_ptr ®istry, ICE::Entity entity) { m_registry = registry; m_entity = entity; - m_open = true; - + std::vector values; - if(!registry->entityHasComponent(entity)) { + if (!registry->entityHasComponent(entity)) { values.push_back("Render Component"); } - if(!registry->entityHasComponent(entity)) { + if (!registry->entityHasComponent(entity)) { values.push_back("Light Component"); } + if (!registry->entityHasComponent(entity)) { + values.push_back("Animation Component"); + } m_components_combo.setValues(values); } void render() { ImGui::PushID("add_component_popup"); - if (m_open) { + if (isOpenRequested()) { ImGui::OpenPopup("Add Component"); - m_open = false; } ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX)); if (ImGui::BeginPopupModal("Add Component", 0, ImGuiWindowFlags_AlwaysAutoResize)) { m_components_combo.render(); if (ImGui::Button("Add")) { - if(m_components_combo.getSelectedItem() == "Render Component") - m_registry->addComponent(m_entity, ICE::RenderComponent(0)); - - if(m_components_combo.getSelectedItem() == "Light Component") + if (m_components_combo.getSelectedItem() == "Render Component") + m_registry->addComponent(m_entity, ICE::RenderComponent(0, 0)); + + if (m_components_combo.getSelectedItem() == "Light Component") m_registry->addComponent(m_entity, ICE::LightComponent(ICE::PointLight, Eigen::Vector3f(1, 1, 1))); - ImGui::CloseCurrentPopup(); - m_accepted = true; + + if (m_components_combo.getSelectedItem() == "Animation Component") + m_registry->addComponent(m_entity, ICE::AnimationComponent{"", 0.0, 1.0, true, true}); + done(DialogResult::Ok); } ImGui::SameLine(); - if(ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); - + if (ImGui::Button("Cancel")) { + done(DialogResult::Cancel); } ImGui::EndPopup(); } ImGui::PopID(); } - bool accepted() { - if (m_accepted) { - m_accepted = false; - return true; - } - return false; - } - private: ComboBox m_components_combo; - bool m_open = false; - bool m_accepted = false; std::shared_ptr m_registry; ICE::Entity m_entity; }; \ No newline at end of file diff --git a/ICEBERG/UI/AnimationComponentWidget.h b/ICEBERG/UI/AnimationComponentWidget.h new file mode 100644 index 00000000..a17bc62e --- /dev/null +++ b/ICEBERG/UI/AnimationComponentWidget.h @@ -0,0 +1,83 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Components/UniformInputs.h" +#include "Widget.h" + +class AnimationComponentWidget : public Widget, ImXML::XMLEventHandler { + public: + explicit AnimationComponentWidget() : m_xml_tree(ImXML::XMLReader().read("XML/AnimationComponentWidget.xml")) { + m_xml_renderer.addDynamicBind("float_time", {&m_time, 1, ImXML::Float}); + m_xml_renderer.addDynamicBind("float_speed", {&m_speed, 1, ImXML::Float}); + m_xml_renderer.addDynamicBind("bool_playing", {&m_playing, 1, ImXML::Bool}); + m_xml_renderer.addDynamicBind("bool_loop", {&m_loop, 1, ImXML::Bool}); + } + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "animation_combo") { + if (ImGui::BeginCombo("##animation_combo", m_current_animation.c_str())) { + for (const auto& [key, anim] : m_animations) { + if (ImGui::Selectable(key.c_str(), key == m_current_animation)) { + m_current_animation = key; + } + } + ImGui::EndCombo(); + } + } else if (node.arg("id") == "time_slider") { + if (m_animations.contains(m_current_animation)) + node.args["max"] = std::to_string(m_animations[m_current_animation].duration); + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override {} + + void render() override { + if (m_ac) { + m_xml_renderer.render(m_xml_tree, *this); + m_ac->currentAnimation = m_current_animation; + m_ac->currentTime = m_time; + m_ac->speed = m_speed; + m_ac->playing = m_playing; + m_ac->loop = m_loop; + } + } + + void setAnimationComponent(ICE::AnimationComponent* ac, const std::unordered_map& animations) { + if (ac && !animations.empty()) { + m_ac = ac; + m_animations = animations; + m_current_animation = m_ac->currentAnimation; + m_time = m_ac->currentTime; + m_speed = m_ac->speed; + m_playing = m_ac->playing; + m_loop = m_ac->loop; + } else { + m_ac = nullptr; + } + } + + private: + ICE::AnimationComponent* m_ac = nullptr; + std::unordered_map m_animations; + + std::string m_current_animation = ""; + + float m_max_time = 1.0; + + float m_time = 0.0; + float m_speed = 1.0; + bool m_playing; + bool m_loop; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/AssetsBrowserWidget.h b/ICEBERG/UI/AssetsBrowserWidget.h new file mode 100644 index 00000000..32415fe1 --- /dev/null +++ b/ICEBERG/UI/AssetsBrowserWidget.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include + +#include "AssetsCategoryWidget.h" +#include "AssetsContentWidget.h" +#include "AssetsPreviewWidget.h" + +class AssetsBrowserWidget : public Widget, ImXML::XMLEventHandler { + public: + explicit AssetsBrowserWidget(const std::vector& asset_categories, void* folder_texture) + : m_xml_tree(ImXML::XMLReader().read("XML/AssetBrowser.xml")), + m_category_widget(asset_categories), + m_content_widget(folder_texture) { + m_category_widget.registerCallback("asset_category_selected", [this](int index) { callback("asset_category_selected", index); }); + m_content_widget.registerCallback("item_clicked", [this](std::string label) { callback("item_clicked", label); }); + m_content_widget.registerCallback("item_selected", [this](std::string label) { callback("item_selected", label); }); + } + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "asset_browser_category") { + m_category_widget.render(); + } else if (node.arg("id") == "asset_browser_content") { + m_content_widget.render(); + } else if (node.arg("id") == "asset_browser_preview") { + m_preview_widget.render(); + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override {} + + void render() override { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + m_xml_renderer.render(m_xml_tree, *this); + ImGui::PopStyleVar(); + } + + void setCurrentView(const AssetView& view) { m_content_widget.setCurrentView(view); } + AssetView getCurrentView() const { return m_content_widget.getCurrentView(); } + void setPreviewTexture(void* tex) { m_preview_widget.setTexture(tex); } + + private: + AssetsCategoryWidget m_category_widget; + AssetsContentWidget m_content_widget; + AssetsPreviewWidget m_preview_widget; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/AssetsCategoryWidget.h b/ICEBERG/UI/AssetsCategoryWidget.h new file mode 100644 index 00000000..be12c314 --- /dev/null +++ b/ICEBERG/UI/AssetsCategoryWidget.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include +#include + +#include "Widget.h" + +class AssetsCategoryWidget : public Widget { + public: + explicit AssetsCategoryWidget(const std::vector &asset_categories) : m_asset_categories(asset_categories) {} + + void render() override { + if (ImGui::BeginTable("asset_folders", 1, ImGuiTableFlags_BordersInnerH)) { + for (int i = 0; i < m_asset_categories.size(); i++) { + ImGui::TableNextColumn(); + if (ImGui::Selectable(m_asset_categories[i].c_str(), i == m_selected_index)) { + m_selected_index = i; + callback("asset_category_selected", i); + } + } + ImGui::EndTable(); + } + } + + private: + int m_selected_index = 0; + std::vector m_asset_categories; +}; diff --git a/ICEBERG/UI/AssetsContentWidget.h b/ICEBERG/UI/AssetsContentWidget.h new file mode 100644 index 00000000..acfb87ee --- /dev/null +++ b/ICEBERG/UI/AssetsContentWidget.h @@ -0,0 +1,132 @@ +#pragma once + +#include + +#include +#include +#include + +#include "Widget.h" + +struct Thumbnail { + void* ptr; + bool flip = true; +}; +struct AssetData { + std::string name; + Thumbnail thumbnail; + std::string asset_path; +}; +struct AssetView { + AssetView* parent = nullptr; + std::string folder_name; + std::vector assets; + std::vector subfolders; + ICE::AssetType type; +}; + +class AssetsContentWidget : public Widget { + public: + AssetsContentWidget(void* folder_texture) : m_folder_texture(folder_texture) {} + + void render() override { + const float thumbnailSize = 64.0f; + const float padding = 16.0f; + float cellSize = thumbnailSize + padding; + + float panelWidth = ImGui::GetContentRegionAvail().x; + int columnCount = static_cast(panelWidth / cellSize); + if (columnCount < 1) + columnCount = 1; + + if (ImGui::BeginTable("FileBrowserTable", columnCount)) { + int itemIndex = 0; + + auto renderItem = [&](const std::string& label, const std::string& path, const Thumbnail& tb) { + itemIndex++; + ImGui::TableNextColumn(); + ImGui::PushID(itemIndex); + + if (itemIndex != m_selected_item) { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + ImGui::BeginGroup(); + ImVec2 uv0 = tb.flip ? ImVec2(0, 1) : ImVec2(0, 0); + ImVec2 uv1 = tb.flip ? ImVec2(1, 0) : ImVec2(1, 1); + ImGui::ImageButton("##item", (ImTextureID) tb.ptr, ImVec2(thumbnailSize, thumbnailSize), uv0, uv1); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + thumbnailSize); + ImGui::TextWrapped("%s", label.c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndGroup(); + if (path != "..") { + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoDisableHover | ImGuiDragDropFlags_SourceAllowNullID)) { + ImGui::SetDragDropPayload(m_DND_name.c_str(), path.c_str(), path.size() + 1); + ImGui::Text("%s", label.c_str()); + ImGui::EndDragDropSource(); + } + } + + if (itemIndex != m_selected_item) { + ImGui::PopStyleColor(); + } + if (ImGui::IsItemClicked(0)) { + m_selected_item = itemIndex; + callback("item_selected", label); + if (ImGui::IsMouseDoubleClicked(0)) { + callback("item_clicked", label); + } + } + + ImGui::PopID(); + }; + + if (m_current_view.parent != nullptr) { + renderItem("..", "..", {m_folder_texture, false}); + } + for (const auto& folder : m_current_view.subfolders) + renderItem(folder.folder_name, folder.folder_name, {m_folder_texture, false}); + + for (const auto& asset : m_current_view.assets) + renderItem(asset.name, asset.asset_path, asset.thumbnail); + + ImGui::EndTable(); + } + } + + void setCurrentView(const AssetView& view) { + m_current_view = view; + m_selected_item = -1; + m_current_type = view.type; + switch (view.type) { + case ICE::AssetType::EModel: + m_DND_name = "DND_ASSET_MODEL"; + break; + case ICE::AssetType::EMesh: + m_DND_name = "DND_ASSET_MESH"; + break; + case ICE::AssetType::EMaterial: + m_DND_name = "DND_ASSET_MATERIAL"; + break; + case ICE::AssetType::EShader: + m_DND_name = "DND_ASSET_SHADER"; + break; + case ICE::AssetType::ETex2D: + m_DND_name = "DND_ASSET_TEXTURE2D"; + break; + case ICE::AssetType::ETexCube: + m_DND_name = "DND_ASSET_TEXTURECUBE"; + break; + default: + m_DND_name = "DND_ASSET_UNKNOWN"; + break; + } + } + AssetView getCurrentView() const { return m_current_view; } + + private: + void* m_folder_texture; + AssetView m_current_view; + int m_selected_item = -1; + ICE::AssetType m_current_type = ICE::AssetType::EModel; + std::string m_DND_name = "DND_ASSET_MODEL"; +}; diff --git a/ICEBERG/UI/AssetsPreviewWidget.h b/ICEBERG/UI/AssetsPreviewWidget.h new file mode 100644 index 00000000..097978f0 --- /dev/null +++ b/ICEBERG/UI/AssetsPreviewWidget.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "Widget.h" + +class AssetsPreviewWidget : public Widget { + public: + AssetsPreviewWidget() = default; + + void render() override { ImGui::Image(m_texture, {256, 256}, ImVec2(0, 1), ImVec2(1, 0)); } + + void setTexture(void* tex) { m_texture = tex; } + + private: + void* m_texture = nullptr; +}; \ No newline at end of file diff --git a/ICEBERG/UI/AssetsWidget.h b/ICEBERG/UI/AssetsWidget.h deleted file mode 100644 index ab500c93..00000000 --- a/ICEBERG/UI/AssetsWidget.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once -#include - -#include -#include -#include - -#include "Widget.h" - -struct AssetView { - std::string folder_name; - std::vector> assets; - std::vector> subfolders; -}; - -class AssetsWidget : public Widget { - public: - AssetsWidget() = default; - - void render() override { - int flags = ImGuiWindowFlags_NoCollapse; - flags |= ImGuiWindowFlags_NoNavFocus; - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin("Assets", 0, flags); - - if (ImGui::BeginTable("asset_layout", 3, ImGuiTableFlags_Resizable)) { - ImGui::TableSetupColumn("Folders", ImGuiTableColumnFlags_WidthStretch, 0.15); - ImGui::TableSetupColumn("Content", ImGuiTableColumnFlags_WidthStretch, 0.6); - ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthStretch, 0.25); - ImGui::TableHeadersRow(); - ImGui::TableNextColumn(); - //Left hand side: Assets folders - if (ImGui::BeginTable("asset_folders", 1, ImGuiTableFlags_BordersInnerH)) { - for (int i = 0; i < m_assets.size(); i++) { - ImGui::TableNextColumn(); - const auto& asset = m_assets[i]; - if (ImGui::Selectable(asset->folder_name.c_str(), i == m_selected_index)) { - m_selected_index = i; - m_current_view = m_assets[i]; - m_prefix = ""; - } - } - ImGui::EndTable(); - } - - //Middle: assets - ImGui::TableNextColumn(); - if (ImGui::BeginTable("assets_selection", 10, ImGuiTableFlags_Borders)) { - for (int i = 0; i < m_current_view->subfolders.size(); i++) { - const auto& folder = m_current_view->subfolders[i]; - ImGui::TableNextColumn(); - ImGui::Text(folder->folder_name.c_str()); - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) { - m_current_view = folder; - m_prefix += folder->folder_name+"/"; - } - } - - for (int i = 0; i < m_current_view->assets.size(); i++) { - const auto& name = m_current_view->assets[i].first; - const auto& texture = m_current_view->assets[i].second; - ImGui::TableNextColumn(); - ImGui::BeginGroup(); - ImGui::Image(texture, {50, 50}); - ImGui::Text(name.c_str()); - ImGui::EndGroup(); - if (m_assets[m_selected_index]->folder_name == "Materials") { - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) { - callback("material_edit", m_prefix+name); - } - } - if (ImGui::BeginPopupContextItem((name + "_material_context").c_str())) { - if (m_assets[m_selected_index]->folder_name == "Materials") { - if (ImGui::Button("Duplicate")) { - callback("material_duplicate", name); - } - if (ImGui::Button("Edit")) { - callback("material_edit", name); - } - } - if (ImGui::Button("Delete")) { - callback("delete_asset", m_assets[m_selected_index]->folder_name + "/" + name); - } - ImGui::EndPopup(); - } - } - ImGui::EndTable(); - } - - //Right hand side: preview - ImGui::TableNextColumn(); - ImGui::Text("Preview"); - - ImGui::EndTable(); - } - ImGui::End(); - ImGui::PopStyleVar(); - } - - void addAssets(const std::shared_ptr& assets) { - m_assets.push_back(assets); - if (m_current_view == nullptr) { - m_current_view = assets; - } - } - - void reset() { - m_assets.clear(); - m_current_view = nullptr; - m_selected_index = 0; - m_prefix = ""; - } - - private: - std::vector> m_assets; - int m_selected_index = 0; - std::shared_ptr m_current_view = nullptr; - std::string m_prefix; -}; diff --git a/ICEBERG/UI/Dialog.h b/ICEBERG/UI/Dialog.h new file mode 100644 index 00000000..04edda0e --- /dev/null +++ b/ICEBERG/UI/Dialog.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Widget.h" + +enum class DialogResult { None, Pending, Ok, Cancel }; + +class Dialog : public Widget { + public: + Dialog() { m_dialog_id = DIALOG_ID++; } + virtual ~Dialog() = default; + + void open() { + m_open_request = true; + m_result = DialogResult::Pending; + m_is_open = true; + } + + void done(DialogResult result) { + m_is_open = false; + m_result = result; + } + + bool isOpenRequested() { + if (m_open_request) { + m_open_request = false; + return true; + } + return false; + } + + bool isOpen() const { return m_is_open; } + + DialogResult getResult() const { return m_result; } + + protected: + int m_dialog_id; + + private: + static int DIALOG_ID; + bool m_open_request = false; + bool m_is_open = false; + DialogResult m_result = DialogResult::None; +}; \ No newline at end of file diff --git a/ICEBERG/UI/EditorWidget.h b/ICEBERG/UI/EditorWidget.h index edbe20c4..470824a8 100644 --- a/ICEBERG/UI/EditorWidget.h +++ b/ICEBERG/UI/EditorWidget.h @@ -1,90 +1,66 @@ #pragma once -#include -#include +#include +#include +#include +#include +#include #include "Widget.h" -class EditorWidget : public Widget { +class EditorWidget : public Widget, ImXML::XMLEventHandler { public: - EditorWidget() { ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; } + EditorWidget() : m_xml_tree(ImXML::XMLReader().read("XML/EditorWidget.xml")) { ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; } - void render() override { - static int initialized = 0; - ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar; - flags |= ImGuiWindowFlags_NoDocking; - ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->Pos); - ImGui::SetNextWindowSize(viewport->Size); - ImGui::SetNextWindowViewport(viewport->ID); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; - flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar; - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin("ICE Editor", 0, flags); - ImGui::PopStyleVar(); + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "dockspace") { + ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) { - if (ImGui::BeginMenu("New")) { - if (ImGui::MenuItem("Scene")) { - callback("new_scene_clicked"); - } - if (ImGui::MenuItem("Material")) { - callback("new_material_clicked"); - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Import")) { - if (ImGui::MenuItem("Mesh")) { - callback("import_mesh_clicked"); - } - if (ImGui::MenuItem("Texture2D")) { - callback("import_tex2d_clicked"); - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Open")) { - if (ImGui::MenuItem("Scene")) { - callback("open_scene_clicked"); - } - ImGui::EndMenu(); - } - //if (ImGui::MenuItem("Save", "Ctrl+S")) {} - //if (ImGui::MenuItem("Save as..")) {} - ImGui::EndMenu(); - } - ImGui::EndMainMenuBar(); - } - - ImGuiIO& io = ImGui::GetIO(); - ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); + if (!m_docking_initialized) [[unlikely]] { + m_docking_initialized = true; + ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout + ImGui::DockBuilderAddNode(dockspace_id); // Add empty node - if (initialized == 0) { - initialized = 1; - ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout - ImGui::DockBuilderAddNode(dockspace_id); // Add empty node + ImGuiID dock_main_id = dockspace_id; + ImGuiID dock_top = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, 0.80f, NULL, &dock_main_id); + ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.20f, NULL, &dock_main_id); - ImGuiID dock_main_id = - dockspace_id; // This variable will track the document node, however we are not using it here as we aren't docking anything into it. - ImGuiID dock_top = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, 0.80f, NULL, &dock_main_id); - ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.20f, NULL, &dock_main_id); + ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dock_top, ImGuiDir_Left, 0.20f, NULL, &dock_top); + ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dock_top, ImGuiDir_Right, 0.20f, NULL, &dock_top); + ImGuiID dock_id_center = ImGui::DockBuilderSplitNode(dock_top, ImGuiDir_Left, 0.60f, NULL, &dock_top); - ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dock_top, ImGuiDir_Left, 0.20f, NULL, &dock_top); - ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dock_top, ImGuiDir_Right, 0.20f, NULL, &dock_top); - ImGuiID dock_id_center = ImGui::DockBuilderSplitNode(dock_top, ImGuiDir_Left, 0.60f, NULL, &dock_top); + ImGui::DockBuilderDockWindow("Asset Browser", dock_id_bottom); + ImGui::DockBuilderDockWindow("Hierarchy", dock_id_left); + ImGui::DockBuilderDockWindow("Inspector", dock_id_right); + ImGui::DockBuilderDockWindow("Viewport", dock_id_center); + ImGui::DockBuilderFinish(dockspace_id); + } - ImGui::DockBuilderDockWindow("Assets", dock_id_bottom); - ImGui::DockBuilderDockWindow("Hierarchy", dock_id_left); - ImGui::DockBuilderDockWindow("Inspector", dock_id_right); - ImGui::DockBuilderDockWindow("Viewport", dock_id_center); - ImGui::DockBuilderFinish(dockspace_id); + ImGui::DockSpace(dockspace_id); + } else if (node.type == ImXML::ImGuiEnum::MAINMENUBAR) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4, 4)); } + } + void onNodeEnd(ImXML::XMLNode& node) override { + if (node.type == ImXML::ImGuiEnum::MAINMENUBAR) { + ImGui::PopStyleVar(); + } + } + void onEvent(ImXML::XMLNode& node) override { + auto id = node.arg("id"); + callback(id); + } - ImGui::DockSpace(dockspace_id); - - ImGui::End(); - ImGui::PopStyleVar(); + void render() override { + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + ImGui::SetNextWindowViewport(viewport->ID); + m_xml_renderer.render(m_xml_tree, *this); } private: + bool m_docking_initialized = false; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; }; diff --git a/ICEBERG/UI/HierarchyWidget.h b/ICEBERG/UI/HierarchyWidget.h index b6b1f22b..99e19c3f 100644 --- a/ICEBERG/UI/HierarchyWidget.h +++ b/ICEBERG/UI/HierarchyWidget.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -28,13 +28,11 @@ class HierarchyWidget : public Widget { void render() override { int flags = ImGuiWindowFlags_NoCollapse; flags |= ImGuiWindowFlags_NoNavFocus; - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("Hierarchy", 0, flags); renderTree(m_view); ImGui::End(); - ImGui::PopStyleVar(); } private: diff --git a/ICEBERG/UI/InspectorWidget.h b/ICEBERG/UI/InspectorWidget.h index 902d27ae..905579bf 100644 --- a/ICEBERG/UI/InspectorWidget.h +++ b/ICEBERG/UI/InspectorWidget.h @@ -1,125 +1,63 @@ #pragma once -#include -#include +#include +#include "AnimationComponentWidget.h" #include "Components/InputText.h" #include "Components/UniformInputs.h" +#include "LightComponentWidget.h" +#include "RenderComponentWidget.h" +#include "TransformComponentWidget.h" #include "Widget.h" class InspectorWidget : public Widget { public: - InspectorWidget() {} + InspectorWidget() { + m_input_entity_name.onEdit([this](const std::string&, const std::string& text) { callback("entity_name_changed", text); }); + } void render() override { - m_input_entity_name.onEdit([this](const std::string& text) { callback("entity_name_changed", text); }); int flags = ImGuiWindowFlags_NoCollapse; flags |= ImGuiWindowFlags_NoNavFocus; - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin("Inspector", 0, flags); - - ImGui::Text("Entity name"); - m_input_entity_name.render(); - - if (m_tc) { - ImGui::SeparatorText("Transform"); - ImGui::BeginGroup(); - for (auto& input : m_tc_inputs) { - ImGui::Text("%s", input.getLabel().c_str()); - input.render(); - } - ImGui::EndGroup(); - } - if (m_rc) { - ImGui::PushID("rc"); - ImGui::SeparatorText("Render"); - ImGui::BeginGroup(); - if (ImGui::Button("Remove")) { - callback("remove_render_component_clicked"); - } - for (auto& input : m_rc_inputs) { - ImGui::Text("%s", input.getLabel().c_str()); - input.render(); - } - ImGui::EndGroup(); - ImGui::PopID(); - } - if (m_lc) { - ImGui::PushID("lc"); - ImGui::SeparatorText("Light"); - ImGui::BeginGroup(); - if (ImGui::Button("Remove")) { - callback("remove_light_component_clicked"); - } - m_lc_type_combo.render(); - for (auto& input : m_lc_inputs) { - ImGui::Text("%s", input.getLabel().c_str()); - input.render(); + if (ImGui::Begin("Inspector", 0, flags)) { + if (m_entity_selected) { + ImGui::Text("Entity name"); + m_input_entity_name.render(); + + m_tc_widget.render(); + m_rc_widget.render(); + m_lc_widget.render(); + m_ac_widget.render(); + + if (ImGui::Button("Add Component...")) { + callback("add_component_clicked"); + } } - ImGui::EndGroup(); - ImGui::PopID(); + ImGui::End(); } - - if (ImGui::Button("Add Component...")) { - callback("add_component_clicked"); - } - - ImGui::End(); - ImGui::PopStyleVar(); } void setEntityName(const std::string& name) { m_input_entity_name.setText(name); } void setTransformComponent(ICE::TransformComponent* tc) { - m_tc = tc; - m_tc_inputs.clear(); - if (tc) { - m_tc_inputs.emplace_back("Position", tc->getPosition()); - m_tc_inputs.back().setForceVectorNumeric(true); - m_tc_inputs.back().onValueChanged([this](const ICE::UniformValue& v) { m_tc->setPosition(std::get(v)); }); - m_tc_inputs.emplace_back("Rotation", tc->getRotation()); - m_tc_inputs.back().setForceVectorNumeric(true); - m_tc_inputs.back().onValueChanged([this](const ICE::UniformValue& v) { m_tc->setRotation(std::get(v)); }); - m_tc_inputs.emplace_back("Scale", tc->getScale()); - m_tc_inputs.back().setForceVectorNumeric(true); - m_tc_inputs.back().onValueChanged([this](const ICE::UniformValue& v) { m_tc->setScale(std::get(v)); }); - } + m_entity_selected = (tc != nullptr); + m_tc_widget.setTransformComponent(tc); } - - void setRenderComponent(ICE::RenderComponent* rc, const std::vector& meshes_paths, const std::vector& meshes_ids) { - m_rc = rc; - m_rc_inputs.clear(); - if (rc) { - m_rc_inputs.reserve(2); - UniformInputs in_mesh("Mesh", rc->model); - in_mesh.onValueChanged([this](const ICE::UniformValue& v) { m_rc->model = std::get(v); }); - in_mesh.setAssetComboList(meshes_paths, meshes_ids); - m_rc_inputs.push_back(in_mesh); - } + void setLightComponent(ICE::LightComponent* lc) { m_lc_widget.setLightComponent(lc); } + void setAnimationComponent(ICE::AnimationComponent* ac, const std::unordered_map& animations) { + m_ac_widget.setAnimationComponent(ac, animations); } - - void setLightComponent(ICE::LightComponent* lc) { - m_lc = lc; - m_lc_inputs.clear(); - if (lc) { - m_lc_type_combo.setSelected(lc->type); - m_lc_type_combo.onSelectionChanged([this](const std::string&, int idx) { m_lc->type = static_cast(idx); }); - m_lc_inputs.emplace_back("Color", m_lc->color); - m_lc_inputs.back().onValueChanged([this](const ICE::UniformValue& v) { m_lc->color = std::get(v); }); - m_lc_inputs.emplace_back("Distance Dropoff", m_lc->distance_dropoff); - m_lc_inputs.back().onValueChanged([this](const ICE::UniformValue& v) { m_lc->distance_dropoff = std::get(v); }); - } + void setRenderComponent(ICE::RenderComponent* rc, const std::vector& meshes_paths, const std::vector& meshes_ids, + const std::vector& material_paths, const std::vector& material_ids) { + m_rc_widget.setRenderComponent(rc, meshes_paths, meshes_ids, material_paths, material_ids); } private: - ICE::TransformComponent* m_tc = nullptr; - std::vector m_tc_inputs; - - ICE::RenderComponent* m_rc = nullptr; - std::vector m_rc_inputs; + TransformComponentWidget m_tc_widget; + RenderComponentWidget m_rc_widget; + LightComponentWidget m_lc_widget; + AnimationComponentWidget m_ac_widget; - ICE::LightComponent* m_lc = nullptr; - std::vector m_lc_inputs; - ComboBox m_lc_type_combo{"Light Type", {"Point Light", "Directional Light", "Spot Light"}}; + bool m_entity_selected = false; InputText m_input_entity_name{"##inspector_entity_name", ""}; }; diff --git a/ICEBERG/UI/LightComponentWidget.h b/ICEBERG/UI/LightComponentWidget.h new file mode 100644 index 00000000..8555a081 --- /dev/null +++ b/ICEBERG/UI/LightComponentWidget.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Components/UniformInputs.h" +#include "Widget.h" + +class LightComponentWidget : public Widget, ImXML::XMLEventHandler { + public: + explicit LightComponentWidget() : m_xml_tree(ImXML::XMLReader().read("XML/LightComponentWidget.xml")) { + m_xml_renderer.addDynamicBind("float_distance_dropoff", {&m_distance_dropoff, 1, ImXML::Float}); + } + + void onNodeBegin(ImXML::XMLNode& node) override { + int light_type_id = static_cast(m_lc->type); + if (node.arg("id") == "light_type_combo") { + node.args["preview_value"] = m_light_types[light_type_id]; + } else if (node.arg("id") == "point_light") { + node.args["selected"] = light_type_id == 0 ? "true" : "false"; + } else if (node.arg("id") == "directional_light") { + node.args["selected"] = light_type_id == 1 ? "true" : "false"; + } else if (node.arg("id") == "spot_light") { + node.args["selected"] = light_type_id == 2 ? "true" : "false"; + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override { + if (node.arg("id") == "point_light") { + m_lc->type = ICE::PointLight; + } else if (node.arg("id") == "directional_light") { + m_lc->type = ICE::DirectionalLight; + } else if (node.arg("id") == "spot_light") { + m_lc->type = ICE::SpotLight; + } + } + + void render() override { + if (m_lc) { + m_xml_renderer.render(m_xml_tree, *this); + m_lc->distance_dropoff = m_distance_dropoff; + } + } + + void setLightComponent(ICE::LightComponent* lc) { + m_lc = lc; + if (lc) { + m_distance_dropoff = lc->distance_dropoff; + } + } + + private: + ICE::LightComponent* m_lc = nullptr; + + float m_distance_dropoff; + + const std::vector m_light_types = {"Point Light", "Directional Light", "Spot Light"}; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/MaterialEditDialog.h b/ICEBERG/UI/MaterialEditDialog.h new file mode 100644 index 00000000..e4e232bd --- /dev/null +++ b/ICEBERG/UI/MaterialEditDialog.h @@ -0,0 +1,153 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Components/ComboBox.h" +#include "Components/InputText.h" +#include "Components/UniformInputs.h" +#include "Dialog.h" + +class MaterialEditDialog : public Dialog, ImXML::XMLEventHandler { + public: + MaterialEditDialog() : m_xml_tree(ImXML::XMLReader().read("XML/MaterialEditPopup.xml")) { + m_xml_renderer.addDynamicBind("bool_opaque", {&m_mtl_opaque, 1, ImXML::Bool}); + m_xml_renderer.addDynamicBind("str_material_name", {m_mtl_name, 512, ImXML::Chars}); + m_shaders_combo.onSelectionChanged([this](const std::string&, int index) { callback("shader_selected", index); }); + } + + void render() override { + ImGui::PushID(m_dialog_id); + if (isOpenRequested()) + ImGui::OpenPopup("Material Editor"); + m_xml_renderer.render(m_xml_tree, *this); + ImGui::PopID(); + } + + void setPreviewTexture(void* tex) { m_preview_texture = tex; } + + void addUniformInput(const std::string& name, const ICE::UniformValue& value) { + int ctr = m_uniform_inputs.size(); + m_uniform_inputs.emplace_back(std::make_unique(std::format("##input_{}", ctr), value)); + m_uniform_names.emplace_back(std::make_unique(std::format("##name_{}", ctr), name)); + m_uniform_types.emplace_back(std::make_unique(std::format("##type_{}", ctr), m_uniform_types_list)); + m_uniform_types.back()->setSelected(value.index()); + + m_uniform_inputs.back()->onValueChanged([this, id = ctr](const auto& val) { m_material->setUniform(m_uniform_names[id]->getText(), val); }); + m_uniform_names.back()->onEdit([this](std::string prev_name, std::string new_name) { m_material->renameUniform(prev_name, new_name); }); + m_uniform_types.back()->onSelectionChanged([this, id = ctr](const std::string&, int index) { + ICE::UniformValue val; + switch (index) { + case 0: + val = ICE::AssetUID(0); + break; + case 1: + val = 0; + break; + case 2: + val = 0.0f; + break; + case 3: + val = Eigen::Vector2f(); + break; + case 4: + val = Eigen::Vector3f(); + break; + case 5: + val = Eigen::Vector4f(); + break; + case 6: + val = Eigen::Matrix4f(); + break; + default: + break; + } + m_uniform_inputs[id]->setValue(val); + }); + + std::vector tex_paths; + std::vector tex_ids; + for (const auto& [path, id] : m_textures) { + tex_paths.push_back(path); + tex_ids.push_back(id); + } + m_uniform_inputs.back()->setAssetComboList(tex_paths, tex_ids); + } + + void setShaderList(const std::vector& list, int selected = 0) { + m_shaders_combo.setValues(list); + m_shaders_combo.setSelected(selected); + } + void setTextureList(const std::unordered_map& textures) { m_textures = textures; } + void setMaterialName(const std::string& name) { strncpy(m_mtl_name, name.c_str(), 512); } + void setMaterial(std::shared_ptr mtl) { + m_material = mtl; + m_mtl_opaque = !mtl->isTransparent(); + m_uniform_inputs.clear(); + m_uniform_names.clear(); + m_uniform_types.clear(); + for (const auto& [name, value] : mtl->getAllUniforms()) { + addUniformInput(name, value); + } + } + ICE::Material getMaterial() const { return *m_material; } + std::string getName() const { return m_mtl_name; } + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "combo_shader_values") { + m_shaders_combo.render(); + } else if (node.arg("id") == "uniforms_table") { + for (int i = 0; i < m_uniform_inputs.size(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + m_uniform_names[i]->render(); + ImGui::TableNextColumn(); + m_uniform_inputs[i]->render(); + ImGui::TableNextColumn(); + m_uniform_types[i]->render(); + } + } else if (node.arg("id") == "material_preview") { + ImGui::Image(m_preview_texture, {256, 256}, {0, 1}, {1, 0}); + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override { + if (node.arg("id") == "btn_apply") { + ImGui::CloseCurrentPopup(); + done(DialogResult::Ok); + } else if (node.arg("id") == "btn_cancel") { + ImGui::CloseCurrentPopup(); + done(DialogResult::Cancel); + } else if (node.arg("id") == "btn_add_uniform") { + m_material->setUniform("New Uniform", 0); + addUniformInput("New Uniform", 0); + } + } + + private: + bool m_mtl_opaque; + char m_mtl_name[512] = {0}; + void* m_preview_texture = nullptr; + + ComboBox m_shaders_combo{"###shaders", {}}; + + std::vector> m_uniform_inputs; + std::vector> m_uniform_names; + std::vector> m_uniform_types; + + std::unordered_map m_textures; + + const std::vector m_uniform_types_list = {"Texture", "int", "float", "Vector2f", "Vector3f", "Vector4f", "Matrix4f"}; + + std::shared_ptr m_material; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/NewMaterialWidget.h b/ICEBERG/UI/NewMaterialWidget.h deleted file mode 100644 index ece39eaa..00000000 --- a/ICEBERG/UI/NewMaterialWidget.h +++ /dev/null @@ -1,229 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -#include -#include - -#include "Components/ComboBox.h" -#include "Components/InputText.h" -#include "Components/UniformInputs.h" -#include "Widget.h" - -class NewMaterialWidget : public Widget { - public: - NewMaterialWidget(const std::shared_ptr& engine) : m_engine(engine) {} - - void render() override { - ImGui::PushID(m_id); - if (m_open) { - ImGui::OpenPopup("Material Editor"); - m_open = false; - } - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - if (ImGui::BeginPopupModal("Material Editor", 0, 0)) { - if (ImGui::BeginTable("mat_editor_layout", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) { - ImGui::TableNextColumn(); - ImGui::Text("Name:"); - ImGui::SameLine(); - ImGui::InputText("##name", m_name, 512); - ImGui::Text("Shader:"); - ImGui::SameLine(); - m_shaders_combo.render(); - ImGui::Text("Uniforms"); - ImGui::SameLine(); - if (ImGui::BeginTable("uniforms_table", 3)) { - ImGui::TableSetupColumn("Name"); - ImGui::TableSetupColumn("Value"); - ImGui::TableSetupColumn("Type"); - ImGui::TableHeadersRow(); - for (int i = 0; i < m_uniform_names.size(); i++) { - ImGui::TableNextColumn(); - m_uniform_names[i].render(); - ImGui::TableNextColumn(); - m_uniform_inputs[i].render(); - ImGui::TableNextColumn(); - m_uniform_combos[i].render(); - } - ImGui::EndTable(); - } - - if (ImGui::Button("New Uniform")) { - addUniformInput("uNew", 0); - } - - if (ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); - m_id = 0; - } - ImGui::SameLine(); - if (ImGui::Button("Apply")) { - auto new_name = m_engine->getAssetBank()->getName(m_id).prefix() + m_name; - auto rename_ok = m_engine->getAssetBank()->renameAsset(m_engine->getAssetBank()->getName(m_id), new_name); - if (rename_ok) { - m_accepted = true; - ImGui::CloseCurrentPopup(); - m_id = 0; - } - } - ImGui::TableNextColumn(); - ImGui::Image(renderPreview(), {256, 256}); - ImGui::EndTable(); - } - ImGui::EndPopup(); - } - ImGui::PopStyleVar(); - ImGui::PopID(); - } - - void open(ICE::AssetUID id) { - m_id = id; - m_open = true; - m_material = m_engine->getAssetBank()->getAsset(id); - auto shaders = m_engine->getAssetBank()->getAll(); - std::vector shader_names; - int shader_idx = 0; - int i = 0; - for (const auto& [id, shader] : shaders) { - shader_names.push_back(m_engine->getAssetBank()->getName(id).toString()); - if (id == m_material->getShader()) { - shader_idx = i; - } - i++; - } - m_shaders_combo.setValues(shader_names); - m_shaders_combo.setSelected(shader_idx); - auto name = m_engine->getAssetBank()->getName(id).getName(); - memcpy(m_name, name.c_str(), name.size() + 1); - - m_shaders_combo.onSelectionChanged([this](const std::string& name, int) { m_material->setShader(m_engine->getAssetBank()->getUID(name)); }); - - m_uniform_names.clear(); - m_uniform_combos.clear(); - m_uniform_inputs.clear(); - for (const auto& [uname, uniform] : m_material->getAllUniforms()) { - addUniformInput(uname, uniform); - } - } - - void addUniformInput(const std::string& uname, const ICE::UniformValue& value) { - m_uniform_names.emplace_back("##uName_" + std::to_string(m_ctr), uname); - m_uniform_combos.emplace_back("##uType_" + std::to_string(m_ctr), uniform_types_names); - m_uniform_inputs.emplace_back("##uIn_" + std::to_string(m_ctr), value); - - m_uniform_inputs.back().onValueChanged( - [this, in = m_uniform_inputs.size() - 1](const ICE::UniformValue& v) { m_material->setUniform(m_uniform_names[in].getText(), v); }); - - auto textures = m_engine->getAssetBank()->getAll(); - std::vector uids; - std::vector paths; - for (const auto& [id, ptr] : textures) { - uids.push_back(id); - paths.push_back(m_engine->getAssetBank()->getName(id).toString()); - } - if (std::holds_alternative(value)) { - m_uniform_inputs.back().setAssetComboList(paths, uids); - } - - m_uniform_combos.back().setSelected(comboIDFromValue(value)); - m_uniform_combos.back().onSelectionChanged([this, in = m_uniform_inputs.size() - 1](const std::string& selected, int i) { - if (i == 0) { - m_uniform_inputs[in].setValue(ICE::AssetUID(0)); - } else if (i == 1) { - m_uniform_inputs[in].setValue(0); - } else if (i == 2) { - m_uniform_inputs[in].setValue(0.0f); - } else if (i == 3) { - m_uniform_inputs[in].setValue(Eigen::Vector3f(0, 0, 0)); - } else if (i == 4) { - m_uniform_inputs[in].setValue(Eigen::Vector4f(0, 0, 0, 0)); - } else if (i == 4) { - m_uniform_inputs[in].setValue(Eigen::Matrix4f()); - } - }); - - m_ctr++; - } - - int comboIDFromValue(const ICE::UniformValue& value) { - if (std::holds_alternative(value)) { - return 2; - } else if (!std::holds_alternative(value) && std::holds_alternative(value)) { - return 1; - } else if (std::holds_alternative(value)) { - return 0; - } else if (std::holds_alternative(value)) { - return 3; - } else if (std::holds_alternative(value)) { - return 4; - } else if (std::holds_alternative(value)) { - return 5; - } else { - throw std::runtime_error("Uniform type not implemented"); - } - } - - void* renderPreview() { - auto model_uid = m_engine->getAssetBank()->getUID(ICE::AssetPath::WithTypePrefix("sphere")); - - auto model = m_engine->getAssetBank()->getAsset(model_uid); - auto shader = m_engine->getAssetBank()->getAsset(m_material->getShader()); - - auto camera = std::make_shared(60.0, 1.0, 0.01, 10000.0); - camera->backward(2); - camera->up(1); - camera->pitch(-30); - - shader->bind(); - shader->loadMat4("projection", camera->getProjection()); - shader->loadMat4("view", camera->lookThrough()); - - ICE::GeometryPass pass(m_engine->getApi(), m_engine->getGraphicsFactory(), {256, 256, 1}); - std::vector cmds; - std::unordered_map> textures; - for (const auto& [k, v] : m_material->getAllUniforms()) { - if (std::holds_alternative(v)) { - auto id = std::get(v); - textures.try_emplace(id, m_engine->getAssetBank()->getAsset(id)); - } - } - for (const auto& mesh : model->getMeshes()) { - cmds.push_back(ICE::RenderCommand{.mesh = mesh, - .material = m_material, - .shader = shader, - .textures = textures, - .model_matrix = Eigen::Matrix4f::Identity()}); - } - pass.submit(&cmds); - pass.execute(); - - return static_cast(0) + pass.getResult()->getTexture(); - } - - bool accepted() { - if (m_accepted) { - m_accepted = false; - return true; - } - return false; - } - - private: - bool m_open = false; - char m_name[512] = {0}; - int m_shader_index = 0; - ComboBox m_shaders_combo{"##shader_combo", {}}; - std::vector m_uniform_names; - std::vector m_uniform_combos; - std::vector m_uniform_inputs; - const std::vector uniform_types_names = {"Asset", "Int", "Float", "Vector3", "Vector4", "Matrix4"}; - int m_ctr = 0; - bool m_accepted = false; - std::shared_ptr m_engine; - std::shared_ptr m_material; - ICE::AssetUID m_id; -}; diff --git a/ICEBERG/UI/NewProjectPopup.h b/ICEBERG/UI/NewProjectPopup.h new file mode 100644 index 00000000..d9a098e0 --- /dev/null +++ b/ICEBERG/UI/NewProjectPopup.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "Dialog.h" + +class NewProjectPopup : public Dialog, ImXML::XMLEventHandler { + public: + NewProjectPopup() : m_xml_tree(ImXML::XMLReader().read("XML/NewProjectPopup.xml")) { + m_xml_renderer.addDynamicBind("str_project_name", {m_project_name, 512, ImXML::Chars}); + m_xml_renderer.addDynamicBind("str_project_directory", {m_project_dir, 512, ImXML::Chars}); + } + + void render() override { + if (isOpenRequested()) + ImGui::OpenPopup("New Project"); + m_xml_renderer.render(m_xml_tree, *this); + } + + void onNodeBegin(ImXML::XMLNode& node) override {} + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override { + if (node.arg("id") == "btn_create") { + auto path_string = std::string(m_project_dir); + if (!path_string.empty() && std::filesystem::exists(path_string) && !std::string(m_project_name).empty()) { + auto project = std::make_shared(path_string, m_project_name); + project->CreateDirectories(); + ImGui::CloseCurrentPopup(); + done(DialogResult::Ok); + } + } else if (node.arg("id") == "btn_cancel") { + ImGui::CloseCurrentPopup(); + done(DialogResult::Cancel); + } else if (node.arg("id") == "btn_browse_directory") { + auto folder = open_native_folder_dialog(); + std::strncpy(m_project_dir, folder.c_str(), 511); + } + } + + std::string getProjectName() const { return std::string(m_project_name); } + + std::string getProjectDirectory() const { return std::string(m_project_dir); } + + private: + char m_project_name[512] = {0}; + char m_project_dir[512] = {0}; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/NewSceneWidget.h b/ICEBERG/UI/NewSceneDialog.h similarity index 51% rename from ICEBERG/UI/NewSceneWidget.h rename to ICEBERG/UI/NewSceneDialog.h index 162a2f3b..781b9cfe 100644 --- a/ICEBERG/UI/NewSceneWidget.h +++ b/ICEBERG/UI/NewSceneDialog.h @@ -1,52 +1,43 @@ #pragma once #include -#include +#include #include #include #include "Components/InputText.h" -#include "Widget.h" +#include "Dialog.h" -class NewSceneWidget : public Widget { +class NewSceneDialog : public Dialog { public: - NewSceneWidget(const std::shared_ptr& engine) : m_engine(engine) {} + NewSceneDialog(const std::shared_ptr& engine) : m_engine(engine) {} void render() override { ImGui::PushID("scene_edit"); - if (m_open) { + if (isOpenRequested()) { ImGui::OpenPopup("Scene Editor"); - m_open = false; } - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); if (ImGui::BeginPopupModal("Scene Editor", 0, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Scene name:"); + ImGui::SameLine(); m_scene_name_in.render(); if (ImGui::Button("Accept")) { - m_accepted = true; - ImGui::CloseCurrentPopup(); + done(DialogResult::Ok); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + done(DialogResult::Cancel); } ImGui::EndPopup(); } - ImGui::PopStyleVar(); ImGui::PopID(); } std::string getSceneName() { return m_scene_name_in.getText(); } - void open() { m_open = true; } - - bool accepted() { - if (m_accepted) { - m_accepted = false; - return true; - } - return false; - } private: - bool m_open = false; char m_name[512] = {0}; - bool m_accepted = false; std::shared_ptr m_engine; - InputText m_scene_name_in{"Scene name", "New scene"}; + InputText m_scene_name_in{"###Scene name", "New scene"}; }; \ No newline at end of file diff --git a/ICEBERG/UI/OpenSceneWidget.h b/ICEBERG/UI/OpenSceneDialog.h similarity index 57% rename from ICEBERG/UI/OpenSceneWidget.h rename to ICEBERG/UI/OpenSceneDialog.h index e2ac5919..81416a5f 100644 --- a/ICEBERG/UI/OpenSceneWidget.h +++ b/ICEBERG/UI/OpenSceneDialog.h @@ -1,13 +1,13 @@ #pragma once -#include +#include #include "Components/ComboBox.h" #include "Components/InputText.h" -#include "Widget.h" +#include "Dialog.h" -class OpenSceneWidget : public Widget { +class OpenSceneDialog : public Dialog { public: - OpenSceneWidget(const std::shared_ptr& engine) : m_engine(engine), m_scene_name_combo("Scene", {}) { + OpenSceneDialog(const std::shared_ptr& engine) : m_engine(engine), m_scene_name_combo("###SceneNameCombo", {}) { std::vector scenes_names; for (const auto& s : m_engine->getProject()->getScenes()) { scenes_names.push_back(s->getName()); @@ -18,42 +18,30 @@ class OpenSceneWidget : public Widget { void render() override { ImGui::PushID("scene_open"); - if (m_open) { + if (isOpenRequested()) { ImGui::OpenPopup("Scene Selection"); - m_open = false; } - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); if (ImGui::BeginPopupModal("Scene Selection", 0, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Select a scene to open:"); + ImGui::SameLine(); m_scene_name_combo.render(); if (ImGui::Button("Accept")) { - m_accepted = true; - ImGui::CloseCurrentPopup(); + done(DialogResult::Ok); } + ImGui::SameLine(); if (ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); + done(DialogResult::Cancel); } ImGui::EndPopup(); } - ImGui::PopStyleVar(); ImGui::PopID(); } int getSelectedIndex() { return m_scene_name_combo.getSelectedIndex(); } - void open() { m_open = true; } - - bool accepted() { - if (m_accepted) { - m_accepted = false; - return true; - } - return false; - } private: - bool m_open = false; - bool m_accepted = false; std::shared_ptr m_engine; ComboBox m_scene_name_combo; }; \ No newline at end of file diff --git a/ICEBERG/UI/ProjectSelectionWidget.h b/ICEBERG/UI/ProjectSelectionWidget.h index 49910ca7..1ade7808 100644 --- a/ICEBERG/UI/ProjectSelectionWidget.h +++ b/ICEBERG/UI/ProjectSelectionWidget.h @@ -1,6 +1,12 @@ #pragma once -#include +#include +#include +#include +#include +#include +#include +#include "NewProjectPopup.h" #include "Widget.h" struct ProjectView { @@ -9,76 +15,71 @@ struct ProjectView { std::string modified_date; }; -class ProjectSelectionWidget : public Widget { +class ProjectSelectionWidget : public Widget, ImXML::XMLEventHandler { public: - ProjectSelectionWidget() = default; + ProjectSelectionWidget() : m_xml_tree(ImXML::XMLReader().read("XML/ProjectSelection.xml")) { + m_xml_renderer.addDynamicBind("str_project_search", {m_project_search, 512, ImXML::Chars}); + } void render() override { ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); - ImGui::Begin("ICE Project Selection", NULL, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize); - ImGui::BeginTable("layout_split", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_Resizable); - ImGui::TableNextColumn(); - renderNewProjects(); - ImGui::TableNextColumn(); - renderExistingProjects(); - ImGui::EndTable(); - ImGui::End(); - } - - int getSelectedProject() const { return m_selected_project; } + m_xml_renderer.render(m_xml_tree, *this); + m_new_project_popup.render(); - const char* getProjectName() const { return m_project_name; } + if (m_new_project_popup.getResult() == DialogResult::Ok) { + callback("create_clicked", m_new_project_popup.getProjectDirectory(), m_new_project_popup.getProjectName()); + } + } void setProjects(const std::vector& projects) { m_projects = projects; } - private: - void renderNewProjects() { - ImGui::BeginTable("layout_create", 3); - ImGui::TableNextColumn(); - ImGui::Text("Create a new Project"); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Project name: "); - ImGui::TableNextColumn(); - ImGui::InputText("##pname", m_project_name, 512); - ImGui::TableNextColumn(); - if (ImGui::Button("Create")) { - callback("create_clicked"); + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "list_table_body") { + renderExistingProjects(); } - ImGui::TableNextColumn(); - if (ImGui::Button("Load")) { - callback("load_clicked"); + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override { + if (node.arg("id") == "btn_create_project") { + m_new_project_popup.open(); + } else if (node.arg("id") == "btn_add_project") { + auto path = open_native_dialog({{"ICE Projects", "*.ice"}}); + callback("load_clicked", path); } - ImGui::EndTable(); } + private: void renderExistingProjects() { - ImGui::BeginTable("layout_projects", 1, ImGuiTableFlags_BordersH); - ImGui::TableNextColumn(); - ImGui::Text("Load existing project"); for (int i = 0; i < m_projects.size(); i++) { const auto& p = m_projects[i]; ImGui::TableNextColumn(); - ImGui::BeginGroup(); - ImGui::Text(p.name.c_str()); - ImGui::Text(p.path.c_str()); - ImGui::Text(p.modified_date.c_str()); - ImGui::EndGroup(); - if (ImGui::IsItemHovered()) { - ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(0.2, 0.2, 0.2, 1))); + ImGui::PushID(i); + ImGui::Selectable(p.name.c_str(), false, ImGuiSelectableFlags_SpanAllColumns); + if (ImGui::IsItemClicked()) { + m_selected_project = i; + callback("project_selected", i); } + ImGui::TableNextColumn(); + ImGui::Text(p.modified_date.c_str()); + ImGui::TableNextColumn(); + ImGui::Text(p.path.c_str()); + ImGui::TableNextRow(); + if (ImGui::IsItemClicked()) { m_selected_project = i; callback("project_selected", i); } + ImGui::PopID(); } - ImGui::EndTable(); } private: - char m_project_name[512] = {0}; + char m_project_search[512] = {0}; std::vector m_projects; int m_selected_project = -1; + NewProjectPopup m_new_project_popup; + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; }; diff --git a/ICEBERG/UI/RenderComponentWidget.h b/ICEBERG/UI/RenderComponentWidget.h new file mode 100644 index 00000000..f5328019 --- /dev/null +++ b/ICEBERG/UI/RenderComponentWidget.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Components/UniformInputs.h" +#include "Widget.h" + +class RenderComponentWidget : public Widget, ImXML::XMLEventHandler { + public: + explicit RenderComponentWidget() : m_xml_tree(ImXML::XMLReader().read("XML/RenderComponentWidget.xml")) {} + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "models_combo") { + m_models_combo.render(); + } else if (node.arg("id") == "materials_combo") { + m_material_combo.render(); + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override {} + + void render() override { + if (m_rc) { + m_xml_renderer.render(m_xml_tree, *this); + } + } + + void setRenderComponent(ICE::RenderComponent* rc, const std::vector& meshes_paths, const std::vector& meshes_ids, + const std::vector& materials_paths, const std::vector& materials_ids) { + m_rc = rc; + if (m_rc) { + m_models_combo.setValue(rc->mesh); + m_models_combo.setAssetComboList(meshes_paths, meshes_ids); + m_models_combo.onValueChanged([this](const ICE::UniformValue& v) { m_rc->mesh = std::get(v); }); + + m_material_combo.setValue(rc->material); + m_material_combo.setAssetComboList(materials_paths, materials_ids); + m_material_combo.onValueChanged([this](const ICE::UniformValue& v) { m_rc->material = std::get(v); }); + } + } + + private: + ICE::RenderComponent* m_rc = nullptr; + UniformInputs m_models_combo{"##models_combo", 0}; + UniformInputs m_material_combo{"##materials_combo", 0}; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/ShaderEditDialog.h b/ICEBERG/UI/ShaderEditDialog.h new file mode 100644 index 00000000..ff9521a6 --- /dev/null +++ b/ICEBERG/UI/ShaderEditDialog.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Components/ComboBox.h" +#include "Components/InputText.h" +#include "Components/UniformInputs.h" +#include "Dialog.h" +#include "ShaderStageEditWidget.h" + +class ShaderEditDialog : public Dialog, ImXML::XMLEventHandler { + public: + ShaderEditDialog() : m_xml_tree(ImXML::XMLReader().read("XML/ShaderEditDialog.xml")) { + m_xml_renderer.addDynamicBind("str_shader_name", {m_shader_name, 512, ImXML::Chars}); + + m_widgets.try_emplace(ICE::ShaderStage::Vertex, ShaderStageEditWidget(ICE::ShaderStage::Vertex)); + m_widgets.try_emplace(ICE::ShaderStage::Fragment, ShaderStageEditWidget(ICE::ShaderStage::Fragment)); + } + + std::string getName() const { return std::string(m_shader_name); } + + void render() override { + ImGui::PushID(m_dialog_id); + if (isOpenRequested()) + ImGui::OpenPopup("Shader Editor"); + m_xml_renderer.render(m_xml_tree, *this); + ImGui::PopID(); + } + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "stages_widgets") { + for (auto& [stage, widget] : m_widgets) { + widget.render(); + } + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override { + if (node.arg("id") == "btn_apply") { + ImGui::CloseCurrentPopup(); + done(DialogResult::Ok); + } else if (node.arg("id") == "btn_cancel") { + ImGui::CloseCurrentPopup(); + done(DialogResult::Cancel); + } + } + + private: + char m_shader_name[512] = {0}; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; + + std::unordered_map m_widgets; +}; diff --git a/ICEBERG/UI/ShaderStageEditWidget.h b/ICEBERG/UI/ShaderStageEditWidget.h new file mode 100644 index 00000000..513c3c8f --- /dev/null +++ b/ICEBERG/UI/ShaderStageEditWidget.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Components/ComboBox.h" +#include "Components/InputText.h" +#include "Components/UniformInputs.h" +#include "Widget.h" + +class ShaderStageEditWidget : public Widget, ImXML::XMLEventHandler { + public: + ShaderStageEditWidget(ICE::ShaderStage stage) : m_stage(stage), m_xml_tree(ImXML::XMLReader().read("XML/ShaderStageEditWidget.xml")) { + m_xml_renderer.addDynamicBind("str_shader_file", {m_shader_file, 512, ImXML::Chars}); + m_xml_renderer.addDynamicBind("str_shader_source", {m_shader_source, 65535, ImXML::Chars}); + } + + void render() override { m_xml_renderer.render(m_xml_tree, *this); } + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.type == ImXML::ImGuiEnum::TABITEM) { + node.args["label"] = m_stage_names.at(m_stage); + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override {} + + private: + char m_shader_file[512] = {0}; + char m_shader_source[65535] = {0}; + + ICE::ShaderStage m_stage; + + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; + + const std::unordered_map m_stage_names = { + {ICE::ShaderStage::Vertex, "Vertex"}, + {ICE::ShaderStage::Fragment, "Fragment"}, + {ICE::ShaderStage::Geometry, "Geometry"}, + {ICE::ShaderStage::TessControl, "Tessellation Control"}, + {ICE::ShaderStage::TessEval, "Tessellation Evaluation"}, + {ICE::ShaderStage::Compute, "Compute"}, + }; +}; diff --git a/ICEBERG/UI/TransformComponentWidget.h b/ICEBERG/UI/TransformComponentWidget.h new file mode 100644 index 00000000..dba3a504 --- /dev/null +++ b/ICEBERG/UI/TransformComponentWidget.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Components/UniformInputs.h" +#include "Widget.h" + +class TransformComponentWidget : public Widget, ImXML::XMLEventHandler { + public: + explicit TransformComponentWidget() : m_xml_tree(ImXML::XMLReader().read("XML/TransformComponentWidget.xml")) { + m_transform_input.setForceVectorNumeric(true); + m_rotation_input.setForceVectorNumeric(true); + m_scale_input.setForceVectorNumeric(true); + } + + void onNodeBegin(ImXML::XMLNode& node) override { + if (node.arg("id") == "position_vec") { + m_transform_input.render(); + } else if (node.arg("id") == "rotation_vec") { + m_rotation_input.render(); + } else if (node.arg("id") == "scale_vec") { + m_scale_input.render(); + } + } + void onNodeEnd(ImXML::XMLNode& node) override {} + void onEvent(ImXML::XMLNode& node) override {} + + void render() override { + if (m_tc) { + m_xml_renderer.render(m_xml_tree, *this); + } + } + + void setTransformComponent(ICE::TransformComponent* tc) { + m_tc = tc; + if (tc) { + m_transform_input.setValue(tc->getPosition()); + m_transform_input.onValueChanged([this](const ICE::UniformValue& v) { m_tc->setPosition(std::get(v)); }); + m_rotation_input.setValue(tc->getRotationEulerDeg()); + m_rotation_input.onValueChanged([this](const ICE::UniformValue& v) { m_tc->setRotationEulerDeg(std::get(v)); }); + m_scale_input.setValue(tc->getScale()); + m_scale_input.onValueChanged([this](const ICE::UniformValue& v) { m_tc->setScale(std::get(v)); }); + } + } + + private: + ICE::TransformComponent* m_tc = nullptr; + UniformInputs m_transform_input{"##transform_component_position", 0}; + UniformInputs m_rotation_input{"##transform_component_rotation", 0}; + UniformInputs m_scale_input{"##transform_component_scale", 0}; + ImXML::XMLTree m_xml_tree; + ImXML::XMLRenderer m_xml_renderer; +}; diff --git a/ICEBERG/UI/ViewportWidget.h b/ICEBERG/UI/ViewportWidget.h index 77e9d18e..42157734 100644 --- a/ICEBERG/UI/ViewportWidget.h +++ b/ICEBERG/UI/ViewportWidget.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include "Widget.h" @@ -29,6 +29,15 @@ class ViewportWidget : public Widget { const float window_width = ImGui::GetContentRegionAvail().x; const float window_height = ImGui::GetContentRegionAvail().y; ImGui::Image(texture_ptr, {window_width, window_height}, ImVec2(0, 1), ImVec2(1, 0)); + if (ImGui::BeginDragDropTarget()) { + ImGuiDragDropFlags target_flags = 0; + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("DND_ASSET_MODEL", target_flags)) { + auto path = (char*) payload->Data; + callback("spawnTree", path); + } + ImGui::EndDragDropTarget(); + } + auto drag = ImGui::GetMouseDragDelta(0); if (ImGui::IsWindowHovered()) { diff --git a/ICEBERG/UI/Widget.h b/ICEBERG/UI/Widget.h index 76287952..dec01399 100644 --- a/ICEBERG/UI/Widget.h +++ b/ICEBERG/UI/Widget.h @@ -7,6 +7,8 @@ class Widget { public: + virtual ~Widget() = default; + virtual void render() = 0; template diff --git a/ICEBERG/XML/AnimationComponentWidget.xml b/ICEBERG/XML/AnimationComponentWidget.xml new file mode 100644 index 00000000..721edfdf --- /dev/null +++ b/ICEBERG/XML/AnimationComponentWidget.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/ICEBERG/XML/AssetBrowser.xml b/ICEBERG/XML/AssetBrowser.xml new file mode 100644 index 00000000..a91e7ff3 --- /dev/null +++ b/ICEBERG/XML/AssetBrowser.xml @@ -0,0 +1,21 @@ + + + + + + + +
+ + + + + + + + + + + +
+
diff --git a/ICEBERG/XML/EditorWidget.xml b/ICEBERG/XML/EditorWidget.xml new file mode 100644 index 00000000..1a9021f5 --- /dev/null +++ b/ICEBERG/XML/EditorWidget.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ICEBERG/XML/LightComponentWidget.xml b/ICEBERG/XML/LightComponentWidget.xml new file mode 100644 index 00000000..c7db05f8 --- /dev/null +++ b/ICEBERG/XML/LightComponentWidget.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/ICEBERG/XML/MaterialEditPopup.xml b/ICEBERG/XML/MaterialEditPopup.xml new file mode 100644 index 00000000..52c45e09 --- /dev/null +++ b/ICEBERG/XML/MaterialEditPopup.xml @@ -0,0 +1,64 @@ + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + +
+ + + + + + + +