Skip to content

Commit

Permalink
Add save/load to world material modifications
Browse files Browse the repository at this point in the history
  • Loading branch information
enginmanap committed Dec 3, 2024
1 parent 9df15ff commit b636b85
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 7 deletions.
23 changes: 23 additions & 0 deletions src/Assets/AssetManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,29 @@ std::shared_ptr<Material> AssetManager::registerMaterial(std::shared_ptr<Materia
return material;

}

/**
* Registers an overriden material if the material if it is not found, and returns the material that should be used.
* if the material is not found, itself is returned, if it is found, the original one is returned.
*
* for insterting in to possible materials list, original hash is used instead of current, because the material is
* an override for the original hash, not for the current hash
*
* @param material to register or search for the original
* @return material that should be used.
*/
std::shared_ptr<Material> AssetManager::registerOverriddenMaterial(std::shared_ptr<Material> 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> material) {
auto materialIt = materials.find(material->getOriginalHash());
if(materialIt == materials.end()) {
Expand Down
3 changes: 3 additions & 0 deletions src/Assets/AssetManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ class AssetManager {
}

std::shared_ptr<Material> registerMaterial(std::shared_ptr<Material> material);

std::shared_ptr<Material> registerOverriddenMaterial(std::shared_ptr<Material> material);

void unregisterMaterial(std::shared_ptr<Material> material);
const std::map<size_t, std::pair<std::shared_ptr<Material>, uint32_t>>& getMaterials() const;

Expand Down
164 changes: 164 additions & 0 deletions src/Material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#include <ImGui/imgui.h>
#include "Material.h"

#include <WorldLoader.h>
#include <WorldSaver.h>

#include "API/Graphics/GraphicsInterface.h"

void Material::loadGPUSide(AssetManager *assetManager) {
Expand Down Expand Up @@ -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> 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> material = std::make_shared<Material>(assetManager, name, materialIndex, specularExponent, ambientColor, diffuseColor, specularColor,
refractionIndex);

tinyxml2::XMLElement *textureNode = materialNode->FirstChildElement("AmbientTexture");
if (textureNode) {
std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<Material> hashGenerator;
return hashGenerator(*this);
Expand Down
9 changes: 8 additions & 1 deletion src/Material.h
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -270,6 +275,8 @@ class Material {

ImGuiResult addImGuiEditorElements(const ImGuiRequest &request);

bool serialize(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *materialsNode) const;
static std::shared_ptr<Material> deserialize(AssetManager* assetManager, tinyxml2::XMLElement *materialNode);
#ifdef CEREAL_SUPPORT
template<class Archive>
void save(Archive & archive) const {
Expand Down
21 changes: 21 additions & 0 deletions src/WorldLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 = Material::deserialize(world->assetManager.get(), materialNode);
}
return true;
}

bool WorldLoader::loadVec3(tinyxml2::XMLNode *vectorNode, glm::vec3& vector) {
if(vectorNode == nullptr) {
return false;
Expand Down
12 changes: 8 additions & 4 deletions src/WorldLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef LIMONENGINE_WORLDLOADER_H
#define LIMONENGINE_WORLDLOADER_H

#include <Material.h>
#include <string>
#include <vector>
#include <tinyxml2.h>
Expand Down Expand Up @@ -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> assetManager, InputHandler *inputHandler, OptionsUtil::Options *options);

std::unique_ptr<std::string> getLoadingImage(const std::string &worldFile) const;

World *loadWorld(const std::string &worldFile, LimonAPI *limonAPI) const;

static std::vector<std::unique_ptr<ObjectInformation>> loadObject( std::shared_ptr<AssetManager> assetManager, tinyxml2::XMLElement *objectNode,
std::unordered_map<std::string, std::shared_ptr<Sound>> &requiredSounds, LimonAPI *limonAPI,
PhysicalRenderable *parentObject);
std::unordered_map<std::string, std::shared_ptr<Sound>> &requiredSounds, LimonAPI *limonAPI,
PhysicalRenderable *parentObject);


static bool loadVec3(tinyxml2::XMLNode* vectorNode, glm::vec3& vector);
};


Expand Down
20 changes: 20 additions & 0 deletions src/WorldSaver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<size_t, std::pair<std::shared_ptr<Material>, uint32_t>>& materials = world->assetManager->getMaterials();

for (auto it = materials.begin(); it != materials.end(); ++it) {
std::shared_ptr<Material> 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);
Expand Down
5 changes: 3 additions & 2 deletions src/WorldSaver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

Expand Down

0 comments on commit b636b85

Please sign in to comment.