]> git.tdb.fi Git - libs/gl.git/blob - source/texture.cpp
Allow texture mipmap levels to be specified in datafiles
[libs/gl.git] / source / texture.cpp
1 #include <msp/gl/extensions/arb_direct_state_access.h>
2 #include <msp/gl/extensions/arb_shadow.h>
3 #include <msp/gl/extensions/arb_texture_swizzle.h>
4 #include <msp/gl/extensions/ext_framebuffer_object.h>
5 #include <msp/gl/extensions/ext_texture3d.h>
6 #include <msp/gl/extensions/ext_texture_filter_anisotropic.h>
7 #include <msp/io/memory.h>
8 #include <msp/strings/format.h>
9 #include "bindable.h"
10 #include "error.h"
11 #include "resourcemanager.h"
12 #include "resources.h"
13 #include "texture.h"
14 #include "texunit.h"
15
16 using namespace std;
17
18 namespace Msp {
19 namespace GL {
20
21 void operator>>(const LexicalConverter &c, TextureFilter &tf)
22 {
23         if(c.get()=="NEAREST")
24                 tf = NEAREST;
25         else if(c.get()=="LINEAR")
26                 tf = LINEAR;
27         else if(c.get()=="NEAREST_MIPMAP_NEAREST")
28                 tf = NEAREST_MIPMAP_NEAREST;
29         else if(c.get()=="NEAREST_MIPMAP_LINEAR")
30                 tf = NEAREST_MIPMAP_LINEAR;
31         else if(c.get()=="LINEAR_MIPMAP_NEAREST")
32                 tf = LINEAR_MIPMAP_NEAREST;
33         else if(c.get()=="LINEAR_MIPMAP_LINEAR")
34                 tf = LINEAR_MIPMAP_LINEAR;
35         else
36                 throw lexical_error(format("conversion of '%s' to TextureFilter", c.get()));
37 }
38
39
40 void operator>>(const LexicalConverter &c, TextureWrap &tw)
41 {
42         if(c.get()=="REPEAT")
43                 tw = REPEAT;
44         else if(c.get()=="CLAMP_TO_EDGE")
45                 tw = CLAMP_TO_EDGE;
46         else if(c.get()=="MIRRORED_REPEAT")
47                 tw = MIRRORED_REPEAT;
48         else
49                 throw lexical_error(format("conversion of '%s' to TextureWrap", c.get()));
50 }
51
52
53 int Texture::swizzle_orders[] =
54 {
55         GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA,
56         GL_RED, GL_RED, GL_RED, GL_ONE,
57         GL_RED, GL_RED, GL_RED, GL_GREEN
58 };
59
60 Texture::Texture(GLenum t, ResourceManager *m):
61         id(0),
62         target(t),
63         ifmt(RGB),
64         min_filter(NEAREST_MIPMAP_LINEAR),
65         mag_filter(LINEAR),
66         max_anisotropy(1.0f),
67         wrap_s(REPEAT),
68         wrap_t(REPEAT),
69         wrap_r(REPEAT),
70         auto_gen_mipmap(false),
71         compare(false),
72         cmp_func(LEQUAL),
73         dirty_params(0)
74 {
75         if(m)
76                 set_manager(m);
77         else if(ARB_direct_state_access)
78                 glCreateTextures(target, 1, &id);
79         else
80                 glGenTextures(1, &id);
81 }
82
83 Texture::~Texture()
84 {
85         while(TexUnit *unit = TexUnit::find_unit(this))
86                 unbind_from(unit->get_index());
87
88         if(id)
89                 glDeleteTextures(1, &id);
90 }
91
92 DataType Texture::get_alloc_type(PixelFormat fmt)
93 {
94         return (get_base_pixelformat(fmt)==DEPTH_COMPONENT ? UNSIGNED_SHORT : UNSIGNED_BYTE);
95 }
96
97 void Texture::set_internal_format(PixelFormat fmt)
98 {
99         FormatSwizzle swiz = NO_SWIZZLE;
100         if(ARB_texture_rg && ARB_texture_swizzle)
101         {
102                 if(fmt==LUMINANCE)
103                 {
104                         fmt = RED;
105                         swiz = R_TO_LUMINANCE;
106                 }
107                 else if(fmt==LUMINANCE_ALPHA)
108                 {
109                         fmt = RG;
110                         swiz = RG_TO_LUMINANCE_ALPHA;
111                 }
112         }
113
114         if(!get_component_size(fmt) && OES_required_internalformat)
115                 fmt = get_default_sized_pixelformat(fmt);
116
117         require_pixelformat(fmt);
118         ifmt = fmt;
119         swizzle = swiz;
120         if(swizzle)
121                 update_parameter(FORMAT_SWIZZLE);
122 }
123
124 PixelFormat Texture::get_upload_format(PixelFormat fmt) const
125 {
126         if(fmt==LUMINANCE || fmt==LUMINANCE_ALPHA)
127                 return get_base_pixelformat(ifmt);
128         else
129                 return fmt;
130 }
131
132 void Texture::update_parameter(int mask) const
133 {
134         if(!id)
135         {
136                 dirty_params |= mask;
137                 return;
138         }
139
140         if(!ARB_direct_state_access && TexUnit::current().get_texture()!=this)
141         {
142                 TexUnit *unit = TexUnit::find_unit(this);
143                 if(!unit)
144                 {
145                         dirty_params |= mask;
146                         return;
147                 }
148
149                 unit->bind();
150         }
151
152         if(mask&MIN_FILTER)
153                 set_parameter_i(GL_TEXTURE_MIN_FILTER, min_filter);
154         if(mask&MAG_FILTER)
155                 set_parameter_i(GL_TEXTURE_MAG_FILTER, mag_filter);
156         if(mask&MAX_ANISOTROPY)
157                 set_parameter_f(GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
158         if(mask&WRAP_S)
159                 set_parameter_i(GL_TEXTURE_WRAP_S, wrap_s);
160         if(mask&WRAP_T)
161                 set_parameter_i(GL_TEXTURE_WRAP_T, wrap_t);
162         if(mask&WRAP_R)
163                 set_parameter_i(GL_TEXTURE_WRAP_R, wrap_r);
164         if(mask&COMPARE)
165                 set_parameter_i(GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
166         if(mask&COMPARE_FUNC)
167                 set_parameter_i(GL_TEXTURE_COMPARE_FUNC, cmp_func);
168         if(mask&FORMAT_SWIZZLE)
169         {
170                 if(get_gl_api()==OPENGL_ES2)
171                 {
172                         set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]);
173                         set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]);
174                         set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]);
175                         set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]);
176                 }
177                 else
178                 {
179                         if(ARB_direct_state_access)
180                                 glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
181                         else
182                                 glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
183                 }
184         }
185 }
186
187 void Texture::set_parameter_i(GLenum param, int value) const
188 {
189         if(ARB_direct_state_access)
190                 glTextureParameteri(id, param, value);
191         else
192                 glTexParameteri(target, param, value);
193 }
194
195 void Texture::set_parameter_f(GLenum param, float value) const
196 {
197         if(ARB_direct_state_access)
198                 glTextureParameterf(id, param, value);
199         else
200                 glTexParameterf(target, param, value);
201 }
202
203 void Texture::set_min_filter(TextureFilter f)
204 {
205         min_filter = f;
206         update_parameter(MIN_FILTER);
207 }
208
209 void Texture::set_mag_filter(TextureFilter f)
210 {
211         mag_filter = f;
212         update_parameter(MAG_FILTER);
213 }
214
215 void Texture::set_filter(TextureFilter f)
216 {
217         set_min_filter(f);
218         set_mag_filter(f==NEAREST ? NEAREST : LINEAR);
219 }
220
221 void Texture::set_max_anisotropy(float a)
222 {
223         if(a<1.0f)
224                 throw invalid_argument("Texture::set_max_anisotropy");
225         else if(a>1.0f)
226                 static Require _req(EXT_texture_filter_anisotropic);
227         max_anisotropy = a;
228         if(EXT_texture_filter_anisotropic)
229                 update_parameter(MAX_ANISOTROPY);
230 }
231
232 void Texture::set_wrap(TextureWrap w)
233 {
234         set_wrap_s(w);
235         set_wrap_t(w);
236         if(EXT_texture3D)
237                 set_wrap_r(w);
238 }
239
240 void Texture::set_wrap_s(TextureWrap w)
241 {
242         wrap_s = w;
243         update_parameter(WRAP_S);
244 }
245
246 void Texture::set_wrap_t(TextureWrap w)
247 {
248         wrap_t = w;
249         update_parameter(WRAP_T);
250 }
251
252 void Texture::set_wrap_r(TextureWrap w)
253 {
254         static Require _req(EXT_texture3D);
255         wrap_r = w;
256         update_parameter(WRAP_R);
257 }
258
259 bool Texture::can_generate_mipmap()
260 {
261         return EXT_framebuffer_object;
262 }
263
264 void Texture::generate_mipmap()
265 {
266         // glGenerateMipmap is defined here
267         static Require _req(EXT_framebuffer_object);
268
269         if(ARB_direct_state_access)
270                 glGenerateTextureMipmap(id);
271         else
272         {
273                 BindRestore _bind(this);
274                 glGenerateMipmap(target);
275         }
276 }
277
278 void Texture::set_auto_generate_mipmap(bool gm)
279 {
280         if(gm)
281                 static Require _req(EXT_framebuffer_object);
282
283         auto_gen_mipmap = gm;
284 }
285
286 void Texture::set_compare_enabled(bool c)
287 {
288         if(c)
289                 static Require _req(ARB_shadow);
290         compare = c;
291         update_parameter(COMPARE);
292 }
293
294 void Texture::set_compare_func(Predicate f)
295 {
296         static Require _req(ARB_shadow);
297         cmp_func = f;
298         update_parameter(COMPARE_FUNC);
299 }
300
301 void Texture::load_image(const string &fn, bool srgb)
302 {
303         load_image(fn, 0, srgb);
304 }
305
306 void Texture::load_image(const string &fn, unsigned lv, bool srgb)
307 {
308         Graphics::Image img;
309         img.load_file(fn);
310
311         image(img, lv, srgb);
312 }
313
314 void Texture::image(const Graphics::Image &img, bool srgb)
315 {
316         image(img, 0, srgb);
317 }
318
319 void Texture::bind_to(unsigned i) const
320 {
321         if(!id)
322         {
323                 if(manager)
324                         manager->resource_used(*this);
325                 if(!id)
326                 {
327                         unbind_from(i);
328                         return;
329                 }
330         }
331
332         TexUnit &unit = TexUnit::get_unit(i);
333         if(unit.set_texture(this))
334         {
335                 if(manager)
336                         manager->resource_used(*this);
337
338                 if(ARB_direct_state_access)
339                         glBindTextureUnit(i, id);
340                 else
341                 {
342                         unit.bind();
343                         glBindTexture(target, id);
344                 }
345
346                 if(dirty_params)
347                 {
348                         update_parameter(dirty_params);
349                         dirty_params = 0;
350                 }
351         }
352 }
353
354 const Texture *Texture::current(unsigned i)
355 {
356         return TexUnit::get_unit(i).get_texture();
357 }
358
359 void Texture::unbind_from(unsigned i)
360 {
361         TexUnit &unit = TexUnit::get_unit(i);
362         const Texture *cur = unit.get_texture();
363         if(unit.set_texture(0))
364         {
365                 if(ARB_direct_state_access)
366                         glBindTextureUnit(i, 0);
367                 else
368                 {
369                         unit.bind();
370                         glBindTexture(cur->target, 0);
371                 }
372         }
373 }
374
375
376 Texture::Loader::Loader(Texture &t):
377         DataFile::CollectionObjectLoader<Texture>(t, 0)
378 {
379         init();
380 }
381
382 Texture::Loader::Loader(Texture &t, Collection &c):
383         DataFile::CollectionObjectLoader<Texture>(t, &c)
384 {
385         init();
386 }
387
388 void Texture::Loader::init()
389 {
390         levels = 0;
391         if(Resources *res = dynamic_cast<Resources *>(coll))
392                 srgb = res->get_srgb_conversion();
393         else
394                 srgb = false;
395
396         add("external_image", &Loader::external_image);
397         add("filter", &Loader::filter);
398         add("generate_mipmap", &Loader::generate_mipmap);
399         add("image_data", &Loader::image_data);
400         add("mag_filter", &Loader::mag_filter);
401         add("max_anisotropy", &Loader::max_anisotropy);
402         add("min_filter", &Loader::min_filter);
403         add("mipmap_levels", &Loader::mipmap_levels);
404         add("wrap",       &Loader::wrap);
405         add("wrap_r",     &Loader::wrap_r);
406         add("wrap_s",     &Loader::wrap_s);
407         add("wrap_t",     &Loader::wrap_t);
408 }
409
410 unsigned Texture::Loader::get_levels() const
411 {
412         return (is_mipmapped(obj.default_sampler.get_min_filter()) ? levels : 1);
413 }
414
415 void Texture::Loader::external_image(const string &fn)
416 {
417         Graphics::Image img;
418         RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
419         if(!io)
420                 throw IO::file_not_found(fn);
421         img.load_io(*io);
422
423         obj.image(img, get_levels(), srgb);
424 }
425
426 void Texture::Loader::filter(TextureFilter f)
427 {
428         obj.set_filter(f);
429 }
430
431 void Texture::Loader::generate_mipmap(bool gm)
432 {
433         obj.set_auto_generate_mipmap(gm);
434 }
435
436 void Texture::Loader::image_data(const string &data)
437 {
438         Graphics::Image img;
439         IO::Memory mem(data.data(), data.size());
440         img.load_io(mem);
441
442         obj.image(img, get_levels(), srgb);
443 }
444
445 void Texture::Loader::mag_filter(TextureFilter f)
446 {
447         obj.set_mag_filter(f);
448 }
449
450 void Texture::Loader::max_anisotropy(float a)
451 {
452         obj.set_max_anisotropy(a);
453 }
454
455 void Texture::Loader::min_filter(TextureFilter f)
456 {
457         obj.set_min_filter(f);
458 }
459
460 void Texture::Loader::mipmap_levels(unsigned l)
461 {
462         levels = l;
463 }
464
465 void Texture::Loader::wrap(TextureWrap w)
466 {
467         obj.set_wrap(w);
468 }
469
470 void Texture::Loader::wrap_r(TextureWrap w)
471 {
472         obj.set_wrap_r(w);
473 }
474
475 void Texture::Loader::wrap_s(TextureWrap w)
476 {
477         obj.set_wrap_s(w);
478 }
479
480 void Texture::Loader::wrap_t(TextureWrap w)
481 {
482         obj.set_wrap_t(w);
483 }
484
485
486 bool is_mipmapped(TextureFilter filter)
487 {
488         return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR ||
489                 filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
490 }
491
492 } // namespace GL
493 } // namespace Msp