]> Dogcows Code - chaz/yoink/blob - src/Moof/Texture.cc
removed logging from script to fix compile error
[chaz/yoink] / src / Moof / Texture.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include <cstdio> // FILE
13 #include <cstring> // strncmp
14
15 #include <boost/algorithm/string.hpp>
16 #include <boost/bind.hpp>
17
18 #include "Dispatch.hh"
19 #include "Error.hh"
20 #include "Manager.hh"
21 #include "Log.hh"
22 #include "OpenGL.hh"
23 #include "Script.hh"
24 #include "Texture.hh"
25 #include "Video.hh"
26
27
28 namespace Mf {
29
30
31 /**
32 * The texture implementation just contains all the information about the
33 * image which is worth having in memory. The image data itself is not
34 * worth keeping in memory if the texture has been loaded to GL, but the
35 * name of the resource is retained so that it can be reloaded if
36 * necessary. The implementation is a manager so that multiple texture
37 * objects can share the same internal objects and avoid having duplicate
38 * textures loaded to GL.
39 */
40
41 class Texture::Impl : public Manager<Impl>
42 {
43
44 /**
45 * Delete the texture (if it is loaded) from GL.
46 */
47
48 void unloadFromGL()
49 {
50 if (mObject)
51 {
52 if (mObject == gObject)
53 {
54 gObject = 0;
55 }
56
57 glDeleteTextures(1, &mObject);
58 mObject = 0;
59 }
60 }
61
62 /**
63 * If the GL context was recreated, we need to reload the texture.
64 * This may involve reading it from disk again, but hopefully the OS
65 * was smart enough to cache it if the client has plenty of RAM.
66 */
67
68 void contextRecreated()
69 {
70 mObject = gObject = 0;
71 uploadToGL();
72 }
73
74 /**
75 * This is a helper method used by some of the texture loading code.
76 * It returns the first power of two which is greater than the input
77 * value.
78 */
79
80 static int powerOfTwo(int input)
81 {
82 int value = 1;
83
84 while (value < input)
85 {
86 value <<= 1;
87 }
88 return value;
89 }
90
91
92 static void bindScriptConstants(Script& script)
93 {
94 Script::Slot g = script.globals();
95
96 g.setField("CLAMP", GL_CLAMP);
97 g.setField("REPEAT", GL_REPEAT);
98 g.setField("LINEAR", GL_LINEAR);
99 g.setField("NEAREST", GL_NEAREST);
100 g.setField("LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR);
101 g.setField("LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST);
102 g.setField("NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR);
103 g.setField("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST);
104 }
105
106 public:
107
108 /**
109 * Construction is initialization.
110 */
111
112 Impl() :
113 mMinFilter(GL_NEAREST),
114 mMagFilter(GL_NEAREST),
115 mWrapS(GL_CLAMP),
116 mWrapT(GL_CLAMP),
117 mTilesS(1),
118 mTilesT(1),
119 mObject(0)
120 {
121 // make sure we have a video context
122 Video* video = Video::current();
123 ASSERT(video && "should have a video context set");
124
125 // we want to know when the GL context is recreated
126 Dispatch& dispatch = Dispatch::global();
127 mNewContextDispatch = dispatch.addTarget("video.newcontext",
128 boost::bind(&Impl::contextRecreated, this));
129 }
130
131 ~Impl()
132 {
133 unloadFromGL();
134 }
135
136
137 /**
138 * Adapted from some public domain code. This stuff is common enough
139 * that it really should be included in SDL_image... We need this
140 * because images loaded with SDL_image aren't exactly GL-ready right
141 * out of the box. This method makes them ready.
142 */
143
144 /*
145 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
146 {
147 int w = powerOfTwo(surface->w);
148 int h = powerOfTwo(surface->h);
149
150 // 2. OpenGL textures make more sense within the coordinate system
151 // when they are "upside down," so let's flip it.
152
153 flipSurface(surface);
154
155 // 1. OpenGL images must (generally) have dimensions of a
156 // power-of-two. If this one doesn't, we can at least be more
157 // friendly by expanding the dimensions so that they are, though
158 // there will be some empty space within the range of normal
159 // texture coordinates. It's better if textures are the right size
160 // to begin with.
161
162 SDL_Surface* image = SDL_CreateRGBSurface
163 (
164 SDL_SWSURFACE,
165 w, h,
166 32,
167 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
168 0x000000FF,
169 0x0000FF00,
170 0x00FF0000,
171 0xFF000000
172 #else
173 0xFF000000,
174 0x00FF0000,
175 0x0000FF00,
176 0x000000FF
177 #endif
178 );
179
180 if (!image)
181 {
182 return 0;
183 }
184
185 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
186 Uint8 savedAlpha = surface->format->alpha;
187 if (savedFlags & SDL_SRCALPHA)
188 {
189 SDL_SetAlpha(surface, 0, 0);
190 }
191
192 SDL_Rect srcArea, destArea;
193 srcArea.x = 0; destArea.x = 0;
194 srcArea.y = 0; destArea.y = h - surface->h;
195 srcArea.w = surface->w;
196 srcArea.h = surface->h;
197 SDL_BlitSurface(surface, &srcArea, image, &destArea);
198
199 if (savedFlags & SDL_SRCALPHA)
200 {
201 SDL_SetAlpha(surface, savedFlags, savedAlpha);
202 }
203
204 return image;
205 }
206 */
207
208 /**
209 * Use SDL_image to load images from file. A surface with the image
210 * data is returned.
211 * @return Image data.
212 */
213
214 void init(const std::string& name)
215 {
216 std::string path(name);
217
218 Texture::getPath(path);
219
220 mImage = Image::alloc(path);
221 if (!mImage->isValid())
222 {
223 logWarning << "texture not found: " << path << std::endl;
224 Error(Error::RESOURCE_NOT_FOUND, path).raise();
225 }
226
227 mImage->flip();
228
229 Script script;
230
231 importLogFunctions(script);
232 bindScriptConstants(script);
233
234 if (script.doString(mImage->getComment()) != Script::SUCCESS)
235 {
236 std::string str;
237 script[-1].get(str);
238 logWarning(str);
239 }
240 else
241 {
242 logInfo << "loading tiles from texture " << path
243 << std::endl;
244
245 Script::Slot globals = script.globals();
246 globals.get(mTilesS, "tiles_s");
247 globals.get(mTilesT, "tiles_t");
248 globals.get(mMinFilter, "min_filter");
249 globals.get(mMagFilter, "mag_filter");
250 globals.get(mWrapS, "wrap_s");
251 globals.get(mWrapT, "wrap_t");
252 }
253 }
254
255
256 /**
257 * Upload the image to GL so that it will be accessible by a much more
258 * manageable handle and hopefully reside in video memory.
259 */
260
261 void uploadToGL()
262 {
263 if (mObject)
264 {
265 // already loaded
266 return;
267 }
268
269 glGenTextures(1, &mObject);
270 glBindTexture(GL_TEXTURE_2D, mObject);
271
272 glTexImage2D
273 //gluBuild2DMipmaps
274 (
275 GL_TEXTURE_2D,
276 0,
277 mImage->getMode(),
278 //3,
279 mImage->getWidth(),
280 mImage->getHeight(),
281 0,
282 mImage->getMode(),
283 GL_UNSIGNED_BYTE,
284 mImage->getPixels()
285 );
286
287 setProperties();
288 }
289
290
291 /**
292 * Sets some texture properties such as the filters and external
293 * coordinate behavior.
294 */
295
296 void setProperties()
297 {
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
302 }
303
304 inline void setMinFilter(GLuint filter)
305 {
306 bind();
307 mMinFilter = filter;
308 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
309 }
310
311 inline void setMagFilter(GLuint filter)
312 {
313 bind();
314 mMagFilter = filter;
315 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
316 }
317
318 inline void setWrapS(GLuint wrap)
319 {
320 bind();
321 mWrapS = wrap;
322 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
323 }
324
325 inline void setWrapT(GLuint wrap)
326 {
327 bind();
328 mWrapT = wrap;
329 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
330 }
331
332
333 inline void bind()
334 {
335 if (mObject == 0)
336 {
337 uploadToGL();
338 }
339 if (mObject != gObject)
340 {
341 glBindTexture(GL_TEXTURE_2D, mObject);
342 gObject = mObject;
343 }
344 }
345
346
347 bool getTileCoords(Texture::TileIndex index, Scalar coords[8]) const
348 {
349 // make sure the index represents a real tile
350 if (index >= mTilesS * mTilesT) return false;
351
352 Scalar w = 1.0 / Scalar(mTilesS);
353 Scalar h = 1.0 / Scalar(mTilesT);
354
355 coords[0] = Scalar(index % mTilesS) * w;
356 coords[1] = (Scalar(mTilesT - 1) - Scalar(index / mTilesS)) * h;
357 coords[2] = coords[0] + w;
358 coords[3] = coords[1];
359 coords[4] = coords[2];
360 coords[5] = coords[1] + h;
361 coords[6] = coords[0];
362 coords[7] = coords[5];
363
364 return true;
365 }
366
367 ImageP mImage;
368
369 GLuint mMinFilter; ///< Minification filter.
370 GLuint mMagFilter; ///< Magnification filter.
371 GLuint mWrapS; ///< Wrapping behavior horizontally.
372 GLuint mWrapT; ///< Wrapping behavior vertically.
373 unsigned mTilesS;
374 unsigned mTilesT;
375
376 GLuint mObject; ///< GL texture handle.
377 static GLuint gObject; ///< Global GL texture handle.
378
379 Dispatch::Handle mNewContextDispatch;
380 };
381
382 GLuint Texture::Impl::gObject = 0;
383
384
385 Texture::Texture(const std::string& name) : // TODO hmm..
386 Image(name),
387 // pass through
388 mImpl(Texture::Impl::getInstance(name)) {}
389
390
391 /**
392 * Bind the GL texture for mapping, etc.
393 */
394
395 void Texture::bind() const
396 {
397 // pass through
398 mImpl->bind();
399 }
400
401
402 /**
403 * Get the texture object, for the curious.
404 */
405
406 GLuint Texture::getObject() const
407 {
408 // pass through
409 return mImpl->mObject;
410 }
411
412
413 void Texture::resetBind()
414 {
415 glBindTexture(GL_TEXTURE_2D, 0);
416 Impl::gObject = 0;
417 }
418
419
420 void Texture::setMinFilter(GLuint filter)
421 {
422 // pass through
423 mImpl->setMinFilter(filter);
424 }
425
426 void Texture::setMagFilter(GLuint filter)
427 {
428 // pass through
429 mImpl->setMagFilter(filter);
430 }
431
432 void Texture::setWrapS(GLuint wrap)
433 {
434 // pass through
435 mImpl->setWrapS(wrap);
436 }
437
438 void Texture::setWrapT(GLuint wrap)
439 {
440 // pass through
441 mImpl->setWrapT(wrap);
442 }
443
444
445 bool Texture::getTileCoords(TileIndex index, Scalar coords[8]) const
446 {
447 // pass through
448 return mImpl->getTileCoords(index, coords);
449 }
450
451 bool Texture::getTileCoords(TileIndex index, Scalar coords[8],
452 Orientation orientation) const
453 {
454 if (getTileCoords(index, coords))
455 {
456 if (orientation & FLIP)
457 {
458 // this looks kinda weird, but it's just swapping in a way that
459 // doesn't require an intermediate variable
460 coords[1] = coords[5];
461 coords[5] = coords[3];
462 coords[3] = coords[7];
463 coords[7] = coords[5];
464 }
465 if (orientation & REVERSE)
466 {
467 coords[0] = coords[2];
468 coords[2] = coords[6];
469 coords[4] = coords[6];
470 coords[6] = coords[0];
471 }
472
473 return true;
474 }
475
476 return false;
477 }
478
479
480 bool Texture::getPath(std::string& name)
481 {
482 return Resource::getPath(name, "textures/", "png");
483 }
484
485
486 } // namespace Mf
487
This page took 0.050049 seconds and 4 git commands to generate.