1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2017 SuperTuxKart-Team
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 3
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 #include "graphics/stk_texture.hpp"
19 #include "config/user_config.hpp"
20 #include "graphics/central_settings.hpp"
21 #include "graphics/irr_driver.hpp"
22 #include "graphics/graphics_restrictions.hpp"
23 #include "graphics/stk_tex_manager.hpp"
24 #include "graphics/material.hpp"
25 #include "graphics/material_manager.hpp"
26 #include "guiengine/engine.hpp"
27 #include "utils/log.hpp"
28 #include "utils/string_utils.hpp"
29 
30 // ----------------------------------------------------------------------------
STKTexture(const std::string & path,TexConfig * tc,bool no_upload)31 STKTexture::STKTexture(const std::string& path, TexConfig* tc, bool no_upload)
32           : video::ITexture(path.c_str()), m_single_channel(false),
33             m_tex_config(NULL), m_texture_name(0), m_texture_size(0),
34             m_texture_image(NULL)
35 {
36     if (tc != NULL)
37     {
38         m_tex_config = new TexConfig(*tc);
39     }
40 #ifndef SERVER_ONLY
41     if (m_tex_config)
42     {
43         if (GUIEngine::isNoGraphics() ||
44             (!CVS->isDeferredEnabled()) || !CVS->isGLSL())
45         {
46             m_tex_config->m_srgb = false;
47         }
48     }
49     if (!CVS->isARBTextureSwizzleUsable())
50         m_single_channel = false;
51 #endif
52     reload(no_upload);
53 }   // STKTexture
54 
55 // ----------------------------------------------------------------------------
STKTexture(uint8_t * data,const std::string & name,unsigned int size,bool single_channel)56 STKTexture::STKTexture(uint8_t* data, const std::string& name, unsigned int size,
57                        bool single_channel)
58           : video::ITexture(name.c_str()), m_single_channel(single_channel),
59             m_tex_config(NULL), m_texture_name(0), m_texture_size(0),
60             m_texture_image(NULL)
61 {
62     m_size.Width = size;
63     m_size.Height = size;
64     m_orig_size = m_size;
65     reload(false/*no_upload*/, data);
66 }   // STKTexture
67 
68 // ----------------------------------------------------------------------------
STKTexture(video::IImage * img,const std::string & name)69 STKTexture::STKTexture(video::IImage* img, const std::string& name)
70           : video::ITexture(name.c_str()), m_single_channel(false),
71             m_tex_config(NULL), m_texture_name(0), m_texture_size(0),
72             m_texture_image(NULL)
73 {
74     reload(false/*no_upload*/, NULL/*preload_data*/, img);
75 }   // STKTexture
76 
77 // ----------------------------------------------------------------------------
~STKTexture()78 STKTexture::~STKTexture()
79 {
80 #ifndef SERVER_ONLY
81     if (m_texture_name != 0 && !GUIEngine::isNoGraphics())
82     {
83         glDeleteTextures(1, &m_texture_name);
84     }
85 #endif   // !SERVER_ONLY
86     if (m_texture_image != NULL)
87         m_texture_image->drop();
88     delete m_tex_config;
89 }   // ~STKTexture
90 
91 // ----------------------------------------------------------------------------
reload(bool no_upload,uint8_t * preload_data,video::IImage * preload_img)92 void STKTexture::reload(bool no_upload, uint8_t* preload_data,
93                         video::IImage* preload_img)
94 {
95     if (GUIEngine::isNoGraphics())
96     {
97         m_orig_size.Width = 2;
98         m_orig_size.Height = 2;
99         m_size = m_orig_size;
100         m_texture_name = 1;
101         if (preload_data)
102             delete[] preload_data;
103         if (preload_img)
104             preload_img->drop();
105         return;
106     }
107 #ifndef SERVER_ONLY
108 
109     video::IImage* orig_img = NULL;
110     uint8_t* data = preload_data;
111     if (data == NULL)
112     {
113         orig_img = preload_img ? preload_img :
114             irr_driver->getVideoDriver()->createImageFromFile(NamedPath);
115         if (orig_img == NULL)
116         {
117             return;
118         }
119 
120         if (orig_img->getDimension().Width  == 0 ||
121             orig_img->getDimension().Height == 0)
122         {
123             orig_img->drop();
124             return;
125         }
126         orig_img = resizeImage(orig_img, &m_orig_size, &m_size);
127         applyMask(orig_img);
128         data = orig_img ? (uint8_t*)orig_img->lock() : NULL;
129     }
130 
131     const unsigned int w = m_size.Width;
132     const unsigned int h = m_size.Height;
133     unsigned int format = m_single_channel ? GL_RED : GL_BGRA;
134     unsigned int internal_format = m_single_channel ? GL_R8 : isSrgb() ?
135         GL_SRGB8_ALPHA8 : GL_RGBA8;
136 
137     // GLES 2.0 specs doesn't allow GL_RGBA8 internal format
138 #if defined(USE_GLES2)
139     if (!CVS->isGLSL())
140     {
141         internal_format = GL_RGBA;
142     }
143 #endif
144 
145     formatConversion(data, &format, w, h);
146 
147     if (!no_upload)
148     {
149         const bool reload = m_texture_name != 0;
150         if (!reload)
151             glGenTextures(1, &m_texture_name);
152 
153         glBindTexture(GL_TEXTURE_2D, m_texture_name);
154         if (!reload)
155         {
156             if (m_single_channel)
157             {
158                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
159                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
160                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
161                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
162                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
163             }
164             glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
165                 GL_UNSIGNED_BYTE, data);
166         }
167         else
168         {
169             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format,
170                 GL_UNSIGNED_BYTE, data);
171         }
172         if (orig_img)
173             orig_img->unlock();
174         if (hasMipMaps())
175             glGenerateMipmap(GL_TEXTURE_2D);
176     }
177 
178     m_texture_size = w * h * (m_single_channel ? 1 : 4);
179     if (no_upload)
180         m_texture_image = orig_img;
181     else if (orig_img)
182         orig_img->drop();
183     else
184         delete[] data;
185 
186     if (!no_upload)
187         glBindTexture(GL_TEXTURE_2D, 0);
188 
189 #endif   // !SERVER_ONLY
190 }   // reload
191 
192 //-----------------------------------------------------------------------------
formatConversion(uint8_t * data,unsigned int * format,unsigned int w,unsigned int h) const193 void STKTexture::formatConversion(uint8_t* data, unsigned int* format,
194                                   unsigned int w, unsigned int h) const
195 {
196 #ifndef SERVER_ONLY
197 #if defined(USE_GLES2)
198     if (!m_single_channel)
199     {
200         if (format)
201             *format = GL_RGBA;
202         for (unsigned int i = 0; i < w * h; i++)
203         {
204             uint8_t tmp_val = data[i * 4];
205             data[i * 4] = data[i * 4 + 2];
206             data[i * 4 + 2] = tmp_val;
207         }
208     }
209 #endif
210     if (isPremulAlpha() && !m_single_channel)
211     {
212         for (unsigned int i = 0; i < w * h; i++)
213         {
214             float alpha = data[4 * i + 3];
215             if (alpha > 0.0f)
216             {
217                 alpha /= 255.0f;
218 
219                 if (CVS->isDeferredEnabled())
220                 {
221                     alpha = pow(alpha, 1.0f / 2.2f);
222                 }
223             }
224             data[i * 4] = (uint8_t)(data[i * 4] * alpha);
225             data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
226             data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
227         }
228     }
229 #endif   // !SERVER_ONLY
230 }   // formatConversion
231 
232 // ----------------------------------------------------------------------------
resizeImage(video::IImage * orig_img,core::dimension2du * orig_size,core::dimension2du * final_size) const233 video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
234                                        core::dimension2du* orig_size,
235                                        core::dimension2du* final_size) const
236 {
237     video::IImage* image = orig_img;
238 #ifndef SERVER_ONLY
239     if (image == NULL)
240         assert(orig_size && orig_size->Width > 0 && orig_size->Height > 0);
241 
242     video::IVideoDriver* driver = irr_driver->getVideoDriver();
243 
244     core::dimension2du img_size = image ? image->getDimension() : *orig_size;
245 
246     bool has_npot = !GraphicsRestrictions::isDisabled(
247                                 GraphicsRestrictions::GR_NPOT_TEXTURES) &&
248                                 driver->queryFeature(video::EVDF_TEXTURE_NPOT);
249 
250     core::dimension2du tex_size = img_size.getOptimalSize(!has_npot);
251     const core::dimension2du& max_size = driver->getDriverAttributes().
252                                   getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
253 
254     if (tex_size.Width > max_size.Width)
255         tex_size.Width = max_size.Width;
256     if (tex_size.Height > max_size.Height)
257         tex_size.Height = max_size.Height;
258 
259     if (orig_size && final_size)
260     {
261         *orig_size = img_size;
262         *final_size = tex_size;
263     }
264     if (image == NULL)
265         return NULL;
266 
267     if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
268         tex_size != img_size)
269     {
270         video::IImage* new_texture = driver->createImage(video::ECF_A8R8G8B8,
271                                                          tex_size);
272         if (tex_size != img_size)
273             image->copyToScaling(new_texture);
274         else
275             image->copyTo(new_texture);
276         image->drop();
277         image = new_texture;
278     }
279 
280 #endif   // !SERVER_ONLY
281     return image;
282 }   // resizeImage
283 
284 // ----------------------------------------------------------------------------
applyMask(video::IImage * orig_img)285 void STKTexture::applyMask(video::IImage* orig_img)
286 {
287 #ifndef SERVER_ONLY
288     Material* material = NULL;
289     if (material_manager)
290     {
291         material = material_manager->getMaterialFor(this);
292     }
293     if (material && !material->getAlphaMask().empty())
294     {
295         video::IImage* converted_mask = irr_driver->getVideoDriver()
296             ->createImageFromFile(material->getAlphaMask().c_str());
297         if (converted_mask == NULL)
298         {
299             Log::warn("STKTexture", "Applying mask failed for '%s'!",
300                 material->getAlphaMask().c_str());
301             return;
302         }
303         converted_mask = resizeImage(converted_mask);
304         if (converted_mask->lock())
305         {
306             const core::dimension2du& dim = orig_img->getDimension();
307             for (unsigned int x = 0; x < dim.Width; x++)
308             {
309                 for (unsigned int y = 0; y < dim.Height; y++)
310                 {
311                     video::SColor col = orig_img->getPixel(x, y);
312                     video::SColor alpha = converted_mask->getPixel(x, y);
313                     col.setAlpha(alpha.getRed());
314                     orig_img->setPixel(x, y, col, false);
315                 }   // for y
316             }   // for x
317         }
318         converted_mask->unlock();
319         converted_mask->drop();
320     }
321 #endif   // !SERVER_ONLY
322 }   // applyMask
323 
324 //-----------------------------------------------------------------------------
hasMipMaps() const325 bool STKTexture::hasMipMaps() const
326 {
327 #if defined(USE_GLES2)
328     return true;
329 #elif defined(SERVER_ONLY)
330     return false;
331 #else
332     return CVS->getGLSLVersion() >= 130;
333 #endif   // !SERVER_ONLY
334 }   // hasMipMaps
335 
336 //-----------------------------------------------------------------------------
lock(video::E_TEXTURE_LOCK_MODE mode,u32 mipmap_level)337 void* STKTexture::lock(video::E_TEXTURE_LOCK_MODE mode, u32 mipmap_level)
338 {
339     if (m_texture_image)
340         return m_texture_image->lock();
341 
342 #if !(defined(SERVER_ONLY) || defined(USE_GLES2))
343     uint8_t* pixels = new uint8_t[m_size.Width * m_size.Height * 4]();
344     GLint tmp_texture;
345     glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmp_texture);
346     glBindTexture(GL_TEXTURE_2D, m_texture_name);
347     glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
348     glBindTexture(GL_TEXTURE_2D, tmp_texture);
349     return pixels;
350 #endif   // !SERVER_ONLY
351     return NULL;
352 }   // lock
353 
354 //-----------------------------------------------------------------------------
isSrgb() const355 bool STKTexture::isSrgb() const
356 {
357     return m_tex_config && m_tex_config->m_srgb;
358 }   // isSrgb
359 
360 //-----------------------------------------------------------------------------
isPremulAlpha() const361 bool STKTexture::isPremulAlpha() const
362 {
363     return m_tex_config && m_tex_config->m_premul_alpha;
364 }   // isPremulAlpha
365 
366 //-----------------------------------------------------------------------------
isMeshTexture() const367 bool STKTexture::isMeshTexture() const
368 {
369     return m_tex_config && m_tex_config->m_mesh_tex;
370 }   // isMeshTexture
371