OpenGL hacks.
authorCarl Hetherington <cth@carlh.net>
Mon, 28 Jan 2019 22:41:25 +0000 (22:41 +0000)
committerCarl Hetherington <cth@carlh.net>
Mon, 28 Jan 2019 22:41:25 +0000 (22:41 +0000)
hacks/gl/.gitignore [new file with mode: 0644]
hacks/gl/Makefile [new file with mode: 0644]
hacks/gl/basic_gl_pane.cc [new file with mode: 0644]
hacks/gl/basic_gl_pane.h [new file with mode: 0644]
hacks/gl/drawable.cc [new file with mode: 0644]
hacks/gl/drawable.h [new file with mode: 0644]
hacks/gl/image.cc [new file with mode: 0644]
hacks/gl/image.h [new file with mode: 0644]
hacks/gl/image_loader.cc [new file with mode: 0644]
hacks/gl/image_loader.h [new file with mode: 0644]

diff --git a/hacks/gl/.gitignore b/hacks/gl/.gitignore
new file mode 100644 (file)
index 0000000..ccd496b
--- /dev/null
@@ -0,0 +1,3 @@
+gl
+myfile.png
+
diff --git a/hacks/gl/Makefile b/hacks/gl/Makefile
new file mode 100644 (file)
index 0000000..7be9baf
--- /dev/null
@@ -0,0 +1,2 @@
+gl:    drawable.cc basic_gl_pane.cc image.cc image_loader.cc
+       g++ drawable.cc basic_gl_pane.cc image.cc image_loader.cc -o gl `wx-config --cflags --libs --gl-libs` -lGL -lGLU
diff --git a/hacks/gl/basic_gl_pane.cc b/hacks/gl/basic_gl_pane.cc
new file mode 100644 (file)
index 0000000..565bf4c
--- /dev/null
@@ -0,0 +1,167 @@
+#include "wx/wx.h"
+#include "wx/sizer.h"
+#include "wx/glcanvas.h"
+
+#include "basic_gl_pane.h"
+#include "image.h"
+#include "drawable.h"
+
+// include OpenGL
+#ifdef __WXMAC__
+#include "OpenGL/glu.h"
+#include "OpenGL/gl.h"
+#else
+#include <GL/glu.h>
+#include <GL/gl.h>
+#endif
+
+class MyApp: public wxApp
+{
+    virtual bool OnInit();
+
+    wxFrame *frame;
+    BasicGLPane * glPane;
+public:
+
+};
+
+IMPLEMENT_APP(MyApp)
+
+Image* image = NULL;
+DrawableThing* sprite;
+
+bool MyApp::OnInit()
+{
+    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
+    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello GL World"), wxPoint(50,50), wxSize(400,200));
+
+    int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0};
+
+    glPane = new BasicGLPane( (wxFrame*) frame, args);
+    sizer->Add(glPane, 1, wxEXPAND);
+
+    frame->SetSizer(sizer);
+    frame->SetAutoLayout(true);
+
+    frame->Show();
+    return true;
+}
+
+BEGIN_EVENT_TABLE(BasicGLPane, wxGLCanvas)
+EVT_MOTION(BasicGLPane::mouseMoved)
+EVT_LEFT_DOWN(BasicGLPane::mouseDown)
+EVT_LEFT_UP(BasicGLPane::mouseReleased)
+EVT_RIGHT_DOWN(BasicGLPane::rightClick)
+EVT_LEAVE_WINDOW(BasicGLPane::mouseLeftWindow)
+EVT_SIZE(BasicGLPane::resized)
+EVT_KEY_DOWN(BasicGLPane::keyPressed)
+EVT_KEY_UP(BasicGLPane::keyReleased)
+EVT_MOUSEWHEEL(BasicGLPane::mouseWheelMoved)
+EVT_PAINT(BasicGLPane::render)
+END_EVENT_TABLE()
+
+
+// some useful events to use
+void BasicGLPane::mouseMoved(wxMouseEvent& event) {}
+void BasicGLPane::mouseDown(wxMouseEvent& event) {}
+void BasicGLPane::mouseWheelMoved(wxMouseEvent& event) {}
+void BasicGLPane::mouseReleased(wxMouseEvent& event) {}
+void BasicGLPane::rightClick(wxMouseEvent& event) {}
+void BasicGLPane::mouseLeftWindow(wxMouseEvent& event) {}
+void BasicGLPane::keyPressed(wxKeyEvent& event) {}
+void BasicGLPane::keyReleased(wxKeyEvent& event) {}
+
+BasicGLPane::BasicGLPane(wxFrame* parent, int* args) :
+wxGLCanvas(parent, wxID_ANY,  wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas"),  args)
+{
+}
+
+void BasicGLPane::resized(wxSizeEvent& evt)
+{
+    wxGLCanvas::OnSize(evt);
+
+    Refresh();
+}
+
+void BasicGLPane::prepare3DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y)
+{
+    /*
+     *  Inits the OpenGL viewport for drawing in 3D.
+     */
+
+    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
+    glClearDepth(1.0f);        // Depth Buffer Setup
+    glEnable(GL_DEPTH_TEST); // Enables Depth Testing
+    glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+    glEnable(GL_COLOR_MATERIAL);
+
+    glViewport(topleft_x, topleft_y, bottomrigth_x-topleft_x, bottomrigth_y-topleft_y);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+    float ratio_w_h = (float)(bottomrigth_x-topleft_x)/(float)(bottomrigth_y-topleft_y);
+    gluPerspective(45 /*view angle*/, ratio_w_h, 0.1 /*clip close*/, 200 /*clip far*/);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+
+}
+
+void BasicGLPane::prepare2DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y)
+{
+
+    /*
+     *  Inits the OpenGL viewport for drawing in 2D
+     */
+
+    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
+    glEnable(GL_TEXTURE_2D);   // textures
+    glEnable(GL_COLOR_MATERIAL);
+    glEnable(GL_BLEND);
+    glDisable(GL_DEPTH_TEST);
+    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+    glViewport(topleft_x, topleft_y, bottomrigth_x-topleft_x, bottomrigth_y-topleft_y);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+    gluOrtho2D(topleft_x, bottomrigth_x, bottomrigth_y, topleft_y);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+}
+
+int BasicGLPane::getWidth()
+{
+    return GetSize().x;
+}
+
+int BasicGLPane::getHeight()
+{
+    return GetSize().y;
+}
+
+void BasicGLPane::render( wxPaintEvent& evt )
+{
+    if(!IsShown()) return;
+
+    wxGLCanvas::SetCurrent();
+
+
+    if(image == NULL)
+    {
+        image = new Image( wxT("myfile.png") );
+        sprite = new DrawableThing(image);
+    }
+
+    wxPaintDC(this); // only to be used in paint events. use wxClientDC to paint outside the paint event
+
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    // render loaded image
+    prepare2DViewport(0,0,getWidth(), getHeight());
+    sprite->render();
+
+    glFlush();
+    SwapBuffers();
+}
diff --git a/hacks/gl/basic_gl_pane.h b/hacks/gl/basic_gl_pane.h
new file mode 100644 (file)
index 0000000..b085dbb
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _glpane_
+#define _glpane_
+
+#include "wx/wx.h"
+#include "wx/glcanvas.h"
+
+class BasicGLPane : public wxGLCanvas
+{
+    
+public:
+       BasicGLPane(wxFrame* parent, int* args);
+    
+       void resized(wxSizeEvent& evt);
+    
+       int getWidth();
+       int getHeight();
+    
+       void render(wxPaintEvent& evt);
+       void prepare3DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y);
+       void prepare2DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y);
+    
+       // events
+       void mouseMoved(wxMouseEvent& event);
+       void mouseDown(wxMouseEvent& event);
+       void mouseWheelMoved(wxMouseEvent& event);
+       void mouseReleased(wxMouseEvent& event);
+       void rightClick(wxMouseEvent& event);
+       void mouseLeftWindow(wxMouseEvent& event);
+       void keyPressed(wxKeyEvent& event);
+       void keyReleased(wxKeyEvent& event);
+    
+       DECLARE_EVENT_TABLE()
+};
+
+#endif
diff --git a/hacks/gl/drawable.cc b/hacks/gl/drawable.cc
new file mode 100644 (file)
index 0000000..ba790fe
--- /dev/null
@@ -0,0 +1,113 @@
+#include "drawable.h"
+#include <iostream>
+
+#ifdef __WXMAC__
+#include "OpenGL/gl.h"
+#else
+#include <GL/gl.h>
+#endif
+
+#include "wx/wx.h"
+
+/*
+ * This is a simple class built on top of OpenGL that manages drawing images in a higher-level and quicker way.
+ */
+
+DrawableThing::DrawableThing(Image* image_arg)
+{
+
+    x=0;
+    y=0;
+    hotspotX=0;
+    hotspotY=0;
+    angle=0;
+
+    xscale=1;
+    yscale=1;
+
+    xflip=false;
+    yflip=false;
+
+    if(image_arg!=NULL) setImage(image_arg);
+    else image=NULL;
+}
+
+void DrawableThing::setFlip(bool x, bool y)
+{
+    xflip=x;
+    yflip=y;
+}
+
+void DrawableThing::setHotspot(int x, int y)
+{
+    hotspotX=x;
+    hotspotY=y;
+}
+
+void DrawableThing::move(int x, int y)
+{
+    DrawableThing::x=x;
+    DrawableThing::y=y;
+}
+
+void DrawableThing::scale(float x, float y)
+{
+    DrawableThing::xscale=x;
+    DrawableThing::yscale=y;
+}
+
+void DrawableThing::scale(float k)
+{
+    DrawableThing::xscale=k;
+    DrawableThing::yscale=k;
+}
+
+void DrawableThing::setImage(Image* image)
+{
+    DrawableThing::image=image;
+}
+
+void DrawableThing::rotate(int angle)
+{
+    DrawableThing::angle=angle;
+}
+
+void DrawableThing::render()
+{
+    assert(image!=NULL);
+
+    glLoadIdentity();
+
+    glTranslatef(x,y,0);
+
+    if(xscale!=1 || yscale!=1)
+       {
+        glScalef(xscale, yscale, 1);
+    }
+
+    if(angle!=0)
+       {
+        glRotatef(angle, 0,0,1);
+    }
+
+    glBindTexture(GL_TEXTURE_2D, image->getID()[0] );
+
+    glBegin(GL_QUADS);
+
+    printf("%f %f %d %d\n", image->tex_coord_x, image->tex_coord_y, hotspotX, hotspotY);
+
+    glTexCoord2f(xflip? image->tex_coord_x : 0, yflip? 0 : image->tex_coord_y);
+    glVertex2f( -hotspotX, -hotspotY );
+
+    glTexCoord2f(xflip? 0 : image->tex_coord_x, yflip? 0 : image->tex_coord_y);
+    glVertex2f( image->width-hotspotX, -hotspotY );
+
+    glTexCoord2f(xflip? 0 : image->tex_coord_x, yflip? image->tex_coord_y : 0);
+    glVertex2f( image->width-hotspotX, image->height-hotspotY );
+
+    glTexCoord2f(xflip? image->tex_coord_x : 0, yflip? image->tex_coord_y : 0);
+    glVertex2f( -hotspotX, image->height-hotspotY );
+
+    glEnd();
+
+}
diff --git a/hacks/gl/drawable.h b/hacks/gl/drawable.h
new file mode 100644 (file)
index 0000000..660652b
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _drawable_
+#define _drawable_
+
+#include "image.h"
+
+class DrawableThing
+{
+public:
+    int x,y, angle, hotspotX, hotspotY;
+    float xscale, yscale;
+    Image* image;
+    bool xflip, yflip;
+
+    DrawableThing(Image* image=0);
+    void setFlip(bool x, bool y);
+    void move(int x, int y);
+    void setHotspot(int x, int y);
+    void scale(float x, float y);
+    void scale(float k);
+    void setImage(Image* image);
+    void render();
+    void rotate(int angle);
+
+};
+
+
+#endif
diff --git a/hacks/gl/image.cc b/hacks/gl/image.cc
new file mode 100644 (file)
index 0000000..11b8512
--- /dev/null
@@ -0,0 +1,29 @@
+#include "image.h"
+#include "image_loader.h"
+
+Image::Image()
+{
+}
+
+Image::Image(wxString path)
+{
+    load(path);
+}
+
+void Image::load(wxString path)
+{
+    ID=loadImage(path, &width, &height, &textureWidth, &textureHeight);
+
+    tex_coord_x = (float)width/(float)textureWidth;
+    tex_coord_y = (float)height/(float)textureHeight;
+}
+
+GLuint* Image::getID()
+{
+    return ID;
+}
+
+Image::~Image()
+{
+    glDeleteTextures (1, ID);
+}
diff --git a/hacks/gl/image.h b/hacks/gl/image.h
new file mode 100644 (file)
index 0000000..9422f64
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _image_
+#define _image_
+
+// include OpenGL
+#ifdef __WXMAC__
+#include "OpenGL/gl.h"
+#else
+#include <GL/gl.h>
+#endif
+
+#include "wx/wx.h"
+
+class Image
+{
+    GLuint* ID;
+    
+public:
+
+       /*
+        * it is preferable to use textures that are a power of two. this loader will automatically
+        * resize texture to be a power of two, filling the remaining areas with black.
+        * width/height are the width of the actual loaded image.
+        * textureWidth/Height are the total width of the texture, including black filling.
+        * tex_coord_x/y are the texture coord parameter you must give OpenGL when rendering
+        * to get only the image, without the black filling.
+        */
+    int width, height, textureWidth, textureHeight;
+    float tex_coord_x, tex_coord_y;
+    
+    GLuint* getID();
+    Image();
+    Image(wxString path);
+    ~Image();
+    void load(wxString path);
+        
+};
+
+
+#endif
diff --git a/hacks/gl/image_loader.cc b/hacks/gl/image_loader.cc
new file mode 100644 (file)
index 0000000..76df14e
--- /dev/null
@@ -0,0 +1,176 @@
+#include "image_loader.h"
+#include <cmath>
+
+#include "wx/image.h"
+#include "wx/wx.h"
+
+
+GLuint* loadImage(wxString path, int* imageWidth, int* imageHeight, int* textureWidth, int* textureHeight)
+{
+
+       GLuint* ID=new GLuint[1];
+       glGenTextures( 1, &ID[0] );
+
+       glBindTexture( GL_TEXTURE_2D, *ID );
+
+       // the first time, init image handlers (remove this part if you do it somewhere else in your app)
+       static bool is_first_time = true;
+       if(is_first_time)
+       {
+               wxInitAllImageHandlers();
+
+               is_first_time = false;
+       }
+
+       // check the file exists
+       if(!wxFileExists(path))
+       {
+               wxMessageBox( _("Failed to load resource image") );
+               exit(1);
+       }
+
+       wxImage* img=new wxImage( path );
+
+       (*imageWidth)=img->GetWidth();
+       (*imageHeight)=img->GetHeight();
+
+       glPixelStorei(GL_UNPACK_ALIGNMENT,   1   );
+
+    /*
+     * Many graphics card require that textures be power of two.
+     * Below is a simple implementation, probably not optimal but working.
+     * If your texture sizes are not restricted to power of 2s, you can
+     * of course adapt the bit below as needed.
+     */
+
+       float power_of_two_that_gives_correct_width=std::log((float)(*imageWidth))/std::log(2.0);
+       float power_of_two_that_gives_correct_height=std::log((float)(*imageHeight))/std::log(2.0);
+
+        // check if image dimensions are a power of two
+        if( (int)power_of_two_that_gives_correct_width == power_of_two_that_gives_correct_width &&
+            (int)power_of_two_that_gives_correct_height == power_of_two_that_gives_correct_height)
+        {
+                // note: must make a local copy before passing the data to OpenGL, as GetData() returns RGB
+                // and we want the Alpha channel if it's present. Additionally OpenGL seems to interpret the
+                // data upside-down so we need to compensate for that.
+                GLubyte *bitmapData=img->GetData();
+                GLubyte *alphaData=img->GetAlpha();
+
+                int bytesPerPixel = img->HasAlpha() ?  4 : 3;
+
+                int imageSize = (*imageWidth) * (*imageHeight) * bytesPerPixel;
+                GLubyte *imageData=new GLubyte[imageSize];
+
+                int rev_val=(*imageHeight)-1;
+
+                for(int y=0; y<(*imageHeight); y++)
+                {
+                        for(int x=0; x<(*imageWidth); x++)
+                        {
+                                imageData[(x+y*(*imageWidth))*bytesPerPixel+0]=
+                                        bitmapData[( x+(rev_val-y)*(*imageWidth))*3];
+
+                                imageData[(x+y*(*imageWidth))*bytesPerPixel+1]=
+                                        bitmapData[( x+(rev_val-y)*(*imageWidth))*3 + 1];
+
+                                imageData[(x+y*(*imageWidth))*bytesPerPixel+2]=
+                                        bitmapData[( x+(rev_val-y)*(*imageWidth))*3 + 2];
+
+                                if(bytesPerPixel==4) imageData[(x+y*(*imageWidth))*bytesPerPixel+3]=
+                                        alphaData[ x+(rev_val-y)*(*imageWidth) ];
+                        }//next
+                }//next
+
+                // if yes, everything is fine
+                glTexImage2D(GL_TEXTURE_2D,
+                             0,
+                             bytesPerPixel,
+                             *imageWidth,
+                             *imageHeight,
+                             0,
+                             img->HasAlpha() ?  GL_RGBA : GL_RGB,
+                             GL_UNSIGNED_BYTE,
+                             imageData);
+
+                (*textureWidth)  = (*imageWidth);
+                (*textureHeight) = (*imageHeight);
+
+                delete [] imageData;
+        }
+       else // texture is not a power of two. We need to resize it
+       {
+
+               int newWidth=(int)std::pow( 2.0, (int)(std::ceil(power_of_two_that_gives_correct_width)) );
+               int newHeight=(int)std::pow( 2.0, (int)(std::ceil(power_of_two_that_gives_correct_height)) );
+
+               //printf("Unsupported image size. Recommand values: %i %i\n",newWidth,newHeight);
+
+               GLubyte *bitmapData=img->GetData();
+               GLubyte        *alphaData=img->GetAlpha();
+
+               int old_bytesPerPixel = 3;
+               int bytesPerPixel = img->HasAlpha() ?  4 : 3;
+
+               int imageSize = newWidth * newHeight * bytesPerPixel;
+               GLubyte *imageData=new GLubyte[imageSize];
+
+               int rev_val=(*imageHeight)-1;
+
+               for(int y=0; y<newHeight; y++)
+               {
+                       for(int x=0; x<newWidth; x++)
+                       {
+
+                               if( x<(*imageWidth) && y<(*imageHeight) ){
+                                       imageData[(x+y*newWidth)*bytesPerPixel+0]=
+                                       bitmapData[( x+(rev_val-y)*(*imageWidth))*old_bytesPerPixel + 0];
+
+                                       imageData[(x+y*newWidth)*bytesPerPixel+1]=
+                                               bitmapData[( x+(rev_val-y)*(*imageWidth))*old_bytesPerPixel + 1];
+
+                                       imageData[(x+y*newWidth)*bytesPerPixel+2]=
+                                               bitmapData[( x+(rev_val-y)*(*imageWidth))*old_bytesPerPixel + 2];
+
+                                       if(bytesPerPixel==4) imageData[(x+y*newWidth)*bytesPerPixel+3]=
+                                               alphaData[ x+(rev_val-y)*(*imageWidth) ];
+
+                               }
+                               else
+                               {
+
+                                       imageData[(x+y*newWidth)*bytesPerPixel+0] = 0;
+                                       imageData[(x+y*newWidth)*bytesPerPixel+1] = 0;
+                                       imageData[(x+y*newWidth)*bytesPerPixel+2] = 0;
+                                       if(bytesPerPixel==4) imageData[(x+y*newWidth)*bytesPerPixel+3] = 0;
+                               }
+
+                       }//next
+               }//next
+
+
+               glTexImage2D(GL_TEXTURE_2D,
+                                        0,
+                                        img->HasAlpha() ?  4 : 3,
+                                        newWidth,
+                                        newHeight,
+                                        0,
+                                        img->HasAlpha() ?  GL_RGBA : GL_RGB,
+                                        GL_UNSIGNED_BYTE,
+                                        imageData);
+
+               (*textureWidth)=newWidth;
+               (*textureHeight)=newHeight;
+
+               delete [] imageData;
+       }
+
+       // set texture parameters as you wish
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // GL_LINEAR
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+       return ID;
+
+}
diff --git a/hacks/gl/image_loader.h b/hacks/gl/image_loader.h
new file mode 100644 (file)
index 0000000..1212fae
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _image_loader
+#define _image_loader
+
+#ifdef __WXMAC__
+#include "OpenGL/gl.h"
+#else
+#include <GL/gl.h>
+#endif
+#include "wx/wx.h"
+       
+GLuint* loadImage(wxString path, int* imageWidth, int* imageHeight, int* textureWidth, int* textureHeight);
+
+#endif