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