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 "TextureBrowserView.h"
21 
22 #include "Renderer/GL.h"
23 #include "PreferenceManager.h"
24 #include "Preferences.h"
25 #include "Assets/Texture.h"
26 #include "Assets/TextureCollection.h"
27 #include "Renderer/FontManager.h"
28 #include "Renderer/Shaders.h"
29 #include "Renderer/ShaderManager.h"
30 #include "Renderer/TextureFont.h"
31 #include "Renderer/VertexArray.h"
32 #include "View/TextureSelectedCommand.h"
33 
34 namespace TrenchBroom {
35     namespace View {
TextureCellData(Assets::Texture * i_texture,const Renderer::FontDescriptor & i_fontDescriptor)36         TextureCellData::TextureCellData(Assets::Texture* i_texture, const Renderer::FontDescriptor& i_fontDescriptor) :
37         texture(i_texture),
38         fontDescriptor(i_fontDescriptor) {}
39 
TextureBrowserView(wxWindow * parent,wxScrollBar * scrollBar,GLContextManager & contextManager,Assets::TextureManager & textureManager)40         TextureBrowserView::TextureBrowserView(wxWindow* parent,
41                                                wxScrollBar* scrollBar,
42                                                GLContextManager& contextManager,
43                                                Assets::TextureManager& textureManager) :
44         CellView(parent, contextManager, buildAttribs(), scrollBar),
45         m_textureManager(textureManager),
46         m_group(false),
47         m_hideUnused(false),
48         m_sortOrder(Assets::TextureManager::SortOrder_Name),
49         m_selectedTexture(NULL) {}
50 
~TextureBrowserView()51         TextureBrowserView::~TextureBrowserView() {
52             clear();
53         }
54 
setSortOrder(const Assets::TextureManager::SortOrder sortOrder)55         void TextureBrowserView::setSortOrder(const Assets::TextureManager::SortOrder sortOrder) {
56             if (sortOrder == m_sortOrder)
57                 return;
58             m_sortOrder = sortOrder;
59             reload();
60             Refresh();
61         }
62 
setGroup(const bool group)63         void TextureBrowserView::setGroup(const bool group) {
64             if (group == m_group)
65                 return;
66             m_group = group;
67             reload();
68             Refresh();
69         }
70 
setHideUnused(const bool hideUnused)71         void TextureBrowserView::setHideUnused(const bool hideUnused) {
72             if (hideUnused == m_hideUnused)
73                 return;
74             m_hideUnused = hideUnused;
75             reload();
76             Refresh();
77         }
78 
setFilterText(const String & filterText)79         void TextureBrowserView::setFilterText(const String& filterText) {
80             if (filterText == m_filterText)
81                 return;
82             m_filterText = filterText;
83             reload();
84             Refresh();
85         }
86 
selectedTexture() const87         Assets::Texture* TextureBrowserView::selectedTexture() const {
88             return m_selectedTexture;
89         }
90 
setSelectedTexture(Assets::Texture * selectedTexture)91         void TextureBrowserView::setSelectedTexture(Assets::Texture* selectedTexture) {
92             if (m_selectedTexture == selectedTexture)
93                 return;
94             m_selectedTexture = selectedTexture;
95             Refresh();
96         }
97 
doInitLayout(Layout & layout)98         void TextureBrowserView::doInitLayout(Layout& layout) {
99             const float scaleFactor = pref(Preferences::TextureBrowserIconSize);
100 
101             layout.setOuterMargin(5.0f);
102             layout.setGroupMargin(5.0f);
103             layout.setRowMargin(5.0f);
104             layout.setCellMargin(5.0f);
105             layout.setTitleMargin(2.0f);
106             layout.setCellWidth(scaleFactor * 64.0f, scaleFactor * 64.0f);
107             layout.setCellHeight(scaleFactor * 64.0f, scaleFactor * 128.0f);
108         }
109 
doReloadLayout(Layout & layout)110         void TextureBrowserView::doReloadLayout(Layout& layout) {
111             const IO::Path& fontPath = pref(Preferences::RendererFontPath());
112             int fontSize = pref(Preferences::BrowserFontSize);
113             assert(fontSize > 0);
114 
115             const Renderer::FontDescriptor font(fontPath, static_cast<size_t>(fontSize));
116 
117             if (m_group) {
118                 const Assets::TextureManager::GroupList& groups = m_textureManager.groups(m_sortOrder);
119                 Assets::TextureManager::GroupList::const_iterator gIt, gEnd;
120                 for (gIt = groups.begin(), gEnd = groups.end(); gIt != gEnd; ++gIt) {
121                     const Assets::TextureCollection* collection = gIt->first;
122                     const Assets::TextureList& textures = gIt->second;
123                     const IO::Path collectionPath(collection->name());
124 
125                     layout.addGroup(collectionPath.lastComponent().asString(), fontSize + 2.0f);
126 
127                     Assets::TextureList::const_iterator tIt, tEnd;
128                     for (tIt = textures.begin(), tEnd = textures.end(); tIt != tEnd; ++tIt) {
129                         Assets::Texture* texture = *tIt;
130                         addTextureToLayout(layout, texture, font);
131                     }
132                 }
133             } else {
134                 const Assets::TextureList& textures = m_textureManager.textures(m_sortOrder);
135                 Assets::TextureList::const_iterator it, end;
136                 for (it = textures.begin(), end = textures.end(); it != end; ++it) {
137                     Assets::Texture* texture = *it;
138                     addTextureToLayout(layout, texture, font);
139                 }
140             }
141         }
142 
addTextureToLayout(Layout & layout,Assets::Texture * texture,const Renderer::FontDescriptor & font)143         void TextureBrowserView::addTextureToLayout(Layout& layout, Assets::Texture* texture, const Renderer::FontDescriptor& font) {
144             if ((!m_hideUnused || texture->usageCount() > 0) &&
145                 (m_filterText.empty() || StringUtils::containsCaseInsensitive(texture->name(), m_filterText))) {
146                 const float maxCellWidth = layout.maxCellWidth();
147                 const Renderer::FontDescriptor actualFont = fontManager().selectFontSize(font, texture->name(), maxCellWidth, 5);
148                 const Vec2f actualSize = fontManager().font(actualFont).measure(texture->name());
149 
150                 const float scaleFactor = pref(Preferences::TextureBrowserIconSize);
151                 const size_t scaledTextureWidth = static_cast<size_t>(Math::round(scaleFactor * static_cast<float>(texture->width())));
152                 const size_t scaledTextureHeight = static_cast<size_t>(Math::round(scaleFactor * static_cast<float>(texture->height())));
153 
154                 layout.addItem(TextureCellData(texture, actualFont),
155                                scaledTextureWidth,
156                                scaledTextureHeight,
157                                actualSize.x(),
158                                font.size() + 2.0f);
159             }
160         }
161 
doClear()162         void TextureBrowserView::doClear() {}
163 
doRender(Layout & layout,const float y,const float height)164         void TextureBrowserView::doRender(Layout& layout, const float y, const float height) {
165             m_textureManager.commitChanges();
166 
167             const float viewLeft      = static_cast<float>(GetClientRect().GetLeft());
168             const float viewTop       = static_cast<float>(GetClientRect().GetBottom());
169             const float viewRight     = static_cast<float>(GetClientRect().GetRight());
170             const float viewBottom    = static_cast<float>(GetClientRect().GetTop());
171 
172             const Mat4x4f projection = orthoMatrix(-1.0f, 1.0f, viewLeft, viewTop, viewRight, viewBottom);
173             const Mat4x4f view = viewMatrix(Vec3f::NegZ, Vec3f::PosY) * translationMatrix(Vec3f(0.0f, 0.0f, 0.1f));
174             const Renderer::Transformation transformation(projection, view);
175 
176             Renderer::ActivateVbo activate(vertexVbo());
177 
178             glAssert(glDisable(GL_DEPTH_TEST));
179             glAssert(glFrontFace(GL_CCW));
180 
181             renderBounds(layout, y, height);
182             renderTextures(layout, y, height);
183             renderNames(layout, y, height);
184         }
185 
doShouldRenderFocusIndicator() const186         bool TextureBrowserView::doShouldRenderFocusIndicator() const {
187             return false;
188         }
189 
renderBounds(Layout & layout,const float y,const float height)190         void TextureBrowserView::renderBounds(Layout& layout, const float y, const float height) {
191             typedef Renderer::VertexSpecs::P2C4::Vertex BoundsVertex;
192             BoundsVertex::List vertices;
193 
194             for (size_t i = 0; i < layout.size(); ++i) {
195                 const Layout::Group& group = layout[i];
196                 if (group.intersectsY(y, height)) {
197                     for (size_t j = 0; j < group.size(); ++j) {
198                         const Layout::Group::Row& row = group[j];
199                         if (row.intersectsY(y, height)) {
200                             for (size_t k = 0; k < row.size(); ++k) {
201                                 const Layout::Group::Row::Cell& cell = row[k];
202                                 const LayoutBounds& bounds = cell.itemBounds();
203                                 const Assets::Texture* texture = cell.item().texture;
204                                 const Color& color = textureColor(*texture);
205                                 vertices.push_back(BoundsVertex(Vec2f(bounds.left() - 2.0f, height - (bounds.top() - 2.0f - y)), color));
206                                 vertices.push_back(BoundsVertex(Vec2f(bounds.left() - 2.0f, height - (bounds.bottom() + 2.0f - y)), color));
207                                 vertices.push_back(BoundsVertex(Vec2f(bounds.right() + 2.0f, height - (bounds.bottom() + 2.0f - y)), color));
208                                 vertices.push_back(BoundsVertex(Vec2f(bounds.right() + 2.0f, height - (bounds.top() - 2.0f - y)), color));
209                             }
210                         }
211                     }
212                 }
213             }
214 
215             Renderer::VertexArray vertexArray = Renderer::VertexArray::swap(vertices);
216             Renderer::ActiveShader shader(shaderManager(), Renderer::Shaders::TextureBrowserBorderShader);
217 
218             Renderer::ActivateVbo activate(vertexVbo());
219             vertexArray.prepare(vertexVbo());
220             vertexArray.render(GL_QUADS);
221         }
222 
textureColor(const Assets::Texture & texture) const223         const Color& TextureBrowserView::textureColor(const Assets::Texture& texture) const {
224             if (&texture == m_selectedTexture)
225                 return pref(Preferences::TextureBrowserSelectedColor);
226             if (texture.usageCount() > 0)
227                 return pref(Preferences::TextureBrowserUsedColor);
228             return pref(Preferences::TextureBrowserDefaultColor);
229         }
230 
renderTextures(Layout & layout,const float y,const float height)231         void TextureBrowserView::renderTextures(Layout& layout, const float y, const float height) {
232             typedef Renderer::VertexSpecs::P2T2::Vertex TextureVertex;
233             TextureVertex::List vertices(4);
234 
235             Renderer::ActiveShader shader(shaderManager(), Renderer::Shaders::TextureBrowserShader);
236             shader.set("ApplyTinting", false);
237             shader.set("Texture", 0);
238             shader.set("Brightness", pref(Preferences::Brightness));
239 
240             size_t num = 0;
241 
242             Renderer::ActivateVbo activate(vertexVbo());
243 
244             for (size_t i = 0; i < layout.size(); ++i) {
245                 const Layout::Group& group = layout[i];
246                 if (group.intersectsY(y, height)) {
247                     for (size_t j = 0; j < group.size(); ++j) {
248                         const Layout::Group::Row& row = group[j];
249                         if (row.intersectsY(y, height)) {
250                             for (size_t k = 0; k < row.size(); ++k) {
251                                 const Layout::Group::Row::Cell& cell = row[k];
252                                 const LayoutBounds& bounds = cell.itemBounds();
253                                 const Assets::Texture* texture = cell.item().texture;
254 
255                                 vertices[0] = TextureVertex(Vec2f(bounds.left(),  height - (bounds.top() - y)),    Vec2f(0.0f, 0.0f));
256                                 vertices[1] = TextureVertex(Vec2f(bounds.left(),  height - (bounds.bottom() - y)), Vec2f(0.0f, 1.0f));
257                                 vertices[2] = TextureVertex(Vec2f(bounds.right(), height - (bounds.bottom() - y)), Vec2f(1.0f, 1.0f));
258                                 vertices[3] = TextureVertex(Vec2f(bounds.right(), height - (bounds.top() - y)),    Vec2f(1.0f, 0.0f));
259 
260                                 Renderer::VertexArray vertexArray = Renderer::VertexArray::copy(vertices);
261 
262                                 shader.set("GrayScale", texture->overridden());
263                                 texture->activate();
264 
265                                 vertexArray.prepare(vertexVbo());
266                                 vertexArray.render(GL_QUADS);
267 
268                                 ++num;
269                             }
270                         }
271                     }
272                 }
273             }
274         }
275 
renderNames(Layout & layout,const float y,const float height)276         void TextureBrowserView::renderNames(Layout& layout, const float y, const float height) {
277             renderGroupTitleBackgrounds(layout, y, height);
278             renderStrings(layout, y, height);
279         }
280 
renderGroupTitleBackgrounds(Layout & layout,const float y,const float height)281         void TextureBrowserView::renderGroupTitleBackgrounds(Layout& layout, const float y, const float height) {
282             typedef Renderer::VertexSpecs::P2::Vertex Vertex;
283             Vertex::List vertices;
284 
285             for (size_t i = 0; i < layout.size(); ++i) {
286                 const Layout::Group& group = layout[i];
287                 if (group.intersectsY(y, height)) {
288                     const LayoutBounds titleBounds = layout.titleBoundsForVisibleRect(group, y, height);
289                     vertices.push_back(Vertex(Vec2f(titleBounds.left(), height - (titleBounds.top() - y))));
290                     vertices.push_back(Vertex(Vec2f(titleBounds.left(), height - (titleBounds.bottom() - y))));
291                     vertices.push_back(Vertex(Vec2f(titleBounds.right(), height - (titleBounds.bottom() - y))));
292                     vertices.push_back(Vertex(Vec2f(titleBounds.right(), height - (titleBounds.top() - y))));
293                 }
294             }
295 
296             Renderer::ActiveShader shader(shaderManager(), Renderer::Shaders::BrowserGroupShader);
297             shader.set("Color", pref(Preferences::BrowserGroupBackgroundColor));
298 
299             Renderer::VertexArray vertexArray = Renderer::VertexArray::swap(vertices);
300 
301             Renderer::ActivateVbo activate(vertexVbo());
302             vertexArray.prepare(vertexVbo());
303             vertexArray.render(GL_QUADS);
304         }
305 
renderStrings(Layout & layout,const float y,const float height)306         void TextureBrowserView::renderStrings(Layout& layout, const float y, const float height) {
307             typedef std::map<Renderer::FontDescriptor, Renderer::VertexArray> StringRendererMap;
308             StringRendererMap stringRenderers;
309 
310             Renderer::ActivateVbo activate(vertexVbo());
311 
312             { // create and upload all vertex arrays
313                 const StringMap stringVertices = collectStringVertices(layout, y, height);
314                 StringMap::const_iterator it, end;
315                 for (it = stringVertices.begin(), end = stringVertices.end(); it != end; ++it) {
316                     const Renderer::FontDescriptor& descriptor = it->first;
317                     const TextVertex::List& vertices = it->second;
318                     stringRenderers[descriptor] = Renderer::VertexArray::ref(vertices);
319                     stringRenderers[descriptor].prepare(vertexVbo());
320                 }
321             }
322 
323             Renderer::ActiveShader shader(shaderManager(), Renderer::Shaders::ColoredTextShader);
324             shader.set("Texture", 0);
325 
326             StringRendererMap::iterator it, end;
327             for (it = stringRenderers.begin(), end = stringRenderers.end(); it != end; ++it) {
328                 const Renderer::FontDescriptor& descriptor = it->first;
329                 Renderer::VertexArray& vertexArray = it->second;
330 
331                 Renderer::TextureFont& font = fontManager().font(descriptor);
332                 font.activate();
333                 vertexArray.render(GL_QUADS);
334                 font.deactivate();
335             }
336         }
337 
collectStringVertices(Layout & layout,const float y,const float height)338         TextureBrowserView::StringMap TextureBrowserView::collectStringVertices(Layout& layout, const float y, const float height) {
339             Renderer::FontDescriptor defaultDescriptor(pref(Preferences::RendererFontPath()),
340                                                        static_cast<size_t>(pref(Preferences::BrowserFontSize)));
341 
342             const Color::List textColor(1, pref(Preferences::BrowserTextColor));
343 
344             StringMap stringVertices;
345             for (size_t i = 0; i < layout.size(); ++i) {
346                 const Layout::Group& group = layout[i];
347                 if (group.intersectsY(y, height)) {
348                     const String& title = group.item();
349                     if (!title.empty()) {
350                         const LayoutBounds titleBounds = layout.titleBoundsForVisibleRect(group, y, height);
351                         const Vec2f offset(titleBounds.left() + 2.0f, height - (titleBounds.top() - y) - titleBounds.height());
352 
353                         Renderer::TextureFont& font = fontManager().font(defaultDescriptor);
354                         const Vec2f::List quads = font.quads(title, false, offset);
355                         const TextVertex::List titleVertices = TextVertex::fromLists(quads, quads, textColor, quads.size() / 2, 0, 2, 1, 2, 0, 0);
356                         TextVertex::List& vertices = stringVertices[defaultDescriptor];
357                         vertices.insert(vertices.end(), titleVertices.begin(), titleVertices.end());
358                     }
359 
360                     for (size_t j = 0; j < group.size(); ++j) {
361                         const Layout::Group::Row& row = group[j];
362                         if (row.intersectsY(y, height)) {
363                             for (unsigned int k = 0; k < row.size(); k++) {
364                                 const Layout::Group::Row::Cell& cell = row[k];
365                                 const LayoutBounds titleBounds = cell.titleBounds();
366                                 const Vec2f offset(titleBounds.left(), height - (titleBounds.top() - y) - titleBounds.height());
367 
368                                 Renderer::TextureFont& font = fontManager().font(cell.item().fontDescriptor);
369                                 const Vec2f::List quads = font.quads(cell.item().texture->name(), false, offset);
370                                 const TextVertex::List titleVertices = TextVertex::fromLists(quads, quads, textColor, quads.size() / 2, 0, 2, 1, 2, 0, 0);
371                                 TextVertex::List& vertices = stringVertices[cell.item().fontDescriptor];
372                                 vertices.insert(vertices.end(), titleVertices.begin(), titleVertices.end());
373                             }
374                         }
375                     }
376                 }
377             }
378 
379             return stringVertices;
380         }
381 
doLeftClick(Layout & layout,const float x,const float y)382         void TextureBrowserView::doLeftClick(Layout& layout, const float x, const float y) {
383             const Layout::Group::Row::Cell* result = NULL;
384             if (layout.cellAt(x, y, &result)) {
385                 if (!result->item().texture->overridden()) {
386                     Assets::Texture* texture = result->item().texture;
387 
388                     TextureSelectedCommand command;
389                     command.setTexture(texture);
390                     command.SetEventObject(this);
391                     command.SetId(GetId());
392                     ProcessEvent(command);
393 
394                     if (command.IsAllowed())
395                         m_selectedTexture = texture;
396 
397                     Refresh();
398                 }
399             }
400         }
401 
tooltip(const Layout::Group::Row::Cell & cell)402         wxString TextureBrowserView::tooltip(const Layout::Group::Row::Cell& cell) {
403             wxString tooltip;
404             tooltip << cell.item().texture->name() << "\n";
405             tooltip << cell.item().texture->width() << "x" << cell.item().texture->height();
406             return tooltip;
407         }
408     }
409 }
410