]> Dogcows Code - chaz/yoink/blob - src/Moof/Texture.cc
341b5598f13f9b8c7d42f93643f26ca3f4727568
[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 "Exception.hh"
38 #include "Library.hh"
39 #include "Log.hh"
40 #include "OpenGL.hh"
41 #include "Texture.hh"
42
43
44 namespace Mf {
45
46
47 /**
48 * The texture implementation just contains all the information about the image
49 * which is worth having in memory. The image data itself is not worth keeping
50 * in memory if the texture has been loaded to GL, but the name of the resource
51 * is retained so that it can be reloaded if necessary. The implementation is a
52 * mippleton so that multiple texture objects can share the same internal
53 * objects and avoid having duplicate textures loaded to GL.
54 */
55
56 class Texture::Impl : public Library<Impl>
57 {
58
59 /**
60 * Delete the texture (if it is loaded) from GL.
61 */
62
63 void unloadFromGL()
64 {
65 if (mObject)
66 {
67 if (mObject == globalObject_)
68 {
69 globalObject_ = 0;
70 }
71
72 glDeleteTextures(1, &mObject);
73 mObject = 0;
74 }
75 }
76
77 /**
78 * If the GL context was recreated, we need to reload the texture. This may
79 * involve reading it from disk again, but hopefully the OS was smart enough
80 * to cache it if the client has plenty of RAM.
81 */
82
83 void contextRecreated(const Notification* note)
84 {
85 mObject = 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 Library<Impl>(name),
136 mContext(0),
137 mWidth(0),
138 mHeight(0),
139 mMode(0),
140 mMinFilter(GL_NEAREST),
141 mMagFilter(GL_NEAREST),
142 mWrapS(GL_CLAMP),
143 mWrapT(GL_CLAMP),
144 mObject(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 (mContext)
156 {
157 SDL_FreeSurface(mContext);
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 logWarning("texture not found: %s", getName().c_str());
249 throw Exception(ErrorCode::FILE_NOT_FOUND, getName().c_str());
250 }
251
252 SDL_Surface* temp = prepareImageForGL(surface);
253 SDL_FreeSurface(surface);
254
255 if (!temp)
256 {
257 throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT);
258 }
259
260 if (temp->format->BytesPerPixel == 3)
261 {
262 mMode = GL_RGB;
263 }
264 else if (temp->format->BytesPerPixel == 4)
265 {
266 mMode = GL_RGBA;
267 }
268 else
269 {
270 SDL_FreeSurface(temp);
271 throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT);
272 }
273
274 mWidth = temp->w;
275 mHeight = temp->h;
276
277 mContext = temp;
278 }
279
280
281 /**
282 * Upload the image to GL so that it will be accessible by a much more
283 * manageable handle and hopefully reside in video memory.
284 */
285
286 void uploadToGL()
287 {
288 if (mObject)
289 {
290 // already loaded
291 return;
292 }
293
294 if (!mContext) loadFromFile();
295
296 glGenTextures(1, &mObject);
297 glBindTexture(GL_TEXTURE_2D, mObject);
298
299 glTexImage2D
300 //gluBuild2DMipmaps
301 (
302 GL_TEXTURE_2D,
303 0,
304 mMode,
305 //3,
306 mContext->w,
307 mContext->h,
308 0,
309 mMode,
310 GL_UNSIGNED_BYTE,
311 mContext->pixels
312 );
313
314 setProperties();
315
316 SDL_FreeSurface(mContext);
317 mContext = 0;
318 }
319
320
321 /**
322 * Sets some texture properties such as the filters and external coordinate
323 * behavior.
324 */
325
326 void setProperties()
327 {
328 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
329 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
330 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
331 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
332 }
333
334 inline void setMinFilter(GLuint filter)
335 {
336 bind();
337 mMinFilter = filter;
338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
339 }
340
341 inline void setMagFilter(GLuint filter)
342 {
343 bind();
344 mMagFilter = filter;
345 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
346 }
347
348 inline void setWrapS(GLuint wrap)
349 {
350 bind();
351 mWrapS = wrap;
352 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
353 }
354
355 inline void setWrapT(GLuint wrap)
356 {
357 bind();
358 mWrapT = wrap;
359 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
360 }
361
362
363 inline void bind()
364 {
365 if (mObject == 0)
366 {
367 uploadToGL();
368 }
369 if (mObject != globalObject_)
370 {
371 glBindTexture(GL_TEXTURE_2D, mObject);
372 globalObject_ = mObject;
373 }
374 }
375
376
377 SDL_Surface* mContext;
378 unsigned mWidth; ///< Horizontal dimension of the image.
379 unsigned mHeight; ///< Vertical dimension.
380
381 GLuint mMode; ///< Depth of the image, GL_RGB or GL_RGBA.
382 GLuint mMinFilter; ///< Minifcation filter.
383 GLuint mMagFilter; ///< Magnification filter.
384 GLuint mWrapS; ///< Wrapping behavior horizontally.
385 GLuint mWrapT; ///< Wrapping behavior vertically.
386
387 GLuint mObject; ///< GL texture handle.
388 static GLuint globalObject_; ///< Global GL texture handle.
389 };
390
391 GLuint Texture::Impl::globalObject_ = 0;
392
393
394 Texture::Texture(const std::string& name) :
395 // pass through
396 mImpl(Texture::Impl::getInstance(name)) {}
397
398
399 /**
400 * Bind the GL texture for mapping, etc.
401 */
402
403 void Texture::bind() const
404 {
405 // pass through
406 mImpl->bind();
407 }
408
409
410 /**
411 * Get the texture object, for the curious.
412 */
413
414 GLuint Texture::getObject() const
415 {
416 // pass through
417 return mImpl->mObject;
418 }
419
420
421 void Texture::resetBind()
422 {
423 glBindTexture(GL_TEXTURE_2D, 0);
424 Impl::globalObject_ = 0;
425 }
426
427
428 unsigned Texture::getWidth() const
429 {
430 // pass through
431 return mImpl->mWidth;
432 }
433
434 unsigned Texture::getHeight() const
435 {
436 // pass through
437 return mImpl->mHeight;
438 }
439
440
441 void Texture::setMinFilter(GLuint filter)
442 {
443 // pass through
444 mImpl->setMinFilter(filter);
445 }
446
447 void Texture::setMagFilter(GLuint filter)
448 {
449 // pass through
450 mImpl->setMagFilter(filter);
451 }
452
453 void Texture::setWrapS(GLuint wrap)
454 {
455 // pass through
456 mImpl->setWrapS(wrap);
457 }
458
459 void Texture::setWrapT(GLuint wrap)
460 {
461 // pass through
462 mImpl->setWrapT(wrap);
463 }
464
465
466 std::string Texture::getPath(const std::string& name)
467 {
468 std::string path = Resource::getPath("textures/" + name + ".png");
469 return path;
470 }
471
472
473 } // namespace Mf
474
475 /** vim: set ts=4 sw=4 tw=80: *************************************************/
476
This page took 0.061834 seconds and 3 git commands to generate.