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