]> Dogcows Code - chaz/yoink/blob - src/Moof/Texture.cc
extreme refactoring
[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 <cstdlib>
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
42 namespace Mf {
43
44
45 /**
46 * The texture implementation just contains all the information about the image
47 * which is worth having in memory. The image data itself is not worth keeping
48 * in memory if the texture has been loaded to GL, but the name of the resource
49 * is retained so that it can be reloaded if necessary. The implementation is a
50 * mippleton so that multiple texture objects can share the same internal
51 * objects and avoid having duplicate textures loaded to GL.
52 */
53
54 class Texture::TextureImpl : public Mippleton<TextureImpl>
55 {
56
57 /**
58 * Delete the texture (if it is loaded) from GL.
59 */
60
61 void unloadFromGL()
62 {
63 if (object_)
64 {
65 glDeleteTextures(1, &object_);
66 object_ = 0;
67 }
68 }
69
70 /**
71 * If the GL context was recreated, we probably need to reload the texture.
72 * This may involve reading it from disk again, but hopefully the OS was
73 * smart enough to cache it if the client has plenty of RAM.
74 */
75
76 void contextRecreated(const Notification& note)
77 {
78 unloadFromGL();
79 uploadToGL();
80 }
81
82 /**
83 * This is a helper method used by some of the texture loading code. It
84 * returns the first power of two which is greater than the input value.
85 */
86
87 static int powerOfTwo(int input)
88 {
89 int value = 1;
90
91 while (value < input)
92 {
93 value <<= 1;
94 }
95 return value;
96 }
97
98 public:
99
100 /**
101 * Construction is initialization.
102 */
103
104 explicit TextureImpl(const std::string& name) :
105 Mippleton<TextureImpl>(name),
106 width_(0),
107 height_(0),
108 mode_(0),
109 minFilter_(GL_NEAREST),
110 maxFilter_(GL_NEAREST),
111 wrapU_(GL_CLAMP),
112 wrapV_(GL_CLAMP),
113 object_(0)
114 {
115 uploadToGL();
116
117 // we want to know when the GL context is recreated
118 Dispatcher::instance().addHandler("video.context_recreated",
119 boost::bind(&TextureImpl::contextRecreated, this, _1), this);
120 }
121
122 ~TextureImpl()
123 {
124 unloadFromGL();
125
126 Dispatcher::instance().removeHandler(this);
127 }
128
129
130 /**
131 * Adapted from some public domain code. This stuff is common enough that
132 * it really should be included in SDL_image... We need this because images
133 * loaded with SDL_image aren't exactly GL-ready right out of the box. This
134 * method makes them ready.
135 */
136
137 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
138 {
139 int w = powerOfTwo(surface->w);
140 int h = powerOfTwo(surface->h);
141
142 // 1. OpenGL images must (generally) have dimensions of a power-of-two.
143 // If this one doesn't, we can at least be more friendly by expanding
144 // the dimensions so that they are, though there will be some empty
145 // space within the range of normal texture coordinates. It's better of
146 // textures are the right size to begin with.
147
148 SDL_Surface* image = SDL_CreateRGBSurface
149 (
150 SDL_SWSURFACE,
151 w, h,
152 32,
153 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
154 0x000000FF,
155 0x0000FF00,
156 0x00FF0000,
157 0xFF000000
158 #else
159 0xFF000000,
160 0x00FF0000,
161 0x0000FF00,
162 0x000000FF
163 #endif
164 );
165
166 if (!image)
167 {
168 return 0;
169 }
170
171 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
172 Uint8 savedAlpha = surface->format->alpha;
173 if (savedFlags & SDL_SRCALPHA)
174 {
175 SDL_SetAlpha(surface, 0, 0);
176 }
177
178 SDL_Rect srcArea, destArea;
179 srcArea.x = 0; destArea.x = 0;
180 srcArea.y = 0; destArea.y = h - surface->h;
181 srcArea.w = surface->w;
182 srcArea.h = surface->h;
183 SDL_BlitSurface(surface, &srcArea, image, &destArea);
184
185 if (savedFlags & SDL_SRCALPHA)
186 {
187 SDL_SetAlpha(surface, savedFlags, savedAlpha);
188 }
189
190 // 2. OpenGL textures make more sense within the coordinate system when
191 // they are "upside down," so let's flip it.
192
193 Uint8 line[image->pitch];
194
195 Uint8 *pixels = static_cast<Uint8*>(image->pixels);
196 Uint16 pitch = image->pitch;
197 int ybegin = 0;
198 int yend = image->h - 1;
199
200 if (SDL_MUSTLOCK(image)) SDL_LockSurface(image);
201 while (ybegin < yend)
202 {
203 memcpy(line, pixels + pitch * ybegin, pitch);
204 memcpy(pixels + pitch * ybegin, pixels + pitch * yend, pitch);
205 memcpy(pixels + pitch * yend, line, pitch);
206 ybegin++;
207 yend--;
208 }
209 if (SDL_MUSTLOCK(image)) SDL_UnlockSurface(image);
210
211 return image;
212 }
213
214 /**
215 * Use SDL_image to load images from file. A surface with the image data is
216 * returned.
217 * @return Image data.
218 */
219
220 SDL_Surface* loadImageData()
221 {
222 SDL_Surface* surface;
223
224 surface = IMG_Load(Texture::getPathToResource(getName()).c_str());
225
226 if (!surface)
227 {
228 throw Texture::Exception("loading failed");
229 }
230
231 SDL_Surface* temp = prepareImageForGL(surface);
232 SDL_FreeSurface(surface);
233
234 if (!temp)
235 {
236 throw Texture::Exception("image couldn't be prepared for GL");
237 }
238
239 if (temp->format->BytesPerPixel == 3)
240 {
241 mode_ = GL_RGB;
242 }
243 else if (temp->format->BytesPerPixel == 4)
244 {
245 mode_ = GL_RGBA;
246 }
247 else
248 {
249 SDL_FreeSurface(temp);
250 throw Texture::Exception("image is not the required 24 or 32 bpp");
251 }
252
253 width_ = temp->w;
254 height_ = temp->h;
255
256 return temp;
257 }
258
259
260 /**
261 * Upload the image to GL so that it will be accessible by a much more
262 * manageable handle and hopefully reside in video memory.
263 */
264
265 void uploadToGL()
266 {
267 if (object_)
268 {
269 // Already loaded.
270 return;
271 }
272
273 SDL_Surface* imageData = loadImageData();
274
275 glGenTextures(1, &object_);
276 glBindTexture(GL_TEXTURE_2D, object_);
277
278 glTexImage2D
279 (
280 GL_TEXTURE_2D,
281 0,
282 mode_,
283 imageData->w,
284 imageData->h,
285 0,
286 mode_,
287 GL_UNSIGNED_BYTE,
288 imageData->pixels
289 );
290
291 setProperties();
292
293 SDL_FreeSurface(imageData);
294 }
295
296
297 /**
298 * Sets some texture properties such as the filters and external coordinate
299 * behavior.
300 */
301
302 void setProperties()
303 {
304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_);
305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter_);
306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapU_);
307 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapV_);
308 }
309
310
311 unsigned width_; ///< Horizontal dimension of the image.
312 unsigned height_; ///< Vertical dimension.
313
314 GLuint mode_; ///< Depth of the image, GL_RGB or GL_RGBA.
315 GLuint minFilter_; ///< Filter.
316 GLuint maxFilter_; ///< Filter.
317 GLuint wrapU_; ///< Wrapping behavior horizontally.
318 GLuint wrapV_; ///< Wrapping behavior vertically.
319 GLuint object_; ///< GL texture handle.
320 };
321
322
323 Texture::Texture(const std::string& name) :
324 // pass through
325 impl_(Texture::TextureImpl::retain(name), &Texture::TextureImpl::release)
326 {}
327
328
329 /**
330 * Bind the GL texture for mapping, etc.
331 */
332
333 void Texture::bind()
334 {
335 glBindTexture(GL_TEXTURE_2D, getObject());
336 }
337
338
339 /**
340 * Get the texture object, for the curious.
341 */
342
343 GLuint Texture::getObject()
344 {
345 // pass through
346 return impl_->object_;
347 }
348
349
350 unsigned Texture::getWidth()
351 {
352 // pass through
353 return impl_->width_;
354 }
355
356 unsigned Texture::getHeight()
357 {
358 // pass through
359 return impl_->height_;
360 }
361
362
363 void Texture::setMinFilter(GLuint filter)
364 {
365 impl_->minFilter_ = filter;
366 }
367
368 void Texture::setMaxFilter(GLuint filter)
369 {
370 impl_->maxFilter_ = filter;
371 }
372
373 void Texture::setWrapU(GLuint wrap)
374 {
375 impl_->wrapU_ = wrap;
376 }
377
378 void Texture::setWrapV(GLuint wrap)
379 {
380 impl_->wrapV_ = wrap;
381 }
382
383
384 void Texture::applyChanges()
385 {
386 bind();
387 impl_->setProperties();
388 }
389
390
391 std::string Texture::getPathToResource(const std::string& name)
392 {
393 // TODO since this is a generic library class, more than PNG should be
394 // supported
395 return Resource::getPathToResource("textures/" + name + ".png");
396 }
397
398
399 } // namespace Mf
400
401 /** vim: set ts=4 sw=4 tw=80: *************************************************/
402
This page took 0.047541 seconds and 4 git commands to generate.