/*********************************************************************
* 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