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