#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 Gdk::GL::ConfigMode glMode = Gdk::GL::MODE_RGB | Gdk::GL::MODE_DEPTH | Gdk::GL::MODE_DOUBLE; Glib::RefPtr glconfig; EXIT_IF_FAIL(2, glconfig=Gdk::GL::Config::create(glMode) ); // Initialize the OpenGL scene widget (realization came later) 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); //FIXME : handle main loop quit, maybe do that logic inside MyGTKGLSceneWidget, find bug with multiple mouse button release while ( true ) { while ( Gtk::Main::events_pending() ) { Gtk::Main::iteration(false); } if ( glScene.continuous_play ) { glScene.step(); glScene.queue_draw(); std::cout << "." << std::flush; } else { Gtk::Main::iteration(true); std::cout << "!" << std::flush; } } 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) { 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 this->camera.rx = -64.0f; this->camera.ry = -16.0f; this->camera.tz = -2.0f; need_recompute=false; time=0.0f; continuous_play=false; } MyGTKGLSceneWidget::~MyGTKGLSceneWidget() { } void MyGTKGLSceneWidget::on_size_request(Gtk::Requisition* requisition) { CALL_TRACE; // Technical stuff : GTK call this to ask the 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 << "Oups : 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 ); size_t mesh_width=512, mesh_height=512, group_size=256; // TODO : not here 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 initOpenCL 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(); // XXX 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) { unsigned int nx = get_global_id(0); unsigned int ny = get_global_id(1); /* calculate uv coordinates of the mesh point [0.0;1.0] */ float u = nx / (float) width; float v = ny / (float) height; /* calculate centered normalized coordinates [-1.0;1.0] */ float x = u*2.0-1.0; float y = v*2.0-1.0; /* Calculate the desirated value of the mesh point */ float freq = 8.0 * 3.14; float amp = 1.0 / 10.0; /* 0.1 does NOT works ! WTF !!! */ float speed = 1.0; float dist = sqrt(x*x+y*y); float z; //for(int i = 0; i < 40000; i++){ z = amp * sin( freq * dist - speed * time ) / dist ; //} /* We only use normalized quaterinons here */ float w = 1.0; /* Write output vertex (centered) */ pos[ny*width+nx] = (float4)(x, y, z, w); } ); std::list knames; knames.push_back("water1"); cl_res = this->clKit.compileKernels(knames, source, sizeof(source)); EXIT_IF_FAIL(6, cl_res==0); knames.clear(); // Projection setup is done at on_configure_event // Camera setup (ie initial 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 *** //FIXME could segfault if get_gl_window() has failed if (!glwindow->gl_begin(get_gl_context())) { std::cerr << "Oups : 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 Glib::RefPtr glwindow = get_gl_window(); // *** OpenCL BEGIN if (this->need_recompute) { this->need_recompute=false; // std::cout << "execKernel(\"water1\", " << this->time << ");" << std::endl; 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 << "Oups : 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); 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 glColor4f(0.40f,0.78f,0.97f,1.0f); glBindBuffer(GL_ARRAY_BUFFER, this->clKit.getGLVBO()); glVertexPointer(4, GL_FLOAT, 0, (GLvoid *) 0); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_POINTS, 0, this->clKit.getMeshItemCount()); glDisableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, 0); glwindow->gl_end(); // *** OpenGL END *** glwindow->swap_buffers(); // Display the rendered image return true; } void MyGTKGLSceneWidget::step() { this->time += 0.01f; this->need_recompute = true; } bool MyGTKGLSceneWidget::on_motion_notify_event (GdkEventMotion *event) { return do_mouse_logic(event->type, event->state, event->x, event->y); } bool MyGTKGLSceneWidget::on_button_press_event(GdkEventButton *event) { return do_mouse_logic(event->type, event->state | 1<<(7+event->button) , event->x, event->y); } bool MyGTKGLSceneWidget::on_button_release_event(GdkEventButton *event) { return do_mouse_logic(event->type, event->state, event->x, event->y); } bool MyGTKGLSceneWidget::on_scroll_event(GdkEventScroll *event) { return do_mouse_logic(event->type, event->state | 1<<(7+event->direction) , event->x, event->y); } #define ALL_KEYBOARD_MODIFIERS ( GDK_SHIFT_MASK | GDK_CONTROL_MASK \ | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ) #define MOUSE_CLIC(button, required_modifier_mask, allowed_extra_modifier_mask) if ( \ type == GDK_BUTTON_RELEASE \ && ( state & button ) == button \ && ( state & required_modifier_mask ) == required_modifier_mask \ && ( ( state & ALL_KEYBOARD_MODIFIERS ) & ~( required_modifier_mask | allowed_extra_modifier_mask ) ) == 0 \ ) #define MOUSE_WHEEL(direction, required_modifier_mask, allowed_extra_modifier_mask) if ( \ type == GDK_SCROLL \ && ( state & (1<<(7+direction)) ) == (1<<(7+direction)) \ && ( state & required_modifier_mask ) == required_modifier_mask \ && ( ( state & ALL_KEYBOARD_MODIFIERS ) & ~( required_modifier_mask | allowed_extra_modifier_mask ) ) == 0 \ ) #define MOUSE_DOUBLECLIC(state_mask) if ( \ type == GDK_BUTTON_RELEASE \ && ( state & state_mask ) == state_mask \ && prev_type == GDK_2BUTTON_PRESS \ ) #define MOUSE_DRAG_START(state_mask) if ( \ type == GDK_BUTTON_PRESS \ && ( state & state_mask ) == state_mask \ ) #define MOUSE_DRAGING(state_mask) if ( \ ( type == GDK_MOTION_NOTIFY ) \ && ( state & state_mask ) == state_mask \ ) #define MOUSE_DRAG_END(state_mask) if ( \ type == GDK_BUTTON_RELEASE \ && ( state & state_mask ) == state_mask \ ) bool MyGTKGLSceneWidget::do_mouse_logic(GdkEventType type, guint state, guint x, guint y) { /* * type : the type of the event. * Simple motion : GDK_MOTION_NOTIFY (3) * Simple clic : GDK_BUTTON_PRESS then GDK_BUTTON_RELEASE (4 then 7) * Double clic : GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE, GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE (4 7 4 5 7) * Scroll : GDK_SCROLL (31) * event type 31 state 144 on (475,67) * event type 31 state 272 on (475,67) * * * state : a bit-mask representing the state of the modifier keys and the pointer buttons. * GDK_BUTTON1_MASK, ... , GDK_BUTTON5_MASK (mouse buttons) * GDK_SCROLL_UP, GDK_SCROLL_DOWN, GDK_SCROLL_LEFT, GDK_SCROLL_RIGHT (mouse wheel scroll) * GDK_SHIFT_MASK, GDK_LOCK_MASK, GDK_CONTROL_MASK (keyboard standard modifier keys) * GDK_MOD1_MASK, ... (normally MOD1 it is the Alt key) * GDK_SUPER_MASK, GDK_HYPER_MASK, GDK_META_MASK (extra keybord modifier keys) */ static GdkEventType prev_type = GDK_NOTHING; // Static variable to hold previous mouse button event static guint drag_x=0, drag_y=0; // Static for DRAGING displacement calculus bool recompute=false; // Setting it to true will call OpenCL on next redraw bool redraw=false; // Setting it to true will queue a redraw to the widget (invalidate) // For event filter debug (display all events) //std::cout << "event type " << type << " state " << state << " on (" << x << "," << y << ") " << std::endl; /* *** BEGIN event filtering *** */ MOUSE_DRAG_START(GDK_BUTTON2_MASK) { drag_x=x; drag_y=y; } // Carmera rotation MOUSE_DRAGING(GDK_BUTTON2_MASK) { float mouse_sensivity = 0.2f; gint dx = drag_x - x; // Delta movement (since last event) gint dy = drag_y - y; // Not unsigned ! this->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; 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; redraw=true; } } /* MOUSE_CLIC(GDK_BUTTON1_MASK, 0, 0) { //TODO this->clKit.execKernel("water1", 0.0f); redraw=true; }*/ MOUSE_DRAG_START(GDK_BUTTON1_MASK) { this->continuous_play=true; } 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 if ( type == GDK_BUTTON_PRESS || type == GDK_2BUTTON_PRESS || type == GDK_BUTTON_RELEASE ) { prev_type=type; } if ( redraw ) queue_draw(); if ( recompute ) this->need_recompute=true; return true; }