1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "TextureManager.h"
21 
22 #include "Exceptions.h"
23 #include "CollectionUtils.h"
24 #include "Logger.h"
25 #include "Assets/Texture.h"
26 #include "Assets/TextureCollection.h"
27 #include "Assets/TextureCollectionSpec.h"
28 #include "IO/TextureLoader.h"
29 
30 namespace TrenchBroom {
31     namespace Assets {
32         class CompareByName {
33         public:
CompareByName()34             CompareByName() {}
operator ()(const Texture * left,const Texture * right) const35             bool operator() (const Texture* left, const Texture* right) const {
36                 return left->name() < right->name();
37             }
38         };
39 
40         class CompareByUsage {
41         public:
operator ()(const Texture * left,const Texture * right) const42             bool operator() (const Texture* left, const Texture* right) const {
43                 if (left->usageCount() == right->usageCount())
44                     return left->name() < right->name();
45                 return left->usageCount() > right->usageCount();
46             }
47         };
48 
TextureManager(Logger * logger,int minFilter,int magFilter)49         TextureManager::TextureManager(Logger* logger, int minFilter, int magFilter) :
50         m_logger(logger),
51         m_loader(NULL),
52         m_minFilter(minFilter),
53         m_magFilter(magFilter),
54         m_resetTextureMode(false) {}
55 
~TextureManager()56         TextureManager::~TextureManager() {
57             clear();
58         }
59 
setBuiltinTextureCollections(const IO::Path::List & paths)60         void TextureManager::setBuiltinTextureCollections(const IO::Path::List& paths) {
61             clearBuiltinTextureCollections();
62 
63             TextureCollectionList newCollections;
64             TextureCollectionMap newCollectionsByName;
65 
66             try {
67                 IO::Path::List::const_iterator it, end;
68                 for (it = paths.begin(), end = paths.end(); it != end; ++it) {
69                     const IO::Path& path = *it;
70                     const TextureCollectionSpec spec(path.suffix(2).asString(), path);
71                     addTextureCollection(spec, newCollections, newCollectionsByName);
72                 }
73 
74                 using std::swap;
75                 std::swap(m_builtinCollections, newCollections);
76                 std::swap(m_builtinCollectionsByName, newCollectionsByName);
77 
78                 updateTextures();
79             } catch (...) {
80                 updateTextures();
81                 VectorUtils::deleteAll(newCollections);
82                 throw;
83             }
84         }
85 
addExternalTextureCollection(const TextureCollectionSpec & spec)86         void TextureManager::addExternalTextureCollection(const TextureCollectionSpec& spec) {
87             try {
88                 addTextureCollection(spec, m_externalCollections, m_externalCollectionsByName);
89                 updateTextures();
90             } catch (...) {
91                 TextureCollection* dummy = new TextureCollection(spec.name());
92                 m_externalCollections.push_back(dummy);
93                 m_externalCollectionsByName[spec.name()] = dummy;
94                 updateTextures();
95 
96                 throw;
97             }
98         }
99 
removeExternalTextureCollection(const String & name)100         void TextureManager::removeExternalTextureCollection(const String& name) {
101             removeTextureCollection(name, m_externalCollections, m_externalCollectionsByName);
102             updateTextures();
103         }
104 
moveExternalTextureCollectionUp(const String & name)105         void TextureManager::moveExternalTextureCollectionUp(const String& name) {
106             TextureCollectionMap::iterator it = m_externalCollectionsByName.find(name);
107             if (it == m_externalCollectionsByName.end())
108                 throw AssetException("Unknown external texture collection: '" + name + "'");
109 
110             TextureCollection* collection = it->second;
111             const size_t index = VectorUtils::indexOf(m_externalCollections, collection);
112             if (index == 0)
113                 throw AssetException("Could not move texture collection");
114 
115             using std::swap;
116             swap(m_externalCollections[index-1], m_externalCollections[index]);
117             updateTextures();
118         }
119 
moveExternalTextureCollectionDown(const String & name)120         void TextureManager::moveExternalTextureCollectionDown(const String& name) {
121             TextureCollectionMap::iterator it = m_externalCollectionsByName.find(name);
122             if (it == m_externalCollectionsByName.end())
123                 throw AssetException("Unknown external texture collection: '" + name + "'");
124 
125             TextureCollection* collection = it->second;
126             const size_t index = VectorUtils::indexOf(m_externalCollections, collection);
127             if (index == m_externalCollections.size() - 1)
128                 throw AssetException("Could not move texture collection");
129 
130             using std::swap;
131             swap(m_externalCollections[index+1], m_externalCollections[index]);
132             updateTextures();
133         }
134 
clear()135         void TextureManager::clear() {
136             VectorUtils::clearAndDelete(m_builtinCollections);
137             VectorUtils::clearAndDelete(m_externalCollections);
138             MapUtils::clearAndDelete(m_toRemove);
139 
140             m_toPrepare.clear();
141             m_builtinCollectionsByName.clear();
142             m_externalCollectionsByName.clear();
143             m_allCollections.clear();
144             m_texturesByName.clear();
145 
146             for (size_t i = 0; i < 2; ++i) {
147                 m_sortedTextures[i].clear();
148                 m_sortedGroups[i].clear();
149             }
150 
151             if (m_logger != NULL)
152                 m_logger->debug("Cleared texture collections");
153         }
154 
setTextureMode(const int minFilter,const int magFilter)155         void TextureManager::setTextureMode(const int minFilter, const int magFilter) {
156             m_minFilter = minFilter;
157             m_magFilter = magFilter;
158             m_resetTextureMode = true;
159         }
160 
setLoader(const IO::TextureLoader * loader)161         void TextureManager::setLoader(const IO::TextureLoader* loader) {
162             clear();
163             m_loader = loader;
164             updateTextures();
165         }
166 
commitChanges()167         void TextureManager::commitChanges() {
168             resetTextureMode();
169             prepare();
170             MapUtils::clearAndDelete(m_toRemove);
171         }
172 
texture(const String & name) const173         Texture* TextureManager::texture(const String& name) const {
174             TextureMap::const_iterator it = m_texturesByName.find(StringUtils::toLower(name));
175             if (it == m_texturesByName.end())
176                 return NULL;
177             return it->second;
178         }
179 
textures(const SortOrder sortOrder) const180         const TextureList& TextureManager::textures(const SortOrder sortOrder) const {
181             return m_sortedTextures[sortOrder];
182         }
183 
groups(const SortOrder sortOrder) const184         const TextureManager::GroupList& TextureManager::groups(const SortOrder sortOrder) const {
185             return m_sortedGroups[sortOrder];
186         }
187 
collections() const188         const TextureCollectionList& TextureManager::collections() const {
189             return m_allCollections;
190         }
191 
externalCollectionNames() const192         const StringList TextureManager::externalCollectionNames() const {
193             StringList names;
194             names.reserve(m_externalCollections.size());
195 
196             TextureCollectionList::const_iterator it, end;
197             for (it = m_externalCollections.begin(), end = m_externalCollections.end(); it != end; ++it) {
198                 const TextureCollection* collection = *it;
199                 names.push_back(collection->name());
200             }
201 
202             return names;
203         }
204 
addTextureCollection(const TextureCollectionSpec & spec,TextureCollectionList & collections,TextureCollectionMap & collectionsByName)205         void TextureManager::addTextureCollection(const TextureCollectionSpec& spec, TextureCollectionList& collections, TextureCollectionMap& collectionsByName) {
206 
207             const String& name = spec.name();
208             if (collectionsByName.find(spec.name()) == collectionsByName.end()) {
209                 TextureCollection* collection = loadTextureCollection(spec);
210                 collections.push_back(collection);
211                 collectionsByName.insert(std::make_pair(name, collection));
212 
213                 m_toPrepare.insert(TextureCollectionMapEntry(name, collection));
214                 m_toRemove.erase(name);
215 
216                 if (m_logger != NULL)
217                     m_logger->debug("Added texture collection %s", name.c_str());
218             }
219         }
220 
removeTextureCollection(const String & name,TextureCollectionList & collections,TextureCollectionMap & collectionsByName)221         void TextureManager::removeTextureCollection(const String& name, TextureCollectionList& collections, TextureCollectionMap& collectionsByName) {
222             TextureCollectionMap::iterator it = collectionsByName.find(name);
223             if (it == collectionsByName.end())
224                 throw AssetException("Unknown external texture collection: '" + name + "'");
225 
226             TextureCollection* collection = it->second;
227             VectorUtils::erase(collections, collection);
228 
229             collectionsByName.erase(it);
230             m_toPrepare.erase(name);
231             m_toRemove.insert(TextureCollectionMapEntry(name, collection));
232 
233             if (m_logger != NULL)
234                 m_logger->debug("Removed texture collection '%s'", name.c_str());
235         }
236 
loadTextureCollection(const TextureCollectionSpec & spec) const237         TextureCollection* TextureManager::loadTextureCollection(const TextureCollectionSpec& spec) const {
238             assert(m_loader != NULL);
239             return m_loader->loadTextureCollection(spec);
240         }
241 
resetTextureMode()242         void TextureManager::resetTextureMode() {
243             if (m_resetTextureMode) {
244                 TextureCollectionList::const_iterator it, end;
245                 for (it = m_allCollections.begin(), end = m_allCollections.end(); it != end; ++it) {
246                     TextureCollection* collection = *it;
247                     collection->setTextureMode(m_minFilter, m_magFilter);
248                 }
249                 m_resetTextureMode = false;
250             }
251         }
252 
prepare()253         void TextureManager::prepare() {
254             TextureCollectionMap::const_iterator it, end;
255             for (it = m_toPrepare.begin(), end = m_toPrepare.end(); it != end; ++it) {
256                 TextureCollection* collection = it->second;
257                 collection->prepare(m_minFilter, m_magFilter);
258             }
259             m_toPrepare.clear();
260         }
261 
clearBuiltinTextureCollections()262         void TextureManager::clearBuiltinTextureCollections() {
263             m_toRemove.insert(m_builtinCollectionsByName.begin(), m_builtinCollectionsByName.end());
264             m_builtinCollections.clear();
265             m_builtinCollectionsByName.clear();
266 
267             if (m_logger != NULL)
268                 m_logger->debug("Cleared builtin texture collections");
269         }
270 
clearExternalTextureCollections()271         void TextureManager::clearExternalTextureCollections() {
272             m_toRemove.insert(m_externalCollectionsByName.begin(), m_externalCollectionsByName.end());
273             m_externalCollections.clear();
274             m_externalCollectionsByName.clear();
275 
276             if (m_logger != NULL)
277                 m_logger->debug("Cleared builtin texture collections");
278         }
279 
updateTextures()280         void TextureManager::updateTextures() {
281             m_allCollections = VectorUtils::concatenate(m_builtinCollections, m_externalCollections);
282             m_texturesByName.clear();
283             m_sortedGroups[SortOrder_Name].clear();
284             m_sortedGroups[SortOrder_Usage].clear();
285 
286             TextureCollectionList::iterator cIt, cEnd;
287             for (cIt = m_allCollections.begin(), cEnd = m_allCollections.end(); cIt != cEnd; ++cIt) {
288                 TextureCollection* collection = *cIt;
289                 const TextureList textures = collection->textures();
290 
291                 TextureList::const_iterator tIt, tEnd;
292                 for (tIt = textures.begin(), tEnd = textures.end(); tIt != tEnd; ++tIt) {
293                     Texture* texture = *tIt;
294                     const String key = StringUtils::toLower(texture->name());
295                     texture->setOverridden(false);
296 
297                     TextureMap::iterator mIt = m_texturesByName.find(key);
298                     if (mIt != m_texturesByName.end()) {
299                         mIt->second->setOverridden(true);
300                         mIt->second = texture;
301                     } else {
302                         m_texturesByName.insert(std::make_pair(key, texture));
303                     }
304                 }
305 
306                 const Group group = std::make_pair(collection, textures);
307                 m_sortedGroups[SortOrder_Name].push_back(group);
308                 m_sortedGroups[SortOrder_Usage].push_back(group);
309                 std::sort(m_sortedGroups[SortOrder_Name].back().second.begin(),
310                           m_sortedGroups[SortOrder_Name].back().second.end(),
311                           CompareByName());
312                 std::sort(m_sortedGroups[SortOrder_Usage].back().second.begin(),
313                           m_sortedGroups[SortOrder_Usage].back().second.end(),
314                           CompareByUsage());
315             }
316 
317             m_sortedTextures[SortOrder_Name] = m_sortedTextures[SortOrder_Usage] = textureList();
318             std::sort(m_sortedTextures[SortOrder_Name].begin(), m_sortedTextures[SortOrder_Name].end(), CompareByName());
319             std::sort(m_sortedTextures[SortOrder_Usage].begin(), m_sortedTextures[SortOrder_Usage].end(), CompareByUsage());
320         }
321 
textureList() const322         TextureList TextureManager::textureList() const {
323             TextureList result;
324             TextureCollectionList::const_iterator cIt, cEnd;
325             for (cIt = m_allCollections.begin(), cEnd = m_allCollections.end(); cIt != cEnd; ++cIt) {
326                 const TextureCollection* collection = *cIt;
327                 const TextureList textures = collection->textures();
328 
329                 TextureList::const_iterator tIt, tEnd;
330                 for (tIt = textures.begin(), tEnd = textures.end(); tIt != tEnd; ++tIt) {
331                     Texture* texture = *tIt;
332                     result.push_back(texture);
333                 }
334             }
335 
336             return result;
337         }
338     }
339 }
340