]> Dogcows Code - chaz/yoink/blob - src/moof/texture.cc
begin cleaning up resource management
[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 #include <stdexcept>
15
16 #include <boost/algorithm/string.hpp>
17 #include <boost/bind.hpp>
18
19 #include "dispatcher.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 moof {
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 unload_from_gl()
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 context_recreated()
69 {
70 mObject = gObject = 0;
71 upload_to_gl();
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 power_of_two(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 bind_script_constants(script& script)
93 {
94 script::slot g = script.globals();
95
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);
104 }
105
106 public:
107
108 /*
109 * Construction is initialization.
110 */
111 impl() :
112 mMinFilter(GL_NEAREST),
113 mMagFilter(GL_NEAREST),
114 mWrapS(GL_CLAMP),
115 mWrapT(GL_CLAMP),
116 mTilesS(1),
117 mTilesT(1),
118 mObject(0)
119 {
120 // make sure we have a video context
121 video* video = video::current();
122 ASSERT(video && "should have a video context set");
123
124 // we want to know when the GL context is recreated
125 dispatcher& dispatcher = dispatcher::global();
126 mNewContextDispatch = dispatcher.add_target("video.newcontext",
127 boost::bind(&impl::context_recreated, this));
128 }
129
130 ~impl()
131 {
132 unload_from_gl();
133 }
134
135
136 void init(const std::string& name)
137 {
138 std::string path(name);
139
140 resource::find(path);
141
142 mImage = image::alloc(path);
143 if (!mImage->is_valid())
144 {
145 throw std::runtime_error("texture not found: " + name);
146 }
147
148 mImage->flip();
149
150 script script;
151
152 bind_script_constants(script);
153 log::import(script);
154
155 if (script.do_string(mImage->comment()) != script::success)
156 {
157 std::string str;
158 script[-1].get(str);
159 log_warning(str);
160 }
161 else
162 {
163 log_info("loading tiles from texture", path);
164
165 script::slot globals = script.globals();
166 globals.get(mTilesS, "tiles_s");
167 globals.get(mTilesT, "tiles_t");
168 globals.get(mMinFilter, "min_filter");
169 globals.get(mMagFilter, "mag_filter");
170 globals.get(mWrapS, "wrap_s");
171 globals.get(mWrapT, "wrap_t");
172 }
173 }
174
175
176 /*
177 * Upload the image to GL so that it will be accessible by a much more
178 * manageable handle and hopefully reside in video memory.
179 */
180 void upload_to_gl()
181 {
182 if (mObject)
183 {
184 // already loaded
185 return;
186 }
187
188 glGenTextures(1, &mObject);
189 glBindTexture(GL_TEXTURE_2D, mObject);
190
191 glTexImage2D
192 //gluBuild2DMipmaps
193 (
194 GL_TEXTURE_2D,
195 0,
196 mImage->mode(),
197 //3,
198 mImage->width(),
199 mImage->height(),
200 0,
201 mImage->mode(),
202 GL_UNSIGNED_BYTE,
203 mImage->pixels()
204 );
205
206 set_properties();
207 }
208
209
210 /*
211 * Sets some texture properties such as the filters and external
212 * coordinate behavior.
213 */
214 void set_properties()
215 {
216 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
217 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
218 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
219 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
220 }
221
222 void min_filter(GLuint filter)
223 {
224 bind();
225 mMinFilter = filter;
226 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
227 }
228
229 void mag_filter(GLuint filter)
230 {
231 bind();
232 mMagFilter = filter;
233 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
234 }
235
236 void wrap_s(GLuint wrap)
237 {
238 bind();
239 mWrapS = wrap;
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
241 }
242
243 void wrap_t(GLuint wrap)
244 {
245 bind();
246 mWrapT = wrap;
247 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
248 }
249
250
251 void bind()
252 {
253 if (mObject == 0)
254 {
255 upload_to_gl();
256 }
257 if (mObject != gObject)
258 {
259 glBindTexture(GL_TEXTURE_2D, mObject);
260 gObject = mObject;
261 }
262 }
263
264
265 bool tile_coordinates(int index, scalar coords[8]) const
266 {
267 // make sure the index represents a real tile
268 if (index < 0 && index >= mTilesS * mTilesT) return false;
269
270 scalar w = 1.0 / scalar(mTilesS);
271 scalar h = 1.0 / scalar(mTilesT);
272
273 coords[0] = scalar(index % mTilesS) * w;
274 coords[1] = (scalar(mTilesT - 1) - scalar(index / mTilesS)) * h;
275 coords[2] = coords[0] + w;
276 coords[3] = coords[1];
277 coords[4] = coords[2];
278 coords[5] = coords[1] + h;
279 coords[6] = coords[0];
280 coords[7] = coords[5];
281
282 return true;
283 }
284
285 image_ptr mImage;
286
287 GLuint mMinFilter; ///< Minification filter.
288 GLuint mMagFilter; ///< Magnification filter.
289 GLuint mWrapS; ///< Wrapping behavior horizontally.
290 GLuint mWrapT; ///< Wrapping behavior vertically.
291 int mTilesS;
292 int mTilesT;
293
294 GLuint mObject; ///< GL texture handle.
295 static GLuint gObject; ///< Global GL texture handle.
296
297 dispatcher::handle mNewContextDispatch;
298 };
299
300 GLuint texture::impl::gObject = 0;
301
302
303 texture::texture(const std::string& name) : // FIXME: this is really weird
304 image(name),
305 // pass through
306 impl_(texture::impl::instance(name)) {}
307
308
309 /**
310 * Bind the GL texture for mapping, etc.
311 */
312
313 void texture::bind() const
314 {
315 // pass through
316 impl_->bind();
317 }
318
319
320 /**
321 * Get the texture object, for the curious.
322 */
323
324 GLuint texture::object() const
325 {
326 // pass through
327 return impl_->mObject;
328 }
329
330
331 void texture::reset_binding()
332 {
333 glBindTexture(GL_TEXTURE_2D, 0);
334 impl::gObject = 0;
335 }
336
337
338 void texture::min_filter(GLuint filter)
339 {
340 // pass through
341 impl_->min_filter(filter);
342 }
343
344 void texture::mag_filter(GLuint filter)
345 {
346 // pass through
347 impl_->mag_filter(filter);
348 }
349
350 void texture::wrap_s(GLuint wrap)
351 {
352 // pass through
353 impl_->wrap_s(wrap);
354 }
355
356 void texture::wrap_t(GLuint wrap)
357 {
358 // pass through
359 impl_->wrap_t(wrap);
360 }
361
362
363 bool texture::tile_coordinates(int index, scalar coords[8]) const
364 {
365 // pass through
366 return impl_->tile_coordinates(index, coords);
367 }
368
369 bool texture::tile_coordinates(int index, scalar coords[8],
370 orientation orientation) const
371 {
372 if (tile_coordinates(index, coords))
373 {
374 if (orientation & flip)
375 {
376 // this looks kinda weird, but it's just swapping in a way that
377 // doesn't require an intermediate variable
378 coords[1] = coords[5];
379 coords[5] = coords[3];
380 coords[3] = coords[7];
381 coords[7] = coords[5];
382 }
383 if (orientation & reverse)
384 {
385 coords[0] = coords[2];
386 coords[2] = coords[6];
387 coords[4] = coords[6];
388 coords[6] = coords[0];
389 }
390
391 return true;
392 }
393
394 return false;
395 }
396
397
398 } // namespace moof
399
This page took 0.051871 seconds and 4 git commands to generate.