mirror of
https://github.com/glfw/glfw.git
synced 2024-11-22 13:04:35 +00:00
d7e30b1c74
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.
546 lines
15 KiB
C
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);
|
|
}
|
|
|