]> git.tdb.fi Git - libs/gl.git/blob - source/texture.cpp
Move internal format management to the Texture base class
[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(MSP_sized_internal_formats)
91                 fmt = get_sized_pixelformat(fmt);
92
93         require_pixelformat(fmt);
94         ifmt = fmt;
95 }
96
97 void Texture::update_parameter(int mask) const
98 {
99         if(!ARB_direct_state_access && TexUnit::current().get_texture()!=this)
100         {
101                 TexUnit *unit = TexUnit::find_unit(this);
102                 if(!unit)
103                 {
104                         dirty_params |= mask;
105                         return;
106                 }
107
108                 unit->bind();
109         }
110
111         if(mask&MIN_FILTER)
112                 set_parameter_i(GL_TEXTURE_MIN_FILTER, min_filter);
113         if(mask&MAG_FILTER)
114                 set_parameter_i(GL_TEXTURE_MAG_FILTER, mag_filter);
115         if(mask&MAX_ANISOTROPY)
116                 set_parameter_f(GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
117         if(mask&WRAP_S)
118                 set_parameter_i(GL_TEXTURE_WRAP_S, wrap_s);
119         if(mask&WRAP_T)
120                 set_parameter_i(GL_TEXTURE_WRAP_T, wrap_t);
121         if(mask&WRAP_R)
122                 set_parameter_i(GL_TEXTURE_WRAP_R, wrap_r);
123         if(mask&GENERATE_MIPMAP)
124                 set_parameter_i(GL_GENERATE_MIPMAP, gen_mipmap);
125         if(mask&COMPARE)
126                 set_parameter_i(GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
127         if(mask&COMPARE_FUNC)
128                 set_parameter_i(GL_TEXTURE_COMPARE_FUNC, cmp_func);
129 }
130
131 void Texture::set_parameter_i(GLenum param, int value) const
132 {
133         if(ARB_direct_state_access)
134                 glTextureParameteri(id, param, value);
135         else
136                 glTexParameteri(target, param, value);
137 }
138
139 void Texture::set_parameter_f(GLenum param, float value) const
140 {
141         if(ARB_direct_state_access)
142                 glTextureParameterf(id, param, value);
143         else
144                 glTexParameterf(target, param, value);
145 }
146
147 void Texture::set_min_filter(TextureFilter f)
148 {
149         min_filter = f;
150         update_parameter(MIN_FILTER);
151 }
152
153 void Texture::set_mag_filter(TextureFilter f)
154 {
155         mag_filter = f;
156         update_parameter(MAG_FILTER);
157 }
158
159 void Texture::set_filter(TextureFilter f)
160 {
161         set_min_filter(f);
162         set_mag_filter(f==NEAREST ? NEAREST : LINEAR);
163 }
164
165 void Texture::set_max_anisotropy(float a)
166 {
167         if(a<1.0f)
168                 throw invalid_argument("Texture::set_max_anisotropy");
169         else if(a>1.0f)
170                 static Require _req(EXT_texture_filter_anisotropic);
171         max_anisotropy = a;
172         update_parameter(MAX_ANISOTROPY);
173 }
174
175 void Texture::set_wrap(TextureWrap w)
176 {
177         set_wrap_s(w);
178         set_wrap_t(w);
179         if(EXT_texture3D)
180                 set_wrap_r(w);
181 }
182
183 void Texture::set_wrap_s(TextureWrap w)
184 {
185         wrap_s = w;
186         update_parameter(WRAP_S);
187 }
188
189 void Texture::set_wrap_t(TextureWrap w)
190 {
191         wrap_t = w;
192         update_parameter(WRAP_T);
193 }
194
195 void Texture::set_wrap_r(TextureWrap w)
196 {
197         static Require _req(EXT_texture3D);
198         wrap_r = w;
199         update_parameter(WRAP_R);
200 }
201
202 void Texture::set_generate_mipmap(bool gm)
203 {
204         if(gm && !EXT_framebuffer_object)
205                 static Require _req(SGIS_generate_mipmap);
206         gen_mipmap = gm;
207         if(!EXT_framebuffer_object)
208                 update_parameter(GENERATE_MIPMAP);
209 }
210
211 void Texture::auto_generate_mipmap()
212 {
213         // glGenerateMipmap is defined here
214         if(EXT_framebuffer_object)
215         {
216                 if(ARB_direct_state_access)
217                         glGenerateTextureMipmap(id);
218                 else
219                         glGenerateMipmap(target);
220         }
221 }
222
223 void Texture::set_compare_enabled(bool c)
224 {
225         if(c)
226                 static Require _req(ARB_shadow);
227         compare = c;
228         update_parameter(COMPARE);
229 }
230
231 void Texture::set_compare_func(Predicate f)
232 {
233         static Require _req(ARB_shadow);
234         cmp_func = f;
235         update_parameter(COMPARE_FUNC);
236 }
237
238 void Texture::load_image(const string &fn, bool srgb)
239 {
240         Graphics::Image img;
241         img.load_file(fn);
242
243         image(img, srgb);
244 }
245
246 void Texture::bind_to(unsigned i) const
247 {
248         if(!id)
249         {
250                 if(manager)
251                         manager->resource_used(*this);
252                 if(!id)
253                 {
254                         unbind_from(i);
255                         return;
256                 }
257         }
258
259         TexUnit &unit = TexUnit::get_unit(i);
260         const Texture *old = unit.get_texture();
261         if(unit.set_texture(this))
262         {
263                 if(manager)
264                         manager->resource_used(*this);
265
266                 unit.bind();
267                 if(unit.supports_legacy())
268                 {
269                         if(old && old->target!=target)
270                                 glDisable(old->target);
271                         if(!old || old->target!=target)
272                                 glEnable(target);
273                 }
274                 glBindTexture(target, id);
275
276                 if(dirty_params)
277                 {
278                         update_parameter(dirty_params);
279                         dirty_params = 0;
280                 }
281         }
282 }
283
284 const Texture *Texture::current(unsigned i)
285 {
286         return TexUnit::get_unit(i).get_texture();
287 }
288
289 void Texture::unbind_from(unsigned i)
290 {
291         TexUnit &unit = TexUnit::get_unit(i);
292         const Texture *cur = unit.get_texture();
293         if(unit.set_texture(0))
294         {
295                 unit.bind();
296                 glBindTexture(cur->target, 0);
297                 if(unit.supports_legacy())
298                         glDisable(cur->target);
299         }
300 }
301
302
303 Texture::Loader::Loader(Texture &t):
304         DataFile::CollectionObjectLoader<Texture>(t, 0)
305 {
306         init();
307 }
308
309 Texture::Loader::Loader(Texture &t, Collection &c):
310         DataFile::CollectionObjectLoader<Texture>(t, &c)
311 {
312         init();
313 }
314
315 void Texture::Loader::init()
316 {
317         if(Resources *res = dynamic_cast<Resources *>(coll))
318                 srgb = res->get_srgb_conversion();
319         else
320                 srgb = false;
321
322         add("external_image", &Loader::external_image);
323         add("filter", &Loader::filter);
324         add("generate_mipmap", &Loader::generate_mipmap);
325         add("image_data", &Loader::image_data);
326         add("mag_filter", &Loader::mag_filter);
327         add("max_anisotropy", &Loader::max_anisotropy);
328         add("min_filter", &Loader::min_filter);
329         add("wrap",       &Loader::wrap);
330         add("wrap_r",     &Loader::wrap_r);
331         add("wrap_s",     &Loader::wrap_s);
332         add("wrap_t",     &Loader::wrap_t);
333 }
334
335 void Texture::Loader::external_image(const string &fn)
336 {
337         Graphics::Image img;
338         RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
339         img.load_io(*io);
340
341         obj.image(img, srgb);
342 }
343
344 void Texture::Loader::filter(TextureFilter f)
345 {
346         obj.set_filter(f);
347 }
348
349 void Texture::Loader::generate_mipmap(bool gm)
350 {
351         obj.set_generate_mipmap(gm);
352 }
353
354 void Texture::Loader::image_data(const string &data)
355 {
356         Graphics::Image img;
357         IO::Memory mem(data.data(), data.size());
358         img.load_io(mem);
359
360         obj.image(img, srgb);
361 }
362
363 void Texture::Loader::mag_filter(TextureFilter f)
364 {
365         obj.set_mag_filter(f);
366 }
367
368 void Texture::Loader::max_anisotropy(float a)
369 {
370         obj.set_max_anisotropy(a);
371 }
372
373 void Texture::Loader::min_filter(TextureFilter f)
374 {
375         obj.set_min_filter(f);
376 }
377
378 void Texture::Loader::wrap(TextureWrap w)
379 {
380         obj.set_wrap(w);
381 }
382
383 void Texture::Loader::wrap_r(TextureWrap w)
384 {
385         obj.set_wrap_r(w);
386 }
387
388 void Texture::Loader::wrap_s(TextureWrap w)
389 {
390         obj.set_wrap_s(w);
391 }
392
393 void Texture::Loader::wrap_t(TextureWrap w)
394 {
395         obj.set_wrap_t(w);
396 }
397
398
399 bool is_mipmapped(TextureFilter filter)
400 {
401         return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR ||
402                 filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
403 }
404
405 } // namespace GL
406 } // namespace Msp