2 /*******************************************************************************
4 Copyright (c) 2009, Charles McGarvey
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
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.
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.
27 *******************************************************************************/
29 #include <cstdio> // FILE
30 #include <cstring> // strncmp
32 #include <boost/algorithm/string.hpp>
33 #include <boost/bind.hpp>
35 #include "Dispatch.hh"
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 * manager so that multiple texture objects can share the same internal objects
54 * and avoid having duplicate textures loaded to GL.
57 class Texture::Impl
: public Manager
<Impl
>
61 * Delete the texture (if it is loaded) from GL.
68 if (mObject
== gObject
)
73 glDeleteTextures(1, &mObject
);
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.
84 void contextRecreated()
86 mObject
= gObject
= 0;
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.
95 static int powerOfTwo(int input
)
107 static void bindScriptConstants(Mf::Script
& script
)
109 script
.push(GL_CLAMP
); script
.set("CLAMP");
110 script
.push(GL_REPEAT
); script
.set("REPEAT");
111 script
.push(GL_LINEAR
); script
.set("LINEAR");
112 script
.push(GL_NEAREST
); script
.set("NEAREST");
113 script
.push(GL_LINEAR_MIPMAP_LINEAR
); script
.set("LINEAR_MIPMAP_LINEAR");
114 script
.push(GL_LINEAR_MIPMAP_NEAREST
); script
.set("LINEAR_MIPMAP_NEAREST");
115 script
.push(GL_NEAREST_MIPMAP_LINEAR
); script
.set("NEAREST_MIPMAP_LINEAR");
116 script
.push(GL_NEAREST_MIPMAP_NEAREST
); script
.set("NEAREST_MIPMAP_NEAREST");
122 * Construction is initialization.
126 mMinFilter(GL_NEAREST
),
127 mMagFilter(GL_NEAREST
),
134 // make sure we have a video context
135 ASSERT(video
&& "cannot load textures without a current video context");
137 // we want to know when the GL context is recreated
138 mDispatchHandler
= core
.addHandler("video.newcontext",
139 boost::bind(&Impl::contextRecreated
, this));
149 * Adapted from some public domain code. This stuff is common enough that
150 * it really should be included in SDL_image... We need this because images
151 * loaded with SDL_image aren't exactly GL-ready right out of the box. This
152 * method makes them ready.
156 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
158 int w = powerOfTwo(surface->w);
159 int h = powerOfTwo(surface->h);
161 // 2. OpenGL textures make more sense within the coordinate system when
162 // they are "upside down," so let's flip it.
164 flipSurface(surface);
166 // 1. OpenGL images must (generally) have dimensions of a power-of-two.
167 // If this one doesn't, we can at least be more friendly by expanding
168 // the dimensions so that they are, though there will be some empty
169 // space within the range of normal texture coordinates. It's better if
170 // textures are the right size to begin with.
172 SDL_Surface* image = SDL_CreateRGBSurface
177 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
195 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
196 Uint8 savedAlpha = surface->format->alpha;
197 if (savedFlags & SDL_SRCALPHA)
199 SDL_SetAlpha(surface, 0, 0);
202 SDL_Rect srcArea, destArea;
203 srcArea.x = 0; destArea.x = 0;
204 srcArea.y = 0; destArea.y = h - surface->h;
205 srcArea.w = surface->w;
206 srcArea.h = surface->h;
207 SDL_BlitSurface(surface, &srcArea, image, &destArea);
209 if (savedFlags & SDL_SRCALPHA)
211 SDL_SetAlpha(surface, savedFlags, savedAlpha);
219 * Use SDL_image to load images from file. A surface with the image data is
221 * @return Image data.
224 void init(const std::string
& name
)
226 std::string path
= Texture::getPath(name
);
228 mImage
= Image::alloc(path
);
229 if (!mImage
->isValid())
231 logWarning
<< "texture not found: " << path
<< std::endl
;
232 Error(Error::RESOURCE_NOT_FOUND
, path
).raise();
239 importLogFunctions(script
);
240 bindScriptConstants(script
);
242 if (script
.doString(mImage
->getComment()) != Mf::Script::SUCCESS
)
250 Mf::logInfo
<< "loading tiles from texture " << path
<< std::endl
;
252 Mf::Script::Slot globals
= script
.getGlobalTable();
253 Mf::Script::Slot top
= script
[-1];
255 globals
.pushField("tiles_s");
258 globals
.pushField("tiles_t");
261 globals
.pushField("min_filter");
264 globals
.pushField("mag_filter");
267 globals
.pushField("wrap_s");
270 globals
.pushField("wrap_t");
277 * Upload the image to GL so that it will be accessible by a much more
278 * manageable handle and hopefully reside in video memory.
289 glGenTextures(1, &mObject
);
290 glBindTexture(GL_TEXTURE_2D
, mObject
);
312 * Sets some texture properties such as the filters and external coordinate
318 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, mMinFilter
);
319 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mMagFilter
);
320 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, mWrapS
);
321 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, mWrapT
);
324 inline void setMinFilter(GLuint filter
)
328 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, mMinFilter
);
331 inline void setMagFilter(GLuint filter
)
335 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mMagFilter
);
338 inline void setWrapS(GLuint wrap
)
342 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, mWrapS
);
345 inline void setWrapT(GLuint wrap
)
349 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, mWrapT
);
359 if (mObject
!= gObject
)
361 glBindTexture(GL_TEXTURE_2D
, mObject
);
367 bool getTileCoords(Texture::TileIndex index
, Scalar coords
[8]) const
369 // make sure the index represents a real tile
370 if (index
>= mTilesS
* mTilesT
) return false;
372 Scalar w
= 1.0 / Scalar(mTilesS
);
373 Scalar h
= 1.0 / Scalar(mTilesT
);
375 coords
[0] = Scalar(index
% mTilesS
) * w
;
376 coords
[1] = (Scalar(mTilesT
- 1) -
377 Scalar(index
/ mTilesS
)) * h
;
378 coords
[2] = coords
[0] + w
;
379 coords
[3] = coords
[1];
380 coords
[4] = coords
[2];
381 coords
[5] = coords
[1] + h
;
382 coords
[6] = coords
[0];
383 coords
[7] = coords
[5];
390 GLuint mMinFilter
; ///< Minification filter.
391 GLuint mMagFilter
; ///< Magnification filter.
392 GLuint mWrapS
; ///< Wrapping behavior horizontally.
393 GLuint mWrapT
; ///< Wrapping behavior vertically.
397 GLuint mObject
; ///< GL texture handle.
398 static GLuint gObject
; ///< Global GL texture handle.
400 Dispatch::Handler mDispatchHandler
;
403 GLuint
Texture::Impl::gObject
= 0;
406 Texture::Texture(const std::string
& name
) :
407 Image(Texture::getPath(name
)),
409 mImpl(Texture::Impl::getInstance(name
)) {}
413 * Bind the GL texture for mapping, etc.
416 void Texture::bind() const
424 * Get the texture object, for the curious.
427 GLuint
Texture::getObject() const
430 return mImpl
->mObject
;
434 void Texture::resetBind()
436 glBindTexture(GL_TEXTURE_2D
, 0);
441 void Texture::setMinFilter(GLuint filter
)
444 mImpl
->setMinFilter(filter
);
447 void Texture::setMagFilter(GLuint filter
)
450 mImpl
->setMagFilter(filter
);
453 void Texture::setWrapS(GLuint wrap
)
456 mImpl
->setWrapS(wrap
);
459 void Texture::setWrapT(GLuint wrap
)
462 mImpl
->setWrapT(wrap
);
466 bool Texture::getTileCoords(TileIndex index
, Scalar coords
[8]) const
469 return mImpl
->getTileCoords(index
, coords
);
472 bool Texture::getTileCoords(TileIndex index
, Scalar coords
[8],
473 Orientation orientation
) const
475 if (getTileCoords(index
, coords
))
477 if (orientation
& FLIP
)
479 // this looks kinda weird, but it's just swapping in a way that
480 // doesn't require an intermediate variable
481 coords
[1] = coords
[5];
482 coords
[5] = coords
[3];
483 coords
[3] = coords
[7];
484 coords
[7] = coords
[5];
486 if (orientation
& REVERSE
)
488 coords
[0] = coords
[2];
489 coords
[2] = coords
[6];
490 coords
[4] = coords
[6];
491 coords
[6] = coords
[0];
501 std::string
Texture::getPath(const std::string
& name
)
503 if (boost::find_last(name
, ".png"))
505 return Resource::getPath(name
);
509 std::string
path("textures/");
512 return Resource::getPath(path
);
519 /** vim: set ts=4 sw=4 tw=80: *************************************************/