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
16 #include <boost/algorithm/string.hpp>
17 #include <boost/bind.hpp>
19 #include "dispatcher.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 context_recreated()
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 power_of_two(int input
)
92 static void bind_script_constants(script
& script
)
94 script::slot g
= script
.globals();
96 g
.set_field("CLAMP", GL_CLAMP
);
97 g
.set_field("REPEAT", GL_REPEAT
);
98 g
.set_field("LINEAR", GL_LINEAR
);
99 g
.set_field("NEAREST", GL_NEAREST
);
100 g
.set_field("LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR
);
101 g
.set_field("LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST
);
102 g
.set_field("NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR
);
103 g
.set_field("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST
);
109 * Construction is initialization.
113 mMinFilter(GL_NEAREST
),
114 mMagFilter(GL_NEAREST
),
121 // make sure we have a video context
122 video
* video
= video::current();
123 ASSERT(video
&& "should have a video context set");
125 // we want to know when the GL context is recreated
126 dispatcher
& dispatcher
= dispatcher::global();
127 mNewContextDispatch
= dispatcher
.add_target("video.newcontext",
128 boost::bind(&impl::context_recreated
, this));
137 void init(const std::string
& name
)
139 std::string
path(name
);
141 texture::find_path(path
);
143 mImage
= image::alloc(path
);
144 if (!mImage
->is_valid())
146 throw std::runtime_error("texture not found: " + name
);
153 bind_script_constants(script
);
156 if (script
.do_string(mImage
->comment()) != script::success
)
164 log_info
<< "loading tiles from texture " << path
167 script::slot globals
= script
.globals();
168 globals
.get(mTilesS
, "tiles_s");
169 globals
.get(mTilesT
, "tiles_t");
170 globals
.get(mMinFilter
, "min_filter");
171 globals
.get(mMagFilter
, "mag_filter");
172 globals
.get(mWrapS
, "wrap_s");
173 globals
.get(mWrapT
, "wrap_t");
179 * Upload the image to GL so that it will be accessible by a much more
180 * manageable handle and hopefully reside in video memory.
191 glGenTextures(1, &mObject
);
192 glBindTexture(GL_TEXTURE_2D
, mObject
);
214 * Sets some texture properties such as the filters and external
215 * coordinate behavior.
218 void set_properties()
220 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, mMinFilter
);
221 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mMagFilter
);
222 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, mWrapS
);
223 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, mWrapT
);
226 void min_filter(GLuint filter
)
230 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, mMinFilter
);
233 void mag_filter(GLuint filter
)
237 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mMagFilter
);
240 void wrap_s(GLuint wrap
)
244 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, mWrapS
);
247 void wrap_t(GLuint wrap
)
251 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, mWrapT
);
261 if (mObject
!= gObject
)
263 glBindTexture(GL_TEXTURE_2D
, mObject
);
269 bool tile_coordinates(int index
, scalar coords
[8]) const
271 // make sure the index represents a real tile
272 if (index
< 0 && index
>= mTilesS
* mTilesT
) return false;
274 scalar w
= 1.0 / scalar(mTilesS
);
275 scalar h
= 1.0 / scalar(mTilesT
);
277 coords
[0] = scalar(index
% mTilesS
) * w
;
278 coords
[1] = (scalar(mTilesT
- 1) - scalar(index
/ mTilesS
)) * h
;
279 coords
[2] = coords
[0] + w
;
280 coords
[3] = coords
[1];
281 coords
[4] = coords
[2];
282 coords
[5] = coords
[1] + h
;
283 coords
[6] = coords
[0];
284 coords
[7] = coords
[5];
291 GLuint mMinFilter
; ///< Minification filter.
292 GLuint mMagFilter
; ///< Magnification filter.
293 GLuint mWrapS
; ///< Wrapping behavior horizontally.
294 GLuint mWrapT
; ///< Wrapping behavior vertically.
298 GLuint mObject
; ///< GL texture handle.
299 static GLuint gObject
; ///< Global GL texture handle.
301 dispatcher::handle mNewContextDispatch
;
304 GLuint
texture::impl::gObject
= 0;
307 texture::texture(const std::string
& name
) : // FIXME: this is really weird
310 impl_(texture::impl::instance(name
)) {}
314 * Bind the GL texture for mapping, etc.
317 void texture::bind() const
325 * Get the texture object, for the curious.
328 GLuint
texture::object() const
331 return impl_
->mObject
;
335 void texture::reset_binding()
337 glBindTexture(GL_TEXTURE_2D
, 0);
342 void texture::min_filter(GLuint filter
)
345 impl_
->min_filter(filter
);
348 void texture::mag_filter(GLuint filter
)
351 impl_
->mag_filter(filter
);
354 void texture::wrap_s(GLuint wrap
)
360 void texture::wrap_t(GLuint wrap
)
367 bool texture::tile_coordinates(int index
, scalar coords
[8]) const
370 return impl_
->tile_coordinates(index
, coords
);
373 bool texture::tile_coordinates(int index
, scalar coords
[8],
374 orientation orientation
) const
376 if (tile_coordinates(index
, coords
))
378 if (orientation
& flip
)
380 // this looks kinda weird, but it's just swapping in a way that
381 // doesn't require an intermediate variable
382 coords
[1] = coords
[5];
383 coords
[5] = coords
[3];
384 coords
[3] = coords
[7];
385 coords
[7] = coords
[5];
387 if (orientation
& reverse
)
389 coords
[0] = coords
[2];
390 coords
[2] = coords
[6];
391 coords
[4] = coords
[6];
392 coords
[6] = coords
[0];
402 bool texture::find_path(std::string
& name
)
404 return resource::find_path(name
, "textures/", "png");