]> Dogcows Code - chaz/yoink/blob - src/Moof/Texture.cc
78dc0c8148b478c092f0375ac46c3ecff2d7f707
[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 "Library.hh"
38 #include "Log.hh"
39 #include "OpenGL.hh"
40 #include "Texture.hh"
41
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 Library<Impl>
56 {
57
58 /**
59 * Delete the texture (if it is loaded) from GL.
60 */
61
62 void unloadFromGL()
63 {
64 if (mObject)
65 {
66 if (mObject == globalObject_)
67 {
68 globalObject_ = 0;
69 }
70
71 glDeleteTextures(1, &mObject);
72 mObject = 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 mObject = globalObject_ = 0;
85 uploadToGL();
86 }
87
88 /**
89 * This is a helper method used by some of the texture loading code. It
90 * returns the first power of two which is greater than the input value.
91 */
92
93 static int powerOfTwo(int input)
94 {
95 int value = 1;
96
97 while (value < input)
98 {
99 value <<= 1;
100 }
101 return value;
102 }
103
104
105 static void flipSurface(SDL_Surface* image)
106 {
107 unsigned char* pixels = (Uint8*)(image->pixels);
108
109 unsigned pitch = image->pitch;
110 unsigned char line[pitch];
111
112 int yBegin = 0;
113 int yEnd = image->h - 1;
114
115 if (SDL_MUSTLOCK(image)) SDL_LockSurface(image);
116 while (yBegin < yEnd)
117 {
118 memcpy(line, pixels + pitch * yBegin, pitch);
119 memcpy(pixels + pitch * yBegin, pixels + pitch * yEnd, pitch);
120 memcpy(pixels + pitch * yEnd, line, pitch);
121 yBegin++;
122 yEnd--;
123 }
124 if (SDL_MUSTLOCK(image)) SDL_UnlockSurface(image);
125 }
126
127 public:
128
129 /**
130 * Construction is initialization.
131 */
132
133 explicit Impl(const std::string& name) :
134 Library<Impl>(name),
135 mContext(0),
136 mWidth(0),
137 mHeight(0),
138 mMode(0),
139 mMinFilter(GL_NEAREST),
140 mMagFilter(GL_NEAREST),
141 mWrapS(GL_CLAMP),
142 mWrapT(GL_CLAMP),
143 mObject(0)
144 {
145 loadFromFile();
146
147 // we want to know when the GL context is recreated
148 Dispatcher::getInstance().addHandler("video.context_recreated",
149 boost::bind(&Impl::contextRecreated, this, _1), this);
150 }
151
152 ~Impl()
153 {
154 if (mContext)
155 {
156 SDL_FreeSurface(mContext);
157 }
158
159 unloadFromGL();
160
161 Dispatcher::getInstance().removeHandler(this);
162 }
163
164
165 /**
166 * Adapted from some public domain code. This stuff is common enough that
167 * it really should be included in SDL_image... We need this because images
168 * loaded with SDL_image aren't exactly GL-ready right out of the box. This
169 * method makes them ready.
170 */
171
172 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
173 {
174 int w = powerOfTwo(surface->w);
175 int h = powerOfTwo(surface->h);
176
177 // 2. OpenGL textures make more sense within the coordinate system when
178 // they are "upside down," so let's flip it.
179
180 flipSurface(surface);
181
182 // 1. OpenGL images must (generally) have dimensions of a power-of-two.
183 // If this one doesn't, we can at least be more friendly by expanding
184 // the dimensions so that they are, though there will be some empty
185 // space within the range of normal texture coordinates. It's better if
186 // textures are the right size to begin with.
187
188 SDL_Surface* image = SDL_CreateRGBSurface
189 (
190 SDL_SWSURFACE,
191 w, h,
192 32,
193 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
194 0x000000FF,
195 0x0000FF00,
196 0x00FF0000,
197 0xFF000000
198 #else
199 0xFF000000,
200 0x00FF0000,
201 0x0000FF00,
202 0x000000FF
203 #endif
204 );
205
206 if (!image)
207 {
208 return 0;
209 }
210
211 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
212 Uint8 savedAlpha = surface->format->alpha;
213 if (savedFlags & SDL_SRCALPHA)
214 {
215 SDL_SetAlpha(surface, 0, 0);
216 }
217
218 SDL_Rect srcArea, destArea;
219 srcArea.x = 0; destArea.x = 0;
220 srcArea.y = 0; destArea.y = h - surface->h;
221 srcArea.w = surface->w;
222 srcArea.h = surface->h;
223 SDL_BlitSurface(surface, &srcArea, image, &destArea);
224
225 if (savedFlags & SDL_SRCALPHA)
226 {
227 SDL_SetAlpha(surface, savedFlags, savedAlpha);
228 }
229
230 return image;
231 }
232
233 /**
234 * Use SDL_image to load images from file. A surface with the image data is
235 * returned.
236 * @return Image data.
237 */
238
239 void loadFromFile()
240 {
241 SDL_Surface* surface;
242
243 surface = IMG_Load(Texture::getPath(getName()).c_str());
244
245 if (!surface)
246 {
247 logWarning("texture not found: %s", getName().c_str());
248 throw Exception(Exception::FILE_NOT_FOUND);
249 }
250
251 SDL_Surface* temp = prepareImageForGL(surface);
252 SDL_FreeSurface(surface);
253
254 if (!temp)
255 {
256 throw Exception(Exception::OPENGL_ERROR);
257 }
258
259 if (temp->format->BytesPerPixel == 3)
260 {
261 mMode = GL_RGB;
262 }
263 else if (temp->format->BytesPerPixel == 4)
264 {
265 mMode = GL_RGBA;
266 }
267 else
268 {
269 SDL_FreeSurface(temp);
270 throw Exception(Exception::BAD_IMAGE_FORMAT);
271 }
272
273 mWidth = temp->w;
274 mHeight = temp->h;
275
276 mContext = 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 (mObject)
288 {
289 // already loaded
290 return;
291 }
292
293 if (!mContext) loadFromFile();
294
295 glGenTextures(1, &mObject);
296 glBindTexture(GL_TEXTURE_2D, mObject);
297
298 glTexImage2D
299 //gluBuild2DMipmaps
300 (
301 GL_TEXTURE_2D,
302 0,
303 mMode,
304 //3,
305 mContext->w,
306 mContext->h,
307 0,
308 mMode,
309 GL_UNSIGNED_BYTE,
310 mContext->pixels
311 );
312
313 setProperties();
314
315 SDL_FreeSurface(mContext);
316 mContext = 0;
317 }
318
319
320 /**
321 * Sets some texture properties such as the filters and external coordinate
322 * behavior.
323 */
324
325 void setProperties()
326 {
327 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
328 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
329 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
330 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
331 }
332
333 inline void setMinFilter(GLuint filter)
334 {
335 bind();
336 mMinFilter = filter;
337 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
338 }
339
340 inline void setMagFilter(GLuint filter)
341 {
342 bind();
343 mMagFilter = filter;
344 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
345 }
346
347 inline void setWrapS(GLuint wrap)
348 {
349 bind();
350 mWrapS = wrap;
351 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
352 }
353
354 inline void setWrapT(GLuint wrap)
355 {
356 bind();
357 mWrapT = wrap;
358 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
359 }
360
361
362 inline void bind()
363 {
364 if (mObject == 0)
365 {
366 uploadToGL();
367 }
368 if (mObject != globalObject_)
369 {
370 glBindTexture(GL_TEXTURE_2D, mObject);
371 globalObject_ = mObject;
372 }
373 }
374
375
376 SDL_Surface* mContext;
377 unsigned mWidth; ///< Horizontal dimension of the image.
378 unsigned mHeight; ///< Vertical dimension.
379
380 GLuint mMode; ///< Depth of the image, GL_RGB or GL_RGBA.
381 GLuint mMinFilter; ///< Minifcation filter.
382 GLuint mMagFilter; ///< Magnification filter.
383 GLuint mWrapS; ///< Wrapping behavior horizontally.
384 GLuint mWrapT; ///< Wrapping behavior vertically.
385
386 GLuint mObject; ///< GL texture handle.
387 static GLuint globalObject_; ///< Global GL texture handle.
388 };
389
390 GLuint Texture::Impl::globalObject_ = 0;
391
392
393 Texture::Texture(const std::string& name) :
394 // pass through
395 mImpl(Texture::Impl::getInstance(name)) {}
396
397
398 /**
399 * Bind the GL texture for mapping, etc.
400 */
401
402 void Texture::bind() const
403 {
404 // pass through
405 mImpl->bind();
406 }
407
408
409 /**
410 * Get the texture object, for the curious.
411 */
412
413 GLuint Texture::getObject() const
414 {
415 // pass through
416 return mImpl->mObject;
417 }
418
419
420 void Texture::resetBind()
421 {
422 glBindTexture(GL_TEXTURE_2D, 0);
423 Impl::globalObject_ = 0;
424 }
425
426
427 unsigned Texture::getWidth() const
428 {
429 // pass through
430 return mImpl->mWidth;
431 }
432
433 unsigned Texture::getHeight() const
434 {
435 // pass through
436 return mImpl->mHeight;
437 }
438
439
440 void Texture::setMinFilter(GLuint filter)
441 {
442 // pass through
443 mImpl->setMinFilter(filter);
444 }
445
446 void Texture::setMagFilter(GLuint filter)
447 {
448 // pass through
449 mImpl->setMagFilter(filter);
450 }
451
452 void Texture::setWrapS(GLuint wrap)
453 {
454 // pass through
455 mImpl->setWrapS(wrap);
456 }
457
458 void Texture::setWrapT(GLuint wrap)
459 {
460 // pass through
461 mImpl->setWrapT(wrap);
462 }
463
464
465 std::string Texture::getPath(const std::string& name)
466 {
467 std::string path = Resource::getPath("textures/" + name + ".png");
468 return path;
469 }
470
471
472 } // namespace Mf
473
474 /** vim: set ts=4 sw=4 tw=80: *************************************************/
475
This page took 0.050573 seconds and 3 git commands to generate.