Skip to content

Commit

Permalink
Add saving and loading material overrides of single models
Browse files Browse the repository at this point in the history
  • Loading branch information
enginmanap committed Dec 27, 2024
1 parent c9b8016 commit d1372f1
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/Assets/ModelAsset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ void ModelAsset::serializeCustomizations() {

tinyxml2::XMLElement * animationsSectionsNode = customizationDocument.NewElement("Animation");
rootNode->InsertEndChild(animationsSectionsNode);
for(auto it=this->animationSections.begin(); it != this->animationSections.end(); it++) {
for(auto it=this->animationSections.begin(); it != this->animationSections.end(); ++it) {
tinyxml2::XMLElement *sectionElement = customizationDocument.NewElement("Section");
animationsSectionsNode->InsertEndChild(sectionElement);

Expand Down
1 change: 1 addition & 0 deletions src/Assets/ModelAsset.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class ModelAsset : public Asset {
}
return meshMaterialMap.at(mesh);
}

~ModelAsset() override;

std::vector<std::shared_ptr<MeshAsset>> getMeshes() const {
Expand Down
51 changes: 51 additions & 0 deletions src/GameObjects/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,22 @@ bool Model::fillObjects(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *o
if(child->fillObjects(document, childNode)) {
childrenNode->InsertEndChild(childNode);
}
}
}

//Material customizations
const std::vector<std::pair<std::string, std::shared_ptr<const Material>>> meshMaterialMap = getNewMeshMaterials();
if (!meshMaterialMap.empty()) {
tinyxml2::XMLElement *childrenNode = document.NewElement("MeshMaterialList");
tinyxml2::XMLElement *childrenCountNode = document.NewElement("Count");
childrenCountNode->SetText(std::to_string(meshMaterialMap.size()).c_str());
childrenNode->InsertEndChild(childrenCountNode);
objectElement->InsertEndChild(childrenNode);
for (auto& meshMaterial:meshMaterialMap) {
tinyxml2::XMLElement *childNode = document.NewElement("MeshMaterial");
childNode->SetAttribute("MeshName", meshMaterial.first.c_str());
meshMaterial.second->serialize(document, childNode);
childrenNode->InsertEndChild(childNode);
}
}

Expand Down Expand Up @@ -586,6 +601,42 @@ ImGuiResult Model::putAIonGUI(ActorInterface *actorInterface,
return result;
}

std::vector<std::pair<std::string, std::shared_ptr<const Material>>> Model::getNewMeshMaterials() const{
std::vector<std::pair<std::string, std::shared_ptr<const Material>>> meshMaterialList;

for (const auto& thisMeshMaterial:meshMetaData) {
std::shared_ptr<Material> material = this->modelAsset->getMeshMaterial(thisMeshMaterial->mesh);
if (material == nullptr) {
std::cerr << "Model has a mesh that is not part of asset. This should not happen. " << std::endl;
continue;
}
if (material != thisMeshMaterial->material) {//difference in materials, we should save this.
meshMaterialList.emplace_back(thisMeshMaterial->mesh->getName(), thisMeshMaterial->material);
}
}
return meshMaterialList;
}

void Model::loadOverriddenMeshMaterial(std::vector<std::pair<std::string, std::shared_ptr<Material>>> &customisedMeshMaterialList) {
for (const auto& thisMeshMaterial:customisedMeshMaterialList) {
//find the mesh
bool found = false;
for (auto& meshMetaData: this->meshMetaData) {
if (meshMetaData->mesh->getName() == thisMeshMaterial.first) {
std::shared_ptr<Material> newMaterial = thisMeshMaterial.second;
newMaterial->loadGPUSide(assetManager.get());
newMaterial = assetManager->registerMaterial(newMaterial);
graphicsWrapper->setMaterial(*newMaterial);
meshMetaData->material = newMaterial;
found = true;
}
}
if (!found) {
std::cerr << "Model " << this->name << " doesn't have a mesh with name " << thisMeshMaterial.first << " This should never happen" << std::endl;
}
}
}

void Model::attachAI(ActorInterface *AIActor) {
//after this, clearing the AI is job of the model.
this->AIActor = AIActor;
Expand Down
19 changes: 17 additions & 2 deletions src/GameObjects/Model.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,22 @@ class Model : public PhysicalRenderable, public GameObject {
static ImGuiResult putAIonGUI(ActorInterface *actorInterface, std::vector<LimonTypes::GenericParameter> &parameters,
const ImGuiRequest &request, std::string &lastSelectedAIName);

/**
* This method returns a list of MeshAsset->Material pairs, that has been customized specifically for this model, that is not shared with the
* ModelAsset. Model asset has a default material, and world has overrides for that asset. This method returns overrides for not the asset level,
* but the model level.
*
* This method will check one by one all meshes and compare with the asset. That is because we don't want to make the Model object use more memory
* for something thats expected to apply only a very small portion of the models.
* Consider this a slow method.
*
* @return list of meshes that had different material than asset
*/
std::vector<std::pair<std::string, std::shared_ptr<const Material>>> getNewMeshMaterials() const;

public:
void loadOverriddenMeshMaterial(std::vector<std::pair<std::string, std::shared_ptr<Material>>> & customisedMeshMaterialList);

Model(uint32_t objectID, std::shared_ptr<AssetManager> assetManager, const std::string &modelFile) : Model(objectID, assetManager,
0, modelFile, false) {};

Expand Down Expand Up @@ -149,7 +164,7 @@ class Model : public PhysicalRenderable, public GameObject {
return modelAsset->isTransparent();
}

~Model();
~Model() override;

bool fillObjects(tinyxml2::XMLDocument &document, tinyxml2::XMLElement *objectsNode) const override;

Expand Down Expand Up @@ -180,7 +195,7 @@ class Model : public PhysicalRenderable, public GameObject {

std::string getName() const override {
return name + "_" + std::to_string(objectID);
};
}

ImGuiResult addImGuiEditorElements(const ImGuiRequest &request) override;
/************Game Object methods **************/
Expand Down
25 changes: 25 additions & 0 deletions src/WorldLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,31 @@ WorldLoader::loadObject( std::shared_ptr<AssetManager> assetManager, tinyxml2::X
}
}
}
//Now Load material changes
std::vector<std::pair<std::string, std::shared_ptr<Material>>> custumizedMeshMaterialList;
tinyxml2::XMLElement* meshMaterialListNode = objectNode->FirstChildElement("MeshMaterialList");
if (meshMaterialListNode != nullptr) {
tinyxml2::XMLElement *meshMaterialNode = meshMaterialListNode->FirstChildElement("MeshMaterial");
while (meshMaterialNode != nullptr) {
if (meshMaterialNode->Attribute("MeshName") == nullptr) {
std::cerr << "A mesh material definition with no MeshName found. Can't be processed. Skipping." << std::endl;
} else {
std::string meshName = meshMaterialNode->Attribute("MeshName");
tinyxml2::XMLElement *materialNode = meshMaterialNode->FirstChildElement("Material");
if (materialNode == nullptr) {
std::cerr << "A mesh material definition with no Material found. Can't be processed. Skipping." << std::endl;
} else {
std::shared_ptr<Material> newMaterial = Material::deserialize(assetManager.get(), materialNode);
custumizedMeshMaterialList.emplace_back(meshName, newMaterial);
}
}
meshMaterialNode = meshMaterialNode->NextSiblingElement("MeshMaterial");
}
}
if (!custumizedMeshMaterialList.empty()) {
loadedObjectInformation->model->loadOverriddenMeshMaterial(custumizedMeshMaterialList);
}

loadedObjects.push_back(std::move(loadedObjectInformation));


Expand Down

0 comments on commit d1372f1

Please sign in to comment.