From 84bc56b96c21c831104a22e0cbd0f3b72ab5d8c3 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 12 Jun 2007 12:06:12 +0000 Subject: [PATCH] Add vertex arrays and buffers Add (some) matrix operations Add transform operations Texture changes: - Throw if trying to bind a texture without a target - Track textures per-unit - Move the dimension getting functions from the base class to dimensioned textures Font changes: - Change descent of glyphs to x and y offsets - Add wstring versions of get_string_width and draw_string - Use a vertex array to store glyph vertices - Add a default size for fonts --- source/error.h | 19 ++++ source/font.cpp | 122 ++++++++++++++++++-------- source/font.h | 21 ++++- source/matrix.cpp | 27 ++++++ source/matrix.h | 24 +++++ source/select.cpp | 14 ++- source/texenv.h | 13 +++ source/texture.cpp | 59 ++++++------- source/texture.h | 14 +-- source/texture2d.cpp | 40 +++++---- source/texture2d.h | 4 + source/texunit.cpp | 44 ++++++++++ source/texunit.h | 32 +++++++ source/transform.cpp | 28 ++++++ source/transform.h | 15 ++++ source/vertexarray.cpp | 188 ++++++++++++++++++++++++++++++++++++++++ source/vertexarray.h | 91 +++++++++++++++++++ source/vertexbuffer.cpp | 36 ++++++++ source/vertexbuffer.h | 27 ++++++ 19 files changed, 718 insertions(+), 100 deletions(-) create mode 100644 source/error.h create mode 100644 source/matrix.cpp create mode 100644 source/matrix.h create mode 100644 source/texunit.cpp create mode 100644 source/texunit.h create mode 100644 source/transform.cpp create mode 100644 source/transform.h create mode 100644 source/vertexarray.cpp create mode 100644 source/vertexarray.h create mode 100644 source/vertexbuffer.cpp create mode 100644 source/vertexbuffer.h diff --git a/source/error.h b/source/error.h new file mode 100644 index 00000000..8ead50d8 --- /dev/null +++ b/source/error.h @@ -0,0 +1,19 @@ +#ifndef MSP_GL_ERROR_H_ +#define MSP_GL_ERROR_H_ + +#include + +namespace Msp { +namespace GL { + +class InvalidOperation: public Exception +{ +public: + InvalidOperation(const std::string &w_): Exception(w_) { } + ~InvalidOperation() throw() { } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/font.cpp b/source/font.cpp index 31f43f73..f15e372a 100644 --- a/source/font.cpp +++ b/source/font.cpp @@ -9,7 +9,9 @@ namespace GL { Font::Font(): tex(0), - own_tex(false) + own_tex(false), + default_size(1), + varray((TEXCOORD2, VERTEX2)) { } void Font::set_texture(const Texture2D &t) @@ -17,7 +19,7 @@ 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; @@ -27,8 +29,14 @@ void Font::add_glyph(wchar_t code, float x1, float y1, float x2, float y2, float 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 va_builder=varray.modify(); + create_glyph_vertices(glyph, *va_builder); } float Font::get_string_width(const string &str) const @@ -36,30 +44,39 @@ 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(*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(*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() @@ -68,46 +85,72 @@ Font::~Font() delete tex; } -void Font::prepare(float *data) const +void Font::create_glyph_vertices() { - tex->bind(); + varray.clear(); + RefPtr 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; @@ -125,12 +168,13 @@ void Font::Loader::glyph(unsigned c) 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); } @@ -148,5 +192,11 @@ void Font::Glyph::Loader::size(float w, float h) glyph.h=h; } +void Font::Glyph::Loader::offset(float x, float y) +{ + glyph.off_x=x; + glyph.off_y=y; +} + } // namespace GL } // namespace Msp diff --git a/source/font.h b/source/font.h index a4600fc2..ebaa8e7a 100644 --- a/source/font.h +++ b/source/font.h @@ -4,6 +4,7 @@ #include #include #include +#include "vertexarray.h" namespace Msp { namespace GL { @@ -17,6 +18,8 @@ public: { public: Loader(Font &); + ~Loader(); + Font &get_object() { return font; } private: Font &font; @@ -26,10 +29,13 @@ public: 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: @@ -45,23 +51,30 @@ 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 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 diff --git a/source/matrix.cpp b/source/matrix.cpp new file mode 100644 index 00000000..925961b4 --- /dev/null +++ b/source/matrix.cpp @@ -0,0 +1,27 @@ +#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 diff --git a/source/matrix.h b/source/matrix.h new file mode 100644 index 00000000..66848b2d --- /dev/null +++ b/source/matrix.h @@ -0,0 +1,24 @@ +#ifndef MSP_GL_MATRIX_H_ +#define MSP_GL_MATRIX_H_ + +#include + +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 diff --git a/source/select.cpp b/source/select.cpp index e989de4e..60838e68 100644 --- a/source/select.cpp +++ b/source/select.cpp @@ -1,14 +1,21 @@ #include +#include "error.h" #include "select.h" using namespace std; -namespace Msp { -namespace GL { +namespace { + +using namespace Msp::GL; vector *select_buf=0; vector select_buf_int; +} + +namespace Msp { +namespace GL { + void select_buffer(vector &buf) { select_buf_int.resize(1024); @@ -38,8 +45,7 @@ void parse_select_records(const uint *buf, uint count, vector &tbu 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); } diff --git a/source/texenv.h b/source/texenv.h index 8a341db3..fbacff3f 100644 --- a/source/texenv.h +++ b/source/texenv.h @@ -4,8 +4,21 @@ 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 diff --git a/source/texture.cpp b/source/texture.cpp index d12e18e0..c367397f 100644 --- a/source/texture.cpp +++ b/source/texture.cpp @@ -1,61 +1,52 @@ +#include #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(): @@ -64,7 +55,11 @@ 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 diff --git a/source/texture.h b/source/texture.h index 7c298657..cccf35b0 100644 --- a/source/texture.h +++ b/source/texture.h @@ -30,23 +30,25 @@ enum TextureFormat 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 diff --git a/source/texture2d.cpp b/source/texture2d.cpp index e8e97592..e8c18bb8 100644 --- a/source/texture2d.cpp +++ b/source/texture2d.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "texture2d.h" using namespace std; @@ -15,16 +15,19 @@ Texture2D::Texture2D() /** 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; @@ -35,13 +38,13 @@ void Texture2D::image(int level, sizei width, sizei height, TextureFormat tfmt, 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); } /** @@ -72,8 +75,8 @@ void Texture2D::image(const string &fn) 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); @@ -96,17 +99,18 @@ void Texture2D::image(const string &fn) 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 +#include +#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::units; +TexUnit *TexUnit::cur_unit=0; + +} // namespace GL +} // namespace Msp diff --git a/source/texunit.h b/source/texunit.h new file mode 100644 index 00000000..5aec5b31 --- /dev/null +++ b/source/texunit.h @@ -0,0 +1,32 @@ +#ifndef MSP_GL_TEXUNIT_H_ +#define MSP_GL_TEXUNIT_H_ + +#include + +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 units; + static TexUnit *cur_unit; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/transform.cpp b/source/transform.cpp new file mode 100644 index 00000000..6361fc50 --- /dev/null +++ b/source/transform.cpp @@ -0,0 +1,28 @@ +#include +#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 diff --git a/source/transform.h b/source/transform.h new file mode 100644 index 00000000..520cf3be --- /dev/null +++ b/source/transform.h @@ -0,0 +1,15 @@ +#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 diff --git a/source/vertexarray.cpp b/source/vertexarray.cpp new file mode 100644 index 00000000..5d5c244a --- /dev/null +++ b/source/vertexarray.cpp @@ -0,0 +1,188 @@ +#include +#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 &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 "<>=4) + format=(format, static_cast(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 diff --git a/source/vertexarray.h b/source/vertexarray.h new file mode 100644 index 00000000..23d53be0 --- /dev/null +++ b/source/vertexarray.h @@ -0,0 +1,91 @@ +#ifndef MSP_GL_VERTEXARRAY_H_ +#define MSP_GL_VERTEXARRAY_H_ + +#include +#include +#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 &data; + + VertexArrayBuilder(VertexArray &, std::vector &); + 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 &get_data() const { return data; } + void use_vertex_buffer(); + void use_vertex_buffer(VertexBuffer *); + void clear(); + RefPtr modify() { return new VertexArrayBuilder(*this, data); } + void apply() const; + void update_data(); +private: + VertexFormat format; + std::vector data; + uint stride; + VertexBuffer *vbuf; + bool own_vbuf; + + void set_array(unsigned, unsigned, unsigned) const; + + static unsigned enabled_arrays; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/vertexbuffer.cpp b/source/vertexbuffer.cpp new file mode 100644 index 00000000..4385c2f1 --- /dev/null +++ b/source/vertexbuffer.cpp @@ -0,0 +1,36 @@ +#define GL_GLEXT_PROTOTYPES +#include +//XXX gl.h seems to include glext.h, but can I rely on this? +//#include +#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 diff --git a/source/vertexbuffer.h b/source/vertexbuffer.h new file mode 100644 index 00000000..22fcdfb1 --- /dev/null +++ b/source/vertexbuffer.h @@ -0,0 +1,27 @@ +#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 -- 2.45.2