glfw/examples/splitview.c

549 lines
15 KiB
C
Raw Normal View History

2010-09-07 17:34:51 +02:00
//========================================================================
// 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. uesful for 3D modelling software). This
// demo uses scissors to separete 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)
//========================================================================
2015-07-21 21:58:24 +02:00
#define GLFW_INCLUDE_GLEXT
#include <GLFW/glfw3.h>
#if defined(_MSC_VER)
// Make MS math.h define M_PI
#define _USE_MATH_DEFINES
#endif
2010-09-07 17:34:51 +02:00
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
2015-08-09 15:44:20 +02:00
#include <linmath.h>
2010-09-07 17:34:51 +02:00
//========================================================================
// Global variables
//========================================================================
// Mouse position
2013-04-04 16:16:21 +02:00
static double xpos = 0, ypos = 0;
2010-09-07 17:34:51 +02:00
// 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
2010-10-24 14:56:35 +02:00
static void drawTorus(void)
2010-09-07 17:34:51 +02:00
{
static GLuint torus_list = 0;
int i, j, k;
double s, t, x, y, z, nx, ny, nz, scale, twopi;
2010-10-24 14:56:35 +02:00
if (!torus_list)
2010-09-07 17:34:51 +02:00
{
// Start recording displaylist
2010-10-24 14:56:35 +02:00
torus_list = glGenLists(1);
glNewList(torus_list, GL_COMPILE_AND_EXECUTE);
2010-09-07 17:34:51 +02:00
// Draw torus
twopi = 2.0 * M_PI;
2010-10-24 14:56:35 +02:00
for (i = 0; i < TORUS_MINOR_RES; i++)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
glBegin(GL_QUAD_STRIP);
for (j = 0; j <= TORUS_MAJOR_RES; j++)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
for (k = 1; k >= 0; k--)
2010-09-07 17:34:51 +02:00
{
s = (i + k) % TORUS_MINOR_RES + 0.5;
t = j % TORUS_MAJOR_RES;
// Calculate point on surface
2010-10-24 14:56:35 +02:00
x = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * cos(t * twopi / TORUS_MAJOR_RES);
2010-09-07 17:34:51 +02:00
y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES);
2010-10-24 14:56:35 +02:00
z = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * sin(t * twopi / TORUS_MAJOR_RES);
2010-09-07 17:34:51 +02:00
// Calculate surface normal
2010-10-24 14:56:35 +02:00
nx = x - TORUS_MAJOR * cos(t * twopi / TORUS_MAJOR_RES);
2010-09-07 17:34:51 +02:00
ny = y;
2010-10-24 14:56:35 +02:00
nz = z - TORUS_MAJOR * sin(t * twopi / TORUS_MAJOR_RES);
scale = 1.0 / sqrt(nx*nx + ny*ny + nz*nz);
2010-09-07 17:34:51 +02:00
nx *= scale;
ny *= scale;
nz *= scale;
2010-10-24 14:56:35 +02:00
glNormal3f((float) nx, (float) ny, (float) nz);
glVertex3f((float) x, (float) y, (float) z);
2010-09-07 17:34:51 +02:00
}
}
2010-10-24 14:56:35 +02:00
2010-09-07 17:34:51 +02:00
glEnd();
}
// Stop recording displaylist
glEndList();
}
else
{
// Playback displaylist
2010-10-24 14:56:35 +02:00
glCallList(torus_list);
2010-09-07 17:34:51 +02:00
}
}
//========================================================================
// Draw the scene (a rotating torus)
//========================================================================
2010-10-24 14:56:35 +02:00
static void drawScene(void)
2010-09-07 17:34:51 +02:00
{
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
2010-10-24 14:56:35 +02:00
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);
2010-09-07 17:34:51 +02:00
// Set model color (used for orthogonal views, lighting disabled)
2010-10-24 14:56:35 +02:00
glColor4fv(model_diffuse);
2010-09-07 17:34:51 +02:00
// Set model material (used for perspective view, lighting enabled)
2010-10-24 14:56:35 +02:00
glMaterialfv(GL_FRONT, GL_DIFFUSE, model_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, model_specular);
glMaterialf(GL_FRONT, GL_SHININESS, model_shininess);
2010-09-07 17:34:51 +02:00
// Draw torus
drawTorus();
glPopMatrix();
}
//========================================================================
// Draw a 2D grid (used for orthogonal views)
//========================================================================
2010-10-24 14:56:35 +02:00
static void drawGrid(float scale, int steps)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
int i;
2010-09-07 17:34:51 +02:00
float x, y;
2015-08-09 15:44:20 +02:00
mat4x4 view;
2010-09-07 17:34:51 +02:00
glPushMatrix();
// Set background to some dark bluish grey
2010-10-24 14:56:35 +02:00
glClearColor(0.05f, 0.05f, 0.2f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
2010-09-07 17:34:51 +02:00
// Setup modelview matrix (flat XY view)
2015-08-09 15:44:20 +02:00
{
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);
2010-09-07 17:34:51 +02:00
// We don't want to update the Z-buffer
2010-10-24 14:56:35 +02:00
glDepthMask(GL_FALSE);
2010-09-07 17:34:51 +02:00
// Set grid color
2010-10-24 14:56:35 +02:00
glColor3f(0.0f, 0.5f, 0.5f);
2010-09-07 17:34:51 +02:00
2010-10-24 14:56:35 +02:00
glBegin(GL_LINES);
2010-09-07 17:34:51 +02:00
// Horizontal lines
2010-10-24 14:56:35 +02:00
x = scale * 0.5f * (float) (steps - 1);
y = -scale * 0.5f * (float) (steps - 1);
for (i = 0; i < steps; i++)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
glVertex3f(-x, y, 0.0f);
glVertex3f(x, y, 0.0f);
2010-09-07 17:34:51 +02:00
y += scale;
}
// Vertical lines
2010-10-24 14:56:35 +02:00
x = -scale * 0.5f * (float) (steps - 1);
y = scale * 0.5f * (float) (steps - 1);
for (i = 0; i < steps; i++)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
glVertex3f(x, -y, 0.0f);
glVertex3f(x, y, 0.0f);
2010-09-07 17:34:51 +02:00
x += scale;
}
glEnd();
// Enable Z-buffer writing again
2010-10-24 14:56:35 +02:00
glDepthMask(GL_TRUE);
2010-09-07 17:34:51 +02:00
glPopMatrix();
}
//========================================================================
// Draw all views
//========================================================================
2010-10-24 14:56:35 +02:00
static void drawAllViews(void)
2010-09-07 17:34:51 +02:00
{
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};
2015-08-09 15:44:20 +02:00
float aspect;
mat4x4 view, projection;
2010-09-07 17:34:51 +02:00
// Calculate aspect of window
2010-10-24 14:56:35 +02:00
if (height > 0)
2015-08-09 15:44:20 +02:00
aspect = (float) width / (float) height;
2010-09-07 17:34:51 +02:00
else
2015-08-09 15:44:20 +02:00
aspect = 1.f;
2010-09-07 17:34:51 +02:00
// Clear screen
2010-10-24 14:56:35 +02:00
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2010-09-07 17:34:51 +02:00
// Enable scissor test
2010-10-24 14:56:35 +02:00
glEnable(GL_SCISSOR_TEST);
2010-09-07 17:34:51 +02:00
// Enable depth test
2010-10-24 14:56:35 +02:00
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
2010-09-07 17:34:51 +02:00
// ** ORTHOGONAL VIEWS **
// For orthogonal views, use wireframe rendering
2010-10-24 14:56:35 +02:00
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2010-09-07 17:34:51 +02:00
// Enable line anti-aliasing
2010-10-24 14:56:35 +02:00
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2010-09-07 17:34:51 +02:00
// Setup orthogonal projection matrix
2010-10-24 14:56:35 +02:00
glMatrixMode(GL_PROJECTION);
2010-09-07 17:34:51 +02:00
glLoadIdentity();
2010-10-24 14:56:35 +02:00
glOrtho(-3.0 * aspect, 3.0 * aspect, -3.0, 3.0, 1.0, 50.0);
2010-09-07 17:34:51 +02:00
// Upper left view (TOP VIEW)
2010-10-24 14:56:35 +02:00
glViewport(0, height / 2, width / 2, height / 2);
glScissor(0, height / 2, width / 2, height / 2);
glMatrixMode(GL_MODELVIEW);
2015-08-09 15:44:20 +02:00
{
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);
2010-10-24 14:56:35 +02:00
drawGrid(0.5, 12);
2010-09-07 17:34:51 +02:00
drawScene();
// Lower left view (FRONT VIEW)
2010-10-24 14:56:35 +02:00
glViewport(0, 0, width / 2, height / 2);
glScissor(0, 0, width / 2, height / 2);
glMatrixMode(GL_MODELVIEW);
2015-08-09 15:44:20 +02:00
{
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);
2010-10-24 14:56:35 +02:00
drawGrid(0.5, 12);
2010-09-07 17:34:51 +02:00
drawScene();
// Lower right view (SIDE VIEW)
2010-10-24 14:56:35 +02:00
glViewport(width / 2, 0, width / 2, height / 2);
glScissor(width / 2, 0, width / 2, height / 2);
glMatrixMode(GL_MODELVIEW);
2015-08-09 15:44:20 +02:00
{
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);
2010-10-24 14:56:35 +02:00
drawGrid(0.5, 12);
2010-09-07 17:34:51 +02:00
drawScene();
// Disable line anti-aliasing
2010-10-24 14:56:35 +02:00
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
2010-09-07 17:34:51 +02:00
// ** PERSPECTIVE VIEW **
// For perspective view, use solid rendering
2010-10-24 14:56:35 +02:00
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2010-09-07 17:34:51 +02:00
// Enable face culling (faster rendering)
2010-10-24 14:56:35 +02:00
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
2010-09-07 17:34:51 +02:00
// Setup perspective projection matrix
2010-10-24 14:56:35 +02:00
glMatrixMode(GL_PROJECTION);
2015-08-09 15:44:20 +02:00
mat4x4_perspective(projection,
65.f * (float) M_PI / 180.f,
aspect,
1.f, 50.f);
glLoadMatrixf((const GLfloat*) projection);
2010-09-07 17:34:51 +02:00
// Upper right view (PERSPECTIVE VIEW)
2010-10-24 14:56:35 +02:00
glViewport(width / 2, height / 2, width / 2, height / 2);
glScissor(width / 2, height / 2, width / 2, height / 2);
glMatrixMode(GL_MODELVIEW);
2015-08-09 15:44:20 +02:00
{
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);
2010-09-07 17:34:51 +02:00
// Configure and enable light source 1
2010-10-24 14:56:35 +02:00
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);
2010-09-07 17:34:51 +02:00
// Draw scene
drawScene();
// Disable lighting
2010-10-24 14:56:35 +02:00
glDisable(GL_LIGHTING);
2010-09-07 17:34:51 +02:00
// Disable face culling
2010-10-24 14:56:35 +02:00
glDisable(GL_CULL_FACE);
2010-09-07 17:34:51 +02:00
// Disable depth test
2010-10-24 14:56:35 +02:00
glDisable(GL_DEPTH_TEST);
2010-09-07 17:34:51 +02:00
// Disable scissor test
2010-10-24 14:56:35 +02:00
glDisable(GL_SCISSOR_TEST);
2010-09-07 17:34:51 +02:00
// Draw a border around the active view
2010-10-24 14:56:35 +02:00
if (active_view > 0 && active_view != 2)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
2010-09-07 17:34:51 +02:00
glLoadIdentity();
2010-10-24 14:56:35 +02:00
glOrtho(0.0, 2.0, 0.0, 2.0, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
2010-09-07 17:34:51 +02:00
glLoadIdentity();
2010-10-24 14:56:35 +02:00
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);
2010-09-07 17:34:51 +02:00
glEnd();
}
}
//========================================================================
// Framebuffer size callback function
2010-09-07 17:34:51 +02:00
//========================================================================
static void framebufferSizeFun(GLFWwindow* window, int w, int h)
2010-09-07 17:34:51 +02:00
{
width = w;
height = h > 0 ? h : 1;
do_redraw = 1;
}
//========================================================================
// Window refresh callback function
//========================================================================
static void windowRefreshFun(GLFWwindow* window)
2010-09-07 17:34:51 +02:00
{
drawAllViews();
glfwSwapBuffers(window);
do_redraw = 0;
2010-09-07 17:34:51 +02:00
}
//========================================================================
// Mouse position callback function
//========================================================================
2013-04-04 16:16:21 +02:00
static void cursorPosFun(GLFWwindow* window, double x, double y)
2010-09-07 17:34:51 +02:00
{
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;
2010-09-07 17:34:51 +02:00
// Depending on which view was selected, rotate around different axes
2010-10-24 14:56:35 +02:00
switch (active_view)
2010-09-07 17:34:51 +02:00
{
case 1:
2013-06-12 20:36:43 +02:00
rot_x += (int) (y - ypos);
rot_z += (int) (x - xpos);
2010-09-07 17:34:51 +02:00
do_redraw = 1;
break;
case 3:
2013-06-12 20:36:43 +02:00
rot_x += (int) (y - ypos);
rot_y += (int) (x - xpos);
2010-09-07 17:34:51 +02:00
do_redraw = 1;
break;
case 4:
2013-06-12 20:36:43 +02:00
rot_y += (int) (x - xpos);
rot_z += (int) (y - ypos);
2010-09-07 17:34:51 +02:00
do_redraw = 1;
break;
default:
// Do nothing for perspective view, or if no view is selected
break;
}
// Remember cursor position
2010-09-07 17:34:51 +02:00
xpos = x;
ypos = y;
}
//========================================================================
// Mouse button callback function
//========================================================================
static void mouseButtonFun(GLFWwindow* window, int button, int action, int mods)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
if ((button == GLFW_MOUSE_BUTTON_LEFT) && action == GLFW_PRESS)
2010-09-07 17:34:51 +02:00
{
// Detect which of the four views was clicked
active_view = 1;
2010-10-24 14:56:35 +02:00
if (xpos >= width / 2)
2010-09-07 17:34:51 +02:00
active_view += 1;
2010-10-24 14:56:35 +02:00
if (ypos >= height / 2)
2010-09-07 17:34:51 +02:00
active_view += 2;
}
2010-10-24 14:56:35 +02:00
else if (button == GLFW_MOUSE_BUTTON_LEFT)
2010-09-07 17:34:51 +02:00
{
// Deselect any previously selected view
active_view = 0;
}
do_redraw = 1;
}
2013-05-30 17:19:12 +02:00
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
2015-08-23 19:30:04 +02:00
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
2010-09-07 17:34:51 +02:00
//========================================================================
2010-10-24 14:56:35 +02:00
// main
2010-09-07 17:34:51 +02:00
//========================================================================
2010-10-24 14:56:35 +02:00
int main(void)
2010-09-07 17:34:51 +02:00
{
GLFWwindow* window;
2010-09-07 17:34:51 +02:00
// Initialise GLFW
2012-02-07 14:58:58 +01:00
if (!glfwInit())
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
fprintf(stderr, "Failed to initialize GLFW\n");
exit(EXIT_FAILURE);
2010-09-07 17:34:51 +02:00
}
2015-07-21 21:58:24 +02:00
glfwWindowHint(GLFW_SAMPLES, 4);
2010-09-07 17:34:51 +02:00
// Open OpenGL window
2012-09-27 21:37:36 +02:00
window = glfwCreateWindow(500, 500, "Split view demo", NULL, NULL);
if (!window)
2010-09-07 17:34:51 +02:00
{
2010-10-24 14:56:35 +02:00
fprintf(stderr, "Failed to open GLFW window\n");
glfwTerminate();
2010-10-24 14:56:35 +02:00
exit(EXIT_FAILURE);
2010-09-07 17:34:51 +02:00
}
// Set callback functions
glfwSetFramebufferSizeCallback(window, framebufferSizeFun);
glfwSetWindowRefreshCallback(window, windowRefreshFun);
glfwSetCursorPosCallback(window, cursorPosFun);
glfwSetMouseButtonCallback(window, mouseButtonFun);
glfwSetKeyCallback(window, key_callback);
2010-09-07 17:34:51 +02:00
// Enable vsync
glfwMakeContextCurrent(window);
2010-10-24 14:56:35 +02:00
glfwSwapInterval(1);
2010-09-07 17:34:51 +02:00
2015-07-21 21:58:24 +02:00
if (glfwExtensionSupported("GL_ARB_multisample") ||
glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR) >= 2 ||
glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR) >= 3)
{
glEnable(GL_MULTISAMPLE_ARB);
}
glfwGetFramebufferSize(window, &width, &height);
framebufferSizeFun(window, width, height);
2010-09-07 17:34:51 +02:00
// Main loop
for (;;)
2010-09-07 17:34:51 +02:00
{
// Only redraw if we need to
2010-10-24 14:56:35 +02:00
if (do_redraw)
windowRefreshFun(window);
2010-09-07 17:34:51 +02:00
// Wait for new events
glfwWaitEvents();
// Check if the window should be closed
if (glfwWindowShouldClose(window))
break;
}
2010-09-07 17:34:51 +02:00
// Close OpenGL window and terminate GLFW
glfwTerminate();
2010-10-24 14:56:35 +02:00
exit(EXIT_SUCCESS);
2010-09-07 17:34:51 +02:00
}