2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
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.
10 **************************************************************************/
12 #include <cstdio> // FILE
13 #include <cstring> // strncmp
15 #include <boost/algorithm/string.hpp>
16 #include <boost/bind.hpp>
18 #include "Dispatch.hh"
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.
41 class Texture::Impl
: public Manager
<Impl
>
45 * Delete the texture (if it is loaded) from GL.
52 if (mObject
== gObject
)
57 glDeleteTextures(1, &mObject
);
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.
68 void contextRecreated()
70 mObject
= gObject
= 0;
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
80 static int powerOfTwo(int input
)
92 static void bindScriptConstants(Mf::Script
& script
)
94 script
.push(GL_CLAMP
); script
.set("CLAMP");
95 script
.push(GL_REPEAT
); script
.set("REPEAT");
96 script
.push(GL_LINEAR
); script
.set("LINEAR");
97 script
.push(GL_NEAREST
); script
.set("NEAREST");
98 script
.push(GL_LINEAR_MIPMAP_LINEAR
);
99 script
.set("LINEAR_MIPMAP_LINEAR");
100 script
.push(GL_LINEAR_MIPMAP_NEAREST
);
101 script
.set("LINEAR_MIPMAP_NEAREST");
102 script
.push(GL_NEAREST_MIPMAP_LINEAR
);
103 script
.set("NEAREST_MIPMAP_LINEAR");
104 script
.push(GL_NEAREST_MIPMAP_NEAREST
);
105 script
.set("NEAREST_MIPMAP_NEAREST");
111 * Construction is initialization.
115 mMinFilter(GL_NEAREST
),
116 mMagFilter(GL_NEAREST
),
123 // make sure we have a video context
124 Video
* video
= Video::current();
125 ASSERT(video
&& "should have a video context set");
127 // we want to know when the GL context is recreated
128 Dispatch
& dispatch
= Dispatch::global();
129 mNewContextDispatch
= dispatch
.addTarget("video.newcontext",
130 boost::bind(&Impl::contextRecreated
, this));
140 * Adapted from some public domain code. This stuff is common enough
141 * that it really should be included in SDL_image... We need this
142 * because images loaded with SDL_image aren't exactly GL-ready right
143 * out of the box. This method makes them ready.
147 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
149 int w = powerOfTwo(surface->w);
150 int h = powerOfTwo(surface->h);
152 // 2. OpenGL textures make more sense within the coordinate system
153 // when they are "upside down," so let's flip it.
155 flipSurface(surface);
157 // 1. OpenGL images must (generally) have dimensions of a
158 // power-of-two. If this one doesn't, we can at least be more
159 // friendly by expanding the dimensions so that they are, though
160 // there will be some empty space within the range of normal
161 // texture coordinates. It's better if textures are the right size
164 SDL_Surface* image = SDL_CreateRGBSurface
169 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
187 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
188 Uint8 savedAlpha = surface->format->alpha;
189 if (savedFlags & SDL_SRCALPHA)
191 SDL_SetAlpha(surface, 0, 0);
194 SDL_Rect srcArea, destArea;
195 srcArea.x = 0; destArea.x = 0;
196 srcArea.y = 0; destArea.y = h - surface->h;
197 srcArea.w = surface->w;
198 srcArea.h = surface->h;
199 SDL_BlitSurface(surface, &srcArea, image, &destArea);
201 if (savedFlags & SDL_SRCALPHA)
203 SDL_SetAlpha(surface, savedFlags, savedAlpha);
211 * Use SDL_image to load images from file. A surface with the image
213 * @return Image data.
216 void init(const std::string
& name
)
218 std::string path
= Texture::getPath(name
);
220 mImage
= Image::alloc(path
);
221 if (!mImage
->isValid())
223 logWarning
<< "texture not found: " << path
<< std::endl
;
224 Error(Error::RESOURCE_NOT_FOUND
, path
).raise();
231 importLogFunctions(script
);
232 bindScriptConstants(script
);
234 if (script
.doString(mImage
->getComment()) != Mf::Script::SUCCESS
)
242 Mf::logInfo
<< "loading tiles from texture " << path
245 Mf::Script::Slot globals
= script
.getGlobalTable();
246 Mf::Script::Slot top
= script
[-1];
248 globals
.pushField("tiles_s");
251 globals
.pushField("tiles_t");
254 globals
.pushField("min_filter");
257 globals
.pushField("mag_filter");
260 globals
.pushField("wrap_s");
263 globals
.pushField("wrap_t");
270 * Upload the image to GL so that it will be accessible by a much more
271 * manageable handle and hopefully reside in video memory.
282 glGenTextures(1, &mObject
);
283 glBindTexture(GL_TEXTURE_2D
, mObject
);
305 * Sets some texture properties such as the filters and external
306 * coordinate behavior.
311 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, mMinFilter
);
312 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mMagFilter
);
313 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, mWrapS
);
314 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, mWrapT
);
317 inline void setMinFilter(GLuint filter
)
321 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, mMinFilter
);
324 inline void setMagFilter(GLuint filter
)
328 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mMagFilter
);
331 inline void setWrapS(GLuint wrap
)
335 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, mWrapS
);
338 inline void setWrapT(GLuint wrap
)
342 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, mWrapT
);
352 if (mObject
!= gObject
)
354 glBindTexture(GL_TEXTURE_2D
, mObject
);
360 bool getTileCoords(Texture::TileIndex index
, Scalar coords
[8]) const
362 // make sure the index represents a real tile
363 if (index
>= mTilesS
* mTilesT
) return false;
365 Scalar w
= 1.0 / Scalar(mTilesS
);
366 Scalar h
= 1.0 / Scalar(mTilesT
);
368 coords
[0] = Scalar(index
% mTilesS
) * w
;
369 coords
[1] = (Scalar(mTilesT
- 1) - Scalar(index
/ mTilesS
)) * h
;
370 coords
[2] = coords
[0] + w
;
371 coords
[3] = coords
[1];
372 coords
[4] = coords
[2];
373 coords
[5] = coords
[1] + h
;
374 coords
[6] = coords
[0];
375 coords
[7] = coords
[5];
382 GLuint mMinFilter
; ///< Minification filter.
383 GLuint mMagFilter
; ///< Magnification filter.
384 GLuint mWrapS
; ///< Wrapping behavior horizontally.
385 GLuint mWrapT
; ///< Wrapping behavior vertically.
389 GLuint mObject
; ///< GL texture handle.
390 static GLuint gObject
; ///< Global GL texture handle.
392 Dispatch::Handle mNewContextDispatch
;
395 GLuint
Texture::Impl::gObject
= 0;
398 Texture::Texture(const std::string
& name
) :
399 Image(Texture::getPath(name
)),
401 mImpl(Texture::Impl::getInstance(name
)) {}
405 * Bind the GL texture for mapping, etc.
408 void Texture::bind() const
416 * Get the texture object, for the curious.
419 GLuint
Texture::getObject() const
422 return mImpl
->mObject
;
426 void Texture::resetBind()
428 glBindTexture(GL_TEXTURE_2D
, 0);
433 void Texture::setMinFilter(GLuint filter
)
436 mImpl
->setMinFilter(filter
);
439 void Texture::setMagFilter(GLuint filter
)
442 mImpl
->setMagFilter(filter
);
445 void Texture::setWrapS(GLuint wrap
)
448 mImpl
->setWrapS(wrap
);
451 void Texture::setWrapT(GLuint wrap
)
454 mImpl
->setWrapT(wrap
);
458 bool Texture::getTileCoords(TileIndex index
, Scalar coords
[8]) const
461 return mImpl
->getTileCoords(index
, coords
);
464 bool Texture::getTileCoords(TileIndex index
, Scalar coords
[8],
465 Orientation orientation
) const
467 if (getTileCoords(index
, coords
))
469 if (orientation
& FLIP
)
471 // this looks kinda weird, but it's just swapping in a way that
472 // doesn't require an intermediate variable
473 coords
[1] = coords
[5];
474 coords
[5] = coords
[3];
475 coords
[3] = coords
[7];
476 coords
[7] = coords
[5];
478 if (orientation
& REVERSE
)
480 coords
[0] = coords
[2];
481 coords
[2] = coords
[6];
482 coords
[4] = coords
[6];
483 coords
[6] = coords
[0];
493 std::string
Texture::getPath(const std::string
& name
)
495 if (boost::find_last(name
, ".png"))
497 return Resource::getPath(name
);
501 std::string
path("textures/");
504 return Resource::getPath(path
);