Files
fltk/examples/OpenGL3test.cxx
ManoloFLTK 89f9671b40 Add cross-platform support for adding widgets to an OpenGL3-based Fl_Gl_Window.
Under non-macOS platforms, the key is to call glUseProgram(0); after having used OpenGL 3
which allows to then use OpenGL 1 and draw FLTK widgets over the OpenGL3 scene.

Under macOS, this is impossible because macOS GL3 contexts are not compatible
with GL1. The solution implemented here is to create an additional Fl_Gl_Window
placed above and sized as the GL3-based window, to give it a non opaque,
GL1-based context, and to put the FLTK widgets in that additional window.
2022-09-25 16:39:40 +02:00

265 lines
8.6 KiB
C++

//
// Tiny OpenGL v3 demo program for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
#include <stdarg.h>
#include <FL/Fl.H>
#include <FL/platform.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Text_Buffer.H>
#if defined(__APPLE__)
# include <OpenGL/gl3.h> // defines OpenGL 3.0+ functions
#else
# if defined(_WIN32)
# define GLEW_STATIC 1
# endif
# include <GL/glew.h>
#endif
void add_output(const char *format, ...);
class SimpleGL3Window : public Fl_Gl_Window {
GLuint shaderProgram;
GLuint vertexArrayObject;
GLuint vertexBuffer;
GLint positionUniform;
GLint colourAttribute;
GLint positionAttribute;
int gl_version_major;
public:
SimpleGL3Window(int x, int y, int w, int h) : Fl_Gl_Window(x, y, w, h) {
mode(FL_RGB8 | FL_DOUBLE | FL_OPENGL3);
shaderProgram = 0;
gl_version_major = 0;
}
void draw(void) {
if (gl_version_major < 3) return;
if (!shaderProgram) {
GLuint vs;
GLuint fs;
int Mslv, mslv; // major and minor version numbers of the shading language
sscanf((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), "%d.%d", &Mslv, &mslv);
add_output("Shading Language Version=%d.%d\n",Mslv, mslv);
const char *vss_format="#version %d%d\n\
uniform vec2 p;\
in vec4 position;\
in vec4 colour;\
out vec4 colourV;\
void main (void)\
{\
colourV = colour;\
gl_Position = vec4(p, 0.0, 0.0) + position;\
}";
char vss_string[300]; const char *vss = vss_string;
sprintf(vss_string, vss_format, Mslv, mslv);
const char *fss_format="#version %d%d\n\
in vec4 colourV;\
out vec4 fragColour;\
void main(void)\
{\
fragColour = colourV;\
}";
char fss_string[200]; const char *fss = fss_string;
sprintf(fss_string, fss_format, Mslv, mslv);
GLint err; GLchar CLOG[1000]; GLsizei length;
vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vss, NULL);
glCompileShader(vs);
glGetShaderiv(vs, GL_COMPILE_STATUS, &err);
if (err != GL_TRUE) {
glGetShaderInfoLog(vs, sizeof(CLOG), &length, CLOG);
add_output("vs ShaderInfoLog=%s\n",CLOG);
}
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fss, NULL);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &err);
if (err != GL_TRUE) {
glGetShaderInfoLog(fs, sizeof(CLOG), &length, CLOG);
add_output("fs ShaderInfoLog=%s\n",CLOG);
}
// Attach the shaders
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vs);
glAttachShader(shaderProgram, fs);
glBindFragDataLocation(shaderProgram, 0, "fragColour");
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &err);
if (err != GL_TRUE) {
glGetProgramInfoLog(shaderProgram, sizeof(CLOG), &length, CLOG);
add_output("link log=%s\n", CLOG);
}
// Get pointers to uniforms and attributes
positionUniform = glGetUniformLocation(shaderProgram, "p");
colourAttribute = glGetAttribLocation(shaderProgram, "colour");
positionAttribute = glGetAttribLocation(shaderProgram, "position");
glDeleteShader(vs);
glDeleteShader(fs);
// Upload vertices (1st four values in a row) and colours (following four values)
GLfloat vertexData[]= { -0.5,-0.5,0.0,1.0, 1.0,0.0,0.0,1.0,
-0.5, 0.5,0.0,1.0, 0.0,1.0,0.0,1.0,
0.5, 0.5,0.0,1.0, 0.0,0.0,1.0,1.0,
0.5,-0.5,0.0,1.0, 1.0,1.0,1.0,1.0};
glGenVertexArrays(1, &vertexArrayObject);
glBindVertexArray(vertexArrayObject);
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 4*8*sizeof(GLfloat), vertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray((GLuint)positionAttribute);
glEnableVertexAttribArray((GLuint)colourAttribute );
glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), 0);
glVertexAttribPointer((GLuint)colourAttribute , 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (char*)0+4*sizeof(GLfloat));
}
else if ((!valid())) {
glViewport(0, 0, pixel_w(), pixel_h());
}
glClearColor(0.08, 0.8, 0.8, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
GLfloat p[]={0,0};
glUniform2fv(positionUniform, 1, (const GLfloat *)&p);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
#ifndef __APPLE__
// suggested by https://stackoverflow.com/questions/22293870/mixing-fixed-function-pipeline-and-programmable-pipeline-in-opengl
// Switch from GL3-style to GL1-style drawing;
// good under Windows, X11 and Wayland; impossible under macOS.
glUseProgram(0);
Fl_Gl_Window::draw(); // Draw FLTK child widgets.
#endif
}
virtual int handle(int event) {
static int first = 1;
if (first && event == FL_SHOW && shown()) {
first = 0;
make_current();
#ifndef __APPLE__
GLenum err = glewInit(); // defines pters to functions of OpenGL V 1.2 and above
# ifdef FLTK_USE_WAYLAND
// glewInit returns GLEW_ERROR_NO_GLX_DISPLAY with Wayland
// see https://github.com/nigels-com/glew/issues/273
if (fl_wl_display() && err == GLEW_ERROR_NO_GLX_DISPLAY) err = GLEW_OK;
# endif
if (err) Fl::warning("glewInit() failed returning %u", err);
else add_output("Using GLEW %s\n", glewGetString(GLEW_VERSION));
#endif
const uchar *glv = glGetString(GL_VERSION);
add_output("GL_VERSION=%s\n", glv);
sscanf((const char *)glv, "%d", &gl_version_major);
if (gl_version_major < 3) add_output("\nThis platform does not support OpenGL V3\n\n");
redraw();
}
int retval = Fl_Gl_Window::handle(event);
if (retval) return retval;
if (event == FL_PUSH && gl_version_major >= 3) {
static float factor = 1.1;
GLfloat data[4];
glGetBufferSubData(GL_ARRAY_BUFFER, 0, 4*sizeof(GLfloat), data);
if (data[0] < -0.88 || data[0] > -0.5) factor = 1/factor;
data[0] *= factor;
glBufferSubData(GL_ARRAY_BUFFER, 0, 4*sizeof(GLfloat), data);
glGetBufferSubData(GL_ARRAY_BUFFER, 24*sizeof(GLfloat), 4*sizeof(GLfloat), data);
data[0] *= factor;
glBufferSubData(GL_ARRAY_BUFFER, 24*sizeof(GLfloat), 4*sizeof(GLfloat), data);
redraw();
add_output("push Fl_Gl_Window::pixels_per_unit()=%.1f\n", pixels_per_unit());
return 1;
}
return retval;
}
void reset(void) { shaderProgram = 0; }
};
void toggle_double(Fl_Widget *wid, void *data) {
static bool doublebuff = true;
doublebuff = !doublebuff;
SimpleGL3Window *glwin = (SimpleGL3Window*)data;
int flags = glwin->mode();
if (doublebuff) flags |= FL_DOUBLE; else flags &= ~FL_DOUBLE;
glwin->mode(flags);
glwin->reset();
}
Fl_Text_Display *output; // shared between output_win() and add_output()
void output_win(SimpleGL3Window *gl)
{
output = new Fl_Text_Display(300,0,500, 280);
Fl_Light_Button *lb = new Fl_Light_Button(300, 280, 500, 20, "Double-Buffered");
lb->callback(toggle_double);
lb->user_data(gl);
lb->value(1);
output->buffer(new Fl_Text_Buffer());
}
void add_output(const char *format, ...)
{
va_list args;
char line_buffer[10000];
va_start(args, format);
vsnprintf(line_buffer, sizeof(line_buffer)-1, format, args);
va_end(args);
output->buffer()->append(line_buffer);
output->scroll(10000, 0);
output->redraw();
}
void button_cb(Fl_Widget *, void *) {
add_output("run button callback\n");
}
void add_widgets(Fl_Gl_Window *g) {
#ifdef __APPLE__
g = fl_mac_prepare_add_widgets_to_GL3_win(g);
#endif
Fl::set_color(FL_FREE_COLOR, 255, 0, 0, 140); // partially transparent red
g->begin();
// Create here widgets to go above the GL3 scene
Fl_Button* b = new Fl_Button( 0, 170, 60, 30, "button");
b->color(FL_FREE_COLOR);
b->box(FL_BORDER_BOX );
b->callback(button_cb, NULL);
g->end();
}
int main(int argc, char **argv)
{
Fl::use_high_res_GL(1);
Fl_Window *topwin = new Fl_Window(800, 300);
SimpleGL3Window *win = new SimpleGL3Window(0, 0, 300, 300);
win->end();
output_win(win);
add_widgets(win);
topwin->end();
topwin->resizable(win);
topwin->label("Click GL panel to reshape");
topwin->show(argc, argv);
Fl::run();
}