diff --git a/src/Assets/AssetManager.cpp b/src/Assets/AssetManager.cpp index 0ca523cc..9c4c0bd6 100644 --- a/src/Assets/AssetManager.cpp +++ b/src/Assets/AssetManager.cpp @@ -206,6 +206,29 @@ std::shared_ptr AssetManager::registerMaterial(std::shared_ptr AssetManager::registerOverriddenMaterial(std::shared_ptr material) { + const auto foundIt = materials.find(material->getOriginalHash()); + if(foundIt != materials.end()) { + //std::cerr << "Material found, return "<< material->getName() << std::endl; + foundIt->second.second++; + return foundIt->second.first; + } + //not found, add + //std::cerr << "Material not found, create for " << material->getName() << std::endl; + materials[material->getOriginalHash()] = std::make_pair(material, 1); + return material; +} void AssetManager::unregisterMaterial(std::shared_ptr material) { auto materialIt = materials.find(material->getOriginalHash()); if(materialIt == materials.end()) { diff --git a/src/Assets/AssetManager.h b/src/Assets/AssetManager.h index e71a076d..9012daf9 100644 --- a/src/Assets/AssetManager.h +++ b/src/Assets/AssetManager.h @@ -443,6 +443,9 @@ class AssetManager { } std::shared_ptr registerMaterial(std::shared_ptr material); + + std::shared_ptr registerOverriddenMaterial(std::shared_ptr material); + void unregisterMaterial(std::shared_ptr material); const std::map, uint32_t>>& getMaterials() const; diff --git a/src/Material.cpp b/src/Material.cpp index 39fc8083..544ce3db 100644 --- a/src/Material.cpp +++ b/src/Material.cpp @@ -4,6 +4,10 @@ #include #include "Material.h" + +#include +#include + #include "API/Graphics/GraphicsInterface.h" void Material::loadGPUSide(AssetManager *assetManager) { @@ -74,6 +78,166 @@ ImGuiResult Material::addImGuiEditorElements(const ImGuiRequest &request [[gnu:: return result; } +bool Material::serialize(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *materialsNode) const { + tinyxml2::XMLElement *materialNode = document.NewElement("Material"); + materialsNode->InsertEndChild(materialNode); + tinyxml2::XMLElement *materialNameNode = document.NewElement("Name"); + materialNameNode->SetText(this->getName().c_str()); + materialNode->InsertEndChild(materialNameNode); + tinyxml2::XMLElement *materialAmbientColorNode = document.NewElement("AmbientColor"); + WorldSaver::serializeVec3(document, materialAmbientColorNode, this->getAmbientColor()); + materialNode->InsertEndChild(materialAmbientColorNode); + tinyxml2::XMLElement *materialDiffuseColorNode = document.NewElement("DiffuseColor"); + WorldSaver::serializeVec3(document, materialDiffuseColorNode, this->getDiffuseColor()); + materialNode->InsertEndChild(materialDiffuseColorNode); + tinyxml2::XMLElement *materialSpecularColorNode = document.NewElement("SpecularColor"); + WorldSaver::serializeVec3(document, materialSpecularColorNode, this->getSpecularColor()); + materialNode->InsertEndChild(materialSpecularColorNode); + tinyxml2::XMLElement *materialIndexNode = document.NewElement("MaterialIndex"); + materialIndexNode->SetText(this->getMaterialIndex()); + materialNode->InsertEndChild(materialIndexNode); + tinyxml2::XMLElement *materialSpecularExponentNode = document.NewElement("SpecularExponent"); + materialSpecularExponentNode->SetText(this->getSpecularExponent()); + materialNode->InsertEndChild(materialSpecularExponentNode); + tinyxml2::XMLElement *materialRefractionIndexNode = document.NewElement("RefractionIndex"); + materialRefractionIndexNode->SetText(this->getRefractionIndex()); + materialNode->InsertEndChild(materialRefractionIndexNode); + tinyxml2::XMLElement *materialOriginalHashNode = document.NewElement("OriginalHash"); + materialOriginalHashNode->SetText(this->getOriginalHash()); + materialNode->InsertEndChild(materialOriginalHashNode); + + //now the textures. They might or might not exists, we need to check + if (this->getAmbientTexture() != nullptr) { + tinyxml2::XMLElement *materialAmbientTextureNode = document.NewElement("AmbientTexture"); + materialAmbientTextureNode->SetText(StringUtils::join(this->getAmbientTexture()->getName(), ",").c_str()); + materialNode->InsertEndChild(materialAmbientTextureNode); + } + + if (this->getDiffuseTexture() != nullptr) { + tinyxml2::XMLElement *materialDiffuseTextureNode = document.NewElement("DiffuseTexture"); + materialDiffuseTextureNode->SetText(StringUtils::join(this->getDiffuseTexture()->getName(), ",").c_str()); + materialNode->InsertEndChild(materialDiffuseTextureNode); + } + + if (this->getSpecularTexture() != nullptr) { + tinyxml2::XMLElement *materialSpecularTextureNode = document.NewElement("SpecularTexture"); + materialSpecularTextureNode->SetText(StringUtils::join(this->getSpecularTexture()->getName(), ",").c_str()); + materialNode->InsertEndChild(materialSpecularTextureNode); + } + + if (this->getNormalTexture() != nullptr) { + tinyxml2::XMLElement *materialNormalTextureNode = document.NewElement("NormalTexture"); + materialNormalTextureNode->SetText(StringUtils::join(this->getNormalTexture()->getName(), ",").c_str()); + materialNode->InsertEndChild(materialNormalTextureNode); + } + + if (this->getOpacityTexture() != nullptr) { + tinyxml2::XMLElement *materialOpacityTextureNode = document.NewElement("OpacityTexture"); + materialOpacityTextureNode->SetText(StringUtils::join(this->getOpacityTexture()->getName(), ",").c_str()); + materialNode->InsertEndChild(materialOpacityTextureNode); + } + return true; +} + +std::shared_ptr Material::deserialize(AssetManager* assetManager, tinyxml2::XMLElement *materialNode) { + + std::string name = materialNode->FirstChildElement("Name")->GetText(); + + glm::vec3 ambientColor; + WorldLoader::loadVec3(materialNode->FirstChildElement("AmbientColor"), ambientColor); + + glm::vec3 diffuseColor; + WorldLoader::loadVec3(materialNode->FirstChildElement("DiffuseColor"), diffuseColor); + + glm::vec3 specularColor; + WorldLoader::loadVec3(materialNode->FirstChildElement("SpecularColor"), specularColor); + + size_t originalHash = 0; + uint32_t materialIndex = std::stoi(materialNode->FirstChildElement("MaterialIndex")->GetText()); + float specularExponent = std::stof(materialNode->FirstChildElement("SpecularExponent")->GetText()); + float refractionIndex = std::stof(materialNode->FirstChildElement("RefractionIndex")->GetText()); + if (materialNode->FirstChildElement("OriginalHash") != nullptr && materialNode->FirstChildElement("OriginalHash")->GetText() != nullptr) { + std::string originalHashStr = materialNode->FirstChildElement("OriginalHash")->GetText(); + std::stringstream stream(originalHashStr); + stream >> originalHash; + } + + std::shared_ptr material = std::make_shared(assetManager, name, materialIndex, specularExponent, ambientColor, diffuseColor, specularColor, + refractionIndex); + + tinyxml2::XMLElement *textureNode = materialNode->FirstChildElement("AmbientTexture"); + if (textureNode) { + std::vector textureNames = StringUtils::split(textureNode->GetText(), ","); + if(textureNames.size() == 1) { + material->setAmbientTexture(textureNames[0]); + } else if (textureNames.size() == 2) { + material->setAmbientTexture(textureNames[0], &textureNames[1]); + } + } + textureNode = materialNode->FirstChildElement("DiffuseTexture"); + if (textureNode) { + std::vector textureNames = StringUtils::split(textureNode->GetText(), ","); + if(textureNames.size() == 1) { + material->setDiffuseTexture(textureNames[0]); + } else if (textureNames.size() == 2) { + material->setDiffuseTexture(textureNames[0], &textureNames[1]); + } + } + + textureNode = materialNode->FirstChildElement("SpecularTexture"); + if (textureNode) { + std::vector textureNames = StringUtils::split(textureNode->GetText(), ","); + if(textureNames.size() == 1) { + material->setSpecularTexture(textureNames[0]); + } else if (textureNames.size() == 2) { + material->setSpecularTexture(textureNames[0], &textureNames[1]); + } + } + + textureNode = materialNode->FirstChildElement("NormalTexture"); + if (textureNode) { + std::vector textureNames = StringUtils::split(textureNode->GetText(), ","); + if(textureNames.size() == 1) { + material->setNormalTexture(textureNames[0]); + } else if (textureNames.size() == 2) { + material->setNormalTexture(textureNames[0], &textureNames[1]); + } + } + + textureNode = materialNode->FirstChildElement("OpacityTexture"); + if (textureNode) { + std::vector textureNames = StringUtils::split(textureNode->GetText(), ","); + if(textureNames.size() == 1) { + material->setOpacityTexture(textureNames[0]); + } else if (textureNames.size() == 2) { + material->setOpacityTexture(textureNames[0], &textureNames[1]); + } + } + uint32_t maps = 0; + + if(material->hasNormalMap()) { + maps +=16; + } + if(material->hasAmbientMap()) { + maps +=8; + } + if(material->hasDiffuseMap()) { + maps +=4; + } + if(material->hasSpecularMap()) { + maps +=2; + } + if(material->hasOpacityMap()) { + maps +=1; + } + material->originalHash = originalHash; + material->setMaps(maps); + assetManager->registerOverriddenMaterial(material); + return material; + + +} + size_t Material::getHash() const { std::hash hashGenerator; return hashGenerator(*this); diff --git a/src/Material.h b/src/Material.h index 78c19c10..09cf17c2 100644 --- a/src/Material.h +++ b/src/Material.h @@ -61,9 +61,14 @@ class Material { friend class AssetManager; Material() {}; + friend class WorldLoader; + + void setOriginalHash(size_t originalHash) { + this->originalHash = originalHash; + } public: Material(AssetManager *assetManager, const std::string &name, uint32_t materialIndex, float specularExponent, const glm::vec3 &ambientColor, - const glm::vec3 &diffuseColor, const glm::vec3 &specularColor, float refractionIndex) + const glm::vec3 &diffuseColor, const glm::vec3 &specularColor, float refractionIndex)//FIXME: this should not use raw pointer : name(name), ambientColor(ambientColor), diffuseColor(diffuseColor), @@ -270,6 +275,8 @@ class Material { ImGuiResult addImGuiEditorElements(const ImGuiRequest &request); + bool serialize(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *materialsNode) const; + static std::shared_ptr deserialize(AssetManager* assetManager, tinyxml2::XMLElement *materialNode); #ifdef CEREAL_SUPPORT template void save(Archive & archive) const { diff --git a/src/WorldLoader.cpp b/src/WorldLoader.cpp index cf0a9d34..f27ed3e8 100644 --- a/src/WorldLoader.cpp +++ b/src/WorldLoader.cpp @@ -262,6 +262,11 @@ World * WorldLoader::loadMapFromXML(const std::string &worldFileName, LimonAPI * } } + if (!loadMaterials(worldNode, world)) { + delete world; + return nullptr; + } + //load objects if(!loadObjectsFromXML(worldNode, world, limonAPI)) { @@ -1644,6 +1649,22 @@ bool WorldLoader::loadGUILayersAndElements(tinyxml2::XMLNode *worldNode, World * return true; } + +bool WorldLoader::loadMaterials(tinyxml2::XMLNode *worldNode, World *world) const { + tinyxml2::XMLElement *materialsNode = worldNode->FirstChildElement("Materials"); + if (!materialsNode) { + std::cerr << "No materials found in XML." << std::endl; + return false; + } + + for (tinyxml2::XMLElement *materialNode = materialsNode->FirstChildElement("Material"); + materialNode != nullptr; + materialNode = materialNode->NextSiblingElement("Material")) { + std::shared_ptr material = Material::deserialize(world->assetManager.get(), materialNode); + } + return true; +} + bool WorldLoader::loadVec3(tinyxml2::XMLNode *vectorNode, glm::vec3& vector) { if(vectorNode == nullptr) { return false; diff --git a/src/WorldLoader.h b/src/WorldLoader.h index 82f004b9..4a156c76 100644 --- a/src/WorldLoader.h +++ b/src/WorldLoader.h @@ -5,6 +5,7 @@ #ifndef LIMONENGINE_WORLDLOADER_H #define LIMONENGINE_WORLDLOADER_H +#include #include #include #include @@ -53,20 +54,23 @@ class WorldLoader { bool loadOnLoadActions(tinyxml2::XMLNode *worldNode, World *world) const; bool loadOnLoadAnimations(tinyxml2::XMLNode *worldNode, World *world) const; bool loadGUILayersAndElements(tinyxml2::XMLNode *worldNode, World *world) const; + bool loadMaterials(tinyxml2::XMLNode *worldNode, World *world) const; - - static bool loadVec3(tinyxml2::XMLNode* vectorNode, glm::vec3& vector); void attachedAPIMethodsToWorld(World *world, LimonAPI *limonAPI) const; public: WorldLoader(std::shared_ptr assetManager, InputHandler *inputHandler, OptionsUtil::Options *options); + std::unique_ptr getLoadingImage(const std::string &worldFile) const; World *loadWorld(const std::string &worldFile, LimonAPI *limonAPI) const; static std::vector> loadObject( std::shared_ptr assetManager, tinyxml2::XMLElement *objectNode, - std::unordered_map> &requiredSounds, LimonAPI *limonAPI, - PhysicalRenderable *parentObject); + std::unordered_map> &requiredSounds, LimonAPI *limonAPI, + PhysicalRenderable *parentObject); + + + static bool loadVec3(tinyxml2::XMLNode* vectorNode, glm::vec3& vector); }; diff --git a/src/WorldSaver.cpp b/src/WorldSaver.cpp index f664bcb9..50a85f61 100644 --- a/src/WorldSaver.cpp +++ b/src/WorldSaver.cpp @@ -137,6 +137,12 @@ bool WorldSaver::saveWorld(const std::string& mapName, const World* world) { currentElement->SetText(world->quitWorldName.c_str()); rootNode->InsertEndChild(currentElement); + currentElement = mapDocument.NewElement("Materials"); + if(!fillMaterials(mapDocument, currentElement, world)) { + return false; + }; + rootNode->InsertEndChild(currentElement);//add objects + //after current element is inserted, we can reuse currentElement = mapDocument.NewElement("Objects"); if(!fillObjects(mapDocument, currentElement, world)) { @@ -593,6 +599,20 @@ bool WorldSaver::fillGUILayersAndElements(tinyxml2::XMLDocument &document, tinyx return true; } +bool WorldSaver::fillMaterials(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *materialsNode, const World *world) { + const std::map, uint32_t>>& materials = world->assetManager->getMaterials(); + + for (auto it = materials.begin(); it != materials.end(); ++it) { + std::shared_ptr currentMaterial = it->second.first; + if (currentMaterial->getHash() == currentMaterial->getOriginalHash()) { + //if material is not modified, we don't need to save + continue; + } + currentMaterial->serialize(document,materialsNode); + } + return true; +} + void WorldSaver::serializeVec3(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *parentNode, const glm::vec3& vector){ tinyxml2::XMLElement *currentElement = document.NewElement("X"); currentElement->SetText(vector.x); diff --git a/src/WorldSaver.h b/src/WorldSaver.h index dc219103..fe6176a9 100644 --- a/src/WorldSaver.h +++ b/src/WorldSaver.h @@ -22,9 +22,10 @@ class WorldSaver { static bool fillOnloadActions(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *onloadActionsNode, const World *world); static bool fillOnloadAnimations(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *onloadAnimationsNode, const World *world); static bool fillGUILayersAndElements(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *GUILayersListNode, const World *world); - - static void serializeVec3(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *parentNode, const glm::vec3& vector); + static bool fillMaterials(tinyxml2::XMLDocument & document, tinyxml2::XMLElement * materialsNode, const World * world); public: + static void serializeVec3(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *parentNode, const glm::vec3& vector); + static bool saveWorld(const std::string& mapName, const World* world); };