]> git.tdb.fi Git - libs/gl.git/commitdiff
Add vertex arrays and buffers
authorMikko Rasa <tdb@tdb.fi>
Tue, 12 Jun 2007 12:06:12 +0000 (12:06 +0000)
committerMikko Rasa <tdb@tdb.fi>
Tue, 12 Jun 2007 12:06:12 +0000 (12:06 +0000)
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

19 files changed:
source/error.h [new file with mode: 0644]
source/font.cpp
source/font.h
source/matrix.cpp [new file with mode: 0644]
source/matrix.h [new file with mode: 0644]
source/select.cpp
source/texenv.h
source/texture.cpp
source/texture.h
source/texture2d.cpp
source/texture2d.h
source/texunit.cpp [new file with mode: 0644]
source/texunit.h [new file with mode: 0644]
source/transform.cpp [new file with mode: 0644]
source/transform.h [new file with mode: 0644]
source/vertexarray.cpp [new file with mode: 0644]
source/vertexarray.h [new file with mode: 0644]
source/vertexbuffer.cpp [new file with mode: 0644]
source/vertexbuffer.h [new file with mode: 0644]

diff --git a/source/error.h b/source/error.h
new file mode 100644 (file)
index 0000000..8ead50d
--- /dev/null
@@ -0,0 +1,19 @@
+#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
index 31f43f7347802f6124c0388c15519cea4864444b..f15e372afaabfaf0946f1aa9ec6093ff80008e66 100644 (file)
@@ -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<VertexArrayBuilder> 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<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()
@@ -68,46 +85,72 @@ 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;
@@ -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
index a4600fc2558652950dc8088fa146086f592765bf..ebaa8e7a7f43f469b7ae1ae246db6d92ad860bde 100644 (file)
@@ -4,6 +4,7 @@
 #include <map>
 #include <string>
 #include <msp/parser/loader.h>
+#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<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
diff --git a/source/matrix.cpp b/source/matrix.cpp
new file mode 100644 (file)
index 0000000..925961b
--- /dev/null
@@ -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 (file)
index 0000000..66848b2
--- /dev/null
@@ -0,0 +1,24 @@
+#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
index e989de4e99b59a931fc18cbd750656240acd513c..60838e6857dd206cff99d15593d9f2bd3d71d5f3 100644 (file)
@@ -1,14 +1,21 @@
 #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);
@@ -38,8 +45,7 @@ void parse_select_records(const uint *buf, uint count, vector<SelectRecord> &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);
 }
 
index 8a341db331e13371939fbd7216499e3cc76b10df..fbacff3f69bec629ce8a17a307b250247b4e314e 100644 (file)
@@ -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
index d12e18e0fa4766efd60bf46ff594376f7796b546..c367397fb76bb19d2392b17971b915b1a31ea9a8 100644 (file)
@@ -1,61 +1,52 @@
+#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():
@@ -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
index 7c29865747e79f6e7972bc329885303ea09fe52e..cccf35b038a461e5d3335dca0ca1d9b050d5f4c7 100644 (file)
@@ -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
index e8e975926e62d683c9bc9c2f978654e796fc0ac7..e8c18bb8ea5ecf890a44eb513a8c0f2b08551ef4 100644 (file)
@@ -1,5 +1,5 @@
 #include <png.h>
-#include <msp/error.h>
+#include <msp/core/error.h>
 #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<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);
index 86c2a89bccb700b87e0424aa7599a17fdc7d7337..ef6261cb98678d7efdfb24f0934e06453f0cbc0e 100644 (file)
@@ -14,7 +14,11 @@ public:
        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
diff --git a/source/texunit.cpp b/source/texunit.cpp
new file mode 100644 (file)
index 0000000..fd0fe31
--- /dev/null
@@ -0,0 +1,44 @@
+#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
diff --git a/source/texunit.h b/source/texunit.h
new file mode 100644 (file)
index 0000000..5aec5b3
--- /dev/null
@@ -0,0 +1,32 @@
+#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 &current();
+private:
+       const Texture *texture;
+       //TexEnv env;
+
+       static std::vector<TexUnit> units;
+       static TexUnit *cur_unit;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/transform.cpp b/source/transform.cpp
new file mode 100644 (file)
index 0000000..6361fc5
--- /dev/null
@@ -0,0 +1,28 @@
+#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
diff --git a/source/transform.h b/source/transform.h
new file mode 100644 (file)
index 0000000..520cf3b
--- /dev/null
@@ -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 (file)
index 0000000..5d5c244
--- /dev/null
@@ -0,0 +1,188 @@
+#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
diff --git a/source/vertexarray.h b/source/vertexarray.h
new file mode 100644 (file)
index 0000000..23d53be
--- /dev/null
@@ -0,0 +1,91 @@
+#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
diff --git a/source/vertexbuffer.cpp b/source/vertexbuffer.cpp
new file mode 100644 (file)
index 0000000..4385c2f
--- /dev/null
@@ -0,0 +1,36 @@
+#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
diff --git a/source/vertexbuffer.h b/source/vertexbuffer.h
new file mode 100644 (file)
index 0000000..22fcdfb
--- /dev/null
@@ -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