glfw/examples/splitview.c
Camilla Löwy d7e30b1c74 Replace glad and the Vulkan SDK with glad2
This removes all dependencies from the GLFW test programs on the Vulkan
SDK.

It also removes support for linking the GLFW shared library (dynamic
library, DLL) against the Vulkan loader static library.
2019-04-15 02:45:48 +02:00

546 lines
15 KiB
C

//========================================================================
// 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)
//========================================================================
#include <glad/gl.h>
#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);
}