/********************************************************************* * Software License Agreement (AGPL-3 License) * * OpenViBE Designer * Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015 * Copyright (C) Inria, 2015-2017,V1.0 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. * If not, see . */ #pragma once #include "m_defines.hpp" #include "m_VisualizationTools.hpp" #include "m_GtkGL.hpp" #include #include #if defined TARGET_OS_Windows #include #endif #include #include #include #include // #define __DIRECT_RENDER__ namespace OpenViBE { namespace AdvancedVisualization { template class TGtkGLWidget { public: TGtkGLWidget() : m_box(nullptr) { } virtual ~TGtkGLWidget() { if (m_widget) { if (m_textureId) { GtkGL::preRender(m_widget); glDeleteTextures(1, &m_textureId); GtkGL::postRender(m_widget); } if (m_timeoutSrc) { g_source_destroy(m_timeoutSrc); } GtkGL::uninitialize(m_widget); } } virtual void initialize(TBox& box, GtkWidget* widget, GtkWidget* left, GtkWidget* right, GtkWidget* bottom) { GtkGL::initialize(widget); { m_box = &box; m_widget = widget; m_left = left; m_right = right; m_bottom = bottom; ::g_signal_connect(widget, "configure-event", G_CALLBACK(TGtkGLWidget::configureCB), m_box); ::g_signal_connect(widget, "expose-event", G_CALLBACK(TGtkGLWidget::exposeCB), m_box); ::g_signal_connect(widget, "button-press-event", G_CALLBACK(TGtkGLWidget::mouseButtonCB), m_box); ::g_signal_connect(widget, "button-release-event", G_CALLBACK(TGtkGLWidget::mouseButtonCB), m_box); ::g_signal_connect(widget, "motion-notify-event", G_CALLBACK(TGtkGLWidget::motionNotifyCB), m_box); ::g_signal_connect(widget, "enter-notify-event", G_CALLBACK(TGtkGLWidget::enterNotifyCB), m_box); ::g_signal_connect(gtk_widget_get_parent(widget), "key-press-event", G_CALLBACK(TGtkGLWidget::keyPressCB), m_box); ::g_signal_connect(gtk_widget_get_parent(widget), "key-release-event", G_CALLBACK(TGtkGLWidget::keyReleaseCB), m_box); ::g_signal_connect_after(left, "expose-event", G_CALLBACK(TGtkGLWidget::exposeLeftCB), m_box); ::g_signal_connect_after(right, "expose-event", G_CALLBACK(TGtkGLWidget::exposeRightCB), m_box); ::g_signal_connect_after(bottom, "expose-event", G_CALLBACK(TGtkGLWidget::exposeBottomCB), m_box); m_timeoutSrc = g_timeout_source_new(250); // timeouts every 50 ms g_source_set_priority(m_timeoutSrc, G_PRIORITY_LOW); g_source_set_callback(m_timeoutSrc, GSourceFunc(timeoutRedrawCB), m_box, nullptr); g_source_attach(m_timeoutSrc, nullptr); gtk_widget_queue_resize(widget); } } virtual void redrawTopLevelWindow(const bool immediate = false) { GtkWidget* top = gtk_widget_get_toplevel(m_widget); if (top != nullptr) { if (immediate) { gdk_window_process_updates(top->window, false); gtk_widget_queue_draw(top); } else { gdk_window_invalidate_rect(top->window, nullptr, true); } } } virtual void redraw(const bool immediate = false) { if (immediate) { gdk_window_process_updates(m_widget->window, false); gtk_widget_queue_draw(m_widget); } else { gdk_window_invalidate_rect(m_widget->window, nullptr, true); } } virtual void redrawLeft(const bool immediate = false) { if (immediate) { gdk_window_process_updates(m_left->window, false); gtk_widget_queue_draw(m_left); } else { gdk_window_invalidate_rect(m_left->window, nullptr, true); } } virtual void redrawRight(const bool immediate = false) { if (immediate) { gdk_window_process_updates(m_right->window, false); gtk_widget_queue_draw(m_right); } else { gdk_window_invalidate_rect(m_right->window, nullptr, true); } } virtual void redrawBottom(const bool immediate = false) { if (immediate) { gdk_window_process_updates(m_bottom->window, false); gtk_widget_queue_draw(m_bottom); } else { gdk_window_invalidate_rect(m_bottom->window, nullptr, true); } } virtual void setPointSmoothingActive(const bool active = false) { if (active) { glEnable(GL_POINT_SMOOTH); } else { glDisable(GL_POINT_SMOOTH); } } virtual uint32_t createTexture(const std::string& value) { #define M_GRADIENT_SIZE 128 if (m_textureId == 0) { const std::string str = (value.empty() ? "0:0,0,100; 25:0,100,100; 50:0,49,0; 75:100,100,0; 100:100,0,0" : value); CMatrix gradientBase, gradient; VisualizationToolkit::ColorGradient::parse(gradientBase, str.c_str()); VisualizationToolkit::ColorGradient::interpolate(gradient, gradientBase, M_GRADIENT_SIZE); float texture[M_GRADIENT_SIZE][3]; for (size_t i = 0; i < M_GRADIENT_SIZE; ++i) { texture[i][0] = float(gradient[i * 4 + 1] * .01); texture[i][1] = float(gradient[i * 4 + 2] * .01); texture[i][2] = float(gradient[i * 4 + 3] * .01); } glGenTextures(1, &m_textureId); glBindTexture(GL_TEXTURE_1D, m_textureId); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, __SIZE__, 0, GL_RGB, GL_UNSIGNED_BYTE, texture); gluBuild1DMipmaps(GL_TEXTURE_1D, GL_RGB, M_GRADIENT_SIZE, GL_RGB, GL_FLOAT, texture); } #undef M_GRADIENT_SIZE return m_textureId; } protected: GtkWidget* m_widget = nullptr; GtkWidget* m_left = nullptr; GtkWidget* m_right = nullptr; GtkWidget* m_bottom = nullptr; GSource* m_timeoutSrc = nullptr; TBox* m_box = nullptr; uint32_t m_textureId = 0; private: static gboolean timeoutRedrawCB(TBox* box) { box->redraw(); return TRUE; } static gboolean configureCB(GtkWidget* widget, GdkEventConfigure* /*event*/, TBox* box) { GtkGL::preRender(widget); glViewport(0, 0, widget->allocation.width, widget->allocation.height); box->reshape(widget->allocation.width, widget->allocation.height); GtkGL::postRender(widget); return TRUE; } static gboolean exposeCB(GtkWidget* widget, GdkEventExpose* /*event*/, TBox* box) { const float d = 1.F; const float dx = d / (widget->allocation.width - d); const float dy = d / (widget->allocation.height - d); GtkGL::preRender(widget); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glTranslatef(0.5, 0, 0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0 - dx, 1 + dx, 0 - dy, 1 + dy); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0, 1.0); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glLineWidth(1); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_TEXTURE_1D); glDisable(GL_DEPTH_TEST); glClearDepth(100); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1, 1, 1); // Lighting const float fAmbient = 0.0F; const float fDiffuse = 1.0F; const float fSpecular = 1.0F; GLfloat ambient[] = { fAmbient, fAmbient, fAmbient, 1 }; GLfloat diffuse[] = { fDiffuse, fDiffuse, fDiffuse, 1 }; GLfloat specular[] = { fSpecular, fSpecular, fSpecular, 1 }; GLfloat position0[] = { 3, 1, 2, 1 }; GLfloat position1[] = { -3, 0, -2, 1 }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, specular); glLightfv(GL_LIGHT0, GL_POSITION, position0); glLightfv(GL_LIGHT1, GL_AMBIENT, ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, specular); glLightfv(GL_LIGHT1, GL_POSITION, position1); glShadeModel(GL_SMOOTH); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); box->draw(); GtkGL::postRender(widget); return TRUE; } static gboolean enterNotifyCB(GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, TBox* box) { box->redraw(); //box->request(); //box->m_redrawNeeded = true; return TRUE; } static gboolean exposeLeftCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, TBox* box) { box->drawLeft(); return TRUE; } static gboolean exposeRightCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, TBox* box) { box->drawRight(); return TRUE; } static gboolean exposeBottomCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, TBox* box) { box->drawBottom(); return TRUE; } static gboolean mouseButtonCB(GtkWidget* /*widget*/, GdkEventButton* event, TBox* box) { int status = 0; switch (event->type) { case GDK_BUTTON_PRESS: status = 1; break; case GDK_2BUTTON_PRESS: status = 2; break; case GDK_3BUTTON_PRESS: status = 3; break; default: break; } box->mouseButton(int(event->x), int(event->y), event->button, status); box->draw(); return TRUE; } static gboolean motionNotifyCB(GtkWidget* /*widget*/, GdkEventMotion* event, TBox* box) { box->mouseMotion(int(event->x), int(event->y)); return TRUE; } static gboolean keyPressCB(GtkWidget* /*widget*/, GdkEventKey* event, TBox* box) { box->keyboard(0, 0, /*event->x, event->y,*/ event->keyval, true); return TRUE; } static gboolean keyReleaseCB(GtkWidget* /*widget*/, GdkEventKey* event, TBox* box) { box->keyboard(0, 0, /*event->x, event->y,*/ event->keyval, false); return TRUE; } }; } // namespace AdvancedVisualization } // namespace OpenViBE