#include <iostream> #include <GL/glew.h> #include "gtk_win_main.hpp" #include <gdk/x11/gdkglx.h> // 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<Gdk::GL::Config> 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<Gdk::GL::Config> &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<Gdk::GL::Window> 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) { 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; /* set some constants and calculate some intermediate values */ float freq = 8.0 * 3.14; float speed = 1.0; float amp = 1.0 / 10.0; /* 0.1 does NOT works for me ! WTF !!! */ float dist = sqrt(x*x+y*y); /* Calculate the desirated value of the mesh point */ float 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); } ); // TODO : change API, use only one string and split it at each blanks //std::list<std::string> knames; //knames.push_back("water1"); cl_res = this->clKit.compileKernels(/*knames,*/ source, sizeof(source)); //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<Gdk::GL::Window> 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<Gdk::GL::Window> 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="<<c1<<" c2="<<c2<<" c3="<<c3<<" alpha="<<alpha<<std::endl; // The compacted one float alpha=2.0f*(s_h*s_h)/(m_w*m_h)/(t_z*t_z); if (alpha < 0.01f) alpha = 0.01f; if (alpha > 1.0f) alpha = 1.0f; glColor4f(0.40f,0.78f,0.97f,alpha); // XXX Blue is great but it's fixed here and shouldn't 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; } // Update time (and flags) for continuous_play mode void MyGTKGLSceneWidget::step() { this->time += 0.01f; this->need_recompute = true; } // Combine all kind of mouse event and redirect them to a unique method : do_mouse_logic() 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 helper macros to filter mouse events in do_mouse_logic() #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 \ ) //FIXME find bug with multiple mouse button release #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) { //CALL_TRACE ; // This one runs when a mouse event is catched by the GTK GL Widget std::cout << "m" << std::flush; /* * 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) * * 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) //XXX 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; //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; }