--- /dev/null
+#ifndef MSP_GL_ERROR_H_
+#define MSP_GL_ERROR_H_
+
+#include <msp/core/error.h>
+
+namespace Msp {
+namespace GL {
+
+class InvalidOperation: public Exception
+{
+public:
+ InvalidOperation(const std::string &w_): Exception(w_) { }
+ ~InvalidOperation() throw() { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
Font::Font():
tex(0),
- own_tex(false)
+ own_tex(false),
+ default_size(1),
+ varray((TEXCOORD2, VERTEX2))
{ }
void Font::set_texture(const Texture2D &t)
tex=&t;
}
-void Font::add_glyph(wchar_t code, float x1, float y1, float x2, float y2, float w, float h, float desc, float adv)
+void Font::add_glyph(wchar_t code, float x1, float y1, float x2, float y2, float w, float h, float ox, float oy, float adv)
{
Glyph glyph;
glyph.code=code;
glyph.y2=y2;
glyph.w=w;
glyph.h=h;
- glyph.descent=desc;
+ glyph.off_x=ox;
+ glyph.off_y=oy;
glyph.advance=adv;
+ glyph.index=glyphs.size();
+ glyphs.insert(GlyphMap::value_type(code, glyph));
+
+ RefPtr<VertexArrayBuilder> va_builder=varray.modify();
+ create_glyph_vertices(glyph, *va_builder);
}
float Font::get_string_width(const string &str) const
float x=0;
for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
- {
- GlyphMap::const_iterator j=glyphs.find((unsigned char)*i);
- if(j==glyphs.end())
- continue;
+ x+=get_glyph_advance(static_cast<unsigned char>(*i));
- const Glyph &glyph=j->second;
+ return x;
+}
- x+=glyph.advance;
- }
+float Font::get_string_width(const wstring &str) const
+{
+ float x=0;
+
+ for(wstring::const_iterator i=str.begin(); i!=str.end(); ++i)
+ x+=get_glyph_advance(*i);
return x;
}
void Font::draw_string(const string &str) const
{
- float x=0;
- float data[16];
-
- prepare(data);
+ prepare_render();
for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
- draw_glyph((unsigned char)*i, data, x);
+ draw_glyph(static_cast<unsigned char>(*i));
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix();
+}
+
+void Font::draw_string(const wstring &str) const
+{
+ prepare_render();
+
+ for(wstring::const_iterator i=str.begin(); i!=str.end(); ++i)
+ draw_glyph(*i);
+
+ glPopMatrix();
}
Font::~Font()
delete tex;
}
-void Font::prepare(float *data) const
+void Font::create_glyph_vertices()
{
- tex->bind();
+ varray.clear();
+ RefPtr<VertexArrayBuilder> va_builder=varray.modify();
+
+ unsigned n=0;
+ for(GlyphMap::iterator i=glyphs.begin(); i!=glyphs.end(); ++i, ++n)
+ {
+ i->second.index=n;
+ create_glyph_vertices(i->second, *va_builder);
+ }
+}
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glEnableClientState(GL_VERTEX_ARRAY);
- glTexCoordPointer(2, GL_FLOAT, 4*sizeof(float), data);
- glVertexPointer(2, GL_FLOAT, 4*sizeof(float), data+2);
+void Font::create_glyph_vertices(const Glyph &glyph, VertexArrayBuilder &va_builder)
+{
+ va_builder.texcoord(glyph.x1, glyph.y1);
+ va_builder.vertex(glyph.off_x, glyph.off_y);
+ va_builder.texcoord(glyph.x2, glyph.y1);
+ va_builder.vertex(glyph.off_x+glyph.w, glyph.off_y);
+ va_builder.texcoord(glyph.x2, glyph.y2);
+ va_builder.vertex(glyph.off_x+glyph.w, glyph.off_y+glyph.h);
+ va_builder.texcoord(glyph.x1, glyph.y2);
+ va_builder.vertex(glyph.off_x, glyph.off_y+glyph.h);
+}
+
+void Font::prepare_render() const
+{
+ tex->bind();
+ varray.apply();
+ glPushMatrix();
}
-void Font::draw_glyph(wchar_t code, float *data, float &x) const
+void Font::draw_glyph(wchar_t code) const
{
GlyphMap::const_iterator i=glyphs.find(code);
if(i==glyphs.end())
return;
- const Glyph &glyph=i->second;
-
- data[0]=data[12]=glyph.x1;
- data[1]=data[5]=glyph.y1;
- data[4]=data[8]=glyph.x2;
- data[9]=data[13]=glyph.y2;
+ glDrawArrays(GL_QUADS, i->second.index*4, 4);
- data[2]=data[14]=x;
- data[3]=data[7]=glyph.descent;
- data[6]=data[10]=x+glyph.w;
- data[11]=data[15]=glyph.h+glyph.descent;
+ glTranslatef(i->second.advance, 0, 0);
+}
- glDrawArrays(GL_QUADS, 0, 4);
+float Font::get_glyph_advance(wchar_t code) const
+{
+ GlyphMap::const_iterator i=glyphs.find(code);
+ if(i==glyphs.end())
+ return 0;
- x+=glyph.advance;
+ return i->second.advance;
}
+
Font::Loader::Loader(Font &f):
font(f)
{
+ add("default_size", &Font::default_size);
add("texture", &Loader::texture);
add("glyph", &Loader::glyph);
}
+Font::Loader::~Loader()
+{
+ font.create_glyph_vertices();
+}
+
void Font::Loader::texture(const string &t)
{
Texture2D *tex=new Texture2D;
font.glyphs.insert(GlyphMap::value_type(c, gl));
}
+
Font::Glyph::Loader::Loader(Glyph &g):
glyph(g)
{
add("texcoords", &Loader::texcoords);
add("size", &Loader::size);
- add("descent", &Glyph::descent);
+ add("offset", &Loader::offset);
add("advance", &Glyph::advance);
}
glyph.h=h;
}
+void Font::Glyph::Loader::offset(float x, float y)
+{
+ glyph.off_x=x;
+ glyph.off_y=y;
+}
+
} // namespace GL
} // namespace Msp
#include <map>
#include <string>
#include <msp/parser/loader.h>
+#include "vertexarray.h"
namespace Msp {
namespace GL {
{
public:
Loader(Font &);
+ ~Loader();
+ Font &get_object() { return font; }
private:
Font &font;
Font();
void set_texture(const Texture2D &);
- void add_glyph(wchar_t, float, float, float, float, float, float, float, float);
+ void add_glyph(wchar_t, float, float, float, float, float, float, float, float, float);
+ float get_default_size() const { return default_size; }
float get_string_width(const std::string &) const;
+ float get_string_width(const std::wstring &) const;
void draw_glyph(wchar_t);
void draw_string(const std::string &) const;
+ void draw_string(const std::wstring &) const;
void draw_multiline(const std::string &) const;
~Font();
private:
void texcoords(float, float, float, float);
void size(float, float);
+ void offset(float, float);
};
wchar_t code;
float x1,y1;
float x2,y2;
float w,h;
- float descent;
+ float off_x, off_y;
float advance;
+ unsigned index;
};
typedef std::map<wchar_t, Glyph> GlyphMap;
const Texture2D *tex;
bool own_tex;
+ float default_size;
GlyphMap glyphs;
+ VertexArray varray;
- void prepare(float *) const;
- void draw_glyph(wchar_t, float *, float &) const;
+ void create_glyph_vertices();
+ void create_glyph_vertices(const Glyph &, VertexArrayBuilder &);
+ void prepare_render() const;
+ void draw_glyph(wchar_t) const;
+ float get_glyph_advance(wchar_t) const;
};
} // namespace GL
--- /dev/null
+#include "matrix.h"
+
+namespace Msp {
+namespace GL {
+
+void matrix_mode(MatrixMode m)
+{
+ glMatrixMode(m);
+}
+
+void load_identity()
+{
+ glLoadIdentity();
+}
+
+void push_matrix()
+{
+ glPushMatrix();
+}
+
+void pop_matrix()
+{
+ glPopMatrix();
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_MATRIX_H_
+#define MSP_GL_MATRIX_H_
+
+#include <GL/gl.h>
+
+namespace Msp {
+namespace GL {
+
+enum MatrixMode
+{
+ MODELVIEW=GL_MODELVIEW,
+ PROJECTION=GL_PROJECTION,
+ TEXTURE=GL_TEXTURE
+};
+
+void matrix_mode(MatrixMode);
+void load_identity();
+void push_matrix();
+void pop_matrix();
+
+} // namespace GL
+} // namespace Msp
+
+#endif
#include <GL/gl.h>
+#include "error.h"
#include "select.h"
using namespace std;
-namespace Msp {
-namespace GL {
+namespace {
+
+using namespace Msp::GL;
vector<SelectRecord> *select_buf=0;
vector<uint> select_buf_int;
+}
+
+namespace Msp {
+namespace GL {
+
void select_buffer(vector<SelectRecord> &buf)
{
select_buf_int.resize(1024);
void _parse_internal_select_records(uint count)
{
if(!select_buf)
- //XXX throw InvalidOperation();
- return;
+ throw InvalidOperation("No select buffer specified");
parse_select_records(&select_buf_int[0], count, *select_buf);
}
namespace Msp {
namespace GL {
+enum TexEnvMode
+{
+ REPLACE = GL_REPLACE,
+ MODULATE = GL_MODULATE,
+ DECAL = GL_DECAL,
+ BLEND = GL_BLEND,
+ ADD = GL_ADD,
+ COMBINE = GL_COMBINE
+};
+
class TexEnv
{
+public:
+ void mode(TexEnvMode);
+ void color(float, float, float, float);
};
} // namespace GL
+#include <msp/core/error.h>
#include "texture.h"
+#include "texunit.h"
namespace Msp {
namespace GL {
void Texture::bind() const
{
- if(!target) return;
-
- glEnable(target);
+ if(!target)
+ throw InvalidState("Attempt to bind a texture without target");
+
+ const Texture *cur=TexUnit::current().get_texture();
+ if(cur && cur->target!=target)
+ glDisable(cur->target);
+ if(!cur || cur->target!=target)
+ glEnable(target);
glBindTexture(target, id);
- bound=this;
+ TexUnit::current().set_texture(this);
}
void Texture::parameter(GLenum param, int value)
{
- if(bound!=this) bind();
+ maybe_bind();
glTexParameteri(target, param, value);
}
void Texture::parameter(GLenum param, float value)
{
- if(bound!=this) bind();
+ maybe_bind();
glTexParameterf(target, param, value);
}
-sizei Texture::get_width(int level) const
-{
- if(bound!=this) bind();
-
- int width;
- glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
- return width;
-}
-
-sizei Texture::get_height(int level) const
+Texture::~Texture()
{
- if(bound!=this) bind();
-
- int height;
- glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
- return height;
+ glDeleteTextures(1, &id);
}
-sizei Texture::get_depth(int level) const
+void Texture::unbind()
{
- if(bound!=this) bind();
+ const Texture *cur=TexUnit::current().get_texture();
+ if(!cur)
+ return;
- int depth;
- glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth);
- return depth;
-}
-
-Texture::~Texture()
-{
- glDeleteTextures(1, &id);
+ glBindTexture(cur->target, 0);
+ glDisable(cur->target);
+ TexUnit::current().set_texture(0);
}
Texture::Texture():
glGenTextures(1, &id);
}
-const Texture *Texture::bound=0;
+void Texture::maybe_bind() const
+{
+ if(TexUnit::current().get_texture()!=this)
+ bind();
+}
} // namespace GL
} // namespace Msp
class Texture
{
public:
+ ~Texture();
+
void bind() const;
void parameter(GLenum, int);
void parameter(GLenum, float);
void set_min_filter(TextureFilter f) { parameter(GL_TEXTURE_MIN_FILTER, f); }
void set_mag_filter(TextureFilter f) { parameter(GL_TEXTURE_MAG_FILTER, f); }
+ GLenum get_target() const { return target; }
uint get_id() const { return id; }
- sizei get_width(int =0) const;
- sizei get_height(int =0) const;
- sizei get_depth(int =0) const;
- ~Texture();
+
+ static void unbind();
protected:
uint id;
GLenum target;
Texture();
-
- static const Texture *bound;
+ Texture(const Texture &);
+ Texture &operator=(const Texture &);
+ void maybe_bind() const;
};
} // namespace GL
#include <png.h>
-#include <msp/error.h>
+#include <msp/core/error.h>
#include "texture2d.h"
using namespace std;
/**
Uploads an image into the texture. Direct wrapper for glTexImage2D.
*/
-void Texture2D::image(int level, int ifmt, sizei width, sizei height, int border, GLenum fmt, GLenum type, void *data)
+void Texture2D::image(int level, int ifmt, sizei wd, sizei ht, int border, GLenum fmt, GLenum type, void *data)
{
- if(bound!=this) bind();
- glTexImage2D(target, level, ifmt, width, height, border, fmt, type, data);
+ maybe_bind();
+
+ glTexImage2D(target, level, ifmt, wd, ht, border, fmt, type, data);
+ width_=wd;
+ height_=ht;
}
/**
-Uploads an image into the texture.
+Uploads an image into the texture, with a simpler interface.
*/
-void Texture2D::image(int level, sizei width, sizei height, TextureFormat tfmt, void *data)
+void Texture2D::image(int level, sizei wd, sizei ht, TextureFormat tfmt, void *data)
{
int ifmt;
int fmt;
case LUMINANCE8: ifmt=GL_LUMINANCE; fmt=GL_LUMINANCE; type=GL_UNSIGNED_BYTE; break;
case LUMINANCE8_ALPHA8: ifmt=GL_LUMINANCE_ALPHA; fmt=GL_LUMINANCE_ALPHA; type=GL_UNSIGNED_BYTE; break;
case RGB8: ifmt=GL_RGB; fmt=GL_RGB; type=GL_UNSIGNED_BYTE; break;
- case RGBA8: ifmt=GL_RGBA; fmt=GL_RGBA; type=GL_UNSIGNED_INT_8_8_8_8; break;
+ case RGBA8: ifmt=GL_RGBA; fmt=GL_RGBA; type=GL_UNSIGNED_INT_8_8_8_8_REV; break;
case BGR8: ifmt=GL_RGB; fmt=GL_BGR; type=GL_UNSIGNED_BYTE; break;
- case BGRA8: ifmt=GL_RGBA; fmt=GL_BGRA; type=GL_UNSIGNED_INT_8_8_8_8; break;
- default: return;
+ case BGRA8: ifmt=GL_RGBA; fmt=GL_BGRA; type=GL_UNSIGNED_INT_8_8_8_8_REV; break;
+ default: throw InvalidParameterValue("Invalid texture format");
}
- image(level, ifmt, width, height, 0, fmt, type, data);
+ image(level, ifmt, wd, ht, 0, fmt, type, data);
}
/**
png_read_info(pngs, pngi);
- unsigned width=png_get_image_width(pngs, pngi);
- unsigned height=png_get_image_height(pngs, pngi);
+ unsigned wd=png_get_image_width(pngs, pngi);
+ unsigned ht=png_get_image_height(pngs, pngi);
unsigned depth=png_get_bit_depth(pngs, pngi);
unsigned ctype=png_get_color_type(pngs, pngi);
case PNG_COLOR_TYPE_GRAY_ALPHA: fmt=LUMINANCE8_ALPHA8; planes=2; break;
case PNG_COLOR_TYPE_RGB: fmt=RGB8; planes=3; break;
case PNG_COLOR_TYPE_RGB_ALPHA: fmt=RGBA8; planes=4; break;
+ default: throw Exception("Invalid color type");
}
- png_byte *data=(png_byte *)malloc(width*height*planes);
- png_byte *row_ptrs[height];
- for(unsigned i=0; i<height; ++i)
- row_ptrs[i]=data+(height-1-i)*width*planes;
+ png_byte *data=new png_byte[wd*ht*planes];
+ png_byte *row_ptrs[ht];
+ for(unsigned i=0; i<ht; ++i)
+ row_ptrs[i]=data+(ht-1-i)*wd*planes;
png_read_image(pngs, row_ptrs);
- image(0, width, height, fmt, data);
- free(data);
+ image(0, wd, ht, fmt, data);
+ delete[] data;
png_destroy_read_struct(&pngs, &pngi, 0);
fclose(file);
void image(int, int, sizei, sizei, int, GLenum, GLenum, void *);
void image(int, sizei, sizei, TextureFormat, void *);
void image(const std::string &);
+ sizei get_width() const { return width_; }
+ sizei get_height() const { return height_; }
private:
+ sizei width_;
+ sizei height_;
};
} // namespace GL
--- /dev/null
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+TexUnit::TexUnit():
+ texture(0)
+{ }
+
+bool TexUnit::set_texture(const Texture *tex)
+{
+ bool result=(tex!=texture);
+ texture=tex;
+ return result;
+}
+
+TexUnit &TexUnit::activate(unsigned n)
+{
+ if(units.size()<=n)
+ units.resize(n+1);
+
+ glActiveTextureARB(GL_TEXTURE0+n);
+ cur_unit=&units[n];
+
+ return units[n];
+}
+
+TexUnit &TexUnit::current()
+{
+ if(!cur_unit)
+ return activate(0);
+ return *cur_unit;
+}
+
+vector<TexUnit> TexUnit::units;
+TexUnit *TexUnit::cur_unit=0;
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_TEXUNIT_H_
+#define MSP_GL_TEXUNIT_H_
+
+#include <vector>
+
+namespace Msp {
+namespace GL {
+
+class Texture;
+
+class TexUnit
+{
+public:
+ TexUnit();
+ bool set_texture(const Texture *);
+ const Texture *get_texture() { return texture; }
+ //TexEnv &get_env() { return env; }
+
+ static TexUnit &activate(unsigned);
+ static TexUnit ¤t();
+private:
+ const Texture *texture;
+ //TexEnv env;
+
+ static std::vector<TexUnit> units;
+ static TexUnit *cur_unit;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <GL/gl.h>
+#include "transform.h"
+
+namespace Msp {
+namespace GL {
+
+void translate(float x, float y, float z)
+{
+ glTranslatef(x, y, z);
+}
+
+void rotate(float a, float x, float y, float z)
+{
+ glRotatef(a, x, y, z);
+}
+
+void scale(float x, float y, float z)
+{
+ glScalef(x, y, z);
+}
+
+void scale_uniform(float s)
+{
+ scale(s, s, s);
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_TRANSFORM_H_
+#define MSP_GL_TRANSFORM_H_
+
+namespace Msp {
+namespace GL {
+
+void translate(float, float, float);
+void rotate(float, float, float, float);
+void scale(float, float, float);
+void scale_uniform(float);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifndef MSP_GL_VERTEXARRAY_H_
+#define MSP_GL_VERTEXARRAY_H_
+
+#include <vector>
+#include <msp/core/refptr.h>
+#include "types.h"
+
+namespace Msp {
+namespace GL {
+
+class VertexArray;
+class VertexBuffer;
+
+enum VertexFormat
+{
+ NODATA=0,
+ VERTEX2=1,
+ VERTEX3,
+ VERTEX4,
+ NORMAL3=6,
+ TEXCOORD1=8,
+ TEXCOORD2,
+ TEXCOORD3,
+ TEXCOORD4,
+ COLOR4_UBYTE=12,
+ COLOR3_FLOAT=14,
+ COLOR4_FLOAT,
+};
+
+inline VertexFormat operator,(VertexFormat a, VertexFormat b) { return VertexFormat((a<<4)|b); }
+uint get_stride(VertexFormat);
+
+class VertexArrayBuilder
+{
+public:
+ std::vector<float> &data;
+
+ VertexArrayBuilder(VertexArray &, std::vector<float> &);
+ void vertex(float x, float y) { vertex(x, y, 0, 1); }
+ void vertex(float x, float y, float z) { vertex(x, y, z, 1); }
+ void vertex(float, float, float, float);
+ void normal(float x, float y, float z) { nx=x; ny=y; nz=z; }
+ void texcoord(float s) { texcoord(s, 0, 0, 1); }
+ void texcoord(float s, float t) { texcoord(s, t, 0, 1); }
+ void texcoord(float s, float t, float r) { texcoord(s, t, r, 1); }
+ void texcoord(float s, float t, float r, float q) { ts=s; tt=t; tr=r; tq=q; }
+ void color(ubyte r, ubyte g, ubyte b) { color(r, g, b, 255); }
+ void color(ubyte r, ubyte g, ubyte b, ubyte a) { color(r/255.f, g/255.f, b/255.f, a/255.f); }
+ void color(float r, float g, float b) { color(r, g, b, 1); }
+ void color(float r, float g, float b, float a) { cr=r; cg=g; cb=b; ca=a; }
+ ~VertexArrayBuilder();
+private:
+ VertexArray &array;
+ VertexFormat format;
+ uint stride;
+
+ float cr, cg, cb, ca; // Color
+ float ts, tt, tr, tq; // TexCoord
+ float nx, ny, nz; // Normal
+};
+
+class VertexArray
+{
+public:
+ VertexArray(VertexFormat);
+ ~VertexArray();
+
+ VertexFormat get_format() const { return format; }
+ const std::vector<float> &get_data() const { return data; }
+ void use_vertex_buffer();
+ void use_vertex_buffer(VertexBuffer *);
+ void clear();
+ RefPtr<VertexArrayBuilder> modify() { return new VertexArrayBuilder(*this, data); }
+ void apply() const;
+ void update_data();
+private:
+ VertexFormat format;
+ std::vector<float> data;
+ uint stride;
+ VertexBuffer *vbuf;
+ bool own_vbuf;
+
+ void set_array(unsigned, unsigned, unsigned) const;
+
+ static unsigned enabled_arrays;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+//XXX gl.h seems to include glext.h, but can I rely on this?
+//#include <GL/glext.h>
+#include "vertexbuffer.h"
+
+namespace Msp {
+namespace GL {
+
+VertexBuffer::VertexBuffer()
+{
+ glGenBuffers(1, &id);
+}
+
+void VertexBuffer::bind() const
+{
+ glBindBuffer(GL_ARRAY_BUFFER, id);
+ bound=this;
+}
+
+void VertexBuffer::data(sizei size, void *d)
+{
+ if(bound!=this) bind();
+
+ glBufferData(GL_ARRAY_BUFFER, size, d, GL_STATIC_DRAW);
+}
+
+VertexBuffer::~VertexBuffer()
+{
+ glDeleteBuffers(1, &id);
+}
+
+const VertexBuffer *VertexBuffer::bound=0;
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_VERTEXBUFFER_H_
+#define MSP_GL_VERTEXBUFFER_H_
+
+#include "types.h"
+
+namespace Msp {
+namespace GL {
+
+class VertexBuffer
+{
+public:
+ VertexBuffer();
+ void bind() const;
+ void data(sizei, void *);
+ ~VertexBuffer();
+
+ static void unbind() { bound=0; }
+private:
+ uint id;
+
+ static const VertexBuffer *bound;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif