]> Dogcows Code - chaz/yoink/blob - src/moof/texture.cc
the massive refactoring effort
[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
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 dispatcher& dispatcher = dispatcher::global();
127 mNewContextDispatch = dispatcher.add_target("video.newcontext",
128 boost::bind(&impl::context_recreated, this));
129 }
130
131 ~impl()
132 {
133 unload_from_gl();
134 }
135
136
137 void init(const std::string& name)
138 {
139 std::string path(name);
140
141 texture::find_path(path);
142
143 mImage = image::alloc(path);
144 if (!mImage->is_valid())
145 {
146 throw std::runtime_error("texture not found: " + name);
147 }
148
149 mImage->flip();
150
151 script script;
152
153 bind_script_constants(script);
154 log::import(script);
155
156 if (script.do_string(mImage->comment()) != script::success)
157 {
158 std::string str;
159 script[-1].get(str);
160 log_warning(str);
161 }
162 else
163 {
164 log_info << "loading tiles from texture " << path
165 << std::endl;
166
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");
174 }
175 }
176
177
178 /**
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.
181 */
182
183 void upload_to_gl()
184 {
185 if (mObject)
186 {
187 // already loaded
188 return;
189 }
190
191 glGenTextures(1, &mObject);
192 glBindTexture(GL_TEXTURE_2D, mObject);
193
194 glTexImage2D
195 //gluBuild2DMipmaps
196 (
197 GL_TEXTURE_2D,
198 0,
199 mImage->mode(),
200 //3,
201 mImage->width(),
202 mImage->height(),
203 0,
204 mImage->mode(),
205 GL_UNSIGNED_BYTE,
206 mImage->pixels()
207 );
208
209 set_properties();
210 }
211
212
213 /**
214 * Sets some texture properties such as the filters and external
215 * coordinate behavior.
216 */
217
218 void set_properties()
219 {
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);
224 }
225
226 void min_filter(GLuint filter)
227 {
228 bind();
229 mMinFilter = filter;
230 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
231 }
232
233 void mag_filter(GLuint filter)
234 {
235 bind();
236 mMagFilter = filter;
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
238 }
239
240 void wrap_s(GLuint wrap)
241 {
242 bind();
243 mWrapS = wrap;
244 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
245 }
246
247 void wrap_t(GLuint wrap)
248 {
249 bind();
250 mWrapT = wrap;
251 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
252 }
253
254
255 void bind()
256 {
257 if (mObject == 0)
258 {
259 upload_to_gl();
260 }
261 if (mObject != gObject)
262 {
263 glBindTexture(GL_TEXTURE_2D, mObject);
264 gObject = mObject;
265 }
266 }
267
268
269 bool tile_coordinates(int index, scalar coords[8]) const
270 {
271 // make sure the index represents a real tile
272 if (index < 0 && index >= mTilesS * mTilesT) return false;
273
274 scalar w = 1.0 / scalar(mTilesS);
275 scalar h = 1.0 / scalar(mTilesT);
276
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];
285
286 return true;
287 }
288
289 image_ptr mImage;
290
291 GLuint mMinFilter; ///< Minification filter.
292 GLuint mMagFilter; ///< Magnification filter.
293 GLuint mWrapS; ///< Wrapping behavior horizontally.
294 GLuint mWrapT; ///< Wrapping behavior vertically.
295 int mTilesS;
296 int mTilesT;
297
298 GLuint mObject; ///< GL texture handle.
299 static GLuint gObject; ///< Global GL texture handle.
300
301 dispatcher::handle mNewContextDispatch;
302 };
303
304 GLuint texture::impl::gObject = 0;
305
306
307 texture::texture(const std::string& name) : // FIXME: this is really weird
308 image(name),
309 // pass through
310 impl_(texture::impl::instance(name)) {}
311
312
313 /**
314 * Bind the GL texture for mapping, etc.
315 */
316
317 void texture::bind() const
318 {
319 // pass through
320 impl_->bind();
321 }
322
323
324 /**
325 * Get the texture object, for the curious.
326 */
327
328 GLuint texture::object() const
329 {
330 // pass through
331 return impl_->mObject;
332 }
333
334
335 void texture::reset_binding()
336 {
337 glBindTexture(GL_TEXTURE_2D, 0);
338 impl::gObject = 0;
339 }
340
341
342 void texture::min_filter(GLuint filter)
343 {
344 // pass through
345 impl_->min_filter(filter);
346 }
347
348 void texture::mag_filter(GLuint filter)
349 {
350 // pass through
351 impl_->mag_filter(filter);
352 }
353
354 void texture::wrap_s(GLuint wrap)
355 {
356 // pass through
357 impl_->wrap_s(wrap);
358 }
359
360 void texture::wrap_t(GLuint wrap)
361 {
362 // pass through
363 impl_->wrap_t(wrap);
364 }
365
366
367 bool texture::tile_coordinates(int index, scalar coords[8]) const
368 {
369 // pass through
370 return impl_->tile_coordinates(index, coords);
371 }
372
373 bool texture::tile_coordinates(int index, scalar coords[8],
374 orientation orientation) const
375 {
376 if (tile_coordinates(index, coords))
377 {
378 if (orientation & flip)
379 {
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];
386 }
387 if (orientation & reverse)
388 {
389 coords[0] = coords[2];
390 coords[2] = coords[6];
391 coords[4] = coords[6];
392 coords[6] = coords[0];
393 }
394
395 return true;
396 }
397
398 return false;
399 }
400
401
402 bool texture::find_path(std::string& name)
403 {
404 return resource::find_path(name, "textures/", "png");
405 }
406
407
408 } // namespace moof
409
This page took 0.048405 seconds and 5 git commands to generate.