]> git.tdb.fi Git - libs/gl.git/blob - source/vertexarray.cpp
Allow applying a VertexArray in non-legacy mode
[libs/gl.git] / source / vertexarray.cpp
1 #include <msp/gl/extensions/arb_multitexture.h>
2 #include <msp/gl/extensions/arb_vertex_shader.h>
3 #include "buffer.h"
4 #include "error.h"
5 #include "gl.h"
6 #include "mesh.h"
7 #include "vertexarray.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace GL {
13
14 bool VertexArray::legacy_used = false;
15
16 VertexArray::VertexArray(const VertexFormat &f)
17 {
18         reset(f);
19 }
20
21 VertexArray::~VertexArray()
22 {
23         /* Unbind accesses the current VertexArray, so a call from ~Bindable would
24         try to access destroyed data. */
25         if(current()==this)
26                 unbind();
27 }
28
29 void VertexArray::reset(const VertexFormat &f)
30 {
31         clear();
32         format = f;
33         stride = get_stride(format);
34         legacy = false;
35
36         arrays.clear();
37
38         unsigned offs = 0;
39         for(const unsigned char *c=format.begin(); c!=format.end(); ++c)
40         {
41                 unsigned slot = get_array_slot(*c);
42                 if(slot>=arrays.size())
43                         arrays.resize(slot+1);
44
45                 Array &arr = arrays[slot];
46                 arr.component = *c;
47                 arr.offset = offs;
48
49                 if(*c<ATTRIB1)
50                         legacy = true;
51
52                 offs += get_component_size(*c);
53         }
54 }
55
56 unsigned VertexArray::get_array_slot(unsigned char comp)
57 {
58         unsigned t = get_component_type(comp);
59         if(t==get_component_type(VERTEX3))
60                 return 0;
61         else if(t==get_component_type(NORMAL3))
62                 return 1;
63         else if(t==get_component_type(COLOR4_FLOAT))
64                 return 2;
65         else if(comp>=TEXCOORD1 && comp<=TEXCOORD4+12)
66         {
67                 t -= get_component_type(TEXCOORD1);
68                 if(t>0)
69                         static Require _req(ARB_multitexture);
70                 return 3+t;
71         }
72         else
73         {
74                 static Require _req(ARB_vertex_shader);
75                 if(comp>=ATTRIB1)
76                         t -= get_component_type(ATTRIB1);
77                 return 7+t;
78         }
79 }
80
81 void VertexArray::clear()
82 {
83         data.clear();
84 }
85
86 void VertexArray::reserve(unsigned n)
87 {
88         data.reserve(n*stride);
89 }
90
91 float *VertexArray::append()
92 {
93         data.insert(data.end(), stride, 0.0f);
94         update_offset();
95         dirty = true;
96         return &*(data.end()-stride);
97 }
98
99 float *VertexArray::modify(unsigned i)
100 {
101         dirty = true;
102         return &data[0]+i*stride;
103 }
104
105 unsigned VertexArray::get_data_size() const
106 {
107         return data.size()*sizeof(float);
108 }
109
110 void VertexArray::apply(bool use_legacy) const
111 {
112         if(format.empty())
113                 throw invalid_operation("VertexArray::apply");
114         // Don't mess up the vertex array object of a mesh
115         if(Mesh::current())
116                 throw invalid_operation("VertexArray::apply");
117
118         /* Unbind first if the legacy flag changes.  The logic for supporting it
119         directly in apply_arrays would get too complicated, and this also allows
120         rebinding the same array with different legacy setting. */
121         if(legacy_used!=use_legacy)
122                 unbind();
123
124         if(!use_legacy)
125                 static Require _req(ARB_vertex_shader);
126
127         const VertexArray *old = current();
128         /* If the array has been modified, apply it even if it was the last one to
129         be applied.  This is necessary to get the data updated to vertex buffer, and
130         to resync things after a format change.  Radeon drivers also have some
131         problems with modifying vertex arrays without re-setting the pointers. */
132         if(!set_current(this) && !dirty)
133                 return;
134
135         Buffer *vbuf = get_buffer();
136         Bind _bind_vbuf(vbuf, ARRAY_BUFFER);
137         if(vbuf && dirty)
138                 update_buffer();
139
140         const float *base = (vbuf ? reinterpret_cast<float *>(get_offset()) : &data[0]);
141         unsigned stride_bytes = stride*sizeof(float);
142         apply_arrays(&arrays, (old ? &old->arrays : 0), base, stride_bytes, use_legacy);
143 }
144
145 void VertexArray::apply_arrays(const vector<Array> *arrays, const vector<Array> *old_arrays, const float *base, unsigned stride_bytes, bool use_legacy)
146 {
147         unsigned active_tex = 0;
148         unsigned n_arrays = arrays ? arrays->size() : 0;
149         if(old_arrays)
150                 n_arrays = max<unsigned>(n_arrays, old_arrays->size());
151         for(unsigned i=0; i<n_arrays; ++i)
152         {
153                 const Array *arr = ((arrays && i<arrays->size() && (*arrays)[i].component) ? &(*arrays)[i] : 0);
154                 const Array *old_arr = ((old_arrays && i<old_arrays->size() && (*old_arrays)[i].component) ? &(*old_arrays)[i] : 0);
155                 if(!arr && !old_arr)
156                         continue;
157
158                 unsigned char comp = (arr ? arr->component : old_arr->component);
159                 unsigned sz = get_component_size(comp);
160                 unsigned t = get_component_type(comp);
161                 if(use_legacy)
162                 {
163                         GLenum array_type = 0;
164                         if(t==get_component_type(VERTEX3))
165                         {
166                                 if(arr)
167                                         glVertexPointer(sz, GL_FLOAT, stride_bytes, base+arr->offset);
168                                 array_type = GL_VERTEX_ARRAY;
169                         }
170                         else if(t==get_component_type(NORMAL3))
171                         {
172                                 if(arr)
173                                         glNormalPointer(GL_FLOAT, stride_bytes, base+arr->offset);
174                                 array_type = GL_NORMAL_ARRAY;
175                         }
176                         else if(t==get_component_type(COLOR4_FLOAT))
177                         {
178                                 if(arr)
179                                 {
180                                         if(sz==1)
181                                                 glColorPointer(4, GL_UNSIGNED_BYTE, stride_bytes, base+arr->offset);
182                                         else
183                                                 glColorPointer(sz, GL_FLOAT, stride_bytes, base+arr->offset);
184                                 }
185                                 array_type = GL_COLOR_ARRAY;
186                         }
187                         else if(comp>=TEXCOORD1 && comp<=TEXCOORD4+12)
188                         {
189                                 t -= get_component_type(TEXCOORD1);
190                                 if(t>0 || active_tex)
191                                 {
192                                         glClientActiveTexture(GL_TEXTURE0+t);
193                                         active_tex = t;
194                                 }
195                                 if(arr)
196                                         glTexCoordPointer(sz, GL_FLOAT, stride_bytes, base+arr->offset);
197                                 array_type = GL_TEXTURE_COORD_ARRAY;
198                         }
199
200                         if(array_type)
201                         {
202                                 // Only change enable state if needed
203                                 if(arr && !old_arr)
204                                         glEnableClientState(array_type);
205                                 else if(old_arr && !arr)
206                                         glDisableClientState(array_type);
207
208                                 continue;
209                         }
210                 }
211
212                 if(t>=get_component_type(ATTRIB1))
213                         t -= get_component_type(ATTRIB1);
214                 if(arr)
215                 {
216                         if(arr->component==COLOR4_UBYTE)
217                                 glVertexAttribPointer(t, 4, GL_UNSIGNED_BYTE, true, stride_bytes, base+arr->offset);
218                         else
219                                 glVertexAttribPointer(t, sz, GL_FLOAT, false, stride_bytes, base+arr->offset);
220                 }
221
222                 // Only change enable state if needed
223                 if(arr && !old_arr)
224                         glEnableVertexAttribArray(t);
225                 else if(old_arr && !arr)
226                         glDisableVertexAttribArray(t);
227         }
228
229         if(active_tex)
230                 glClientActiveTexture(GL_TEXTURE0);
231
232         legacy_used = use_legacy;
233 }
234
235 void VertexArray::unbind()
236 {
237         const VertexArray *old = current();
238         if(set_current(0))
239                 apply_arrays(0, &old->arrays, 0, 0, legacy_used);
240 }
241
242
243 VertexArray::Array::Array():
244         component(0),
245         offset(0)
246 { }
247
248
249 VertexArray::Loader::Loader(VertexArray &a):
250         VertexArrayBuilder(a)
251 {
252         add("vertex2",   static_cast<void (Loader::*)(float, float)>(&Loader::vertex));
253         add("vertex3",   static_cast<void (Loader::*)(float, float, float)>(&Loader::vertex));
254         add("vertex4",   static_cast<void (Loader::*)(float, float, float, float)>(&Loader::vertex));
255         add("normal3",   static_cast<void (Loader::*)(float, float, float)>(&Loader::normal));
256         add("texcoord1", static_cast<void (Loader::*)(float)>(&Loader::texcoord));
257         add("texcoord2", static_cast<void (Loader::*)(float, float)>(&Loader::texcoord));
258         add("texcoord3", static_cast<void (Loader::*)(float, float, float)>(&Loader::texcoord));
259         add("texcoord4", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::texcoord));
260         add("multitexcoord1", static_cast<void (Loader::*)(unsigned, float)>(&Loader::multitexcoord));
261         add("multitexcoord2", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::multitexcoord));
262         add("multitexcoord3", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::multitexcoord));
263         add("multitexcoord4", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::multitexcoord));
264         add("color3",    static_cast<void (Loader::*)(float, float, float)>(&Loader::color));
265         add("color4",    static_cast<void (Loader::*)(float, float, float, float)>(&Loader::color));
266         add("attrib1",   static_cast<void (Loader::*)(unsigned, float)>(&Loader::attrib));
267         add("attrib2",   static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::attrib));
268         add("attrib3",   static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::attrib));
269         add("attrib4",   static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::attrib));
270         add("tangent3",  static_cast<void (Loader::*)(float, float, float)>(&Loader::tangent));
271         add("binormal3", static_cast<void (Loader::*)(float, float, float)>(&Loader::binormal));
272 }
273
274 } // namespace GL
275 } // namespace Msp