]> Dogcows Code - chaz/yoink/blob - src/Moof/Texture.cc
no more useless singleton class
[chaz/yoink] / src / Moof / Texture.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <cstring> // memcpy
30
31 #include <boost/bind.hpp>
32
33 #include <SDL/SDL.h>
34 #include <SDL/SDL_image.h>
35
36 #include "Dispatcher.hh"
37 #include "Mippleton.hh"
38 #include "OpenGL.hh"
39 #include "Texture.hh"
40
41 #include <iostream>
42
43 namespace Mf {
44
45
46 /**
47 * The texture implementation just contains all the information about the image
48 * which is worth having in memory. The image data itself is not worth keeping
49 * in memory if the texture has been loaded to GL, but the name of the resource
50 * is retained so that it can be reloaded if necessary. The implementation is a
51 * mippleton so that multiple texture objects can share the same internal
52 * objects and avoid having duplicate textures loaded to GL.
53 */
54
55 class Texture::Impl : public Mippleton<Impl>
56 {
57
58 /**
59 * Delete the texture (if it is loaded) from GL.
60 */
61
62 void unloadFromGL()
63 {
64 if (object_)
65 {
66 if (object_ == globalObject_)
67 {
68 globalObject_ = 0;
69 }
70
71 glDeleteTextures(1, &object_);
72 object_ = 0;
73 }
74 }
75
76 /**
77 * If the GL context was recreated, we need to reload the texture. This may
78 * involve reading it from disk again, but hopefully the OS was smart enough
79 * to cache it if the client has plenty of RAM.
80 */
81
82 void contextRecreated(const Notification* note)
83 {
84 std::cout << "context recrated!" << std::endl;
85 object_ = globalObject_ = 0;
86 uploadToGL();
87 }
88
89 /**
90 * This is a helper method used by some of the texture loading code. It
91 * returns the first power of two which is greater than the input value.
92 */
93
94 static int powerOfTwo(int input)
95 {
96 int value = 1;
97
98 while (value < input)
99 {
100 value <<= 1;
101 }
102 return value;
103 }
104
105
106 static void flipSurface(SDL_Surface* image)
107 {
108 unsigned char* pixels = (Uint8*)(image->pixels);
109
110 unsigned pitch = image->pitch;
111 unsigned char line[pitch];
112
113 int yBegin = 0;
114 int yEnd = image->h - 1;
115
116 if (SDL_MUSTLOCK(image)) SDL_LockSurface(image);
117 while (yBegin < yEnd)
118 {
119 memcpy(line, pixels + pitch * yBegin, pitch);
120 memcpy(pixels + pitch * yBegin, pixels + pitch * yEnd, pitch);
121 memcpy(pixels + pitch * yEnd, line, pitch);
122 yBegin++;
123 yEnd--;
124 }
125 if (SDL_MUSTLOCK(image)) SDL_UnlockSurface(image);
126 }
127
128 public:
129
130 /**
131 * Construction is initialization.
132 */
133
134 explicit Impl(const std::string& name) :
135 Mippleton<Impl>(name),
136 surface_(0),
137 width_(0),
138 height_(0),
139 mode_(0),
140 minFilter_(GL_NEAREST),
141 magFilter_(GL_NEAREST),
142 wrapS_(GL_CLAMP),
143 wrapT_(GL_CLAMP),
144 object_(0)
145 {
146 loadFromFile();
147
148 // we want to know when the GL context is recreated
149 Dispatcher::getInstance().addHandler("video.context_recreated",
150 boost::bind(&Impl::contextRecreated, this, _1), this);
151 }
152
153 ~Impl()
154 {
155 if (surface_)
156 {
157 SDL_FreeSurface(surface_);
158 }
159
160 unloadFromGL();
161
162 Dispatcher::getInstance().removeHandler(this);
163 }
164
165
166 /**
167 * Adapted from some public domain code. This stuff is common enough that
168 * it really should be included in SDL_image... We need this because images
169 * loaded with SDL_image aren't exactly GL-ready right out of the box. This
170 * method makes them ready.
171 */
172
173 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
174 {
175 int w = powerOfTwo(surface->w);
176 int h = powerOfTwo(surface->h);
177
178 // 2. OpenGL textures make more sense within the coordinate system when
179 // they are "upside down," so let's flip it.
180
181 flipSurface(surface);
182
183 // 1. OpenGL images must (generally) have dimensions of a power-of-two.
184 // If this one doesn't, we can at least be more friendly by expanding
185 // the dimensions so that they are, though there will be some empty
186 // space within the range of normal texture coordinates. It's better if
187 // textures are the right size to begin with.
188
189 SDL_Surface* image = SDL_CreateRGBSurface
190 (
191 SDL_SWSURFACE,
192 w, h,
193 32,
194 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
195 0x000000FF,
196 0x0000FF00,
197 0x00FF0000,
198 0xFF000000
199 #else
200 0xFF000000,
201 0x00FF0000,
202 0x0000FF00,
203 0x000000FF
204 #endif
205 );
206
207 if (!image)
208 {
209 return 0;
210 }
211
212 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
213 Uint8 savedAlpha = surface->format->alpha;
214 if (savedFlags & SDL_SRCALPHA)
215 {
216 SDL_SetAlpha(surface, 0, 0);
217 }
218
219 SDL_Rect srcArea, destArea;
220 srcArea.x = 0; destArea.x = 0;
221 srcArea.y = 0; destArea.y = h - surface->h;
222 srcArea.w = surface->w;
223 srcArea.h = surface->h;
224 SDL_BlitSurface(surface, &srcArea, image, &destArea);
225
226 if (savedFlags & SDL_SRCALPHA)
227 {
228 SDL_SetAlpha(surface, savedFlags, savedAlpha);
229 }
230
231 return image;
232 }
233
234 /**
235 * Use SDL_image to load images from file. A surface with the image data is
236 * returned.
237 * @return Image data.
238 */
239
240 void loadFromFile()
241 {
242 SDL_Surface* surface;
243
244 surface = IMG_Load(Texture::getPath(getName()).c_str());
245
246 if (!surface)
247 {
248 throw Texture::Exception("loading from file failed");
249 }
250
251 SDL_Surface* temp = prepareImageForGL(surface);
252 SDL_FreeSurface(surface);
253
254 if (!temp)
255 {
256 throw Texture::Exception("uploading to opengl failed");
257 }
258
259 if (temp->format->BytesPerPixel == 3)
260 {
261 mode_ = GL_RGB;
262 }
263 else if (temp->format->BytesPerPixel == 4)
264 {
265 mode_ = GL_RGBA;
266 }
267 else
268 {
269 SDL_FreeSurface(temp);
270 throw Texture::Exception("incompatible color mode");
271 }
272
273 width_ = temp->w;
274 height_ = temp->h;
275
276 surface_ = temp;
277 }
278
279
280 /**
281 * Upload the image to GL so that it will be accessible by a much more
282 * manageable handle and hopefully reside in video memory.
283 */
284
285 void uploadToGL()
286 {
287 if (object_)
288 {
289 // already loaded
290 return;
291 }
292
293 if (!surface_) loadFromFile();
294
295 glGenTextures(1, &object_);
296 glBindTexture(GL_TEXTURE_2D, object_);
297
298 glTexImage2D
299 (
300 GL_TEXTURE_2D,
301 0,
302 mode_,
303 surface_->w,
304 surface_->h,
305 0,
306 mode_,
307 GL_UNSIGNED_BYTE,
308 surface_->pixels
309 );
310
311 setProperties();
312
313 SDL_FreeSurface(surface_);
314 surface_ = 0;
315 }
316
317
318 /**
319 * Sets some texture properties such as the filters and external coordinate
320 * behavior.
321 */
322
323 void setProperties()
324 {
325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_);
326 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter_);
327 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS_);
328 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT_);
329 }
330
331 inline void setMinFilter(GLuint filter)
332 {
333 bind();
334 minFilter_ = filter;
335 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_);
336 }
337
338 inline void setMagFilter(GLuint filter)
339 {
340 bind();
341 magFilter_ = filter;
342 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter_);
343 }
344
345 inline void setWrapS(GLuint wrap)
346 {
347 bind();
348 wrapS_ = wrap;
349 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS_);
350 }
351
352 inline void setWrapT(GLuint wrap)
353 {
354 bind();
355 wrapT_ = wrap;
356 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT_);
357 }
358
359
360 inline void bind()
361 {
362 if (object_ == 0)
363 {
364 uploadToGL();
365 }
366 if (object_ != globalObject_)
367 {
368 glBindTexture(GL_TEXTURE_2D, object_);
369 globalObject_ = object_;
370 }
371 }
372
373
374 SDL_Surface* surface_;
375 unsigned width_; ///< Horizontal dimension of the image.
376 unsigned height_; ///< Vertical dimension.
377
378 GLuint mode_; ///< Depth of the image, GL_RGB or GL_RGBA.
379 GLuint minFilter_; ///< Minifcation filter.
380 GLuint magFilter_; ///< Magnification filter.
381 GLuint wrapS_; ///< Wrapping behavior horizontally.
382 GLuint wrapT_; ///< Wrapping behavior vertically.
383
384 GLuint object_; ///< GL texture handle.
385 static GLuint globalObject_; ///< Global GL texture handle.
386 };
387
388 GLuint Texture::Impl::globalObject_ = 0;
389
390
391 Texture::Texture(const std::string& name) :
392 // pass through
393 impl_(Texture::Impl::retain(name), &Texture::Impl::release) {}
394
395
396 /**
397 * Bind the GL texture for mapping, etc.
398 */
399
400 void Texture::bind() const
401 {
402 // pass through
403 impl_->bind();
404 }
405
406
407 /**
408 * Get the texture object, for the curious.
409 */
410
411 GLuint Texture::getObject() const
412 {
413 // pass through
414 return impl_->object_;
415 }
416
417
418 void Texture::resetBind()
419 {
420 glBindTexture(GL_TEXTURE_2D, 0);
421 Impl::globalObject_ = 0;
422 }
423
424
425 unsigned Texture::getWidth() const
426 {
427 // pass through
428 return impl_->width_;
429 }
430
431 unsigned Texture::getHeight() const
432 {
433 // pass through
434 return impl_->height_;
435 }
436
437
438 void Texture::setMinFilter(GLuint filter)
439 {
440 // pass through
441 impl_->setMinFilter(filter);
442 }
443
444 void Texture::setMagFilter(GLuint filter)
445 {
446 // pass through
447 impl_->setMagFilter(filter);
448 }
449
450 void Texture::setWrapS(GLuint wrap)
451 {
452 // pass through
453 impl_->setWrapS(wrap);
454 }
455
456 void Texture::setWrapT(GLuint wrap)
457 {
458 // pass through
459 impl_->setWrapT(wrap);
460 }
461
462
463 std::string Texture::getPath(const std::string& name)
464 {
465 std::string path = Resource::getPath("textures/" + name + ".png");
466 return path;
467 }
468
469
470 } // namespace Mf
471
472 /** vim: set ts=4 sw=4 tw=80: *************************************************/
473
This page took 0.057426 seconds and 5 git commands to generate.