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