+#include <GL/gl.h>
+#include "vertexarray.h"
+#include "vertexbuffer.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+uint get_stride(VertexFormat f)
+{
+ uint stride=0;
+ for(uint fmt=f; fmt; fmt>>=4)
+ stride+=(fmt&3)+1;
+ return stride*sizeof(float);
+}
+
+
+VertexArrayBuilder::VertexArrayBuilder(VertexArray &a, vector<float> &d):
+ data(d),
+ array(a),
+ format(array.get_format()),
+ stride(get_stride(format)),
+ cr(1), cg(1), cb(1), ca(1),
+ ts(0), tt(0), tr(0), tq(0),
+ nx(0), ny(0), nz(1)
+{ }
+
+void VertexArrayBuilder::vertex(float x, float y, float z, float w)
+{
+ for(uint fmt=format; fmt; fmt>>=4)
+ {
+ uint size=(fmt&3)+1;
+ switch(fmt&12)
+ {
+ case 0:
+ data.push_back(x);
+ data.push_back(y);
+ if(size>=3) data.push_back(z);
+ if(size>=4) data.push_back(w);
+ break;
+ case 4:
+ data.push_back(nx);
+ data.push_back(ny);
+ data.push_back(nz);
+ break;
+ case 8:
+ data.push_back(ts);
+ if(size>=2) data.push_back(tt);
+ if(size>=3) data.push_back(tr);
+ if(size>=4) data.push_back(tq);
+ break;
+ case 12:
+ if(size==1)
+ {
+ union { ubyte c[4]; float f; } u;
+ u.c[0]=(ubyte)(cr*255);
+ u.c[1]=(ubyte)(cg*255);
+ u.c[2]=(ubyte)(cb*255);
+ u.c[3]=(ubyte)(ca*255);
+ data.push_back(u.f);
+ }
+ else
+ {
+ data.push_back(cr);
+ data.push_back(cg);
+ data.push_back(cb);
+ if(size>=4) data.push_back(ca);
+ }
+ break;
+ }
+ }
+ //cout<<"Added vertex with "<<data.size()-old_size<<" floats\n";
+}
+
+VertexArrayBuilder::~VertexArrayBuilder()
+{
+ array.update_data();
+}
+
+
+VertexArray::VertexArray(VertexFormat f):
+ format(NODATA),
+ stride(get_stride(f)),
+ vbuf(0),
+ own_vbuf(false)
+{
+ // Reverse the format so the first item is in lowest bits
+ for(uint fmt=f; fmt; fmt>>=4)
+ format=(format, static_cast<VertexFormat>(fmt&15));
+}
+
+VertexArray::~VertexArray()
+{
+ if(own_vbuf)
+ delete vbuf;
+}
+
+void VertexArray::use_vertex_buffer()
+{
+ if(vbuf) return;
+
+ vbuf=new VertexBuffer();
+ own_vbuf=true;
+ update_data();
+}
+
+void VertexArray::use_vertex_buffer(VertexBuffer *b)
+{
+ if(vbuf) return;
+
+ vbuf=b;
+ update_data();
+}
+
+void VertexArray::clear()
+{
+ data.clear();
+}
+
+void VertexArray::apply() const
+{
+ if(vbuf) vbuf->bind();
+
+ const float *base=vbuf?0:&data[0];
+ uint offset=0;
+ uint found=0;
+ for(uint fmt=format; fmt; fmt>>=4)
+ {
+ uint size=(fmt&3)+1;
+ switch(fmt&12)
+ {
+ case 0:
+ glVertexPointer(size, GL_FLOAT, stride, base+offset);
+ break;
+ case 4:
+ glNormalPointer(GL_FLOAT, stride, base+offset);
+ break;
+ case 8:
+ glTexCoordPointer(size, GL_FLOAT, stride, base+offset);
+ break;
+ case 12:
+ if(size==1)
+ glColorPointer(4, GL_UNSIGNED_BYTE, stride, base+offset);
+ else
+ glColorPointer(size, GL_FLOAT, stride, base+offset);
+ break;
+ }
+ found|=1<<((fmt&12)>>2);
+ offset+=size;
+ }
+
+ set_array(GL_VERTEX_ARRAY, found&1, 1);
+ set_array(GL_NORMAL_ARRAY, found&2, 2);
+ set_array(GL_TEXTURE_COORD_ARRAY, found&4, 4);
+ set_array(GL_COLOR_ARRAY, found&8, 8);
+
+ VertexBuffer::unbind();
+}
+
+/**
+Updates the VertexArray data to the VertexBuffer tied to the array, if any.
+*/
+void VertexArray::update_data()
+{
+ if(vbuf)
+ vbuf->data(data.size()*sizeof(float), &data[0]);
+}
+
+void VertexArray::set_array(unsigned array, unsigned bit, unsigned mask) const
+{
+ if((enabled_arrays&mask) && !bit)
+ {
+ glDisableClientState(array);
+ enabled_arrays&=~mask;
+ }
+ else if(!(enabled_arrays&mask) && bit)
+ {
+ glEnableClientState(array);
+ enabled_arrays|=mask;
+ }
+}
+
+unsigned VertexArray::enabled_arrays=0;
+
+
+} // namespace GL
+} // namespace Msp