#include #include #include "gtk_win_main.hpp" #include // X11 specific // Macro to make things readable in main() function #define EXIT_IF_FAIL(val, expr) do { \ if ( ! (expr) ) { \ std::cerr << "Init error " << val << std::endl; \ exit(val); \ } \ } while(0) // For understanding the event sequences #define CALL_TRACE do { \ std::cout << "trace " <<__PRETTY_FUNCTION__ << std::endl; \ } while(0) int main(int argc, char* argv[]) { CALL_TRACE; // Initialize GTK Gtk::Main gtkKit(argc, argv); // gtk itself Gtk::GL::init(argc, argv); // gtkglextmm // Query and print OpenGL version int glVersionMajor, glVersionMinor; EXIT_IF_FAIL(1, Gdk::GL::query_version(glVersionMajor, glVersionMinor) ); std::cout << "OpenGL extension version - " << glVersionMajor << "." << glVersionMinor << std::endl; // Initialize OpenGL Glib::RefPtr glconfig; Gdk::GL::ConfigMode glMode = Gdk::GL::MODE_RGB | Gdk::GL::MODE_DEPTH; EXIT_IF_FAIL(2, glconfig=Gdk::GL::Config::create(glMode) ); // Initialize the OpenGL scene widget (realization came later, no RAII) MyGTKGLSceneWidget glScene(glconfig); // Instantiate the GTK app (and realize glScene) // Could exit() the program if problem with OpenGL or OpenCL GTKWinMain gtkwinmain(glScene); // Run the app gtkKit.run(gtkwinmain); return 0; } // MyGTKGLSceneWidget implementation (extends a Gtk::DrawingArea with Gtk::GL::Widget) // I want to keep all interesting code parts in this file, in natural reading order MyGTKGLSceneWidget::MyGTKGLSceneWidget(Glib::RefPtr &glconfig): continuous_play(false), need_recompute(false), time(0.0f) { CALL_TRACE; set_gl_capability(glconfig); Gdk::EventMask mask = Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_MOTION_MASK \ | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK \ | Gdk::SCROLL_MASK; set_events(mask); // The containing window should have those attributes too // Camera is always looking the center of the mesh // this->camera affects (only) the camera position // rx,ry : the camera rotate around the mesh. Those vars are angles in degrees // tz : distance between the center of the mesh and the camera (roughly) this->camera.rx = -64.0f; this->camera.ry = 16.0f; this->camera.tz = 2.0f; } MyGTKGLSceneWidget::~MyGTKGLSceneWidget() { } void MyGTKGLSceneWidget::on_size_request(Gtk::Requisition* requisition) { CALL_TRACE; // Technical stuff, GTK call this to ask for widget minimal size *requisition = Gtk::Requisition(); requisition->width = 320; requisition->height = 240; } void MyGTKGLSceneWidget::on_realize() { CALL_TRACE; // This one runs once at window creation time // It's time to setup GL things that don't change on each frame Gtk::DrawingArea::on_realize(); Glib::RefPtr glwindow = get_gl_window(); // *** OpenGL BEGIN *** GLenum gl_res; if (!glwindow->gl_begin(get_gl_context())) { std::cerr << "Oops : glwindow->gl_begin(get_gl_context())" << std::endl; return; } EXIT_IF_FAIL(3, Gdk::GL::query_gl_extension("GL_ARB_vertex_buffer_object") ); EXIT_IF_FAIL(4, glewInit() == 0 ); // TODO : mesh size should not be fixed here //size_t mesh_width=256, mesh_height=256, group_size=256; //size_t mesh_width=512, mesh_height=512, group_size=256; size_t mesh_width=1024, mesh_height=1024, group_size=256; //size_t mesh_width=2048, mesh_height=2048, group_size=256; GLuint gl_vbo=0; GLsizeiptr gl_vbo_data_size = mesh_width * mesh_height * sizeof(cl_float4); //std::cout << "gl_vbo_data_size==" << gl_vbo_data_size << std::endl; glGenBuffers(1, &gl_vbo); glBindBuffer(GL_ARRAY_BUFFER, gl_vbo); glBufferData(GL_ARRAY_BUFFER, gl_vbo_data_size, NULL, GL_DYNAMIC_DRAW); gl_res=glGetError(); if ( gl_res != GL_NO_ERROR ) { std::cerr << "glBufferData(). Unable to allocate " << gl_vbo_data_size << " bytes in VRAM" << std::endl; std::cerr << gluErrorString(gl_res); EXIT_IF_FAIL(5, false); } //#ifdef X11 intptr_t gl_context = (intptr_t)glXGetCurrentContext(); intptr_t gl_display = (intptr_t)glXGetCurrentDisplay(); //#else // #error This code works only for X11 systems for now //#endif int cl_res = this->clKit.initCL(gl_display, gl_context, gl_vbo, mesh_width, mesh_height, group_size); EXIT_IF_FAIL(cl_res, cl_res==0); this->clKit.resetVBO(); // Just for displaying a flat mesh at start const char source[]=STRINGIFY( /* This is OpenCL kernel code (Syntax like C but it's a different language) */ __kernel void water1(__global float4 *pos, unsigned int width, unsigned int height, float time) { /* Getting current vertex indices (could be seen as a 2D array of float4) */ unsigned int nx = get_global_id(0); unsigned int ny = get_global_id(1); /* A float4 vector that hold the output vertex */ float4 out; /* Calculate centered mesh coordinates [-1.0;1.0]² */ out.x = nx / (float) width * 2.0f - 1.0f; out.y = ny / (float) height * 2.0f - 1.0f; /* Set some constants (should be preprocessor macros...) */ float freq = 8.0f; float speed = 1.0f; float amp = 1.0f / 10.0f; /* 0.1f does NOT works for me ! WTF !!! */ /* Calculate some intermediate values */ float dist = hypot(out.x,out.y); /* =sqrt(out.x*out.x+out.y*out.y); */ /* Calculate the desirated value of the mesh point z=f(x,y,t) */ out.z = amp * sinpi( freq * dist + speed * time ) / dist ; out.w = 1.0f; /* We always use normalized quaterinons here */ pos[ny*width+nx] = out; /* Write output vertex */ } ); // TODO : change API, use only one string and split it at each blanks //std::list knames; //knames.push_back("water1"); cl_res = this->clKit.compileKernels(/*knames,*/ source, sizeof(source)-1); //knames.clear(); EXIT_IF_FAIL(6, cl_res==0); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Projection setup is done at on_configure_event // Camera setup (ie MODELVIEW matrix) is done at on_expose_event glwindow->gl_end(); // *** OpenGL END *** } bool MyGTKGLSceneWidget::on_configure_event(GdkEventConfigure* event) { CALL_TRACE ; // This one runs mainly when GTK GL Widget is resized float h=this->get_height(); float w=this->get_width(); Glib::RefPtr glwindow = get_gl_window(); // *** OpenGL BEGIN *** if (!glwindow->gl_begin(get_gl_context())) { std::cerr << "Oops : glwindow->gl_begin(get_gl_context())" << std::endl; return false; } glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, w/h, 0.1, 10.0); glwindow->gl_end(); // *** OpenGL END *** return true; } bool MyGTKGLSceneWidget::on_expose_event(GdkEventExpose* event) { // CALL_TRACE ; // This one runs mainly when GTK GL Widget have to be redrawn std::cout << "e" << std::flush; Glib::RefPtr glwindow = get_gl_window(); // *** OpenCL BEGIN *** if (this->need_recompute) { this->need_recompute=false; //std::cout << "execKernel(\"water1\", " << this->time << ");" << std::endl; //TODO : Dynamic kernel usage ? int res = this->clKit.execKernel("water1", this->time); if ( res !=0 ) std::cerr << "execKernel() has returned " << res << std::endl; //std::cout << " -> " << res << std::endl; } // *** OpenCL END *** // *** OpenGL BEGIN *** if (!glwindow->gl_begin(get_gl_context())) { std::cerr << "Oops : glwindow->gl_begin(get_gl_context())" << std::endl; return false; } //Camera position update glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, this->camera.tz * -1.0); glRotatef(this->camera.rx, 1.0, 0.0, 0.0); glRotatef(this->camera.ry, 0.0, 0.0, -1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Drawing all the stuff //TODO : do this (and camera update) in configure event ? float m_w=this->clKit.getMeshWidth(); float m_h=this->clKit.getMeshHeight(); //float s_w=this->get_width(); float s_h=this->get_height(); float t_z=this->camera.tz; // Vertex alpha blending automatic tuning (if big density then high transparency) // The comprehensible one //float c1=(1024*1024)/(m_w*m_h); // coef 1 decreases with mesh point quantity //float c2=(s_h*s_h)/(1024*1024); // coef 2 increases with viewport pixel quantity //float c3=(2*2)/(t_z*t_z); // coef 3 decreases with mesh-camera distance //float alpha=0.5*c1*c2*c3; // Combine it all //if (alpha < 0.01f) alpha = 0.01f; // Prevent values outside acceptable range //if (alpha > 1.0f) alpha = 1.0f; //std::cout <<"c1="<camera.rx -= mouse_sensivity * dy; // Camera position update this->camera.ry += mouse_sensivity * dx; // Yes dy for camera.rx, and -= operator : // GTK mouse coords and OpenGL ones are not on the same coords system drag_x = x; drag_y = y; redraw=true; } // Camera zoom-in MOUSE_WHEEL(GDK_SCROLL_UP, 0, 0) { if (this->camera.tz - 0.5f >= 0.5f) { this->camera.tz -= 0.5f; //std::cout << "camera.tz == " << this->camera.tz << std::endl; redraw=true; } } // Camera zoom-out MOUSE_WHEEL(GDK_SCROLL_DOWN, 0, 0) { if (this->camera.tz + 0.5f <= 9.0f) { this->camera.tz += 0.5f; //std::cout << "camera.tz == " << this->camera.tz << std::endl; redraw=true; } } /* MOUSE_CLIC(GDK_BUTTON1_MASK, 0, 0) { //TODO this->clKit.execKernel("water1", 0.0f); redraw=true; }*/ // Enabling continous_play mode MOUSE_DRAG_START(GDK_BUTTON1_MASK) { this->continuous_play=true; // The trick to have a perpetual redraw : generate a draw event in the idle signal handler Glib::signal_idle().connect( sigc::mem_fun(*this, &MyGTKGLSceneWidget::on_gtk_idle) ); } // Disabling continous_play mode MOUSE_DRAG_END(GDK_BUTTON1_MASK) { this->continuous_play=false; } // Demo filters MOUSE_CLIC(GDK_BUTTON1_MASK, GDK_SHIFT_MASK, GDK_CONTROL_MASK) { std::cout << "Mouse 1 clic with shift or control-shift" << std::endl; } MOUSE_DOUBLECLIC(GDK_BUTTON3_MASK) { std::cout << "Mouse 1 double clic" << std::endl; } /* *** END event filtering *** */ // Previous button event retention for double-clic filtering (used by the macros) if ( type == GDK_BUTTON_PRESS || type == GDK_2BUTTON_PRESS || type == GDK_BUTTON_RELEASE ) { prev_type=type; } if ( redraw ) { queue_draw(); std::cout << "q" << std::flush; } if ( recompute ) { this->need_recompute=true; } return true; } bool MyGTKGLSceneWidget::on_gtk_idle() { //CALL_TRACE ; // This one runs when there is no more GTK event in the current iteration // Note : This signal is not always connected. // Connected by Glib::signal_idle().connect() call // Disconnected automatically the first time it returns false if ( ! continuous_play ) return false; //TODO : compute and display FPS when in continuous_play mode this->step(); this->queue_draw(); return true; }