Skip to content

Instantly share code, notes, and snippets.

@flarn2006
Last active April 28, 2021 03:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save flarn2006/d1ab06b18623f08e87983a2db4e16261 to your computer and use it in GitHub Desktop.
Save flarn2006/d1ab06b18623f08e87983a2db4e16261 to your computer and use it in GitHub Desktop.
FLTK-based application for experimenting with OpenGL matrices
/* Compile with -lGL -lfltk -lfltk_gl */
#include <Fl/Fl.h>
#include <Fl/Fl_Window.h>
#include <Fl/Fl_Box.h>
#include <Fl/Fl_Value_Input.h>
#include <Fl/Fl_Value_Output.h>
#include <Fl/Fl_Dial.h>
#include <Fl/Fl_Button.h>
#include <Fl/Fl_Gl_Window.h>
#include <Fl/gl.h>
#include <map>
#include <cmath>
class MatrixEdit : public Fl_Window
{
static constexpr int padding = 4;
static constexpr int inputWidth = 50;
static constexpr int rowHeight = 25;
static constexpr int dialWidth = rowHeight;
static constexpr int columnWidth = inputWidth + dialWidth;
static constexpr int resetBtnWidth = 52;
static constexpr std::size_t rows = 4;
static constexpr std::size_t cols = 4;
public:
static constexpr int width = resetBtnWidth + columnWidth * cols + 3 * padding;
static constexpr int height = rowHeight * (rows + 1) + 2 * padding;
private:
struct Cell {
std::size_t row, col;
Fl_Value_Input input;
Fl_Dial dial;
Cell(std::size_t r, std::size_t c)
: input(padding + resetBtnWidth + columnWidth * c, padding + rowHeight * r, inputWidth, rowHeight)
, dial(padding + resetBtnWidth + columnWidth * c + inputWidth, padding + rowHeight * r, dialWidth, rowHeight)
{
row = r;
col = c;
input.callback(changedInput, this);
input.minimum(-2.0); input.maximum(2.0);
input.precision(3);
dial.callback(changedDial, this);
dial.minimum(-2.0); dial.maximum(2.0);
reset();
}
void reset()
{
if (row == col) {
input.value(1.0);
dial.value(1.0);
} else {
input.value(0.0);
dial.value(0.0);
}
}
static void update(const Cell& cell)
{
auto mtx = dynamic_cast<MatrixEdit*>(cell.input.parent());
if (mtx->glview)
mtx->glview->redraw();
mtx->update_diagonal(cell.col);
}
static void changedInput(Fl_Widget* widget, void* data)
{
Cell& cell = *(Cell*)data;
cell.dial.value(cell.input.value());
update(cell);
}
static void changedDial(Fl_Widget* widget, void* data)
{
Cell& cell = *(Cell*)data;
cell.input.value(cell.dial.value());
update(cell);
}
};
struct Precision3Output : public Fl_Value_Output
{
Precision3Output(int x, int y, int w, int h)
: Fl_Value_Output(x, y, w, h) {}
virtual int format(char* buf) override
{
return sprintf(buf, "%.3lf", value());
}
};
Cell* cells[rows][cols];
Fl_Value_Output* diagonals[rows];
void update_diagonal(int col)
{
double squares = 0.0;
for (int r = 0; r < rows; ++r) {
Cell*& input_cell = cells[r][col];
double value = input_cell->input.value();
squares += value * value;
}
diagonals[col]->value(std::sqrt(squares));
}
static void reset_callback(Fl_Widget*, void* data)
{
MatrixEdit& mtxedit = *(MatrixEdit*)data;
for (int r=0; r<rows; ++r) {
for (int c=0; c<cols; ++c) {
mtxedit.cells[r][c]->reset();
}
for (int c=0; c<cols; ++c)
mtxedit.update_diagonal(c);
}
if (mtxedit.glview)
mtxedit.glview->redraw();
}
public:
Fl_Widget* glview = nullptr;
Fl_Button reset;
MatrixEdit(int x, int y, const char* title = nullptr)
: Fl_Window(x, y, width, height, title)
, reset(padding, padding, resetBtnWidth, rowHeight * (rows + 1), "Reset")
{
reset.callback(reset_callback, this);
for (int r=0; r<rows; ++r) {
for (int c=0; c<cols; ++c) {
Cell*& cell = cells[r][c];
cell = new Cell(r, c);
}
}
for (int c=0; c<cols; ++c) {
diagonals[c] = new Precision3Output(padding + resetBtnWidth + columnWidth * c, padding + rowHeight * rows, inputWidth, rowHeight);
diagonals[c]->value(1.0);
diagonals[c]->step(0.0);
}
end();
box(FL_DOWN_BOX);
}
~MatrixEdit()
{
for (int r=0; r<rows; ++r) {
for (int c=0; c<cols; ++c) {
delete cells[r][c];
}
delete diagonals[r];
}
}
void value(std::size_t row, std::size_t col, double value)
{
cells[row][col]->input.value(value);
cells[row][col]->dial.value(value);
}
double value(std::size_t row, std::size_t col) const
{
return cells[row][col]->input.value();
}
void glLoad() const
{
double matrix[16];
double* ptr = matrix;
for (int r=0; r<rows; ++r) {
for (int c=0; c<cols; ++c) {
*(ptr++) = value(r, c);
}
}
glLoadMatrixd(matrix);
}
};
struct V3f
{
float x, y, z;
};
class GlView : public Fl_Gl_Window
{
std::map<GLenum, const MatrixEdit*> matrices;
GLuint texture = 0;
static const V3f v[];
static void quadTris(V3f a, V3f b, V3f c, V3f d)
{
glTexCoord2f(0.0f, 0.0f); glVertex3f(a.x, a.y, a.z);
glTexCoord2f(2.0f, 0.0f); glVertex3f(b.x, b.y, b.z);
glTexCoord2f(0.0f, 2.0f); glVertex3f(d.x, d.y, d.z);
glTexCoord2f(0.0f, 2.0f); glVertex3f(d.x, d.y, d.z);
glTexCoord2f(2.0f, 0.0f); glVertex3f(b.x, b.y, b.z);
glTexCoord2f(2.0f, 2.0f); glVertex3f(c.x, c.y, c.z);
}
protected:
virtual void draw()
{
if (!texture) {
GLubyte texdata[4] = {192, 255, 255, 192};
glGenTextures(1, &texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 2, 2, 0, GL_RED, GL_UNSIGNED_BYTE, texdata);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepth(0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_FOG);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
for (auto& pair : matrices) {
glMatrixMode(pair.first);
pair.second->glLoad();
}
/* Using both matrices */
glActiveTexture(GL_TEXTURE1);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 1.0f, 0.5f); quadTris(v[0], v[1], v[2], v[3]);
glColor3f(1.0f, 0.5f, 1.0f); quadTris(v[0], v[1], v[5], v[4]);
glColor3f(1.0f, 0.5f, 0.5f); quadTris(v[1], v[2], v[6], v[5]);
glColor3f(0.5f, 1.0f, 0.5f); quadTris(v[2], v[3], v[7], v[6]);
glColor3f(0.5f, 1.0f, 1.0f); quadTris(v[3], v[0], v[4], v[7]);
glColor3f(0.5f, 0.5f, 1.0f); quadTris(v[4], v[5], v[6], v[7]);
glEnd();
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
/* Only using projection matrix */
glActiveTexture(GL_TEXTURE0);
glBegin(GL_LINES);
glColor3f(0.5f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glColor3f(0.0f, 0.5f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 0.5f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glEnd();
glPopMatrix();
/* Once again using both matrices */
glActiveTexture(GL_TEXTURE0);
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glEnd();
}
public:
GlView(int x, int y, int w, int h)
: Fl_Gl_Window(x, y, w, h)
{
}
void addMatrix(GLenum matrix, const MatrixEdit* control)
{
matrices[matrix] = control;
}
};
const V3f GlView::v[] = {
{-0.5f, -0.5f, -0.5f},
{ 0.5f, -0.5f, -0.5f},
{ 0.5f, 0.5f, -0.5f},
{-0.5f, 0.5f, -0.5f},
{-0.5f, -0.5f, 0.5f},
{ 0.5f, -0.5f, 0.5f},
{ 0.5f, 0.5f, 0.5f},
{-0.5f, 0.5f, 0.5f}
};
int main(int argc, char* argv[])
{
constexpr int padding = 8;
constexpr int window_width = 550;
constexpr int window_width_inner = window_width - 2*padding;
constexpr int label_box_width = window_width_inner - MatrixEdit::width;
Fl_Window win(window_width, window_width + 2*padding + 2*MatrixEdit::height, "OpenGL Matrix Viewer");
win.begin();
MatrixEdit me_projection(padding + label_box_width, window_width);
MatrixEdit me_modelview(padding + label_box_width, window_width + padding + MatrixEdit::height);
Fl_Box lbl_projection(padding, window_width, label_box_width - padding, MatrixEdit::height, "Projection");
Fl_Box lbl_modelview(padding, window_width + padding + MatrixEdit::height, label_box_width - padding, MatrixEdit::height, "Model View");
lbl_projection.box(FL_EMBOSSED_BOX);
lbl_modelview.box(FL_EMBOSSED_BOX);
GlView glv(padding, padding, window_width_inner, window_width_inner);
win.end();
me_projection.glview = me_modelview.glview = &glv;
glv.addMatrix(GL_PROJECTION, &me_projection);
glv.addMatrix(GL_MODELVIEW, &me_modelview);
win.show();
return Fl::run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment