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