以前にwxWidgetsとOpenGLを組み合わせて静止画を表示したが、今回は画像を動かしてアニメーションにしてみようと思う。前々回で作成したウインドウをベースにOpenGL部分を追加していく。
とは言ったものの、実はOpenGL部分は以前のものとあまり変わらないが、一応おさらいとして表示しておく。
- opengl.hpp
#ifndef __OPENGL_HPP__ #define __OPENGL_HPP__ #include <wx/glcanvas.h> #include <GL/glut.h> class OpenGL{ public: void init(); void draw(); void setViewport(int w, int h); }; #endif //__OPENGL_HPP__
- opengl.cpp
#include "opengl.hpp" GLdouble vertex[][3] = { { 0.0, 0.0, 0.0 }, { 1.0, 0.0, 0.0 }, { 1.0, 1.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 1.0, 0.0, 1.0 }, { 1.0, 1.0, 1.0 }, { 0.0, 1.0, 1.0 } }; int edge[][2] = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 }, { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 } }; void OpenGL::init() { glClearColor(1, 1, 1, 1); } void OpenGL::draw() { int i; static int r = 0; glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(3.0, 4.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glRotated((double)r, 0.0, 1.0, 0.0); glColor3d(0.0, 0.0, 0.0); glBegin(GL_LINES); for (i = 0; i < 12; ++i) { glVertex3dv(vertex[edge[i][0]]); glVertex3dv(vertex[edge[i][1]]); } glEnd(); if (++r >= 360) r = 0; } void OpenGL::setViewport(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); }
今回のアニメーション用のOpenGL描画部分は和歌山大学システム工学部デザイン情報学科の床井先生の学生実験資『GLUTによる「手抜き」OpenGL入門』のものを使用した。この資料内のアニメーションの項で作成しているアイドル時に再描画をする仕組みをwxWidgets側に用意する方針にする。
- glpanel.hpp
#include <wx/wx.h> #include <wx/glcanvas.h> #include "opengl.hpp" class GLPanel : public wxGLCanvas { private: bool isInitialized_; OpenGL gl_; public: GLPanel(wxWindow* parent, wxGLContext* sharedContext, wxWindowID id); ~GLPanel(); void OnPaint(wxPaintEvent &event); void OnSize(wxSizeEvent &event); void OnEraseBackground(wxEraseEvent& event); void OnIdle(wxIdleEvent& event); }; #endif //__GLPANEL_HPP__
- glpanel.cpp
#include "glpanel.hpp" GLPanel::GLPanel(wxWindow* parent, wxGLContext* sharedContext, wxWindowID id, int frameRate) : wxGLCanvas(parent, sharedContext, id), isInitialized_(false) { this->Connect(wxEVT_SIZE, wxSizeEventHandler(GLPanel::OnSize)); this->Connect(wxEVT_PAINT, wxPaintEventHandler(GLPanel::OnPaint)); this->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(GLPanel::OnEraseBackground)); this->Connect(wxEVT_IDLE, wxIdleEventHandler(GLPanel::OnIdle)); } void GLPanel::OnPaint(wxPaintEvent &event) { wxPaintDC dc(this); if(!GetContext()){ return; } if(!isInitialized_){ SetCurrent(); gl_.init(); int w; int h; GetClientSize(&w, &h); gl_.setViewport(w, h); isInitialized_=true; } gl_.draw(); glFlush(); SwapBuffers(); } void GLPanel::OnSize(wxSizeEvent &event) { wxGLCanvas::OnSize(event); if(GetContext()) { SetCurrent(); gl_.setViewport(event.GetSize().GetWidth(), event.GetSize().GetHeight()); Refresh(); } } void GLPanel::OnEraseBackground(wxEraseEvent& event) { // Do nothing, to avoid flashing. } void GLPanel::OnIdle(wxIdleEvent& event) { event.RequestMore(true); this->SetCurrent(); this->Refresh(); }
強調部分が今回のコードで追加している部分になる。OnIdleメンバ関数を追加し、wxEVT_IDLEイベントと関連づけを行っている。このwxEVT_IDLEイベントがアイドル時に繰り返し発生し、OnIdle関数内で再描画を行っている。OnIdle関数ないではじめに呼び出しているevent.RequestMore(true)がないと、wxWidgetsが空気を読んで、あまりwxEVT_IDLEイベントを発生させてくれないらしい。
これらをビルドして実行すると、
このように、PCリソースの許す限りの速さでうごくことになる。
コメント