From: Carl Hetherington Date: Mon, 28 Jan 2019 22:41:25 +0000 (+0000) Subject: OpenGL hacks. X-Git-Tag: v2.13.112~31 X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=1409af06f789ee1a832e61ed722cc48ed18f89f3 OpenGL hacks. --- diff --git a/hacks/gl/.gitignore b/hacks/gl/.gitignore new file mode 100644 index 000000000..ccd496b55 --- /dev/null +++ b/hacks/gl/.gitignore @@ -0,0 +1,3 @@ +gl +myfile.png + diff --git a/hacks/gl/Makefile b/hacks/gl/Makefile new file mode 100644 index 000000000..7be9bafa9 --- /dev/null +++ b/hacks/gl/Makefile @@ -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 index 000000000..565bf4cee --- /dev/null +++ b/hacks/gl/basic_gl_pane.cc @@ -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 +#include +#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 index 000000000..b085dbb95 --- /dev/null +++ b/hacks/gl/basic_gl_pane.h @@ -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 index 000000000..ba790fe91 --- /dev/null +++ b/hacks/gl/drawable.cc @@ -0,0 +1,113 @@ +#include "drawable.h" +#include + +#ifdef __WXMAC__ +#include "OpenGL/gl.h" +#else +#include +#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 index 000000000..660652b57 --- /dev/null +++ b/hacks/gl/drawable.h @@ -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 index 000000000..11b851268 --- /dev/null +++ b/hacks/gl/image.cc @@ -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 index 000000000..9422f6446 --- /dev/null +++ b/hacks/gl/image.h @@ -0,0 +1,39 @@ +#ifndef _image_ +#define _image_ + +// include OpenGL +#ifdef __WXMAC__ +#include "OpenGL/gl.h" +#else +#include +#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 index 000000000..76df14e51 --- /dev/null +++ b/hacks/gl/image_loader.cc @@ -0,0 +1,176 @@ +#include "image_loader.h" +#include + +#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; yHasAlpha() ? 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 index 000000000..1212faedb --- /dev/null +++ b/hacks/gl/image_loader.h @@ -0,0 +1,13 @@ +#ifndef _image_loader +#define _image_loader + +#ifdef __WXMAC__ +#include "OpenGL/gl.h" +#else +#include +#endif +#include "wx/wx.h" + +GLuint* loadImage(wxString path, int* imageWidth, int* imageHeight, int* textureWidth, int* textureHeight); + +#endif