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