]> git.tdb.fi Git - libs/gl.git/blob - source/core/texturecube.cpp
Remove the PixelStore class
[libs/gl.git] / source / core / texturecube.cpp
1 #include <msp/datafile/collection.h>
2 #include <msp/gl/extensions/arb_direct_state_access.h>
3 #include <msp/gl/extensions/arb_seamless_cube_map.h>
4 #include <msp/gl/extensions/arb_texture_cube_map.h>
5 #include <msp/gl/extensions/arb_texture_storage.h>
6 #include <msp/io/memory.h>
7 #include <msp/strings/format.h>
8 #include "bindable.h"
9 #include "error.h"
10 #include "texturecube.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace GL {
16
17 TextureCubeFace TextureCube::face_order[6] =
18 {
19         POSITIVE_X,
20         NEGATIVE_X,
21         POSITIVE_Y,
22         NEGATIVE_Y,
23         POSITIVE_Z,
24         NEGATIVE_Z
25 };
26
27 Vector3 TextureCube::directions[6] =
28 {
29         Vector3(1, 0, 0),
30         Vector3(-1, 0, 0),
31         Vector3(0, 1, 0),
32         Vector3(0, -1, 0),
33         Vector3(0, 0, 1),
34         Vector3(0, 0, -1)
35 };
36
37 unsigned TextureCube::orientations[12] =
38 {
39         5, 3,
40         4, 3,
41         0, 4,
42         0, 5,
43         0, 3,
44         1, 3
45 };
46
47 TextureCube::TextureCube():
48         Texture(GL_TEXTURE_CUBE_MAP),
49         size(0),
50         allocated(0)
51 {
52         static Require _req(ARB_texture_cube_map);
53         if(ARB_seamless_cube_map)
54                 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
55 }
56
57 void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv)
58 {
59         if(size>0)
60         {
61                 if(fmt!=format || sz!=size || (lv && lv!=levels))
62                         throw incompatible_data("TextureCube::storage");
63                 return;
64         }
65         if(sz==0)
66                 throw invalid_argument("TextureCube::storage");
67
68         set_format(fmt);
69         size = sz;
70         levels = get_n_levels();
71         if(lv>0)
72                 levels = min(levels, lv);
73 }
74
75 void TextureCube::allocate(unsigned level)
76 {
77         if(size==0)
78                 throw invalid_operation("TextureCube::allocate");
79         if(level>=levels)
80                 throw invalid_argument("TextureCube::allocate");
81         if(allocated&(64<<level))
82                 return;
83
84         bool direct = ARB_texture_storage && ARB_direct_state_access;
85         if(!direct)
86         {
87                 glActiveTexture(GL_TEXTURE0);
88                 glBindTexture(target, id);
89         }
90
91         allocate_(level);
92
93         if(!direct)
94                 glBindTexture(target, 0);
95 }
96
97 void TextureCube::allocate_(unsigned level)
98 {
99         if(ARB_texture_storage)
100         {
101                 if(ARB_direct_state_access)
102                         glTextureStorage2D(id, levels, storage_fmt, size, size);
103                 else
104                         glTexStorage2D(target, levels, storage_fmt, size, size);
105                 apply_swizzle();
106                 allocated |= (64<<levels)-1;
107         }
108         else
109         {
110                 for(unsigned i=0; i<6; ++i)
111                         image_(enumerate_faces(i), level, 0);
112         }
113 }
114
115 void TextureCube::image(TextureCubeFace face, unsigned level, const void *data)
116 {
117         if(size==0)
118                 throw invalid_operation("TextureCube::image");
119         if(level>=levels)
120                 throw out_of_range("TextureCube::image");
121
122         if(ARB_texture_storage)
123         {
124                 unsigned lsz = get_level_size(level);
125                 return sub_image(face, level, 0, 0, lsz, lsz, data);
126         }
127
128         glActiveTexture(GL_TEXTURE0);
129         glBindTexture(target, id);
130
131         image_(face, level, data);
132
133         if(auto_gen_mipmap && level==0 && (allocated&63)==63)
134         {
135                 generate_mipmap_();
136                 allocated |= (64<<levels)-1;
137         }
138
139         glBindTexture(target, 0);
140 }
141
142 void TextureCube::image_(TextureCubeFace face, unsigned level, const void *data)
143 {
144         if(!allocated)
145         {
146                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
147                 apply_swizzle();
148         }
149
150         unsigned lsz = get_level_size(level);
151         PixelComponents comp = get_components(storage_fmt);
152         GLenum type = get_gl_type(get_component_type(storage_fmt));
153         glTexImage2D(face, level, storage_fmt, lsz, lsz, 0, comp, type, data);
154
155         if(level==0)
156         {
157                 allocated |= 1<<get_face_index(face);
158                 if((allocated&63)==63)
159                         allocated |= 64;
160         }
161         else if(!(allocated&(64<<level)))
162         {
163                 for(unsigned i=0; i<6; ++i)
164                         if(enumerate_faces(i)!=face)
165                                 glTexImage2D(enumerate_faces(i), level, storage_fmt, lsz, lsz, 0, comp, type, 0);
166
167                 allocated |= 64<<level;
168         }
169 }
170
171 void TextureCube::image(TextureCubeFace face, unsigned level, PixelComponents comp, DataType type, const void *data)
172 {
173         if(comp!=get_components(format) || type!=get_component_type(format))
174                 throw incompatible_data("TextureCube::image");
175         image(face, level, data);
176 }
177
178 void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
179 {
180         if(size==0)
181                 throw invalid_operation("TextureCube::sub_image");
182         if(level>=levels)
183                 throw out_of_range("TextureCube::sub_image");
184
185         bool direct = (ARB_direct_state_access && (ARB_texture_storage || (allocated&(1<<level))));
186         if(!direct)
187         {
188                 glActiveTexture(GL_TEXTURE0);
189                 glBindTexture(target, id);
190         }
191
192         allocate_(level);
193
194         PixelComponents comp = get_components(storage_fmt);
195         GLenum type = get_gl_type(get_component_type(storage_fmt));
196         if(ARB_direct_state_access)
197                 glTextureSubImage3D(id, level, x, y, get_face_index(face), wd, ht, 1, comp, type, data);
198         else
199                 glTexSubImage2D(face, level, x, y, wd, ht, comp, type, data);
200
201         if(auto_gen_mipmap && level==0)
202                 generate_mipmap_();
203
204         if(!direct)
205                 glBindTexture(target, 0);
206 }
207
208 void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
209 {
210         if(comp!=get_components(format) || type!=get_component_type(format))
211                 throw incompatible_data("TextureCube::subimage");
212         sub_image(face, level, x, y, wd, ht, data);
213 }
214
215 void TextureCube::image(TextureCubeFace face, const Graphics::Image &img)
216 {
217         unsigned w = img.get_width();
218         unsigned h = img.get_height();
219         if(w!=h)
220                 throw incompatible_data("TextureCube::image");
221
222         PixelFormat fmt = pixelformat_from_image(img);
223         storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w);
224
225         image(face, 0, img.get_pixels());
226 }
227
228 void TextureCube::image(TextureCubeFace face, const Graphics::Image &img, bool)
229 {
230         image(face, img);
231 }
232
233 void TextureCube::image(const Graphics::Image &img, unsigned lv)
234 {
235         unsigned w = img.get_width();
236         unsigned h = img.get_height();
237
238         if(h!=w*6)
239                 throw incompatible_data("TextureCube::image");
240         h /= 6;
241
242         PixelFormat fmt = pixelformat_from_image(img);
243         if(size==0)
244                 storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, lv);
245         else if(w!=size || h!=size)
246                 throw incompatible_data("TextureCube::image");
247
248         const char *pixels = reinterpret_cast<const char *>(img.get_pixels());
249         unsigned face_size = img.get_stride()*size;
250         for(unsigned i=0; i<6; ++i)
251                 image(enumerate_faces(i), 0, pixels+i*face_size);
252 }
253
254 unsigned TextureCube::get_n_levels() const
255 {
256         unsigned n = 0;
257         for(unsigned s=size; s; s>>=1, ++n) ;
258         return n;
259 }
260
261 unsigned TextureCube::get_level_size(unsigned level) const
262 {
263         return size>>level;
264 }
265
266 TextureCubeFace TextureCube::enumerate_faces(unsigned i)
267 {
268         if(i>=6)
269                 throw out_of_range("TextureCube::enumerate_faces");
270         return face_order[i];
271 }
272
273 unsigned TextureCube::get_face_index(TextureCubeFace face)
274 {
275         switch(face)
276         {
277         case POSITIVE_X: return 0;
278         case NEGATIVE_X: return 1;
279         case POSITIVE_Y: return 2;
280         case NEGATIVE_Y: return 3;
281         case POSITIVE_Z: return 4;
282         case NEGATIVE_Z: return 5;
283         default: throw invalid_argument("TextureCube::get_face_index");
284         }
285 }
286
287 const Vector3 &TextureCube::get_face_direction(TextureCubeFace face)
288 {
289         return directions[get_face_index(face)];
290 }
291
292 const Vector3 &TextureCube::get_s_direction(TextureCubeFace face)
293 {
294         return directions[orientations[get_face_index(face)*2]];
295 }
296
297 const Vector3 &TextureCube::get_t_direction(TextureCubeFace face)
298 {
299         return directions[orientations[get_face_index(face)*2+1]];
300 }
301
302 Vector3 TextureCube::get_texel_direction(TextureCubeFace face, unsigned u, unsigned v)
303 {
304         float s = (u+0.5f)*2.0f/size-1.0f;
305         float t = (v+0.5f)*2.0f/size-1.0f;
306         const Vector3 &fv = get_face_direction(face);
307         const Vector3 &sv = get_s_direction(face);
308         const Vector3 &tv = get_t_direction(face);
309         return fv+s*sv+t*tv;
310 }
311
312 UInt64 TextureCube::get_data_size() const
313 {
314         return id ? size*size*6*get_pixel_size(storage_fmt) : 0;
315 }
316
317
318 TextureCube::Loader::Loader(TextureCube &t):
319         DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>(t)
320 {
321         init();
322 }
323
324 TextureCube::Loader::Loader(TextureCube &t, Collection &c):
325         DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>(t, c)
326 {
327         init();
328 }
329
330 void TextureCube::Loader::init()
331 {
332         add("external_image", &Loader::external_image);
333         add("image_data", &Loader::image_data);
334         add("raw_data", &Loader::raw_data);
335         add("storage", &Loader::storage);
336         add("storage", &Loader::storage_levels);
337 }
338
339 void TextureCube::Loader::external_image(TextureCubeFace face, const string &fn)
340 {
341         Graphics::Image img;
342         RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
343         img.load_io(*io);
344
345         obj.image(face, img);
346 }
347
348 void TextureCube::Loader::image_data(TextureCubeFace face, const string &data)
349 {
350         Graphics::Image img;
351         IO::Memory mem(data.data(), data.size());
352         img.load_io(mem);
353
354         obj.image(face, img);
355 }
356
357 void TextureCube::Loader::raw_data(TextureCubeFace face, const string &data)
358 {
359         obj.image(face, 0, data.data());
360 }
361
362 void TextureCube::Loader::storage(PixelFormat fmt, unsigned s)
363 {
364         obj.storage(fmt, s);
365 }
366
367 void TextureCube::Loader::storage_levels(PixelFormat fmt, unsigned s, unsigned l)
368 {
369         obj.storage(fmt, s, l);
370 }
371
372
373 void operator>>(const LexicalConverter &conv, TextureCubeFace &face)
374 {
375         const string &str = conv.get();
376         if(str=="POSITIVE_X")
377                 face = POSITIVE_X;
378         else if(str=="NEGATIVE_X")
379                 face = NEGATIVE_X;
380         else if(str=="POSITIVE_Y")
381                 face = POSITIVE_Y;
382         else if(str=="NEGATIVE_Y")
383                 face = NEGATIVE_Y;
384         else if(str=="POSITIVE_Z")
385                 face = POSITIVE_Z;
386         else if(str=="NEGATIVE_Z")
387                 face = NEGATIVE_Z;
388         else
389                 throw lexical_error(format("conversion of '%s' to TextureCubeFace", str));
390 }
391
392 } // namespace GL
393 } // namespace Msp