diff --git a/CMakeLists.txt b/CMakeLists.txt index 7154f844c..df79c52b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -391,6 +391,8 @@ set(OC_CLONK_SOURCES src/lib/StdMeshLoaderXml.cpp src/lib/StdMeshMaterial.cpp src/lib/StdMeshMaterial.h + src/lib/StdMeshUpdate.cpp + src/lib/StdMeshUpdate.h src/lib/StdResStr2.cpp src/lib/StdResStr2.h src/lib/StdResStr.h diff --git a/Makefile.am b/Makefile.am index 6c1874d8f..b828c6d3c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -394,6 +394,8 @@ src/lib/StdMeshLoader.h \ src/lib/StdMeshLoaderXml.cpp \ src/lib/StdMeshMaterial.cpp \ src/lib/StdMeshMaterial.h \ +src/lib/StdMeshUpdate.cpp \ +src/lib/StdMeshUpdate.h \ src/lib/StdResStr.h \ src/lib/texture/C4Facet.cpp \ src/lib/texture/C4FacetEx.cpp \ diff --git a/src/game/object/C4DefGraphics.cpp b/src/game/object/C4DefGraphics.cpp index 4651c024b..3d3cfd87f 100644 --- a/src/game/object/C4DefGraphics.cpp +++ b/src/game/object/C4DefGraphics.cpp @@ -334,7 +334,8 @@ C4AdditionalDefGraphics::C4AdditionalDefGraphics(C4Def *pOwnDef, const char *szN SCopy(szName, Name, C4MaxName); } -C4DefGraphicsPtrBackup::C4DefGraphicsPtrBackup(C4DefGraphics *pSourceGraphics) +C4DefGraphicsPtrBackup::C4DefGraphicsPtrBackup(C4DefGraphics *pSourceGraphics): + MeshMaterialUpdate(::MeshMaterialManager) { // assign graphics + def pGraphicsPtr = pSourceGraphics; @@ -342,6 +343,20 @@ C4DefGraphicsPtrBackup::C4DefGraphicsPtrBackup(C4DefGraphics *pSourceGraphics) // assign name const char *szName = pGraphicsPtr->GetName(); if (szName) SCopy(szName, Name, C4MaxName); else *Name=0; + + // Remove all mesh materials that were loaded from this definition + for(StdMeshMatManager::Iterator iter = ::MeshMaterialManager.Begin(); iter != MeshMaterialManager.End(); ) + { + StdStrBuf Filename; + Filename.Copy(pDef->Filename); + Filename.Append("/"); Filename.Append(GetFilename(iter->FileName.getData())); + + if(Filename == iter->FileName) + iter = ::MeshMaterialManager.Remove(iter, &MeshMaterialUpdate); + else + ++iter; + } + // create next graphics recursively C4DefGraphics *pNextGfx = pGraphicsPtr->pNext; if (pNextGfx) @@ -360,6 +375,8 @@ C4DefGraphicsPtrBackup::~C4DefGraphicsPtrBackup() void C4DefGraphicsPtrBackup::AssignUpdate(C4DefGraphics *pNewGraphics) { + UpdateMeshMaterials(); + // only if graphics are assigned if (pGraphicsPtr) { @@ -378,6 +395,7 @@ void C4DefGraphicsPtrBackup::AssignUpdate(C4DefGraphics *pNewGraphics) pObj->AssignRemoval(); pObj->pGraphics=NULL; } } + // remove any overlay graphics for (;;) { @@ -406,8 +424,36 @@ void C4DefGraphicsPtrBackup::AssignUpdate(C4DefGraphics *pNewGraphics) if (pNext) pNext->AssignUpdate(pNewGraphics); } +void C4DefGraphicsPtrBackup::UpdateMeshMaterials() +{ + // Update mesh materials for all meshes + for(C4DefList::Table::iterator iter = Definitions.table.begin(); iter != Definitions.table.end(); ++iter) + if(iter->second->Graphics.Type == C4DefGraphics::TYPE_Mesh) + MeshMaterialUpdate.Update(iter->second->Graphics.Mesh); + + // Update mesh materials for all mesh instances, except ones which belong + // to this definition. Such graphics have an invalid pointer to the underlying + // StdMesh, and they will be updated separately below. + C4Object *pObj; + for (C4ObjectLink *pLnk = ::Objects.First; pLnk; pLnk=pLnk->Next) + if ((pObj=pLnk->Obj)) if (pObj->Status) + { + //if(pObj->pGraphics != pGraphicsPtr) + if(pObj->pMeshInstance) + MeshMaterialUpdate.Update(pObj->pMeshInstance); + for (C4GraphicsOverlay* pGfxOverlay = pObj->pGfxOverlay; pGfxOverlay; pGfxOverlay = pGfxOverlay->GetNext()) + //if (pGfxOverlay->GetGfx() != pGraphicsPtr) + if(pGfxOverlay->pMeshInstance) + MeshMaterialUpdate.Update(pGfxOverlay->pMeshInstance); + } +} + void C4DefGraphicsPtrBackup::AssignRemoval() { + // Reset all mesh materials to what they were before the update + MeshMaterialUpdate.Cancel(); + UpdateMeshMaterials(); + // only if graphics are assigned if (pGraphicsPtr) { diff --git a/src/game/object/C4DefGraphics.h b/src/game/object/C4DefGraphics.h index de2283be7..e72d92514 100644 --- a/src/game/object/C4DefGraphics.h +++ b/src/game/object/C4DefGraphics.h @@ -29,6 +29,7 @@ #include "C4ObjectPtr.h" #include "C4InputValidation.h" #include "C4Id.h" +#include "StdMeshUpdate.h" class C4Def; @@ -110,6 +111,7 @@ protected: C4Def *pDef; // definition of dead graphics char Name[C4MaxName+1]; // name of graphics C4DefGraphicsPtrBackup *pNext; // next member of linked list + StdMeshMaterialUpdate MeshMaterialUpdate; // Backup of dead mesh materials public: C4DefGraphicsPtrBackup(C4DefGraphics *pSourceGraphics); // ctor @@ -117,6 +119,9 @@ public: void AssignUpdate(C4DefGraphics *pNewGraphics); // update all game objects with new graphics pointers void AssignRemoval(); // remove graphics of this def from all game objects + +private: + void UpdateMeshMaterials(); }; // Helper to compile C4DefGraphics-Pointer @@ -136,6 +141,7 @@ public: // graphics overlay used to attach additional graphics to objects class C4GraphicsOverlay { + friend class C4DefGraphicsPtrBackup; public: enum Mode { diff --git a/src/lib/StdMesh.h b/src/lib/StdMesh.h index 8f4095533..35e13e24c 100644 --- a/src/lib/StdMesh.h +++ b/src/lib/StdMesh.h @@ -234,6 +234,7 @@ class StdSubMesh { friend class StdMesh; friend class StdMeshLoader; + friend class StdMeshMaterialUpdate; public: // Remember bone assignments for vertices class Vertex: public StdMeshVertex @@ -262,6 +263,8 @@ private: class StdMesh { friend class StdMeshLoader; + friend class StdMeshMaterialUpdate; + StdMesh(); public: ~StdMesh(); @@ -300,6 +303,7 @@ private: class StdSubMeshInstance { friend class StdMeshInstance; + friend class StdMeshMaterialUpdate; public: StdSubMeshInstance(const StdSubMesh& submesh); @@ -354,6 +358,7 @@ private: class StdMeshInstance { + friend class StdMeshMaterialUpdate; public: StdMeshInstance(const StdMesh& mesh); ~StdMeshInstance(); @@ -419,8 +424,6 @@ public: NewFunc newfunc; }; - - template struct ID: IDBase { diff --git a/src/lib/StdMeshMaterial.cpp b/src/lib/StdMeshMaterial.cpp index b0707a2ad..cefa3d306 100644 --- a/src/lib/StdMeshMaterial.cpp +++ b/src/lib/StdMeshMaterial.cpp @@ -2,7 +2,7 @@ * OpenClonk, http://www.openclonk.org * * Copyright (c) 2009 Mark Haßelbusch - * Copyright (c) 2009-2010 Armin Burgmeier + * Copyright (c) 2009-2011 Armin Burgmeier * Copyright (c) 2009 Günther Brammer * Copyright (c) 2010 Benjamin Herr * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de @@ -21,6 +21,7 @@ #include "C4Include.h" #include +#include #include #include @@ -1037,4 +1038,10 @@ const StdMeshMaterial* StdMeshMatManager::GetMaterial(const char* material_name) return &iter->second; } +StdMeshMatManager::Iterator StdMeshMatManager::Remove(const Iterator& iter, StdMeshMaterialUpdate* update) +{ + if(update) update->Add(&*iter); + return Iterator(Materials.erase(iter.iter_)); +} + StdMeshMatManager MeshMaterialManager; diff --git a/src/lib/StdMeshMaterial.h b/src/lib/StdMeshMaterial.h index 7c2cbcd3e..0007a30c5 100644 --- a/src/lib/StdMeshMaterial.h +++ b/src/lib/StdMeshMaterial.h @@ -1,7 +1,7 @@ /* * OpenClonk, http://www.openclonk.org * - * Copyright (c) 2009-2010 Armin Burgmeier + * Copyright (c) 2009-2011 Armin Burgmeier * Copyright (c) 2010 Benjamin Herr * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de * @@ -320,7 +320,29 @@ public: class StdMeshMatManager { + friend class StdMeshMaterialUpdate; +private: + typedef std::map MaterialMap; + public: + class Iterator + { + friend class StdMeshMatManager; + public: + Iterator(const MaterialMap::iterator& iter): iter_(iter) {} + Iterator(const Iterator& iter): iter_(iter.iter_) {} + + Iterator operator=(const Iterator& iter) { iter_ = iter.iter_; return *this; } + Iterator& operator++() { ++iter_; return *this; } + bool operator==(const Iterator& other) const { return iter_ == other.iter_; } + bool operator!=(const Iterator& other) const { return iter_ != other.iter_; } + + const StdMeshMaterial& operator*() const { return iter_->second; } + const StdMeshMaterial* operator->() const { return &iter_->second; } + private: + MaterialMap::iterator iter_; + }; + // Remove all materials from manager. Make sure there is no StdMesh // referencing any out there before calling this. void Clear(); @@ -334,8 +356,12 @@ public: // Get material by name. NULL if there is no such material with this name. const StdMeshMaterial* GetMaterial(const char* material_name) const; + Iterator Begin() { return Iterator(Materials.begin()); } + Iterator End() { return Iterator(Materials.end()); } + Iterator Remove(const Iterator& iter, class StdMeshMaterialUpdate* update); + private: - std::map Materials; + MaterialMap Materials; }; extern StdMeshMatManager MeshMaterialManager; diff --git a/src/lib/StdMeshUpdate.cpp b/src/lib/StdMeshUpdate.cpp new file mode 100644 index 000000000..79b1f616c --- /dev/null +++ b/src/lib/StdMeshUpdate.cpp @@ -0,0 +1,91 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2009-2011 Armin Burgmeier + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ + +#include "C4Include.h" +#include +#include +#include + +StdMeshMaterialUpdate::StdMeshMaterialUpdate(StdMeshMatManager& manager): + MaterialManager(manager) +{ +} + +void StdMeshMaterialUpdate::Update(StdMesh* mesh) const +{ + for(std::vector::iterator iter = mesh->SubMeshes.begin(); iter != mesh->SubMeshes.end(); ++iter) + { + std::map::const_iterator mat_iter = Materials.find(iter->Material); + if(mat_iter != Materials.end()) + { + const StdMeshMaterial* new_material = MaterialManager.GetMaterial(mat_iter->second.Name.getData()); + + if(new_material) + { + iter->Material = new_material; + } + else + { + // If no replacement material is available, then re-insert the previous + // material into the material map. This is mainly just to keep things + // going - next time the scenario will be started the mesh will fail + // to load because the material cannot be found. + MaterialManager.Materials[mat_iter->second.Name] = mat_iter->second; + iter->Material = MaterialManager.GetMaterial(mat_iter->second.Name.getData()); + } + } + } +} + +void StdMeshMaterialUpdate::Update(StdMeshInstance* instance) const +{ + UpdateSingle(instance); + + for(StdMeshInstance::AttachedMeshIter iter = instance->AttachedMeshesBegin(); iter != instance->AttachedMeshesEnd(); ++iter) + Update((*iter)->Child); +} + +void StdMeshMaterialUpdate::Cancel() const +{ + // Reset all materials in manager + for(std::map::const_iterator iter = Materials.begin(); iter != Materials.end(); ++iter) + MaterialManager.Materials[iter->second.Name] = iter->second; +} + +void StdMeshMaterialUpdate::UpdateSingle(StdMeshInstance* instance) const +{ + for(unsigned int i = 0; i < instance->SubMeshInstances.size(); ++i) //std::vector::iterator iter = instance->SubMeshInstances->begin(); iter != instance->SubMeshInstances->end(); ++iter) + { + StdSubMeshInstance* sub_instance = instance->SubMeshInstances[i]; + std::map::const_iterator mat_iter = Materials.find(sub_instance->Material); + if(mat_iter != Materials.end()) + { + // Material needs to be updated + const StdMeshMaterial* new_material = MaterialManager.GetMaterial(mat_iter->second.Name.getData()); + // If new material is not available, fall back to StdMesh (definition) material + if(!new_material) new_material = instance->Mesh.GetSubMesh(i).Material; + sub_instance->SetMaterial(*new_material); + } + } + std::vector SubMeshInstances; +} + +void StdMeshMaterialUpdate::Add(const StdMeshMaterial* material) +{ + assert(Materials.find(material) == Materials.end()); + Materials[material] = *material; +} diff --git a/src/lib/StdMeshUpdate.h b/src/lib/StdMeshUpdate.h new file mode 100644 index 000000000..f227282d0 --- /dev/null +++ b/src/lib/StdMeshUpdate.h @@ -0,0 +1,50 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2009-2011 Armin Burgmeier + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ + +#ifndef INC_StdMeshUpdate +#define INC_StdMeshUpdate + +#include + +// This is a helper class to fix pointers after an update of StdMeshMaterials. +// To update one or more materials, remove them from the MaterialManager with +// erase(), then add new materials, then run Update() on all StdMeshes. +// Afterwards, run Update on all StdMeshInstances. +// If Cancel() is called before any Update() call then the Update() calls +// will reset all materials to what they have been before they were removed +// from the material manager. +class StdMeshMaterialUpdate +{ + friend class StdMeshMatManager; // calls Add() for each removed material +public: + StdMeshMaterialUpdate(StdMeshMatManager& manager); + + void Update(StdMesh* mesh) const; + void Update(StdMeshInstance* instance) const; + + void Cancel() const; + +private: + void UpdateSingle(StdMeshInstance* instance) const; + + void Add(const StdMeshMaterial* material); + + StdMeshMatManager& MaterialManager; + std::map Materials; +}; + +#endif