#pragma once #include "../component.h" #include "model_class.h" #include #include #include #include // Déclaration externe de la variable globale définie dans application_class.h /** * Declaration of the global model cache externe variable from application_class.h . * This variable is used to cache models loaded from files to avoid reloading them multiple times. */ extern std::map> g_model_cache; namespace ecs { class RenderComponent : public Component { public: /** * Builder for the RenderComponent class. */ RenderComponent() : m_model(nullptr), m_isVisible(true) {} ~RenderComponent() = default; /** * Initialize the component by setting up the Direct3D device and context from the parent entity. * This method is called when the component is added to an entity. */ void Initialize() override { if (auto parent = GetParent()) { SetDevice(parent->GetDevice()); SetContext(parent->GetContext()); } texture_container_buffer = TextureContainer(); } void Update(float deltaTime) override {} /** * Initialize the RenderComponent with a model. * This method allows the component to be initialized with an existing model instance. * @param model A shared pointer to the model_class instance to use. * @return True if initialization was successful, false otherwise. */ bool InitializeWithModel(std::shared_ptr model) { if (!model) R_FALSE m_model = model; R_TRUE } /** * Initialize the RenderComponent from a model file. * This method checks if the model is already cached; if not, it loads the model from the specified file. * @param device The Direct3D device used for rendering. * @param deviceContext The Direct3D device context used for rendering. * @param modelFilename The path to the model file to load. * @param textureContainer The container for textures used by the model. * @return True if initialization was successful, false otherwise. */ bool InitializeFromFile(ID3D11Device* device, ID3D11DeviceContext* deviceContext, const char* modelFilename, TextureContainer& textureContainer) { // Vérifier si le modèle existe déjà dans le cache std::string filename(modelFilename); auto it = g_model_cache.find(filename); if (it != g_model_cache.end()) { m_model = it->second; } else { // Créer un nouveau modèle auto new_model = std::make_shared(); if (!new_model->Initialize(device, deviceContext, const_cast(modelFilename), textureContainer)) { R_FALSE } g_model_cache[filename] = new_model; m_model = new_model; } m_modelFilePath = modelFilename; R_TRUE } /** * Load textures from a list of file paths into the texture container. * This method uses DirectX's WIC texture loader to load textures from the specified paths. * @param texturePaths A vector of file paths to the textures to load. * @param texturesContainer The container where the loaded textures will be stored. * @param device The Direct3D device used for rendering. * @param deviceContext The Direct3D device context used for rendering. * @return True if all textures were loaded successfully, false otherwise. */ bool LoadTexturesFromPath(std::vector& texturePaths, TextureContainer& texturesContainer, ID3D11Device* device, ID3D11DeviceContext* deviceContext) { HRESULT result; int i = 0; for (const auto& texturePath : texturePaths) { ID3D11ShaderResourceView* texture = nullptr; result = DirectX::CreateWICTextureFromFile(device, deviceContext, texturePath.c_str(), nullptr, &texture); if (FAILED(result)) { R_FALSE } texturesContainer.AssignTexture(texturesContainer, texture, texturePath, i); i++; } R_TRUE } /** * Get the model associated with this RenderComponent. * @return A shared pointer to the model_class instance. */ std::shared_ptr GetModel() const { return m_model; } /** * Set the model for this RenderComponent. * This method allows the component to be set with an existing model instance. * @param model A shared pointer to the model_class instance to set. */ void SetModel(std::shared_ptr model) { m_model = model; } /** * Get the file path of the model associated with this RenderComponent. * @return The file path as a string. */ const std::string& GetModelFilePath() const { return m_modelFilePath; } /** * Set the file path of the model for this RenderComponent. * This method allows the component to be set with a specific model file path. * @param path The file path to set as a string. */ void SetModelFilePath(const std::string& path) { m_modelFilePath = path; } /** * Check if the model is currently visible. * @return True if the model is visible, false otherwise. */ bool IsVisible() const { return m_isVisible; } /** * Set the visibility of the model. * This method allows the component to control whether the model should be rendered or not. * @param visible True to make the model visible, false to hide it. */ void SetVisible(bool visible) { m_isVisible = visible; } /** * Get a texture of a specific type by index. * This method retrieves the texture from the model based on the specified type and index. * @param type The type of texture to retrieve (Diffuse, Normal, Specular, Alpha). * @param index The index of the texture to retrieve (default is 0). * @return A pointer to the ID3D11ShaderResourceView of the texture, or nullptr if not found. */ ID3D11ShaderResourceView* GetTexture(TextureType type, int index = 0) { if (!m_model) return nullptr; switch (type) { case TextureType::Diffuse: return m_model->GetTexture(::TextureType::Diffuse, index); case TextureType::Normal: return m_model->GetTexture(::TextureType::Normal, index); case TextureType::Specular: return m_model->GetTexture(::TextureType::Specular, index); case TextureType::Alpha: return m_model->GetTexture(::TextureType::Alpha, index); default: return nullptr; } } /** * Get the number of vertices in the model. * This method retrieves the vertex count from the model. * @return The number of vertices as an integer. */ int GetIndexCount() const { return m_model ? m_model->GetIndexCount() : 0; } /** * Render the model using the provided device context. * This method calls the Render method of the model if it is initialized and visible. * @param deviceContext The Direct3D device context used for rendering. */ void Render(ID3D11DeviceContext* deviceContext) { if (m_model && m_isVisible) { m_model->Render(deviceContext); } } void RenderOnlyGeometryFromSun(ID3D11DeviceContext* ctx) { if (m_model && m_is_in_sun_pov) m_model->Render(ctx); } void set_is_sun_visible(bool v) { m_is_in_sun_pov = v; } bool is_in_sun_pov() const { return m_is_in_sun_pov; } /** * Serialize the RenderComponent's state to a string. * This method is useful for saving the component's state or debugging. * @return A string representation of the RenderComponent's state. */ std::string Serialize() const override { if (!m_model) return "RenderComponent:NoModel"; TextureContainer texture_container_buffer = m_model->GetTextureContainer(); auto serializePaths = [](const std::vector &paths) -> std::string { std::string result; for (size_t i = 0; i < paths.size(); ++i) { std::string p = std::string(paths[i].begin(), paths[i].end()); result += p; if (i + 1 < paths.size()) { result += ","; // virgule entre chemins mais pas après dernier } } return result; }; int diffuse_count = (int)texture_container_buffer.diffuse.size(); int normal_count = (int)texture_container_buffer.normal.size(); int specular_count = (int)texture_container_buffer.specular.size(); int alpha_count = (int)texture_container_buffer.alpha.size(); std::string diffuse_paths_ = serializePaths(texture_container_buffer.diffusePaths); std::string normal_paths_ = serializePaths(texture_container_buffer.normalPaths); std::string specular_paths_ = serializePaths(texture_container_buffer.specularPaths); std::string alpha_paths_ = serializePaths(texture_container_buffer.alphaPaths); std::stringstream ss; ss << "RenderComponent:HasModel:" << is_shadow_caster_ << ":" << diffuse_count << ":" << diffuse_paths_ << ":" << normal_count << ":" << normal_paths_ << ":" << specular_count << ":" << specular_paths_ << ":" << alpha_count << ":" << alpha_paths_; return ss.str(); } /** * Deserialize the RenderComponent's state from a string. * This method parses the string and sets the component's properties accordingly. * Note: The model will be loaded separately based on the model file path. * @param data The string representation of the RenderComponent's state. * @return True if deserialization was successful, otherwise false. */ bool Deserialize(const std::string& data) override { std::stringstream ss(data); std::string type; if (!std::getline(ss, type, ':') || type != "RenderComponent") R_FALSE std::string token; if (!std::getline(ss, token, ':') || token != "HasModel") R_FALSE std::getline(ss,token, ':'); is_shadow_caster_ = std::stoi(token); int diffuse_count = 0, normal_count = 0, specular_count = 0, alpha_count = 0; std::vector paths_diffuse, paths_normal, paths_specular, paths_alpha; auto read_count = [&](int& count) -> bool { if (!std::getline(ss, token, ':')) R_FALSE try { count = std::stoi(token); } catch (...) { R_FALSE } R_TRUE }; auto clean_token = [](std::string& str) { // Retirer virgules et espaces str.erase(std::remove(str.begin(), str.end(), ','), str.end()); str.erase(0, str.find_first_not_of(" \t\n\r")); str.erase(str.find_last_not_of(" \t\n\r") + 1); }; auto read_paths = [&](int count, std::vector& paths) -> bool { for (int i = 0; i < count; ++i) { if (!std::getline(ss, token, ':')) R_FALSE clean_token(token); if (!token.empty()) { paths.emplace_back(token.begin(), token.end()); LOG_INFO("Loaded path: " + std::string(token.begin(), token.end())); } } R_TRUE }; if (!read_count(diffuse_count)) R_FALSE if (!read_paths(diffuse_count, paths_diffuse)) R_FALSE if (!read_count(normal_count)) R_FALSE if (!read_paths(normal_count, paths_normal)) R_FALSE if (!read_count(specular_count)) R_FALSE if (!read_paths(specular_count, paths_specular)) R_FALSE if (!read_count(alpha_count)) R_FALSE if (!read_paths(alpha_count, paths_alpha)) R_FALSE // Libérer textures existantes for (auto& tex : texture_container_buffer.diffuse) if (tex) { tex->Release(); tex = nullptr; } for (auto& tex : texture_container_buffer.normal) if (tex) { tex->Release(); tex = nullptr; } for (auto& tex : texture_container_buffer.specular) if (tex) { tex->Release(); tex = nullptr; } for (auto& tex : texture_container_buffer.alpha) if (tex) { tex->Release(); tex = nullptr; } texture_container_buffer.diffuse.clear(); texture_container_buffer.normal.clear(); texture_container_buffer.specular.clear(); texture_container_buffer.alpha.clear(); texture_container_buffer.diffusePaths = std::move(paths_diffuse); texture_container_buffer.normalPaths = std::move(paths_normal); texture_container_buffer.specularPaths = std::move(paths_specular); texture_container_buffer.alphaPaths = std::move(paths_alpha); R_TRUE } /** * Render the ImGui interface for the RenderComponent. * This method provides a user interface for inspecting and modifying the component's properties. * It includes options to toggle visibility, display model information, and manage textures. */ void OnImGuiRender() override { ImGui::Checkbox("Visible", &m_isVisible); C_BOX("Is shadow caster" , &is_shadow_caster_); ImGui::Text("Model File Path: %s", m_modelFilePath.c_str()); if (m_model) { ImGui::Text("Index Count: %d", m_model->GetIndexCount()); } else { ImGui::Text("No model loaded."); } ImGui::Separator(); // Définir les types de textures_ std::vector textureCategories = { "Diffuse", "Normal", "Specular", "Alpha" }; std::vector textureTypes = { TextureType::Diffuse, TextureType::Normal, TextureType::Specular, TextureType::Alpha }; // Créer un espace pour afficher les textures_ avec défilement ImGui::BeginChild("TextureChild", ImVec2(0, 200), true, ImGuiWindowFlags_HorizontalScrollbar); // Pour chaque type de texture for (int typeIndex = 0; typeIndex < textureCategories.size(); typeIndex++) { TextureType type = textureTypes[typeIndex]; std::string typeName = textureCategories[typeIndex]; // Afficher le titre de la catégorie ImGui::Text("%s:", typeName.c_str()); ImGui::SameLine(); // Compter combien de textures_ de ce type existent int textureCount = 0; while (GetTexture(type, textureCount) != nullptr) { textureCount++; } // Afficher toutes les textures_ existantes ImGui::BeginGroup(); for (int texIndex = 0; texIndex < textureCount; texIndex++) { ID3D11ShaderResourceView* texture = GetTexture(type, texIndex); if (texture) { // ID unique pour chaque bouton de texture std::string buttonId = "tex##" + std::to_string(typeIndex) + "_" + std::to_string(texIndex); if (ImGui::ImageButton(buttonId.c_str(), (ImTextureID)texture, ImVec2(48, 48))) { // Ouvrir une boîte de dialogue pour changer la texture OPENFILENAME ofn; WCHAR szFile[260]; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = NULL; ofn.lpstrFile = szFile; szFile[0] = '\0'; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = L"Texture\0*.png;*.jpg;*.dds\0"; ofn.nFilterIndex = 1; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (GetOpenFileName(&ofn)) { // Changer la texture existante m_model->ChangeTexture(device, context, ofn.lpstrFile, type, texIndex); } } // Afficher l'indice de texture et prévisualisation au survol if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text("%s %d", typeName.c_str(), texIndex); ImGui::Image((ImTextureID)texture, ImVec2(192, 192)); ImGui::EndTooltip(); } ImGui::SameLine(); } } // Bouton pour ajouter une nouvelle texture std::string addButtonLabel = "+##" + std::to_string(typeIndex); if (ImGui::Button(addButtonLabel.c_str(), ImVec2(48, 48))) { // Ouvrir une boîte de dialogue pour ajouter une texture OPENFILENAME ofn; WCHAR szFile[260]; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = NULL; ofn.lpstrFile = szFile; szFile[0] = '\0'; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = L"Texture\0*.png;*.jpg;*.dds\0"; ofn.nFilterIndex = 1; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (GetOpenFileName(&ofn)) { // Ajouter une nouvelle texture m_model->AddTexture(device, context, ofn.lpstrFile, type); } } ImGui::EndGroup(); ImGui::Separator(); } ImGui::EndChild(); } void SetDevice (ID3D11Device* dev) { device = dev; } void SetContext(ID3D11DeviceContext* ctx) { context = ctx; } void SetTextureContainer(const TextureContainer& texContainer) { texture_container_buffer = texContainer; } const TextureContainer& GetTextureContainerBuffer() const { return texture_container_buffer; } /** * Get whether the model casts shadows. * @return True if the model casts shadows, false otherwise. */ bool IsShadowCaster() const { return is_shadow_caster_; } /** * Set whether the model casts shadows. * @param isCaster True to make the model cast shadows, false otherwise. */ void SetShadowCaster(bool isCaster) { is_shadow_caster_ = isCaster; } private: std::shared_ptr m_model; std::string m_modelFilePath; bool m_isVisible; ID3D11Device* device; ID3D11DeviceContext* context; TextureContainer texture_container_buffer; bool is_shadow_caster_ = true; bool m_is_in_sun_pov; }; } // namespace ecs