]> git.tdb.fi Git - libs/gl.git/blob - source/texture.cpp
Remove support for legacy OpenGL features
[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         mipmap_levels(0),
67         max_anisotropy(1.0f),
68         wrap_s(REPEAT),
69         wrap_t(REPEAT),
70         wrap_r(REPEAT),
71         auto_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)
107                 {
108                         fmt = R8;
109                         swiz = R_TO_LUMINANCE;
110                 }
111                 else if(fmt==LUMINANCE8_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&COMPARE)
160                 set_parameter_i(GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
161         if(mask&COMPARE_FUNC)
162                 set_parameter_i(GL_TEXTURE_COMPARE_FUNC, cmp_func);
163         if(mask&FORMAT_SWIZZLE)
164         {
165                 if(get_gl_api()==OPENGL_ES2)
166                 {
167                         set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]);
168                         set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]);
169                         set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]);
170                         set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]);
171                 }
172                 else
173                 {
174                         if(ARB_direct_state_access)
175                                 glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
176                         else
177                                 glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
178                 }
179         }
180         if(mask&MIPMAP_LEVELS)
181                 set_parameter_i(GL_TEXTURE_MAX_LEVEL, (mipmap_levels ? mipmap_levels-1 : 1000));
182 }
183
184 void Texture::set_parameter_i(GLenum param, int value) const
185 {
186         if(ARB_direct_state_access)
187                 glTextureParameteri(id, param, value);
188         else
189                 glTexParameteri(target, param, value);
190 }
191
192 void Texture::set_parameter_f(GLenum param, float value) const
193 {
194         if(ARB_direct_state_access)
195                 glTextureParameterf(id, param, value);
196         else
197                 glTexParameterf(target, param, value);
198 }
199
200 void Texture::set_min_filter(TextureFilter f)
201 {
202         min_filter = f;
203         update_parameter(MIN_FILTER);
204 }
205
206 void Texture::set_mag_filter(TextureFilter f)
207 {
208         mag_filter = f;
209         update_parameter(MAG_FILTER);
210 }
211
212 void Texture::set_filter(TextureFilter f)
213 {
214         set_min_filter(f);
215         set_mag_filter(f==NEAREST ? NEAREST : LINEAR);
216 }
217
218 void Texture::set_mipmap_levels(unsigned l)
219 {
220         mipmap_levels = l;
221         update_parameter(MIPMAP_LEVELS);
222 }
223
224 void Texture::set_max_anisotropy(float a)
225 {
226         if(a<1.0f)
227                 throw invalid_argument("Texture::set_max_anisotropy");
228         else if(a>1.0f)
229                 static Require _req(EXT_texture_filter_anisotropic);
230         max_anisotropy = a;
231         if(EXT_texture_filter_anisotropic)
232                 update_parameter(MAX_ANISOTROPY);
233 }
234
235 void Texture::set_wrap(TextureWrap w)
236 {
237         set_wrap_s(w);
238         set_wrap_t(w);
239         if(EXT_texture3D)
240                 set_wrap_r(w);
241 }
242
243 void Texture::set_wrap_s(TextureWrap w)
244 {
245         wrap_s = w;
246         update_parameter(WRAP_S);
247 }
248
249 void Texture::set_wrap_t(TextureWrap w)
250 {
251         wrap_t = w;
252         update_parameter(WRAP_T);
253 }
254
255 void Texture::set_wrap_r(TextureWrap w)
256 {
257         static Require _req(EXT_texture3D);
258         wrap_r = w;
259         update_parameter(WRAP_R);
260 }
261
262 bool Texture::can_generate_mipmap()
263 {
264         return EXT_framebuffer_object;
265 }
266
267 void Texture::generate_mipmap()
268 {
269         // glGenerateMipmap is defined here
270         static Require _req(EXT_framebuffer_object);
271
272         if(ARB_direct_state_access)
273                 glGenerateTextureMipmap(id);
274         else
275         {
276                 BindRestore _bind(this);
277                 glGenerateMipmap(target);
278         }
279 }
280
281 void Texture::set_auto_generate_mipmap(bool gm)
282 {
283         if(gm)
284                 static Require _req(EXT_framebuffer_object);
285
286         auto_gen_mipmap = gm;
287 }
288
289 void Texture::set_compare_enabled(bool c)
290 {
291         if(c)
292                 static Require _req(ARB_shadow);
293         compare = c;
294         update_parameter(COMPARE);
295 }
296
297 void Texture::set_compare_func(Predicate f)
298 {
299         static Require _req(ARB_shadow);
300         cmp_func = f;
301         update_parameter(COMPARE_FUNC);
302 }
303
304 void Texture::load_image(const string &fn, bool srgb)
305 {
306         Graphics::Image img;
307         img.load_file(fn);
308
309         image(img, srgb);
310 }
311
312 void Texture::bind_to(unsigned i) const
313 {
314         if(!id)
315         {
316                 if(manager)
317                         manager->resource_used(*this);
318                 if(!id)
319                 {
320                         unbind_from(i);
321                         return;
322                 }
323         }
324
325         TexUnit &unit = TexUnit::get_unit(i);
326         if(unit.set_texture(this))
327         {
328                 if(manager)
329                         manager->resource_used(*this);
330
331                 if(ARB_direct_state_access)
332                         glBindTextureUnit(i, id);
333                 else
334                 {
335                         unit.bind();
336                         glBindTexture(target, id);
337                 }
338
339                 if(dirty_params)
340                 {
341                         update_parameter(dirty_params);
342                         dirty_params = 0;
343                 }
344         }
345 }
346
347 const Texture *Texture::current(unsigned i)
348 {
349         return TexUnit::get_unit(i).get_texture();
350 }
351
352 void Texture::unbind_from(unsigned i)
353 {
354         TexUnit &unit = TexUnit::get_unit(i);
355         const Texture *cur = unit.get_texture();
356         if(unit.set_texture(0))
357         {
358                 if(ARB_direct_state_access)
359                         glBindTextureUnit(i, 0);
360                 else
361                 {
362                         unit.bind();
363                         glBindTexture(cur->target, 0);
364                 }
365         }
366 }
367
368
369 Texture::Loader::Loader(Texture &t):
370         DataFile::CollectionObjectLoader<Texture>(t, 0)
371 {
372         init();
373 }
374
375 Texture::Loader::Loader(Texture &t, Collection &c):
376         DataFile::CollectionObjectLoader<Texture>(t, &c)
377 {
378         init();
379 }
380
381 void Texture::Loader::init()
382 {
383         if(Resources *res = dynamic_cast<Resources *>(coll))
384                 srgb = res->get_srgb_conversion();
385         else
386                 srgb = false;
387
388         add("external_image", &Loader::external_image);
389         add("filter", &Loader::filter);
390         add("generate_mipmap", &Loader::generate_mipmap);
391         add("image_data", &Loader::image_data);
392         add("mag_filter", &Loader::mag_filter);
393         add("max_anisotropy", &Loader::max_anisotropy);
394         add("min_filter", &Loader::min_filter);
395         add("mipmap_levels", &Loader::mipmap_levels);
396         add("wrap",       &Loader::wrap);
397         add("wrap_r",     &Loader::wrap_r);
398         add("wrap_s",     &Loader::wrap_s);
399         add("wrap_t",     &Loader::wrap_t);
400 }
401
402 void Texture::Loader::external_image(const string &fn)
403 {
404         Graphics::Image img;
405         RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
406         if(!io)
407                 throw IO::file_not_found(fn);
408         img.load_io(*io);
409
410         obj.image(img, srgb);
411 }
412
413 void Texture::Loader::filter(TextureFilter f)
414 {
415         obj.set_filter(f);
416 }
417
418 void Texture::Loader::generate_mipmap(bool gm)
419 {
420         obj.set_auto_generate_mipmap(gm);
421 }
422
423 void Texture::Loader::image_data(const string &data)
424 {
425         Graphics::Image img;
426         IO::Memory mem(data.data(), data.size());
427         img.load_io(mem);
428
429         obj.image(img, srgb);
430 }
431
432 void Texture::Loader::mag_filter(TextureFilter f)
433 {
434         obj.set_mag_filter(f);
435 }
436
437 void Texture::Loader::max_anisotropy(float a)
438 {
439         obj.set_max_anisotropy(a);
440 }
441
442 void Texture::Loader::min_filter(TextureFilter f)
443 {
444         obj.set_min_filter(f);
445 }
446
447 void Texture::Loader::mipmap_levels(unsigned l)
448 {
449         obj.set_mipmap_levels(l);
450 }
451
452 void Texture::Loader::wrap(TextureWrap w)
453 {
454         obj.set_wrap(w);
455 }
456
457 void Texture::Loader::wrap_r(TextureWrap w)
458 {
459         obj.set_wrap_r(w);
460 }
461
462 void Texture::Loader::wrap_s(TextureWrap w)
463 {
464         obj.set_wrap_s(w);
465 }
466
467 void Texture::Loader::wrap_t(TextureWrap w)
468 {
469         obj.set_wrap_t(w);
470 }
471
472
473 bool is_mipmapped(TextureFilter filter)
474 {
475         return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR ||
476                 filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
477 }
478
479 } // namespace GL
480 } // namespace Msp