Last active
April 28, 2021 03:14
-
-
Save flarn2006/d1ab06b18623f08e87983a2db4e16261 to your computer and use it in GitHub Desktop.
FLTK-based application for experimenting with OpenGL matrices
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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