You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
547 lines
15 KiB
547 lines
15 KiB
//======================================================================== |
|
// This is an example program for the GLFW library |
|
// |
|
// The program uses a "split window" view, rendering four views of the |
|
// same scene in one window (e.g. useful for 3D modelling software). This |
|
// demo uses scissors to separate the four different rendering areas from |
|
// each other. |
|
// |
|
// (If the code seems a little bit strange here and there, it may be |
|
// because I am not a friend of orthogonal projections) |
|
//======================================================================== |
|
|
|
#define GLAD_GL_IMPLEMENTATION |
|
#include <glad/gl.h> |
|
#define GLFW_INCLUDE_NONE |
|
#include <GLFW/glfw3.h> |
|
|
|
#if defined(_MSC_VER) |
|
// Make MS math.h define M_PI |
|
#define _USE_MATH_DEFINES |
|
#endif |
|
|
|
#include <math.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
#include <linmath.h> |
|
|
|
|
|
//======================================================================== |
|
// Global variables |
|
//======================================================================== |
|
|
|
// Mouse position |
|
static double xpos = 0, ypos = 0; |
|
|
|
// Window size |
|
static int width, height; |
|
|
|
// Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left, |
|
// 4 = lower right |
|
static int active_view = 0; |
|
|
|
// Rotation around each axis |
|
static int rot_x = 0, rot_y = 0, rot_z = 0; |
|
|
|
// Do redraw? |
|
static int do_redraw = 1; |
|
|
|
|
|
//======================================================================== |
|
// Draw a solid torus (use a display list for the model) |
|
//======================================================================== |
|
|
|
#define TORUS_MAJOR 1.5 |
|
#define TORUS_MINOR 0.5 |
|
#define TORUS_MAJOR_RES 32 |
|
#define TORUS_MINOR_RES 32 |
|
|
|
static void drawTorus(void) |
|
{ |
|
static GLuint torus_list = 0; |
|
int i, j, k; |
|
double s, t, x, y, z, nx, ny, nz, scale, twopi; |
|
|
|
if (!torus_list) |
|
{ |
|
// Start recording displaylist |
|
torus_list = glGenLists(1); |
|
glNewList(torus_list, GL_COMPILE_AND_EXECUTE); |
|
|
|
// Draw torus |
|
twopi = 2.0 * M_PI; |
|
for (i = 0; i < TORUS_MINOR_RES; i++) |
|
{ |
|
glBegin(GL_QUAD_STRIP); |
|
for (j = 0; j <= TORUS_MAJOR_RES; j++) |
|
{ |
|
for (k = 1; k >= 0; k--) |
|
{ |
|
s = (i + k) % TORUS_MINOR_RES + 0.5; |
|
t = j % TORUS_MAJOR_RES; |
|
|
|
// Calculate point on surface |
|
x = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * cos(t * twopi / TORUS_MAJOR_RES); |
|
y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES); |
|
z = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * sin(t * twopi / TORUS_MAJOR_RES); |
|
|
|
// Calculate surface normal |
|
nx = x - TORUS_MAJOR * cos(t * twopi / TORUS_MAJOR_RES); |
|
ny = y; |
|
nz = z - TORUS_MAJOR * sin(t * twopi / TORUS_MAJOR_RES); |
|
scale = 1.0 / sqrt(nx*nx + ny*ny + nz*nz); |
|
nx *= scale; |
|
ny *= scale; |
|
nz *= scale; |
|
|
|
glNormal3f((float) nx, (float) ny, (float) nz); |
|
glVertex3f((float) x, (float) y, (float) z); |
|
} |
|
} |
|
|
|
glEnd(); |
|
} |
|
|
|
// Stop recording displaylist |
|
glEndList(); |
|
} |
|
else |
|
{ |
|
// Playback displaylist |
|
glCallList(torus_list); |
|
} |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Draw the scene (a rotating torus) |
|
//======================================================================== |
|
|
|
static void drawScene(void) |
|
{ |
|
const GLfloat model_diffuse[4] = {1.0f, 0.8f, 0.8f, 1.0f}; |
|
const GLfloat model_specular[4] = {0.6f, 0.6f, 0.6f, 1.0f}; |
|
const GLfloat model_shininess = 20.0f; |
|
|
|
glPushMatrix(); |
|
|
|
// Rotate the object |
|
glRotatef((GLfloat) rot_x * 0.5f, 1.0f, 0.0f, 0.0f); |
|
glRotatef((GLfloat) rot_y * 0.5f, 0.0f, 1.0f, 0.0f); |
|
glRotatef((GLfloat) rot_z * 0.5f, 0.0f, 0.0f, 1.0f); |
|
|
|
// Set model color (used for orthogonal views, lighting disabled) |
|
glColor4fv(model_diffuse); |
|
|
|
// Set model material (used for perspective view, lighting enabled) |
|
glMaterialfv(GL_FRONT, GL_DIFFUSE, model_diffuse); |
|
glMaterialfv(GL_FRONT, GL_SPECULAR, model_specular); |
|
glMaterialf(GL_FRONT, GL_SHININESS, model_shininess); |
|
|
|
// Draw torus |
|
drawTorus(); |
|
|
|
glPopMatrix(); |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Draw a 2D grid (used for orthogonal views) |
|
//======================================================================== |
|
|
|
static void drawGrid(float scale, int steps) |
|
{ |
|
int i; |
|
float x, y; |
|
mat4x4 view; |
|
|
|
glPushMatrix(); |
|
|
|
// Set background to some dark bluish grey |
|
glClearColor(0.05f, 0.05f, 0.2f, 0.0f); |
|
glClear(GL_COLOR_BUFFER_BIT); |
|
|
|
// Setup modelview matrix (flat XY view) |
|
{ |
|
vec3 eye = { 0.f, 0.f, 1.f }; |
|
vec3 center = { 0.f, 0.f, 0.f }; |
|
vec3 up = { 0.f, 1.f, 0.f }; |
|
mat4x4_look_at(view, eye, center, up); |
|
} |
|
glLoadMatrixf((const GLfloat*) view); |
|
|
|
// We don't want to update the Z-buffer |
|
glDepthMask(GL_FALSE); |
|
|
|
// Set grid color |
|
glColor3f(0.0f, 0.5f, 0.5f); |
|
|
|
glBegin(GL_LINES); |
|
|
|
// Horizontal lines |
|
x = scale * 0.5f * (float) (steps - 1); |
|
y = -scale * 0.5f * (float) (steps - 1); |
|
for (i = 0; i < steps; i++) |
|
{ |
|
glVertex3f(-x, y, 0.0f); |
|
glVertex3f(x, y, 0.0f); |
|
y += scale; |
|
} |
|
|
|
// Vertical lines |
|
x = -scale * 0.5f * (float) (steps - 1); |
|
y = scale * 0.5f * (float) (steps - 1); |
|
for (i = 0; i < steps; i++) |
|
{ |
|
glVertex3f(x, -y, 0.0f); |
|
glVertex3f(x, y, 0.0f); |
|
x += scale; |
|
} |
|
|
|
glEnd(); |
|
|
|
// Enable Z-buffer writing again |
|
glDepthMask(GL_TRUE); |
|
|
|
glPopMatrix(); |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Draw all views |
|
//======================================================================== |
|
|
|
static void drawAllViews(void) |
|
{ |
|
const GLfloat light_position[4] = {0.0f, 8.0f, 8.0f, 1.0f}; |
|
const GLfloat light_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
|
const GLfloat light_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
|
const GLfloat light_ambient[4] = {0.2f, 0.2f, 0.3f, 1.0f}; |
|
float aspect; |
|
mat4x4 view, projection; |
|
|
|
// Calculate aspect of window |
|
if (height > 0) |
|
aspect = (float) width / (float) height; |
|
else |
|
aspect = 1.f; |
|
|
|
// Clear screen |
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
|
|
// Enable scissor test |
|
glEnable(GL_SCISSOR_TEST); |
|
|
|
// Enable depth test |
|
glEnable(GL_DEPTH_TEST); |
|
glDepthFunc(GL_LEQUAL); |
|
|
|
// ** ORTHOGONAL VIEWS ** |
|
|
|
// For orthogonal views, use wireframe rendering |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
|
|
|
// Enable line anti-aliasing |
|
glEnable(GL_LINE_SMOOTH); |
|
glEnable(GL_BLEND); |
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
|
|
// Setup orthogonal projection matrix |
|
glMatrixMode(GL_PROJECTION); |
|
glLoadIdentity(); |
|
glOrtho(-3.0 * aspect, 3.0 * aspect, -3.0, 3.0, 1.0, 50.0); |
|
|
|
// Upper left view (TOP VIEW) |
|
glViewport(0, height / 2, width / 2, height / 2); |
|
glScissor(0, height / 2, width / 2, height / 2); |
|
glMatrixMode(GL_MODELVIEW); |
|
{ |
|
vec3 eye = { 0.f, 10.f, 1e-3f }; |
|
vec3 center = { 0.f, 0.f, 0.f }; |
|
vec3 up = { 0.f, 1.f, 0.f }; |
|
mat4x4_look_at( view, eye, center, up ); |
|
} |
|
glLoadMatrixf((const GLfloat*) view); |
|
drawGrid(0.5, 12); |
|
drawScene(); |
|
|
|
// Lower left view (FRONT VIEW) |
|
glViewport(0, 0, width / 2, height / 2); |
|
glScissor(0, 0, width / 2, height / 2); |
|
glMatrixMode(GL_MODELVIEW); |
|
{ |
|
vec3 eye = { 0.f, 0.f, 10.f }; |
|
vec3 center = { 0.f, 0.f, 0.f }; |
|
vec3 up = { 0.f, 1.f, 0.f }; |
|
mat4x4_look_at( view, eye, center, up ); |
|
} |
|
glLoadMatrixf((const GLfloat*) view); |
|
drawGrid(0.5, 12); |
|
drawScene(); |
|
|
|
// Lower right view (SIDE VIEW) |
|
glViewport(width / 2, 0, width / 2, height / 2); |
|
glScissor(width / 2, 0, width / 2, height / 2); |
|
glMatrixMode(GL_MODELVIEW); |
|
{ |
|
vec3 eye = { 10.f, 0.f, 0.f }; |
|
vec3 center = { 0.f, 0.f, 0.f }; |
|
vec3 up = { 0.f, 1.f, 0.f }; |
|
mat4x4_look_at( view, eye, center, up ); |
|
} |
|
glLoadMatrixf((const GLfloat*) view); |
|
drawGrid(0.5, 12); |
|
drawScene(); |
|
|
|
// Disable line anti-aliasing |
|
glDisable(GL_LINE_SMOOTH); |
|
glDisable(GL_BLEND); |
|
|
|
// ** PERSPECTIVE VIEW ** |
|
|
|
// For perspective view, use solid rendering |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
|
|
// Enable face culling (faster rendering) |
|
glEnable(GL_CULL_FACE); |
|
glCullFace(GL_BACK); |
|
glFrontFace(GL_CW); |
|
|
|
// Setup perspective projection matrix |
|
glMatrixMode(GL_PROJECTION); |
|
mat4x4_perspective(projection, |
|
65.f * (float) M_PI / 180.f, |
|
aspect, |
|
1.f, 50.f); |
|
glLoadMatrixf((const GLfloat*) projection); |
|
|
|
// Upper right view (PERSPECTIVE VIEW) |
|
glViewport(width / 2, height / 2, width / 2, height / 2); |
|
glScissor(width / 2, height / 2, width / 2, height / 2); |
|
glMatrixMode(GL_MODELVIEW); |
|
{ |
|
vec3 eye = { 3.f, 1.5f, 3.f }; |
|
vec3 center = { 0.f, 0.f, 0.f }; |
|
vec3 up = { 0.f, 1.f, 0.f }; |
|
mat4x4_look_at( view, eye, center, up ); |
|
} |
|
glLoadMatrixf((const GLfloat*) view); |
|
|
|
// Configure and enable light source 1 |
|
glLightfv(GL_LIGHT1, GL_POSITION, light_position); |
|
glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient); |
|
glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); |
|
glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular); |
|
glEnable(GL_LIGHT1); |
|
glEnable(GL_LIGHTING); |
|
|
|
// Draw scene |
|
drawScene(); |
|
|
|
// Disable lighting |
|
glDisable(GL_LIGHTING); |
|
|
|
// Disable face culling |
|
glDisable(GL_CULL_FACE); |
|
|
|
// Disable depth test |
|
glDisable(GL_DEPTH_TEST); |
|
|
|
// Disable scissor test |
|
glDisable(GL_SCISSOR_TEST); |
|
|
|
// Draw a border around the active view |
|
if (active_view > 0 && active_view != 2) |
|
{ |
|
glViewport(0, 0, width, height); |
|
|
|
glMatrixMode(GL_PROJECTION); |
|
glLoadIdentity(); |
|
glOrtho(0.0, 2.0, 0.0, 2.0, 0.0, 1.0); |
|
|
|
glMatrixMode(GL_MODELVIEW); |
|
glLoadIdentity(); |
|
glTranslatef((GLfloat) ((active_view - 1) & 1), (GLfloat) (1 - (active_view - 1) / 2), 0.0f); |
|
|
|
glColor3f(1.0f, 1.0f, 0.6f); |
|
|
|
glBegin(GL_LINE_STRIP); |
|
glVertex2i(0, 0); |
|
glVertex2i(1, 0); |
|
glVertex2i(1, 1); |
|
glVertex2i(0, 1); |
|
glVertex2i(0, 0); |
|
glEnd(); |
|
} |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Framebuffer size callback function |
|
//======================================================================== |
|
|
|
static void framebufferSizeFun(GLFWwindow* window, int w, int h) |
|
{ |
|
width = w; |
|
height = h > 0 ? h : 1; |
|
do_redraw = 1; |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Window refresh callback function |
|
//======================================================================== |
|
|
|
static void windowRefreshFun(GLFWwindow* window) |
|
{ |
|
drawAllViews(); |
|
glfwSwapBuffers(window); |
|
do_redraw = 0; |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Mouse position callback function |
|
//======================================================================== |
|
|
|
static void cursorPosFun(GLFWwindow* window, double x, double y) |
|
{ |
|
int wnd_width, wnd_height, fb_width, fb_height; |
|
double scale; |
|
|
|
glfwGetWindowSize(window, &wnd_width, &wnd_height); |
|
glfwGetFramebufferSize(window, &fb_width, &fb_height); |
|
|
|
scale = (double) fb_width / (double) wnd_width; |
|
|
|
x *= scale; |
|
y *= scale; |
|
|
|
// Depending on which view was selected, rotate around different axes |
|
switch (active_view) |
|
{ |
|
case 1: |
|
rot_x += (int) (y - ypos); |
|
rot_z += (int) (x - xpos); |
|
do_redraw = 1; |
|
break; |
|
case 3: |
|
rot_x += (int) (y - ypos); |
|
rot_y += (int) (x - xpos); |
|
do_redraw = 1; |
|
break; |
|
case 4: |
|
rot_y += (int) (x - xpos); |
|
rot_z += (int) (y - ypos); |
|
do_redraw = 1; |
|
break; |
|
default: |
|
// Do nothing for perspective view, or if no view is selected |
|
break; |
|
} |
|
|
|
// Remember cursor position |
|
xpos = x; |
|
ypos = y; |
|
} |
|
|
|
|
|
//======================================================================== |
|
// Mouse button callback function |
|
//======================================================================== |
|
|
|
static void mouseButtonFun(GLFWwindow* window, int button, int action, int mods) |
|
{ |
|
if ((button == GLFW_MOUSE_BUTTON_LEFT) && action == GLFW_PRESS) |
|
{ |
|
// Detect which of the four views was clicked |
|
active_view = 1; |
|
if (xpos >= width / 2) |
|
active_view += 1; |
|
if (ypos >= height / 2) |
|
active_view += 2; |
|
} |
|
else if (button == GLFW_MOUSE_BUTTON_LEFT) |
|
{ |
|
// Deselect any previously selected view |
|
active_view = 0; |
|
} |
|
|
|
do_redraw = 1; |
|
} |
|
|
|
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) |
|
{ |
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) |
|
glfwSetWindowShouldClose(window, GLFW_TRUE); |
|
} |
|
|
|
|
|
//======================================================================== |
|
// main |
|
//======================================================================== |
|
|
|
int main(void) |
|
{ |
|
GLFWwindow* window; |
|
|
|
// Initialise GLFW |
|
if (!glfwInit()) |
|
{ |
|
fprintf(stderr, "Failed to initialize GLFW\n"); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
glfwWindowHint(GLFW_SAMPLES, 4); |
|
|
|
// Open OpenGL window |
|
window = glfwCreateWindow(500, 500, "Split view demo", NULL, NULL); |
|
if (!window) |
|
{ |
|
fprintf(stderr, "Failed to open GLFW window\n"); |
|
|
|
glfwTerminate(); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
// Set callback functions |
|
glfwSetFramebufferSizeCallback(window, framebufferSizeFun); |
|
glfwSetWindowRefreshCallback(window, windowRefreshFun); |
|
glfwSetCursorPosCallback(window, cursorPosFun); |
|
glfwSetMouseButtonCallback(window, mouseButtonFun); |
|
glfwSetKeyCallback(window, key_callback); |
|
|
|
// Enable vsync |
|
glfwMakeContextCurrent(window); |
|
gladLoadGL(glfwGetProcAddress); |
|
glfwSwapInterval(1); |
|
|
|
if (GLAD_GL_ARB_multisample || GLAD_GL_VERSION_1_3) |
|
glEnable(GL_MULTISAMPLE_ARB); |
|
|
|
glfwGetFramebufferSize(window, &width, &height); |
|
framebufferSizeFun(window, width, height); |
|
|
|
// Main loop |
|
for (;;) |
|
{ |
|
// Only redraw if we need to |
|
if (do_redraw) |
|
windowRefreshFun(window); |
|
|
|
// Wait for new events |
|
glfwWaitEvents(); |
|
|
|
// Check if the window should be closed |
|
if (glfwWindowShouldClose(window)) |
|
break; |
|
} |
|
|
|
// Close OpenGL window and terminate GLFW |
|
glfwTerminate(); |
|
|
|
exit(EXIT_SUCCESS); |
|
} |
|
|
|
|