summaryrefslogtreecommitdiff
path: root/libs/glfw-3.3.8/examples
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-21 20:27:25 -0500
committersanine <sanine.not@pm.me>2022-08-21 20:27:25 -0500
commit5a1275f661f2d879411411a1376b790f8aec7947 (patch)
treee552e2290883d3c6a40c4c123fed1ff5bdedeff9 /libs/glfw-3.3.8/examples
parentced4c513947132cae08c6627fd2030b4345eb39d (diff)
add glfw library
Diffstat (limited to 'libs/glfw-3.3.8/examples')
-rw-r--r--libs/glfw-3.3.8/examples/CMakeLists.txt93
-rw-r--r--libs/glfw-3.3.8/examples/boing.c679
-rw-r--r--libs/glfw-3.3.8/examples/gears.c360
-rw-r--r--libs/glfw-3.3.8/examples/glfw.icnsbin0 -> 27988 bytes
-rw-r--r--libs/glfw-3.3.8/examples/glfw.icobin0 -> 21630 bytes
-rw-r--r--libs/glfw-3.3.8/examples/glfw.rc3
-rw-r--r--libs/glfw-3.3.8/examples/heightmap.c512
-rw-r--r--libs/glfw-3.3.8/examples/offscreen.c177
-rw-r--r--libs/glfw-3.3.8/examples/particles.c1073
-rw-r--r--libs/glfw-3.3.8/examples/sharing.c234
-rw-r--r--libs/glfw-3.3.8/examples/simple.c166
-rw-r--r--libs/glfw-3.3.8/examples/splitview.c546
-rw-r--r--libs/glfw-3.3.8/examples/wave.c462
13 files changed, 4305 insertions, 0 deletions
diff --git a/libs/glfw-3.3.8/examples/CMakeLists.txt b/libs/glfw-3.3.8/examples/CMakeLists.txt
new file mode 100644
index 0000000..0eba4e6
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/CMakeLists.txt
@@ -0,0 +1,93 @@
+
+link_libraries(glfw)
+
+include_directories("${GLFW_SOURCE_DIR}/deps")
+
+if (MATH_LIBRARY)
+ link_libraries("${MATH_LIBRARY}")
+endif()
+
+# Workaround for the MS CRT deprecating parts of the standard library
+if (MSVC OR CMAKE_C_SIMULATE_ID STREQUAL "MSVC")
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+endif()
+
+if (WIN32)
+ set(ICON glfw.rc)
+elseif (APPLE)
+ set(ICON glfw.icns)
+endif()
+
+if (${CMAKE_VERSION} VERSION_EQUAL "3.1.0" OR
+ ${CMAKE_VERSION} VERSION_GREATER "3.1.0")
+ set(CMAKE_C_STANDARD 99)
+else()
+ # Remove this fallback when removing support for CMake version less than 3.1
+ add_compile_options("$<$<C_COMPILER_ID:AppleClang>:-std=c99>"
+ "$<$<C_COMPILER_ID:Clang>:-std=c99>"
+ "$<$<C_COMPILER_ID:GNU>:-std=c99>")
+
+endif()
+
+set(GLAD_GL "${GLFW_SOURCE_DIR}/deps/glad/gl.h"
+ "${GLFW_SOURCE_DIR}/deps/glad_gl.c")
+set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h"
+ "${GLFW_SOURCE_DIR}/deps/getopt.c")
+set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h"
+ "${GLFW_SOURCE_DIR}/deps/tinycthread.c")
+
+add_executable(boing WIN32 MACOSX_BUNDLE boing.c ${ICON} ${GLAD_GL})
+add_executable(gears WIN32 MACOSX_BUNDLE gears.c ${ICON} ${GLAD_GL})
+add_executable(heightmap WIN32 MACOSX_BUNDLE heightmap.c ${ICON} ${GLAD_GL})
+add_executable(offscreen offscreen.c ${ICON} ${GLAD_GL})
+add_executable(particles WIN32 MACOSX_BUNDLE particles.c ${ICON} ${TINYCTHREAD} ${GETOPT} ${GLAD_GL})
+add_executable(sharing WIN32 MACOSX_BUNDLE sharing.c ${ICON} ${GLAD_GL})
+add_executable(simple WIN32 MACOSX_BUNDLE simple.c ${ICON} ${GLAD_GL})
+add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD_GL})
+add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD_GL})
+
+target_link_libraries(particles "${CMAKE_THREAD_LIBS_INIT}")
+if (RT_LIBRARY)
+ target_link_libraries(particles "${RT_LIBRARY}")
+endif()
+
+set(GUI_ONLY_BINARIES boing gears heightmap particles sharing simple splitview
+ wave)
+set(CONSOLE_BINARIES offscreen)
+
+set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES
+ FOLDER "GLFW3/Examples")
+
+if (GLFW_USE_OSMESA)
+ target_compile_definitions(offscreen PRIVATE USE_NATIVE_OSMESA)
+endif()
+
+if (MSVC)
+ # Tell MSVC to use main instead of WinMain
+ set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES
+ LINK_FLAGS "/ENTRY:mainCRTStartup")
+elseif (CMAKE_C_SIMULATE_ID STREQUAL "MSVC")
+ # Tell Clang using MS CRT to use main instead of WinMain
+ set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES
+ LINK_FLAGS "-Wl,/entry:mainCRTStartup")
+endif()
+
+if (APPLE)
+ set_target_properties(boing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Boing")
+ set_target_properties(gears PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gears")
+ set_target_properties(heightmap PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Heightmap")
+ set_target_properties(particles PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Particles")
+ set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing")
+ set_target_properties(simple PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Simple")
+ set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView")
+ set_target_properties(wave PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Wave")
+
+ set_source_files_properties(glfw.icns PROPERTIES
+ MACOSX_PACKAGE_LOCATION "Resources")
+ set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION}
+ MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION}
+ MACOSX_BUNDLE_ICON_FILE glfw.icns
+ MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in")
+endif()
+
diff --git a/libs/glfw-3.3.8/examples/boing.c b/libs/glfw-3.3.8/examples/boing.c
new file mode 100644
index 0000000..ca38908
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/boing.c
@@ -0,0 +1,679 @@
+/*****************************************************************************
+ * Title: GLBoing
+ * Desc: Tribute to Amiga Boing.
+ * Author: Jim Brooks <gfx@jimbrooks.org>
+ * Original Amiga authors were R.J. Mical and Dale Luck.
+ * GLFW conversion by Marcus Geelnard
+ * Notes: - 360' = 2*PI [radian]
+ *
+ * - Distances between objects are created by doing a relative
+ * Z translations.
+ *
+ * - Although OpenGL enticingly supports alpha-blending,
+ * the shadow of the original Boing didn't affect the color
+ * of the grid.
+ *
+ * - [Marcus] Changed timing scheme from interval driven to frame-
+ * time based animation steps (which results in much smoother
+ * movement)
+ *
+ * History of Amiga Boing:
+ *
+ * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in
+ * 1985. According to legend, it was written ad-hoc in one night by
+ * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast
+ * and smooth, attendees did not believe the Amiga prototype was really doing
+ * the rendering. Suspecting a trick, they began looking around the booth for
+ * a hidden computer or VCR.
+ *****************************************************************************/
+
+#if defined(_MSC_VER)
+ // Make MS math.h define M_PI
+ #define _USE_MATH_DEFINES
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <linmath.h>
+
+
+/*****************************************************************************
+ * Various declarations and macros
+ *****************************************************************************/
+
+/* Prototypes */
+void init( void );
+void display( void );
+void reshape( GLFWwindow* window, int w, int h );
+void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods );
+void mouse_button_callback( GLFWwindow* window, int button, int action, int mods );
+void cursor_position_callback( GLFWwindow* window, double x, double y );
+void DrawBoingBall( void );
+void BounceBall( double dt );
+void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi );
+void DrawGrid( void );
+
+#define RADIUS 70.f
+#define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */
+#define STEP_LATITUDE 22.5f
+
+#define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f)
+
+#define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */
+#define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */
+#define BOUNCE_HEIGHT (RADIUS * 2.1f)
+#define BOUNCE_WIDTH (RADIUS * 2.1f)
+
+#define SHADOW_OFFSET_X -20.f
+#define SHADOW_OFFSET_Y 10.f
+#define SHADOW_OFFSET_Z 0.f
+
+#define WALL_L_OFFSET 0.f
+#define WALL_R_OFFSET 5.f
+
+/* Animation speed (50.0 mimics the original GLUT demo speed) */
+#define ANIMATION_SPEED 50.f
+
+/* Maximum allowed delta time per physics iteration */
+#define MAX_DELTA_T 0.02f
+
+/* Draw ball, or its shadow */
+typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
+
+/* Vertex type */
+typedef struct {float x; float y; float z;} vertex_t;
+
+/* Global vars */
+int windowed_xpos, windowed_ypos, windowed_width, windowed_height;
+int width, height;
+GLfloat deg_rot_y = 0.f;
+GLfloat deg_rot_y_inc = 2.f;
+int override_pos = GLFW_FALSE;
+GLfloat cursor_x = 0.f;
+GLfloat cursor_y = 0.f;
+GLfloat ball_x = -RADIUS;
+GLfloat ball_y = -RADIUS;
+GLfloat ball_x_inc = 1.f;
+GLfloat ball_y_inc = 2.f;
+DRAW_BALL_ENUM drawBallHow;
+double t;
+double t_old = 0.f;
+double dt;
+
+/* Random number generator */
+#ifndef RAND_MAX
+ #define RAND_MAX 4095
+#endif
+
+
+/*****************************************************************************
+ * Truncate a degree.
+ *****************************************************************************/
+GLfloat TruncateDeg( GLfloat deg )
+{
+ if ( deg >= 360.f )
+ return (deg - 360.f);
+ else
+ return deg;
+}
+
+/*****************************************************************************
+ * Convert a degree (360-based) into a radian.
+ * 360' = 2 * PI
+ *****************************************************************************/
+double deg2rad( double deg )
+{
+ return deg / 360 * (2 * M_PI);
+}
+
+/*****************************************************************************
+ * 360' sin().
+ *****************************************************************************/
+double sin_deg( double deg )
+{
+ return sin( deg2rad( deg ) );
+}
+
+/*****************************************************************************
+ * 360' cos().
+ *****************************************************************************/
+double cos_deg( double deg )
+{
+ return cos( deg2rad( deg ) );
+}
+
+/*****************************************************************************
+ * Compute a cross product (for a normal vector).
+ *
+ * c = a x b
+ *****************************************************************************/
+void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n )
+{
+ GLfloat u1, u2, u3;
+ GLfloat v1, v2, v3;
+
+ u1 = b.x - a.x;
+ u2 = b.y - a.y;
+ u3 = b.y - a.z;
+
+ v1 = c.x - a.x;
+ v2 = c.y - a.y;
+ v3 = c.z - a.z;
+
+ n->x = u2 * v3 - v2 * u3;
+ n->y = u3 * v1 - v3 * u1;
+ n->z = u1 * v2 - v1 * u2;
+}
+
+
+#define BOING_DEBUG 0
+
+
+/*****************************************************************************
+ * init()
+ *****************************************************************************/
+void init( void )
+{
+ /*
+ * Clear background.
+ */
+ glClearColor( 0.55f, 0.55f, 0.55f, 0.f );
+
+ glShadeModel( GL_FLAT );
+}
+
+
+/*****************************************************************************
+ * display()
+ *****************************************************************************/
+void display(void)
+{
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+ glPushMatrix();
+
+ drawBallHow = DRAW_BALL_SHADOW;
+ DrawBoingBall();
+
+ DrawGrid();
+
+ drawBallHow = DRAW_BALL;
+ DrawBoingBall();
+
+ glPopMatrix();
+ glFlush();
+}
+
+
+/*****************************************************************************
+ * reshape()
+ *****************************************************************************/
+void reshape( GLFWwindow* window, int w, int h )
+{
+ mat4x4 projection, view;
+
+ glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
+
+ glMatrixMode( GL_PROJECTION );
+ mat4x4_perspective( projection,
+ 2.f * (float) atan2( RADIUS, 200.f ),
+ (float)w / (float)h,
+ 1.f, VIEW_SCENE_DIST );
+ glLoadMatrixf((const GLfloat*) projection);
+
+ glMatrixMode( GL_MODELVIEW );
+ {
+ vec3 eye = { 0.f, 0.f, VIEW_SCENE_DIST };
+ 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);
+}
+
+void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods )
+{
+ if (action != GLFW_PRESS)
+ return;
+
+ if (key == GLFW_KEY_ESCAPE && mods == 0)
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ if ((key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) ||
+ (key == GLFW_KEY_F11 && mods == GLFW_MOD_ALT))
+ {
+ if (glfwGetWindowMonitor(window))
+ {
+ glfwSetWindowMonitor(window, NULL,
+ windowed_xpos, windowed_ypos,
+ windowed_width, windowed_height, 0);
+ }
+ else
+ {
+ GLFWmonitor* monitor = glfwGetPrimaryMonitor();
+ if (monitor)
+ {
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+ glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos);
+ glfwGetWindowSize(window, &windowed_width, &windowed_height);
+ glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
+ }
+ }
+ }
+}
+
+static void set_ball_pos ( GLfloat x, GLfloat y )
+{
+ ball_x = (width / 2) - x;
+ ball_y = y - (height / 2);
+}
+
+void mouse_button_callback( GLFWwindow* window, int button, int action, int mods )
+{
+ if (button != GLFW_MOUSE_BUTTON_LEFT)
+ return;
+
+ if (action == GLFW_PRESS)
+ {
+ override_pos = GLFW_TRUE;
+ set_ball_pos(cursor_x, cursor_y);
+ }
+ else
+ {
+ override_pos = GLFW_FALSE;
+ }
+}
+
+void cursor_position_callback( GLFWwindow* window, double x, double y )
+{
+ cursor_x = (float) x;
+ cursor_y = (float) y;
+
+ if ( override_pos )
+ set_ball_pos(cursor_x, cursor_y);
+}
+
+/*****************************************************************************
+ * Draw the Boing ball.
+ *
+ * The Boing ball is sphere in which each facet is a rectangle.
+ * Facet colors alternate between red and white.
+ * The ball is built by stacking latitudinal circles. Each circle is composed
+ * of a widely-separated set of points, so that each facet is noticeably large.
+ *****************************************************************************/
+void DrawBoingBall( void )
+{
+ GLfloat lon_deg; /* degree of longitude */
+ double dt_total, dt2;
+
+ glPushMatrix();
+ glMatrixMode( GL_MODELVIEW );
+
+ /*
+ * Another relative Z translation to separate objects.
+ */
+ glTranslatef( 0.0, 0.0, DIST_BALL );
+
+ /* Update ball position and rotation (iterate if necessary) */
+ dt_total = dt;
+ while( dt_total > 0.0 )
+ {
+ dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
+ dt_total -= dt2;
+ BounceBall( dt2 );
+ deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) );
+ }
+
+ /* Set ball position */
+ glTranslatef( ball_x, ball_y, 0.0 );
+
+ /*
+ * Offset the shadow.
+ */
+ if ( drawBallHow == DRAW_BALL_SHADOW )
+ {
+ glTranslatef( SHADOW_OFFSET_X,
+ SHADOW_OFFSET_Y,
+ SHADOW_OFFSET_Z );
+ }
+
+ /*
+ * Tilt the ball.
+ */
+ glRotatef( -20.0, 0.0, 0.0, 1.0 );
+
+ /*
+ * Continually rotate ball around Y axis.
+ */
+ glRotatef( deg_rot_y, 0.0, 1.0, 0.0 );
+
+ /*
+ * Set OpenGL state for Boing ball.
+ */
+ glCullFace( GL_FRONT );
+ glEnable( GL_CULL_FACE );
+ glEnable( GL_NORMALIZE );
+
+ /*
+ * Build a faceted latitude slice of the Boing ball,
+ * stepping same-sized vertical bands of the sphere.
+ */
+ for ( lon_deg = 0;
+ lon_deg < 180;
+ lon_deg += STEP_LONGITUDE )
+ {
+ /*
+ * Draw a latitude circle at this longitude.
+ */
+ DrawBoingBallBand( lon_deg,
+ lon_deg + STEP_LONGITUDE );
+ }
+
+ glPopMatrix();
+
+ return;
+}
+
+
+/*****************************************************************************
+ * Bounce the ball.
+ *****************************************************************************/
+void BounceBall( double delta_t )
+{
+ GLfloat sign;
+ GLfloat deg;
+
+ if ( override_pos )
+ return;
+
+ /* Bounce on walls */
+ if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) )
+ {
+ ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ deg_rot_y_inc = -deg_rot_y_inc;
+ }
+ if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) )
+ {
+ ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ deg_rot_y_inc = -deg_rot_y_inc;
+ }
+
+ /* Bounce on floor / roof */
+ if ( ball_y > BOUNCE_HEIGHT/2 )
+ {
+ ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ }
+ if ( ball_y < -BOUNCE_HEIGHT/2*0.85 )
+ {
+ ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ }
+
+ /* Update ball position */
+ ball_x += ball_x_inc * ((float)delta_t*ANIMATION_SPEED);
+ ball_y += ball_y_inc * ((float)delta_t*ANIMATION_SPEED);
+
+ /*
+ * Simulate the effects of gravity on Y movement.
+ */
+ if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0;
+
+ deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT;
+ if ( deg > 80 ) deg = 80;
+ if ( deg < 10 ) deg = 10;
+
+ ball_y_inc = sign * 4.f * (float) sin_deg( deg );
+}
+
+
+/*****************************************************************************
+ * Draw a faceted latitude band of the Boing ball.
+ *
+ * Parms: long_lo, long_hi
+ * Low and high longitudes of slice, resp.
+ *****************************************************************************/
+void DrawBoingBallBand( GLfloat long_lo,
+ GLfloat long_hi )
+{
+ vertex_t vert_ne; /* "ne" means south-east, so on */
+ vertex_t vert_nw;
+ vertex_t vert_sw;
+ vertex_t vert_se;
+ vertex_t vert_norm;
+ GLfloat lat_deg;
+ static int colorToggle = 0;
+
+ /*
+ * Iterate through the points of a latitude circle.
+ * A latitude circle is a 2D set of X,Z points.
+ */
+ for ( lat_deg = 0;
+ lat_deg <= (360 - STEP_LATITUDE);
+ lat_deg += STEP_LATITUDE )
+ {
+ /*
+ * Color this polygon with red or white.
+ */
+ if ( colorToggle )
+ glColor3f( 0.8f, 0.1f, 0.1f );
+ else
+ glColor3f( 0.95f, 0.95f, 0.95f );
+#if 0
+ if ( lat_deg >= 180 )
+ if ( colorToggle )
+ glColor3f( 0.1f, 0.8f, 0.1f );
+ else
+ glColor3f( 0.5f, 0.5f, 0.95f );
+#endif
+ colorToggle = ! colorToggle;
+
+ /*
+ * Change color if drawing shadow.
+ */
+ if ( drawBallHow == DRAW_BALL_SHADOW )
+ glColor3f( 0.35f, 0.35f, 0.35f );
+
+ /*
+ * Assign each Y.
+ */
+ vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS;
+ vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS;
+
+ /*
+ * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude.
+ * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude),
+ * while long=90 (sin(90)=1) is at equator.
+ */
+ vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
+ vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
+
+ vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
+ vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
+
+ /*
+ * Draw the facet.
+ */
+ glBegin( GL_POLYGON );
+
+ CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm );
+ glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z );
+
+ glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z );
+ glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z );
+ glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z );
+ glVertex3f( vert_se.x, vert_se.y, vert_se.z );
+
+ glEnd();
+
+#if BOING_DEBUG
+ printf( "----------------------------------------------------------- \n" );
+ printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi );
+ printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z );
+ printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z );
+ printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z );
+ printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z );
+#endif
+
+ }
+
+ /*
+ * Toggle color so that next band will opposite red/white colors than this one.
+ */
+ colorToggle = ! colorToggle;
+
+ /*
+ * This circular band is done.
+ */
+ return;
+}
+
+
+/*****************************************************************************
+ * Draw the purple grid of lines, behind the Boing ball.
+ * When the Workbench is dropped to the bottom, Boing shows 12 rows.
+ *****************************************************************************/
+void DrawGrid( void )
+{
+ int row, col;
+ const int rowTotal = 12; /* must be divisible by 2 */
+ const int colTotal = rowTotal; /* must be same as rowTotal */
+ const GLfloat widthLine = 2.0; /* should be divisible by 2 */
+ const GLfloat sizeCell = GRID_SIZE / rowTotal;
+ const GLfloat z_offset = -40.0;
+ GLfloat xl, xr;
+ GLfloat yt, yb;
+
+ glPushMatrix();
+ glDisable( GL_CULL_FACE );
+
+ /*
+ * Another relative Z translation to separate objects.
+ */
+ glTranslatef( 0.0, 0.0, DIST_BALL );
+
+ /*
+ * Draw vertical lines (as skinny 3D rectangles).
+ */
+ for ( col = 0; col <= colTotal; col++ )
+ {
+ /*
+ * Compute co-ords of line.
+ */
+ xl = -GRID_SIZE / 2 + col * sizeCell;
+ xr = xl + widthLine;
+
+ yt = GRID_SIZE / 2;
+ yb = -GRID_SIZE / 2 - widthLine;
+
+ glBegin( GL_POLYGON );
+
+ glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
+
+ glVertex3f( xr, yt, z_offset ); /* NE */
+ glVertex3f( xl, yt, z_offset ); /* NW */
+ glVertex3f( xl, yb, z_offset ); /* SW */
+ glVertex3f( xr, yb, z_offset ); /* SE */
+
+ glEnd();
+ }
+
+ /*
+ * Draw horizontal lines (as skinny 3D rectangles).
+ */
+ for ( row = 0; row <= rowTotal; row++ )
+ {
+ /*
+ * Compute co-ords of line.
+ */
+ yt = GRID_SIZE / 2 - row * sizeCell;
+ yb = yt - widthLine;
+
+ xl = -GRID_SIZE / 2;
+ xr = GRID_SIZE / 2 + widthLine;
+
+ glBegin( GL_POLYGON );
+
+ glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
+
+ glVertex3f( xr, yt, z_offset ); /* NE */
+ glVertex3f( xl, yt, z_offset ); /* NW */
+ glVertex3f( xl, yb, z_offset ); /* SW */
+ glVertex3f( xr, yb, z_offset ); /* SE */
+
+ glEnd();
+ }
+
+ glPopMatrix();
+
+ return;
+}
+
+
+/*======================================================================*
+ * main()
+ *======================================================================*/
+
+int main( void )
+{
+ GLFWwindow* window;
+
+ /* Init GLFW */
+ if( !glfwInit() )
+ exit( EXIT_FAILURE );
+
+ window = glfwCreateWindow( 400, 400, "Boing (classic Amiga demo)", NULL, NULL );
+ if (!window)
+ {
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ glfwSetWindowAspectRatio(window, 1, 1);
+
+ glfwSetFramebufferSizeCallback(window, reshape);
+ glfwSetKeyCallback(window, key_callback);
+ glfwSetMouseButtonCallback(window, mouse_button_callback);
+ glfwSetCursorPosCallback(window, cursor_position_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval( 1 );
+
+ glfwGetFramebufferSize(window, &width, &height);
+ reshape(window, width, height);
+
+ glfwSetTime( 0.0 );
+
+ init();
+
+ /* Main loop */
+ for (;;)
+ {
+ /* Timing */
+ t = glfwGetTime();
+ dt = t - t_old;
+ t_old = t;
+
+ /* Draw one frame */
+ display();
+
+ /* Swap buffers */
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+
+ /* Check if we are still running */
+ if (glfwWindowShouldClose(window))
+ break;
+ }
+
+ glfwTerminate();
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/libs/glfw-3.3.8/examples/gears.c b/libs/glfw-3.3.8/examples/gears.c
new file mode 100644
index 0000000..292f44b
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/gears.c
@@ -0,0 +1,360 @@
+/*
+ * 3-D gear wheels. This program is in the public domain.
+ *
+ * Command line options:
+ * -info print GL implementation information
+ * -exit automatically exit after 30 seconds
+ *
+ *
+ * Brian Paul
+ *
+ *
+ * Marcus Geelnard:
+ * - Conversion to GLFW
+ * - Time based rendering (frame rate independent)
+ * - Slightly modified camera that should work better for stereo viewing
+ *
+ *
+ * Camilla Löwy:
+ * - Removed FPS counter (this is not a benchmark)
+ * - Added a few comments
+ * - Enabled vsync
+ */
+
+#if defined(_MSC_VER)
+ // Make MS math.h define M_PI
+ #define _USE_MATH_DEFINES
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+/**
+
+ Draw a gear wheel. You'll probably want to call this function when
+ building a display list since we do a lot of trig here.
+
+ Input: inner_radius - radius of hole at center
+ outer_radius - radius at center of teeth
+ width - width of gear teeth - number of teeth
+ tooth_depth - depth of tooth
+
+ **/
+
+static void
+gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
+ GLint teeth, GLfloat tooth_depth)
+{
+ GLint i;
+ GLfloat r0, r1, r2;
+ GLfloat angle, da;
+ GLfloat u, v, len;
+
+ r0 = inner_radius;
+ r1 = outer_radius - tooth_depth / 2.f;
+ r2 = outer_radius + tooth_depth / 2.f;
+
+ da = 2.f * (float) M_PI / teeth / 4.f;
+
+ glShadeModel(GL_FLAT);
+
+ glNormal3f(0.f, 0.f, 1.f);
+
+ /* draw front face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f);
+ if (i < teeth) {
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f);
+ }
+ }
+ glEnd();
+
+ /* draw front sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.f * (float) M_PI / teeth / 4.f;
+ for (i = 0; i < teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f);
+ }
+ glEnd();
+
+ glNormal3f(0.0, 0.0, -1.0);
+
+ /* draw back face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f);
+ if (i < teeth) {
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f);
+ }
+ }
+ glEnd();
+
+ /* draw back sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.f * (float) M_PI / teeth / 4.f;
+ for (i = 0; i < teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f);
+ }
+ glEnd();
+
+ /* draw outward faces of teeth */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i < teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f);
+ u = r2 * (float) cos(angle + da) - r1 * (float) cos(angle);
+ v = r2 * (float) sin(angle + da) - r1 * (float) sin(angle);
+ len = (float) sqrt(u * u + v * v);
+ u /= len;
+ v /= len;
+ glNormal3f(v, -u, 0.0);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f);
+ glNormal3f((float) cos(angle), (float) sin(angle), 0.f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f);
+ u = r1 * (float) cos(angle + 3 * da) - r2 * (float) cos(angle + 2 * da);
+ v = r1 * (float) sin(angle + 3 * da) - r2 * (float) sin(angle + 2 * da);
+ glNormal3f(v, -u, 0.f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f);
+ glNormal3f((float) cos(angle), (float) sin(angle), 0.f);
+ }
+
+ glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), width * 0.5f);
+ glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), -width * 0.5f);
+
+ glEnd();
+
+ glShadeModel(GL_SMOOTH);
+
+ /* draw inside radius cylinder */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+ glNormal3f(-(float) cos(angle), -(float) sin(angle), 0.f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f);
+ }
+ glEnd();
+
+}
+
+
+static GLfloat view_rotx = 20.f, view_roty = 30.f, view_rotz = 0.f;
+static GLint gear1, gear2, gear3;
+static GLfloat angle = 0.f;
+
+/* OpenGL draw function & timing */
+static void draw(void)
+{
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+ glRotatef(view_rotx, 1.0, 0.0, 0.0);
+ glRotatef(view_roty, 0.0, 1.0, 0.0);
+ glRotatef(view_rotz, 0.0, 0.0, 1.0);
+
+ glPushMatrix();
+ glTranslatef(-3.0, -2.0, 0.0);
+ glRotatef(angle, 0.0, 0.0, 1.0);
+ glCallList(gear1);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(3.1f, -2.f, 0.f);
+ glRotatef(-2.f * angle - 9.f, 0.f, 0.f, 1.f);
+ glCallList(gear2);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(-3.1f, 4.2f, 0.f);
+ glRotatef(-2.f * angle - 25.f, 0.f, 0.f, 1.f);
+ glCallList(gear3);
+ glPopMatrix();
+
+ glPopMatrix();
+}
+
+
+/* update animation parameters */
+static void animate(void)
+{
+ angle = 100.f * (float) glfwGetTime();
+}
+
+
+/* change view angle, exit upon ESC */
+void key( GLFWwindow* window, int k, int s, int action, int mods )
+{
+ if( action != GLFW_PRESS ) return;
+
+ switch (k) {
+ case GLFW_KEY_Z:
+ if( mods & GLFW_MOD_SHIFT )
+ view_rotz -= 5.0;
+ else
+ view_rotz += 5.0;
+ break;
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ case GLFW_KEY_UP:
+ view_rotx += 5.0;
+ break;
+ case GLFW_KEY_DOWN:
+ view_rotx -= 5.0;
+ break;
+ case GLFW_KEY_LEFT:
+ view_roty += 5.0;
+ break;
+ case GLFW_KEY_RIGHT:
+ view_roty -= 5.0;
+ break;
+ default:
+ return;
+ }
+}
+
+
+/* new window size */
+void reshape( GLFWwindow* window, int width, int height )
+{
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ GLfloat xmax, znear, zfar;
+
+ znear = 5.0f;
+ zfar = 30.0f;
+ xmax = znear * 0.5f;
+
+ glViewport( 0, 0, (GLint) width, (GLint) height );
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glFrustum( -xmax, xmax, -xmax*h, xmax*h, znear, zfar );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ glTranslatef( 0.0, 0.0, -20.0 );
+}
+
+
+/* program & OpenGL initialization */
+static void init(void)
+{
+ static GLfloat pos[4] = {5.f, 5.f, 10.f, 0.f};
+ static GLfloat red[4] = {0.8f, 0.1f, 0.f, 1.f};
+ static GLfloat green[4] = {0.f, 0.8f, 0.2f, 1.f};
+ static GLfloat blue[4] = {0.2f, 0.2f, 1.f, 1.f};
+
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+
+ /* make the gears */
+ gear1 = glGenLists(1);
+ glNewList(gear1, GL_COMPILE);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
+ gear(1.f, 4.f, 1.f, 20, 0.7f);
+ glEndList();
+
+ gear2 = glGenLists(1);
+ glNewList(gear2, GL_COMPILE);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
+ gear(0.5f, 2.f, 2.f, 10, 0.7f);
+ glEndList();
+
+ gear3 = glGenLists(1);
+ glNewList(gear3, GL_COMPILE);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
+ gear(1.3f, 2.f, 0.5f, 10, 0.7f);
+ glEndList();
+
+ glEnable(GL_NORMALIZE);
+}
+
+
+/* program entry */
+int main(int argc, char *argv[])
+{
+ GLFWwindow* window;
+ int width, height;
+
+ if( !glfwInit() )
+ {
+ fprintf( stderr, "Failed to initialize GLFW\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ glfwWindowHint(GLFW_DEPTH_BITS, 16);
+ glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
+
+ window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL );
+ if (!window)
+ {
+ fprintf( stderr, "Failed to open GLFW window\n" );
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ // Set callback functions
+ glfwSetFramebufferSizeCallback(window, reshape);
+ glfwSetKeyCallback(window, key);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval( 1 );
+
+ glfwGetFramebufferSize(window, &width, &height);
+ reshape(window, width, height);
+
+ // Parse command-line options
+ init();
+
+ // Main loop
+ while( !glfwWindowShouldClose(window) )
+ {
+ // Draw gears
+ draw();
+
+ // Update animation
+ animate();
+
+ // Swap buffers
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ // Terminate GLFW
+ glfwTerminate();
+
+ // Exit program
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/libs/glfw-3.3.8/examples/glfw.icns b/libs/glfw-3.3.8/examples/glfw.icns
new file mode 100644
index 0000000..ad98f39
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/glfw.icns
Binary files differ
diff --git a/libs/glfw-3.3.8/examples/glfw.ico b/libs/glfw-3.3.8/examples/glfw.ico
new file mode 100644
index 0000000..882a660
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/glfw.ico
Binary files differ
diff --git a/libs/glfw-3.3.8/examples/glfw.rc b/libs/glfw-3.3.8/examples/glfw.rc
new file mode 100644
index 0000000..f2b62f6
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/glfw.rc
@@ -0,0 +1,3 @@
+
+GLFW_ICON ICON "glfw.ico"
+
diff --git a/libs/glfw-3.3.8/examples/heightmap.c b/libs/glfw-3.3.8/examples/heightmap.c
new file mode 100644
index 0000000..988dd0b
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/heightmap.c
@@ -0,0 +1,512 @@
+//========================================================================
+// Heightmap example program using OpenGL 3 core profile
+// Copyright (c) 2010 Olivier Delannoy
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+#include <stddef.h>
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+/* Map height updates */
+#define MAX_CIRCLE_SIZE (5.0f)
+#define MAX_DISPLACEMENT (1.0f)
+#define DISPLACEMENT_SIGN_LIMIT (0.3f)
+#define MAX_ITER (200)
+#define NUM_ITER_AT_A_TIME (1)
+
+/* Map general information */
+#define MAP_SIZE (10.0f)
+#define MAP_NUM_VERTICES (80)
+#define MAP_NUM_TOTAL_VERTICES (MAP_NUM_VERTICES*MAP_NUM_VERTICES)
+#define MAP_NUM_LINES (3* (MAP_NUM_VERTICES - 1) * (MAP_NUM_VERTICES - 1) + \
+ 2 * (MAP_NUM_VERTICES - 1))
+
+
+/**********************************************************************
+ * Default shader programs
+ *********************************************************************/
+
+static const char* vertex_shader_text =
+"#version 150\n"
+"uniform mat4 project;\n"
+"uniform mat4 modelview;\n"
+"in float x;\n"
+"in float y;\n"
+"in float z;\n"
+"\n"
+"void main()\n"
+"{\n"
+" gl_Position = project * modelview * vec4(x, y, z, 1.0);\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 150\n"
+"out vec4 color;\n"
+"void main()\n"
+"{\n"
+" color = vec4(0.2, 1.0, 0.2, 1.0); \n"
+"}\n";
+
+/**********************************************************************
+ * Values for shader uniforms
+ *********************************************************************/
+
+/* Frustum configuration */
+static GLfloat view_angle = 45.0f;
+static GLfloat aspect_ratio = 4.0f/3.0f;
+static GLfloat z_near = 1.0f;
+static GLfloat z_far = 100.f;
+
+/* Projection matrix */
+static GLfloat projection_matrix[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+};
+
+/* Model view matrix */
+static GLfloat modelview_matrix[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+};
+
+/**********************************************************************
+ * Heightmap vertex and index data
+ *********************************************************************/
+
+static GLfloat map_vertices[3][MAP_NUM_TOTAL_VERTICES];
+static GLuint map_line_indices[2*MAP_NUM_LINES];
+
+/* Store uniform location for the shaders
+ * Those values are setup as part of the process of creating
+ * the shader program. They should not be used before creating
+ * the program.
+ */
+static GLuint mesh;
+static GLuint mesh_vbo[4];
+
+/**********************************************************************
+ * OpenGL helper functions
+ *********************************************************************/
+
+/* Creates a shader object of the specified type using the specified text
+ */
+static GLuint make_shader(GLenum type, const char* text)
+{
+ GLuint shader;
+ GLint shader_ok;
+ GLsizei log_length;
+ char info_log[8192];
+
+ shader = glCreateShader(type);
+ if (shader != 0)
+ {
+ glShaderSource(shader, 1, (const GLchar**)&text, NULL);
+ glCompileShader(shader);
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
+ if (shader_ok != GL_TRUE)
+ {
+ fprintf(stderr, "ERROR: Failed to compile %s shader\n", (type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex" );
+ glGetShaderInfoLog(shader, 8192, &log_length,info_log);
+ fprintf(stderr, "ERROR: \n%s\n\n", info_log);
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+}
+
+/* Creates a program object using the specified vertex and fragment text
+ */
+static GLuint make_shader_program(const char* vs_text, const char* fs_text)
+{
+ GLuint program = 0u;
+ GLint program_ok;
+ GLuint vertex_shader = 0u;
+ GLuint fragment_shader = 0u;
+ GLsizei log_length;
+ char info_log[8192];
+
+ vertex_shader = make_shader(GL_VERTEX_SHADER, vs_text);
+ if (vertex_shader != 0u)
+ {
+ fragment_shader = make_shader(GL_FRAGMENT_SHADER, fs_text);
+ if (fragment_shader != 0u)
+ {
+ /* make the program that connect the two shader and link it */
+ program = glCreateProgram();
+ if (program != 0u)
+ {
+ /* attach both shader and link */
+ glAttachShader(program, vertex_shader);
+ glAttachShader(program, fragment_shader);
+ glLinkProgram(program);
+ glGetProgramiv(program, GL_LINK_STATUS, &program_ok);
+
+ if (program_ok != GL_TRUE)
+ {
+ fprintf(stderr, "ERROR, failed to link shader program\n");
+ glGetProgramInfoLog(program, 8192, &log_length, info_log);
+ fprintf(stderr, "ERROR: \n%s\n\n", info_log);
+ glDeleteProgram(program);
+ glDeleteShader(fragment_shader);
+ glDeleteShader(vertex_shader);
+ program = 0u;
+ }
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to load fragment shader\n");
+ glDeleteShader(vertex_shader);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to load vertex shader\n");
+ }
+ return program;
+}
+
+/**********************************************************************
+ * Geometry creation functions
+ *********************************************************************/
+
+/* Generate vertices and indices for the heightmap
+ */
+static void init_map(void)
+{
+ int i;
+ int j;
+ int k;
+ GLfloat step = MAP_SIZE / (MAP_NUM_VERTICES - 1);
+ GLfloat x = 0.0f;
+ GLfloat z = 0.0f;
+ /* Create a flat grid */
+ k = 0;
+ for (i = 0 ; i < MAP_NUM_VERTICES ; ++i)
+ {
+ for (j = 0 ; j < MAP_NUM_VERTICES ; ++j)
+ {
+ map_vertices[0][k] = x;
+ map_vertices[1][k] = 0.0f;
+ map_vertices[2][k] = z;
+ z += step;
+ ++k;
+ }
+ x += step;
+ z = 0.0f;
+ }
+#if DEBUG_ENABLED
+ for (i = 0 ; i < MAP_NUM_TOTAL_VERTICES ; ++i)
+ {
+ printf ("Vertice %d (%f, %f, %f)\n",
+ i, map_vertices[0][i], map_vertices[1][i], map_vertices[2][i]);
+
+ }
+#endif
+ /* create indices */
+ /* line fan based on i
+ * i+1
+ * | / i + n + 1
+ * | /
+ * |/
+ * i --- i + n
+ */
+
+ /* close the top of the square */
+ k = 0;
+ for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i)
+ {
+ map_line_indices[k++] = (i + 1) * MAP_NUM_VERTICES -1;
+ map_line_indices[k++] = (i + 2) * MAP_NUM_VERTICES -1;
+ }
+ /* close the right of the square */
+ for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i)
+ {
+ map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i;
+ map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i + 1;
+ }
+
+ for (i = 0 ; i < (MAP_NUM_VERTICES - 1) ; ++i)
+ {
+ for (j = 0 ; j < (MAP_NUM_VERTICES - 1) ; ++j)
+ {
+ int ref = i * (MAP_NUM_VERTICES) + j;
+ map_line_indices[k++] = ref;
+ map_line_indices[k++] = ref + 1;
+
+ map_line_indices[k++] = ref;
+ map_line_indices[k++] = ref + MAP_NUM_VERTICES;
+
+ map_line_indices[k++] = ref;
+ map_line_indices[k++] = ref + MAP_NUM_VERTICES + 1;
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ for (k = 0 ; k < 2 * MAP_NUM_LINES ; k += 2)
+ {
+ int beg, end;
+ beg = map_line_indices[k];
+ end = map_line_indices[k+1];
+ printf ("Line %d: %d -> %d (%f, %f, %f) -> (%f, %f, %f)\n",
+ k / 2, beg, end,
+ map_vertices[0][beg], map_vertices[1][beg], map_vertices[2][beg],
+ map_vertices[0][end], map_vertices[1][end], map_vertices[2][end]);
+ }
+#endif
+}
+
+static void generate_heightmap__circle(float* center_x, float* center_y,
+ float* size, float* displacement)
+{
+ float sign;
+ /* random value for element in between [0-1.0] */
+ *center_x = (MAP_SIZE * rand()) / (float) RAND_MAX;
+ *center_y = (MAP_SIZE * rand()) / (float) RAND_MAX;
+ *size = (MAX_CIRCLE_SIZE * rand()) / (float) RAND_MAX;
+ sign = (1.0f * rand()) / (float) RAND_MAX;
+ sign = (sign < DISPLACEMENT_SIGN_LIMIT) ? -1.0f : 1.0f;
+ *displacement = (sign * (MAX_DISPLACEMENT * rand())) / (float) RAND_MAX;
+}
+
+/* Run the specified number of iterations of the generation process for the
+ * heightmap
+ */
+static void update_map(int num_iter)
+{
+ assert(num_iter > 0);
+ while(num_iter)
+ {
+ /* center of the circle */
+ float center_x;
+ float center_z;
+ float circle_size;
+ float disp;
+ size_t ii;
+ generate_heightmap__circle(&center_x, &center_z, &circle_size, &disp);
+ disp = disp / 2.0f;
+ for (ii = 0u ; ii < MAP_NUM_TOTAL_VERTICES ; ++ii)
+ {
+ GLfloat dx = center_x - map_vertices[0][ii];
+ GLfloat dz = center_z - map_vertices[2][ii];
+ GLfloat pd = (2.0f * (float) sqrt((dx * dx) + (dz * dz))) / circle_size;
+ if (fabs(pd) <= 1.0f)
+ {
+ /* tx,tz is within the circle */
+ GLfloat new_height = disp + (float) (cos(pd*3.14f)*disp);
+ map_vertices[1][ii] += new_height;
+ }
+ }
+ --num_iter;
+ }
+}
+
+/**********************************************************************
+ * OpenGL helper functions
+ *********************************************************************/
+
+/* Create VBO, IBO and VAO objects for the heightmap geometry and bind them to
+ * the specified program object
+ */
+static void make_mesh(GLuint program)
+{
+ GLuint attrloc;
+
+ glGenVertexArrays(1, &mesh);
+ glGenBuffers(4, mesh_vbo);
+ glBindVertexArray(mesh);
+ /* Prepare the data for drawing through a buffer inidices */
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_vbo[3]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* MAP_NUM_LINES * 2, map_line_indices, GL_STATIC_DRAW);
+
+ /* Prepare the attributes for rendering */
+ attrloc = glGetAttribLocation(program, "x");
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[0][0], GL_STATIC_DRAW);
+ glEnableVertexAttribArray(attrloc);
+ glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
+
+ attrloc = glGetAttribLocation(program, "z");
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[2]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[2][0], GL_STATIC_DRAW);
+ glEnableVertexAttribArray(attrloc);
+ glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
+
+ attrloc = glGetAttribLocation(program, "y");
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0], GL_DYNAMIC_DRAW);
+ glEnableVertexAttribArray(attrloc);
+ glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
+}
+
+/* Update VBO vertices from source data
+ */
+static void update_mesh(void)
+{
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0]);
+}
+
+/**********************************************************************
+ * GLFW callback functions
+ *********************************************************************/
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ switch(key)
+ {
+ case GLFW_KEY_ESCAPE:
+ /* Exit program on Escape */
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ }
+}
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+int main(int argc, char** argv)
+{
+ GLFWwindow* window;
+ int iter;
+ double dt;
+ double last_update_time;
+ int frame;
+ float f;
+ GLint uloc_modelview;
+ GLint uloc_project;
+ int width, height;
+
+ GLuint shader_program;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+
+ window = glfwCreateWindow(800, 600, "GLFW OpenGL3 Heightmap demo", NULL, NULL);
+ if (! window )
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ /* Register events callback */
+ glfwSetKeyCallback(window, key_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+
+ /* Prepare opengl resources for rendering */
+ shader_program = make_shader_program(vertex_shader_text, fragment_shader_text);
+
+ if (shader_program == 0u)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glUseProgram(shader_program);
+ uloc_project = glGetUniformLocation(shader_program, "project");
+ uloc_modelview = glGetUniformLocation(shader_program, "modelview");
+
+ /* Compute the projection matrix */
+ f = 1.0f / tanf(view_angle / 2.0f);
+ projection_matrix[0] = f / aspect_ratio;
+ projection_matrix[5] = f;
+ projection_matrix[10] = (z_far + z_near)/ (z_near - z_far);
+ projection_matrix[11] = -1.0f;
+ projection_matrix[14] = 2.0f * (z_far * z_near) / (z_near - z_far);
+ glUniformMatrix4fv(uloc_project, 1, GL_FALSE, projection_matrix);
+
+ /* Set the camera position */
+ modelview_matrix[12] = -5.0f;
+ modelview_matrix[13] = -5.0f;
+ modelview_matrix[14] = -20.0f;
+ glUniformMatrix4fv(uloc_modelview, 1, GL_FALSE, modelview_matrix);
+
+ /* Create mesh data */
+ init_map();
+ make_mesh(shader_program);
+
+ /* Create vao + vbo to store the mesh */
+ /* Create the vbo to store all the information for the grid and the height */
+
+ /* setup the scene ready for rendering */
+ glfwGetFramebufferSize(window, &width, &height);
+ glViewport(0, 0, width, height);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ /* main loop */
+ frame = 0;
+ iter = 0;
+ last_update_time = glfwGetTime();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ ++frame;
+ /* render the next frame */
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawElements(GL_LINES, 2* MAP_NUM_LINES , GL_UNSIGNED_INT, 0);
+
+ /* display and process events through callbacks */
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ /* Check the frame rate and update the heightmap if needed */
+ dt = glfwGetTime();
+ if ((dt - last_update_time) > 0.2)
+ {
+ /* generate the next iteration of the heightmap */
+ if (iter < MAX_ITER)
+ {
+ update_map(NUM_ITER_AT_A_TIME);
+ update_mesh();
+ iter += NUM_ITER_AT_A_TIME;
+ }
+ last_update_time = dt;
+ frame = 0;
+ }
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/examples/offscreen.c b/libs/glfw-3.3.8/examples/offscreen.c
new file mode 100644
index 0000000..16b8f3c
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/offscreen.c
@@ -0,0 +1,177 @@
+//========================================================================
+// Offscreen rendering example
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#if USE_NATIVE_OSMESA
+ #define GLFW_EXPOSE_NATIVE_OSMESA
+ #include <GLFW/glfw3native.h>
+#endif
+
+#include "linmath.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include <stb_image_write.h>
+
+static const struct
+{
+ float x, y;
+ float r, g, b;
+} vertices[3] =
+{
+ { -0.6f, -0.4f, 1.f, 0.f, 0.f },
+ { 0.6f, -0.4f, 0.f, 1.f, 0.f },
+ { 0.f, 0.6f, 0.f, 0.f, 1.f }
+};
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec3 vCol;\n"
+"attribute vec2 vPos;\n"
+"varying vec3 color;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+" color = vCol;\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"varying vec3 color;\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(color, 1.0);\n"
+"}\n";
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+int main(void)
+{
+ GLFWwindow* window;
+ GLuint vertex_buffer, vertex_shader, fragment_shader, program;
+ GLint mvp_location, vpos_location, vcol_location;
+ float ratio;
+ int width, height;
+ mat4x4 mvp;
+ char* buffer;
+
+ glfwSetErrorCallback(error_callback);
+
+ glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_FALSE);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+
+ window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+
+ // NOTE: OpenGL error checks have been omitted for brevity
+
+ glGenBuffers(1, &vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+ vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
+ glCompileShader(vertex_shader);
+
+ fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
+ glCompileShader(fragment_shader);
+
+ program = glCreateProgram();
+ glAttachShader(program, vertex_shader);
+ glAttachShader(program, fragment_shader);
+ glLinkProgram(program);
+
+ mvp_location = glGetUniformLocation(program, "MVP");
+ vpos_location = glGetAttribLocation(program, "vPos");
+ vcol_location = glGetAttribLocation(program, "vCol");
+
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+ glEnableVertexAttribArray(vcol_location);
+ glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) (sizeof(float) * 2));
+
+ glfwGetFramebufferSize(window, &width, &height);
+ ratio = width / (float) height;
+
+ glViewport(0, 0, width, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ mat4x4_ortho(mvp, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
+
+ glUseProgram(program);
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ glFinish();
+
+#if USE_NATIVE_OSMESA
+ glfwGetOSMesaColorBuffer(window, &width, &height, NULL, (void**) &buffer);
+#else
+ buffer = calloc(4, width * height);
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+#endif
+
+ // Write image Y-flipped because OpenGL
+ stbi_write_png("offscreen.png",
+ width, height, 4,
+ buffer + (width * 4 * (height - 1)),
+ -width * 4);
+
+#if USE_NATIVE_OSMESA
+ // Here is where there's nothing
+#else
+ free(buffer);
+#endif
+
+ glfwDestroyWindow(window);
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/examples/particles.c b/libs/glfw-3.3.8/examples/particles.c
new file mode 100644
index 0000000..9556cca
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/particles.c
@@ -0,0 +1,1073 @@
+//========================================================================
+// A simple particle engine with threaded physics
+// Copyright (c) Marcus Geelnard
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+
+#if defined(_MSC_VER)
+ // Make MS math.h define M_PI
+ #define _USE_MATH_DEFINES
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include <tinycthread.h>
+#include <getopt.h>
+#include <linmath.h>
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+// Define tokens for GL_EXT_separate_specular_color if not already defined
+#ifndef GL_EXT_separate_specular_color
+#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8
+#define GL_SINGLE_COLOR_EXT 0x81F9
+#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA
+#endif // GL_EXT_separate_specular_color
+
+
+//========================================================================
+// Type definitions
+//========================================================================
+
+typedef struct
+{
+ float x, y, z;
+} Vec3;
+
+// This structure is used for interleaved vertex arrays (see the
+// draw_particles function)
+//
+// NOTE: This structure SHOULD be packed on most systems. It uses 32-bit fields
+// on 32-bit boundaries, and is a multiple of 64 bits in total (6x32=3x64). If
+// it does not work, try using pragmas or whatever to force the structure to be
+// packed.
+typedef struct
+{
+ GLfloat s, t; // Texture coordinates
+ GLuint rgba; // Color (four ubytes packed into an uint)
+ GLfloat x, y, z; // Vertex coordinates
+} Vertex;
+
+
+//========================================================================
+// Program control global variables
+//========================================================================
+
+// Window dimensions
+float aspect_ratio;
+
+// "wireframe" flag (true if we use wireframe view)
+int wireframe;
+
+// Thread synchronization
+struct {
+ double t; // Time (s)
+ float dt; // Time since last frame (s)
+ int p_frame; // Particle physics frame number
+ int d_frame; // Particle draw frame number
+ cnd_t p_done; // Condition: particle physics done
+ cnd_t d_done; // Condition: particle draw done
+ mtx_t particles_lock; // Particles data sharing mutex
+} thread_sync;
+
+
+//========================================================================
+// Texture declarations (we hard-code them into the source code, since
+// they are so simple)
+//========================================================================
+
+#define P_TEX_WIDTH 8 // Particle texture dimensions
+#define P_TEX_HEIGHT 8
+#define F_TEX_WIDTH 16 // Floor texture dimensions
+#define F_TEX_HEIGHT 16
+
+// Texture object IDs
+GLuint particle_tex_id, floor_tex_id;
+
+// Particle texture (a simple spot)
+const unsigned char particle_texture[ P_TEX_WIDTH * P_TEX_HEIGHT ] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x11, 0x22, 0x22, 0x11, 0x00, 0x00,
+ 0x00, 0x11, 0x33, 0x88, 0x77, 0x33, 0x11, 0x00,
+ 0x00, 0x22, 0x88, 0xff, 0xee, 0x77, 0x22, 0x00,
+ 0x00, 0x22, 0x77, 0xee, 0xff, 0x88, 0x22, 0x00,
+ 0x00, 0x11, 0x33, 0x77, 0x88, 0x33, 0x11, 0x00,
+ 0x00, 0x00, 0x11, 0x33, 0x22, 0x11, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+// Floor texture (your basic checkered floor)
+const unsigned char floor_texture[ F_TEX_WIDTH * F_TEX_HEIGHT ] = {
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xff, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xf0, 0xcc, 0xee, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x66, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xee, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x55, 0x30, 0x30, 0x44, 0x30, 0x30,
+ 0xf0, 0xdd, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0xf0, 0xff, 0xf0, 0xf0, 0xdd, 0xf0, 0xf0, 0xff,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x55, 0x33, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0,
+ 0x30, 0x44, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xaa, 0xf0, 0xf0, 0xcc, 0xf0,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xdd, 0xf0,
+ 0x30, 0x30, 0x30, 0x77, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+};
+
+
+//========================================================================
+// These are fixed constants that control the particle engine. In a
+// modular world, these values should be variables...
+//========================================================================
+
+// Maximum number of particles
+#define MAX_PARTICLES 3000
+
+// Life span of a particle (in seconds)
+#define LIFE_SPAN 8.f
+
+// A new particle is born every [BIRTH_INTERVAL] second
+#define BIRTH_INTERVAL (LIFE_SPAN/(float)MAX_PARTICLES)
+
+// Particle size (meters)
+#define PARTICLE_SIZE 0.7f
+
+// Gravitational constant (m/s^2)
+#define GRAVITY 9.8f
+
+// Base initial velocity (m/s)
+#define VELOCITY 8.f
+
+// Bounce friction (1.0 = no friction, 0.0 = maximum friction)
+#define FRICTION 0.75f
+
+// "Fountain" height (m)
+#define FOUNTAIN_HEIGHT 3.f
+
+// Fountain radius (m)
+#define FOUNTAIN_RADIUS 1.6f
+
+// Minimum delta-time for particle phisics (s)
+#define MIN_DELTA_T (BIRTH_INTERVAL * 0.5f)
+
+
+//========================================================================
+// Particle system global variables
+//========================================================================
+
+// This structure holds all state for a single particle
+typedef struct {
+ float x,y,z; // Position in space
+ float vx,vy,vz; // Velocity vector
+ float r,g,b; // Color of particle
+ float life; // Life of particle (1.0 = newborn, < 0.0 = dead)
+ int active; // Tells if this particle is active
+} PARTICLE;
+
+// Global vectors holding all particles. We use two vectors for double
+// buffering.
+static PARTICLE particles[MAX_PARTICLES];
+
+// Global variable holding the age of the youngest particle
+static float min_age;
+
+// Color of latest born particle (used for fountain lighting)
+static float glow_color[4];
+
+// Position of latest born particle (used for fountain lighting)
+static float glow_pos[4];
+
+
+//========================================================================
+// Object material and fog configuration constants
+//========================================================================
+
+const GLfloat fountain_diffuse[4] = { 0.7f, 1.f, 1.f, 1.f };
+const GLfloat fountain_specular[4] = { 1.f, 1.f, 1.f, 1.f };
+const GLfloat fountain_shininess = 12.f;
+const GLfloat floor_diffuse[4] = { 1.f, 0.6f, 0.6f, 1.f };
+const GLfloat floor_specular[4] = { 0.6f, 0.6f, 0.6f, 1.f };
+const GLfloat floor_shininess = 18.f;
+const GLfloat fog_color[4] = { 0.1f, 0.1f, 0.1f, 1.f };
+
+
+//========================================================================
+// Print usage information
+//========================================================================
+
+static void usage(void)
+{
+ printf("Usage: particles [-bfhs]\n");
+ printf("Options:\n");
+ printf(" -f Run in full screen\n");
+ printf(" -h Display this help\n");
+ printf(" -s Run program as single thread (default is to use two threads)\n");
+ printf("\n");
+ printf("Program runtime controls:\n");
+ printf(" W Toggle wireframe mode\n");
+ printf(" Esc Exit program\n");
+}
+
+
+//========================================================================
+// Initialize a new particle
+//========================================================================
+
+static void init_particle(PARTICLE *p, double t)
+{
+ float xy_angle, velocity;
+
+ // Start position of particle is at the fountain blow-out
+ p->x = 0.f;
+ p->y = 0.f;
+ p->z = FOUNTAIN_HEIGHT;
+
+ // Start velocity is up (Z)...
+ p->vz = 0.7f + (0.3f / 4096.f) * (float) (rand() & 4095);
+
+ // ...and a randomly chosen X/Y direction
+ xy_angle = (2.f * (float) M_PI / 4096.f) * (float) (rand() & 4095);
+ p->vx = 0.4f * (float) cos(xy_angle);
+ p->vy = 0.4f * (float) sin(xy_angle);
+
+ // Scale velocity vector according to a time-varying velocity
+ velocity = VELOCITY * (0.8f + 0.1f * (float) (sin(0.5 * t) + sin(1.31 * t)));
+ p->vx *= velocity;
+ p->vy *= velocity;
+ p->vz *= velocity;
+
+ // Color is time-varying
+ p->r = 0.7f + 0.3f * (float) sin(0.34 * t + 0.1);
+ p->g = 0.6f + 0.4f * (float) sin(0.63 * t + 1.1);
+ p->b = 0.6f + 0.4f * (float) sin(0.91 * t + 2.1);
+
+ // Store settings for fountain glow lighting
+ glow_pos[0] = 0.4f * (float) sin(1.34 * t);
+ glow_pos[1] = 0.4f * (float) sin(3.11 * t);
+ glow_pos[2] = FOUNTAIN_HEIGHT + 1.f;
+ glow_pos[3] = 1.f;
+ glow_color[0] = p->r;
+ glow_color[1] = p->g;
+ glow_color[2] = p->b;
+ glow_color[3] = 1.f;
+
+ // The particle is new-born and active
+ p->life = 1.f;
+ p->active = 1;
+}
+
+
+//========================================================================
+// Update a particle
+//========================================================================
+
+#define FOUNTAIN_R2 (FOUNTAIN_RADIUS+PARTICLE_SIZE/2)*(FOUNTAIN_RADIUS+PARTICLE_SIZE/2)
+
+static void update_particle(PARTICLE *p, float dt)
+{
+ // If the particle is not active, we need not do anything
+ if (!p->active)
+ return;
+
+ // The particle is getting older...
+ p->life -= dt * (1.f / LIFE_SPAN);
+
+ // Did the particle die?
+ if (p->life <= 0.f)
+ {
+ p->active = 0;
+ return;
+ }
+
+ // Apply gravity
+ p->vz = p->vz - GRAVITY * dt;
+
+ // Update particle position
+ p->x = p->x + p->vx * dt;
+ p->y = p->y + p->vy * dt;
+ p->z = p->z + p->vz * dt;
+
+ // Simple collision detection + response
+ if (p->vz < 0.f)
+ {
+ // Particles should bounce on the fountain (with friction)
+ if ((p->x * p->x + p->y * p->y) < FOUNTAIN_R2 &&
+ p->z < (FOUNTAIN_HEIGHT + PARTICLE_SIZE / 2))
+ {
+ p->vz = -FRICTION * p->vz;
+ p->z = FOUNTAIN_HEIGHT + PARTICLE_SIZE / 2 +
+ FRICTION * (FOUNTAIN_HEIGHT +
+ PARTICLE_SIZE / 2 - p->z);
+ }
+
+ // Particles should bounce on the floor (with friction)
+ else if (p->z < PARTICLE_SIZE / 2)
+ {
+ p->vz = -FRICTION * p->vz;
+ p->z = PARTICLE_SIZE / 2 +
+ FRICTION * (PARTICLE_SIZE / 2 - p->z);
+ }
+ }
+}
+
+
+//========================================================================
+// The main frame for the particle engine. Called once per frame.
+//========================================================================
+
+static void particle_engine(double t, float dt)
+{
+ int i;
+ float dt2;
+
+ // Update particles (iterated several times per frame if dt is too large)
+ while (dt > 0.f)
+ {
+ // Calculate delta time for this iteration
+ dt2 = dt < MIN_DELTA_T ? dt : MIN_DELTA_T;
+
+ for (i = 0; i < MAX_PARTICLES; i++)
+ update_particle(&particles[i], dt2);
+
+ min_age += dt2;
+
+ // Should we create any new particle(s)?
+ while (min_age >= BIRTH_INTERVAL)
+ {
+ min_age -= BIRTH_INTERVAL;
+
+ // Find a dead particle to replace with a new one
+ for (i = 0; i < MAX_PARTICLES; i++)
+ {
+ if (!particles[i].active)
+ {
+ init_particle(&particles[i], t + min_age);
+ update_particle(&particles[i], min_age);
+ break;
+ }
+ }
+ }
+
+ dt -= dt2;
+ }
+}
+
+
+//========================================================================
+// Draw all active particles. We use OpenGL 1.1 vertex
+// arrays for this in order to accelerate the drawing.
+//========================================================================
+
+#define BATCH_PARTICLES 70 // Number of particles to draw in each batch
+ // (70 corresponds to 7.5 KB = will not blow
+ // the L1 data cache on most CPUs)
+#define PARTICLE_VERTS 4 // Number of vertices per particle
+
+static void draw_particles(GLFWwindow* window, double t, float dt)
+{
+ int i, particle_count;
+ Vertex vertex_array[BATCH_PARTICLES * PARTICLE_VERTS];
+ Vertex* vptr;
+ float alpha;
+ GLuint rgba;
+ Vec3 quad_lower_left, quad_lower_right;
+ GLfloat mat[16];
+ PARTICLE* pptr;
+
+ // Here comes the real trick with flat single primitive objects (s.c.
+ // "billboards"): We must rotate the textured primitive so that it
+ // always faces the viewer (is coplanar with the view-plane).
+ // We:
+ // 1) Create the primitive around origo (0,0,0)
+ // 2) Rotate it so that it is coplanar with the view plane
+ // 3) Translate it according to the particle position
+ // Note that 1) and 2) is the same for all particles (done only once).
+
+ // Get modelview matrix. We will only use the upper left 3x3 part of
+ // the matrix, which represents the rotation.
+ glGetFloatv(GL_MODELVIEW_MATRIX, mat);
+
+ // 1) & 2) We do it in one swift step:
+ // Although not obvious, the following six lines represent two matrix/
+ // vector multiplications. The matrix is the inverse 3x3 rotation
+ // matrix (i.e. the transpose of the same matrix), and the two vectors
+ // represent the lower left corner of the quad, PARTICLE_SIZE/2 *
+ // (-1,-1,0), and the lower right corner, PARTICLE_SIZE/2 * (1,-1,0).
+ // The upper left/right corners of the quad is always the negative of
+ // the opposite corners (regardless of rotation).
+ quad_lower_left.x = (-PARTICLE_SIZE / 2) * (mat[0] + mat[1]);
+ quad_lower_left.y = (-PARTICLE_SIZE / 2) * (mat[4] + mat[5]);
+ quad_lower_left.z = (-PARTICLE_SIZE / 2) * (mat[8] + mat[9]);
+ quad_lower_right.x = (PARTICLE_SIZE / 2) * (mat[0] - mat[1]);
+ quad_lower_right.y = (PARTICLE_SIZE / 2) * (mat[4] - mat[5]);
+ quad_lower_right.z = (PARTICLE_SIZE / 2) * (mat[8] - mat[9]);
+
+ // Don't update z-buffer, since all particles are transparent!
+ glDepthMask(GL_FALSE);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+ // Select particle texture
+ if (!wireframe)
+ {
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, particle_tex_id);
+ }
+
+ // Set up vertex arrays. We use interleaved arrays, which is easier to
+ // handle (in most situations) and it gives a linear memory access
+ // access pattern (which may give better performance in some
+ // situations). GL_T2F_C4UB_V3F means: 2 floats for texture coords,
+ // 4 ubytes for color and 3 floats for vertex coord (in that order).
+ // Most OpenGL cards / drivers are optimized for this format.
+ glInterleavedArrays(GL_T2F_C4UB_V3F, 0, vertex_array);
+
+ // Wait for particle physics thread to be done
+ mtx_lock(&thread_sync.particles_lock);
+ while (!glfwWindowShouldClose(window) &&
+ thread_sync.p_frame <= thread_sync.d_frame)
+ {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += 100 * 1000 * 1000;
+ ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
+ ts.tv_nsec %= 1000 * 1000 * 1000;
+ cnd_timedwait(&thread_sync.p_done, &thread_sync.particles_lock, &ts);
+ }
+
+ // Store the frame time and delta time for the physics thread
+ thread_sync.t = t;
+ thread_sync.dt = dt;
+
+ // Update frame counter
+ thread_sync.d_frame++;
+
+ // Loop through all particles and build vertex arrays.
+ particle_count = 0;
+ vptr = vertex_array;
+ pptr = particles;
+
+ for (i = 0; i < MAX_PARTICLES; i++)
+ {
+ if (pptr->active)
+ {
+ // Calculate particle intensity (we set it to max during 75%
+ // of its life, then it fades out)
+ alpha = 4.f * pptr->life;
+ if (alpha > 1.f)
+ alpha = 1.f;
+
+ // Convert color from float to 8-bit (store it in a 32-bit
+ // integer using endian independent type casting)
+ ((GLubyte*) &rgba)[0] = (GLubyte)(pptr->r * 255.f);
+ ((GLubyte*) &rgba)[1] = (GLubyte)(pptr->g * 255.f);
+ ((GLubyte*) &rgba)[2] = (GLubyte)(pptr->b * 255.f);
+ ((GLubyte*) &rgba)[3] = (GLubyte)(alpha * 255.f);
+
+ // 3) Translate the quad to the correct position in modelview
+ // space and store its parameters in vertex arrays (we also
+ // store texture coord and color information for each vertex).
+
+ // Lower left corner
+ vptr->s = 0.f;
+ vptr->t = 0.f;
+ vptr->rgba = rgba;
+ vptr->x = pptr->x + quad_lower_left.x;
+ vptr->y = pptr->y + quad_lower_left.y;
+ vptr->z = pptr->z + quad_lower_left.z;
+ vptr ++;
+
+ // Lower right corner
+ vptr->s = 1.f;
+ vptr->t = 0.f;
+ vptr->rgba = rgba;
+ vptr->x = pptr->x + quad_lower_right.x;
+ vptr->y = pptr->y + quad_lower_right.y;
+ vptr->z = pptr->z + quad_lower_right.z;
+ vptr ++;
+
+ // Upper right corner
+ vptr->s = 1.f;
+ vptr->t = 1.f;
+ vptr->rgba = rgba;
+ vptr->x = pptr->x - quad_lower_left.x;
+ vptr->y = pptr->y - quad_lower_left.y;
+ vptr->z = pptr->z - quad_lower_left.z;
+ vptr ++;
+
+ // Upper left corner
+ vptr->s = 0.f;
+ vptr->t = 1.f;
+ vptr->rgba = rgba;
+ vptr->x = pptr->x - quad_lower_right.x;
+ vptr->y = pptr->y - quad_lower_right.y;
+ vptr->z = pptr->z - quad_lower_right.z;
+ vptr ++;
+
+ // Increase count of drawable particles
+ particle_count ++;
+ }
+
+ // If we have filled up one batch of particles, draw it as a set
+ // of quads using glDrawArrays.
+ if (particle_count >= BATCH_PARTICLES)
+ {
+ // The first argument tells which primitive type we use (QUAD)
+ // The second argument tells the index of the first vertex (0)
+ // The last argument is the vertex count
+ glDrawArrays(GL_QUADS, 0, PARTICLE_VERTS * particle_count);
+ particle_count = 0;
+ vptr = vertex_array;
+ }
+
+ // Next particle
+ pptr++;
+ }
+
+ // We are done with the particle data
+ mtx_unlock(&thread_sync.particles_lock);
+ cnd_signal(&thread_sync.d_done);
+
+ // Draw final batch of particles (if any)
+ glDrawArrays(GL_QUADS, 0, PARTICLE_VERTS * particle_count);
+
+ // Disable vertex arrays (Note: glInterleavedArrays implicitly called
+ // glEnableClientState for vertex, texture coord and color arrays)
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+
+ glDepthMask(GL_TRUE);
+}
+
+
+//========================================================================
+// Fountain geometry specification
+//========================================================================
+
+#define FOUNTAIN_SIDE_POINTS 14
+#define FOUNTAIN_SWEEP_STEPS 32
+
+static const float fountain_side[FOUNTAIN_SIDE_POINTS * 2] =
+{
+ 1.2f, 0.f, 1.f, 0.2f, 0.41f, 0.3f, 0.4f, 0.35f,
+ 0.4f, 1.95f, 0.41f, 2.f, 0.8f, 2.2f, 1.2f, 2.4f,
+ 1.5f, 2.7f, 1.55f,2.95f, 1.6f, 3.f, 1.f, 3.f,
+ 0.5f, 3.f, 0.f, 3.f
+};
+
+static const float fountain_normal[FOUNTAIN_SIDE_POINTS * 2] =
+{
+ 1.0000f, 0.0000f, 0.6428f, 0.7660f, 0.3420f, 0.9397f, 1.0000f, 0.0000f,
+ 1.0000f, 0.0000f, 0.3420f,-0.9397f, 0.4226f,-0.9063f, 0.5000f,-0.8660f,
+ 0.7660f,-0.6428f, 0.9063f,-0.4226f, 0.0000f,1.00000f, 0.0000f,1.00000f,
+ 0.0000f,1.00000f, 0.0000f,1.00000f
+};
+
+
+//========================================================================
+// Draw a fountain
+//========================================================================
+
+static void draw_fountain(void)
+{
+ static GLuint fountain_list = 0;
+ double angle;
+ float x, y;
+ int m, n;
+
+ // The first time, we build the fountain display list
+ if (!fountain_list)
+ {
+ fountain_list = glGenLists(1);
+ glNewList(fountain_list, GL_COMPILE_AND_EXECUTE);
+
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, fountain_diffuse);
+ glMaterialfv(GL_FRONT, GL_SPECULAR, fountain_specular);
+ glMaterialf(GL_FRONT, GL_SHININESS, fountain_shininess);
+
+ // Build fountain using triangle strips
+ for (n = 0; n < FOUNTAIN_SIDE_POINTS - 1; n++)
+ {
+ glBegin(GL_TRIANGLE_STRIP);
+ for (m = 0; m <= FOUNTAIN_SWEEP_STEPS; m++)
+ {
+ angle = (double) m * (2.0 * M_PI / (double) FOUNTAIN_SWEEP_STEPS);
+ x = (float) cos(angle);
+ y = (float) sin(angle);
+
+ // Draw triangle strip
+ glNormal3f(x * fountain_normal[n * 2 + 2],
+ y * fountain_normal[n * 2 + 2],
+ fountain_normal[n * 2 + 3]);
+ glVertex3f(x * fountain_side[n * 2 + 2],
+ y * fountain_side[n * 2 + 2],
+ fountain_side[n * 2 +3 ]);
+ glNormal3f(x * fountain_normal[n * 2],
+ y * fountain_normal[n * 2],
+ fountain_normal[n * 2 + 1]);
+ glVertex3f(x * fountain_side[n * 2],
+ y * fountain_side[n * 2],
+ fountain_side[n * 2 + 1]);
+ }
+
+ glEnd();
+ }
+
+ glEndList();
+ }
+ else
+ glCallList(fountain_list);
+}
+
+
+//========================================================================
+// Recursive function for building variable tessellated floor
+//========================================================================
+
+static void tessellate_floor(float x1, float y1, float x2, float y2, int depth)
+{
+ float delta, x, y;
+
+ // Last recursion?
+ if (depth >= 5)
+ delta = 999999.f;
+ else
+ {
+ x = (float) (fabs(x1) < fabs(x2) ? fabs(x1) : fabs(x2));
+ y = (float) (fabs(y1) < fabs(y2) ? fabs(y1) : fabs(y2));
+ delta = x*x + y*y;
+ }
+
+ // Recurse further?
+ if (delta < 0.1f)
+ {
+ x = (x1 + x2) * 0.5f;
+ y = (y1 + y2) * 0.5f;
+ tessellate_floor(x1, y1, x, y, depth + 1);
+ tessellate_floor(x, y1, x2, y, depth + 1);
+ tessellate_floor(x1, y, x, y2, depth + 1);
+ tessellate_floor(x, y, x2, y2, depth + 1);
+ }
+ else
+ {
+ glTexCoord2f(x1 * 30.f, y1 * 30.f);
+ glVertex3f( x1 * 80.f, y1 * 80.f, 0.f);
+ glTexCoord2f(x2 * 30.f, y1 * 30.f);
+ glVertex3f( x2 * 80.f, y1 * 80.f, 0.f);
+ glTexCoord2f(x2 * 30.f, y2 * 30.f);
+ glVertex3f( x2 * 80.f, y2 * 80.f, 0.f);
+ glTexCoord2f(x1 * 30.f, y2 * 30.f);
+ glVertex3f( x1 * 80.f, y2 * 80.f, 0.f);
+ }
+}
+
+
+//========================================================================
+// Draw floor. We build the floor recursively and let the tessellation in the
+// center (near x,y=0,0) be high, while the tessellation around the edges be
+// low.
+//========================================================================
+
+static void draw_floor(void)
+{
+ static GLuint floor_list = 0;
+
+ if (!wireframe)
+ {
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, floor_tex_id);
+ }
+
+ // The first time, we build the floor display list
+ if (!floor_list)
+ {
+ floor_list = glGenLists(1);
+ glNewList(floor_list, GL_COMPILE_AND_EXECUTE);
+
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, floor_diffuse);
+ glMaterialfv(GL_FRONT, GL_SPECULAR, floor_specular);
+ glMaterialf(GL_FRONT, GL_SHININESS, floor_shininess);
+
+ // Draw floor as a bunch of triangle strips (high tessellation
+ // improves lighting)
+ glNormal3f(0.f, 0.f, 1.f);
+ glBegin(GL_QUADS);
+ tessellate_floor(-1.f, -1.f, 0.f, 0.f, 0);
+ tessellate_floor( 0.f, -1.f, 1.f, 0.f, 0);
+ tessellate_floor( 0.f, 0.f, 1.f, 1.f, 0);
+ tessellate_floor(-1.f, 0.f, 0.f, 1.f, 0);
+ glEnd();
+
+ glEndList();
+ }
+ else
+ glCallList(floor_list);
+
+ glDisable(GL_TEXTURE_2D);
+
+}
+
+
+//========================================================================
+// Position and configure light sources
+//========================================================================
+
+static void setup_lights(void)
+{
+ float l1pos[4], l1amb[4], l1dif[4], l1spec[4];
+ float l2pos[4], l2amb[4], l2dif[4], l2spec[4];
+
+ // Set light source 1 parameters
+ l1pos[0] = 0.f; l1pos[1] = -9.f; l1pos[2] = 8.f; l1pos[3] = 1.f;
+ l1amb[0] = 0.2f; l1amb[1] = 0.2f; l1amb[2] = 0.2f; l1amb[3] = 1.f;
+ l1dif[0] = 0.8f; l1dif[1] = 0.4f; l1dif[2] = 0.2f; l1dif[3] = 1.f;
+ l1spec[0] = 1.f; l1spec[1] = 0.6f; l1spec[2] = 0.2f; l1spec[3] = 0.f;
+
+ // Set light source 2 parameters
+ l2pos[0] = -15.f; l2pos[1] = 12.f; l2pos[2] = 1.5f; l2pos[3] = 1.f;
+ l2amb[0] = 0.f; l2amb[1] = 0.f; l2amb[2] = 0.f; l2amb[3] = 1.f;
+ l2dif[0] = 0.2f; l2dif[1] = 0.4f; l2dif[2] = 0.8f; l2dif[3] = 1.f;
+ l2spec[0] = 0.2f; l2spec[1] = 0.6f; l2spec[2] = 1.f; l2spec[3] = 0.f;
+
+ glLightfv(GL_LIGHT1, GL_POSITION, l1pos);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, l1amb);
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, l1dif);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, l1spec);
+ glLightfv(GL_LIGHT2, GL_POSITION, l2pos);
+ glLightfv(GL_LIGHT2, GL_AMBIENT, l2amb);
+ glLightfv(GL_LIGHT2, GL_DIFFUSE, l2dif);
+ glLightfv(GL_LIGHT2, GL_SPECULAR, l2spec);
+ glLightfv(GL_LIGHT3, GL_POSITION, glow_pos);
+ glLightfv(GL_LIGHT3, GL_DIFFUSE, glow_color);
+ glLightfv(GL_LIGHT3, GL_SPECULAR, glow_color);
+
+ glEnable(GL_LIGHT1);
+ glEnable(GL_LIGHT2);
+ glEnable(GL_LIGHT3);
+}
+
+
+//========================================================================
+// Main rendering function
+//========================================================================
+
+static void draw_scene(GLFWwindow* window, double t)
+{
+ double xpos, ypos, zpos, angle_x, angle_y, angle_z;
+ static double t_old = 0.0;
+ float dt;
+ mat4x4 projection;
+
+ // Calculate frame-to-frame delta time
+ dt = (float) (t - t_old);
+ t_old = t;
+
+ mat4x4_perspective(projection,
+ 65.f * (float) M_PI / 180.f,
+ aspect_ratio,
+ 1.0, 60.0);
+
+ glClearColor(0.1f, 0.1f, 0.1f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf((const GLfloat*) projection);
+
+ // Setup camera
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ // Rotate camera
+ angle_x = 90.0 - 10.0;
+ angle_y = 10.0 * sin(0.3 * t);
+ angle_z = 10.0 * t;
+ glRotated(-angle_x, 1.0, 0.0, 0.0);
+ glRotated(-angle_y, 0.0, 1.0, 0.0);
+ glRotated(-angle_z, 0.0, 0.0, 1.0);
+
+ // Translate camera
+ xpos = 15.0 * sin((M_PI / 180.0) * angle_z) +
+ 2.0 * sin((M_PI / 180.0) * 3.1 * t);
+ ypos = -15.0 * cos((M_PI / 180.0) * angle_z) +
+ 2.0 * cos((M_PI / 180.0) * 2.9 * t);
+ zpos = 4.0 + 2.0 * cos((M_PI / 180.0) * 4.9 * t);
+ glTranslated(-xpos, -ypos, -zpos);
+
+ glFrontFace(GL_CCW);
+ glCullFace(GL_BACK);
+ glEnable(GL_CULL_FACE);
+
+ setup_lights();
+ glEnable(GL_LIGHTING);
+
+ glEnable(GL_FOG);
+ glFogi(GL_FOG_MODE, GL_EXP);
+ glFogf(GL_FOG_DENSITY, 0.05f);
+ glFogfv(GL_FOG_COLOR, fog_color);
+
+ draw_floor();
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+
+ draw_fountain();
+
+ glDisable(GL_LIGHTING);
+ glDisable(GL_FOG);
+
+ // Particles must be drawn after all solid objects have been drawn
+ draw_particles(window, t, dt);
+
+ // Z-buffer not needed anymore
+ glDisable(GL_DEPTH_TEST);
+}
+
+
+//========================================================================
+// Window resize callback function
+//========================================================================
+
+static void resize_callback(GLFWwindow* window, int width, int height)
+{
+ glViewport(0, 0, width, height);
+ aspect_ratio = height ? width / (float) height : 1.f;
+}
+
+
+//========================================================================
+// Key callback functions
+//========================================================================
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action == GLFW_PRESS)
+ {
+ switch (key)
+ {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ case GLFW_KEY_W:
+ wireframe = !wireframe;
+ glPolygonMode(GL_FRONT_AND_BACK,
+ wireframe ? GL_LINE : GL_FILL);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+//========================================================================
+// Thread for updating particle physics
+//========================================================================
+
+static int physics_thread_main(void* arg)
+{
+ GLFWwindow* window = arg;
+
+ for (;;)
+ {
+ mtx_lock(&thread_sync.particles_lock);
+
+ // Wait for particle drawing to be done
+ while (!glfwWindowShouldClose(window) &&
+ thread_sync.p_frame > thread_sync.d_frame)
+ {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += 100 * 1000 * 1000;
+ ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
+ ts.tv_nsec %= 1000 * 1000 * 1000;
+ cnd_timedwait(&thread_sync.d_done, &thread_sync.particles_lock, &ts);
+ }
+
+ if (glfwWindowShouldClose(window))
+ break;
+
+ // Update particles
+ particle_engine(thread_sync.t, thread_sync.dt);
+
+ // Update frame counter
+ thread_sync.p_frame++;
+
+ // Unlock mutex and signal drawing thread
+ mtx_unlock(&thread_sync.particles_lock);
+ cnd_signal(&thread_sync.p_done);
+ }
+
+ return 0;
+}
+
+
+//========================================================================
+// main
+//========================================================================
+
+int main(int argc, char** argv)
+{
+ int ch, width, height;
+ thrd_t physics_thread = 0;
+ GLFWwindow* window;
+ GLFWmonitor* monitor = NULL;
+
+ if (!glfwInit())
+ {
+ fprintf(stderr, "Failed to initialize GLFW\n");
+ exit(EXIT_FAILURE);
+ }
+
+ while ((ch = getopt(argc, argv, "fh")) != -1)
+ {
+ switch (ch)
+ {
+ case 'f':
+ monitor = glfwGetPrimaryMonitor();
+ break;
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ if (monitor)
+ {
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+
+ glfwWindowHint(GLFW_RED_BITS, mode->redBits);
+ glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
+ glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
+ glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
+
+ width = mode->width;
+ height = mode->height;
+ }
+ else
+ {
+ width = 640;
+ height = 480;
+ }
+
+ window = glfwCreateWindow(width, height, "Particle Engine", monitor, NULL);
+ if (!window)
+ {
+ fprintf(stderr, "Failed to create GLFW window\n");
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ if (monitor)
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ glfwSetFramebufferSizeCallback(window, resize_callback);
+ glfwSetKeyCallback(window, key_callback);
+
+ // Set initial aspect ratio
+ glfwGetFramebufferSize(window, &width, &height);
+ resize_callback(window, width, height);
+
+ // Upload particle texture
+ glGenTextures(1, &particle_tex_id);
+ glBindTexture(GL_TEXTURE_2D, particle_tex_id);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, P_TEX_WIDTH, P_TEX_HEIGHT,
+ 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, particle_texture);
+
+ // Upload floor texture
+ glGenTextures(1, &floor_tex_id);
+ glBindTexture(GL_TEXTURE_2D, floor_tex_id);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ 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_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, F_TEX_WIDTH, F_TEX_HEIGHT,
+ 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, floor_texture);
+
+ if (glfwExtensionSupported("GL_EXT_separate_specular_color"))
+ {
+ glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT,
+ GL_SEPARATE_SPECULAR_COLOR_EXT);
+ }
+
+ // Set filled polygon mode as default (not wireframe)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ wireframe = 0;
+
+ // Set initial times
+ thread_sync.t = 0.0;
+ thread_sync.dt = 0.001f;
+ thread_sync.p_frame = 0;
+ thread_sync.d_frame = 0;
+
+ mtx_init(&thread_sync.particles_lock, mtx_timed);
+ cnd_init(&thread_sync.p_done);
+ cnd_init(&thread_sync.d_done);
+
+ if (thrd_create(&physics_thread, physics_thread_main, window) != thrd_success)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetTime(0.0);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ draw_scene(window, glfwGetTime());
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ thrd_join(physics_thread, NULL);
+
+ glfwDestroyWindow(window);
+ glfwTerminate();
+
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/examples/sharing.c b/libs/glfw-3.3.8/examples/sharing.c
new file mode 100644
index 0000000..4a1a232
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/sharing.c
@@ -0,0 +1,234 @@
+//========================================================================
+// Context sharing example
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getopt.h"
+#include "linmath.h"
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec2 vPos;\n"
+"varying vec2 texcoord;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+" texcoord = vPos;\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"uniform sampler2D texture;\n"
+"uniform vec3 color;\n"
+"varying vec2 texcoord;\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(color * texture2D(texture, texcoord).rgb, 1.0);\n"
+"}\n";
+
+static const vec2 vertices[4] =
+{
+ { 0.f, 0.f },
+ { 1.f, 0.f },
+ { 1.f, 1.f },
+ { 0.f, 1.f }
+};
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE)
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+}
+
+int main(int argc, char** argv)
+{
+ GLFWwindow* windows[2];
+ GLuint texture, program, vertex_buffer;
+ GLint mvp_location, vpos_location, color_location, texture_location;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ windows[0] = glfwCreateWindow(400, 400, "First", NULL, NULL);
+ if (!windows[0])
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetKeyCallback(windows[0], key_callback);
+
+ glfwMakeContextCurrent(windows[0]);
+
+ // Only enable vsync for the first of the windows to be swapped to
+ // avoid waiting out the interval for each window
+ glfwSwapInterval(1);
+
+ // The contexts are created with the same APIs so the function
+ // pointers should be re-usable between them
+ gladLoadGL(glfwGetProcAddress);
+
+ // Create the OpenGL objects inside the first context, created above
+ // All objects will be shared with the second context, created below
+ {
+ int x, y;
+ char pixels[16 * 16];
+ GLuint vertex_shader, fragment_shader;
+
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ srand((unsigned int) glfwGetTimerValue());
+
+ for (y = 0; y < 16; y++)
+ {
+ for (x = 0; x < 16; x++)
+ pixels[y * 16 + x] = rand() % 256;
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 16, 16, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
+ glCompileShader(vertex_shader);
+
+ fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
+ glCompileShader(fragment_shader);
+
+ program = glCreateProgram();
+ glAttachShader(program, vertex_shader);
+ glAttachShader(program, fragment_shader);
+ glLinkProgram(program);
+
+ mvp_location = glGetUniformLocation(program, "MVP");
+ color_location = glGetUniformLocation(program, "color");
+ texture_location = glGetUniformLocation(program, "texture");
+ vpos_location = glGetAttribLocation(program, "vPos");
+
+ glGenBuffers(1, &vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ }
+
+ glUseProgram(program);
+ glUniform1i(texture_location, 0);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+
+ windows[1] = glfwCreateWindow(400, 400, "Second", NULL, windows[0]);
+ if (!windows[1])
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ // Place the second window to the right of the first
+ {
+ int xpos, ypos, left, right, width;
+
+ glfwGetWindowSize(windows[0], &width, NULL);
+ glfwGetWindowFrameSize(windows[0], &left, NULL, &right, NULL);
+ glfwGetWindowPos(windows[0], &xpos, &ypos);
+
+ glfwSetWindowPos(windows[1], xpos + width + left + right, ypos);
+ }
+
+ glfwSetKeyCallback(windows[1], key_callback);
+
+ glfwMakeContextCurrent(windows[1]);
+
+ // While objects are shared, the global context state is not and will
+ // need to be set up for each context
+
+ glUseProgram(program);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+
+ while (!glfwWindowShouldClose(windows[0]) &&
+ !glfwWindowShouldClose(windows[1]))
+ {
+ int i;
+ const vec3 colors[2] =
+ {
+ { 0.8f, 0.4f, 1.f },
+ { 0.3f, 0.4f, 1.f }
+ };
+
+ for (i = 0; i < 2; i++)
+ {
+ int width, height;
+ mat4x4 mvp;
+
+ glfwGetFramebufferSize(windows[i], &width, &height);
+ glfwMakeContextCurrent(windows[i]);
+
+ glViewport(0, 0, width, height);
+
+ mat4x4_ortho(mvp, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f);
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glUniform3fv(color_location, 1, colors[i]);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glfwSwapBuffers(windows[i]);
+ }
+
+ glfwWaitEvents();
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/examples/simple.c b/libs/glfw-3.3.8/examples/simple.c
new file mode 100644
index 0000000..95d8fe6
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/simple.c
@@ -0,0 +1,166 @@
+//========================================================================
+// Simple GLFW example
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+//! [code]
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include "linmath.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static const struct
+{
+ float x, y;
+ float r, g, b;
+} vertices[3] =
+{
+ { -0.6f, -0.4f, 1.f, 0.f, 0.f },
+ { 0.6f, -0.4f, 0.f, 1.f, 0.f },
+ { 0.f, 0.6f, 0.f, 0.f, 1.f }
+};
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec3 vCol;\n"
+"attribute vec2 vPos;\n"
+"varying vec3 color;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+" color = vCol;\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"varying vec3 color;\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(color, 1.0);\n"
+"}\n";
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+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);
+}
+
+int main(void)
+{
+ GLFWwindow* window;
+ GLuint vertex_buffer, vertex_shader, fragment_shader, program;
+ GLint mvp_location, vpos_location, vcol_location;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetKeyCallback(window, key_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ // NOTE: OpenGL error checks have been omitted for brevity
+
+ glGenBuffers(1, &vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+ vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
+ glCompileShader(vertex_shader);
+
+ fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
+ glCompileShader(fragment_shader);
+
+ program = glCreateProgram();
+ glAttachShader(program, vertex_shader);
+ glAttachShader(program, fragment_shader);
+ glLinkProgram(program);
+
+ mvp_location = glGetUniformLocation(program, "MVP");
+ vpos_location = glGetAttribLocation(program, "vPos");
+ vcol_location = glGetAttribLocation(program, "vCol");
+
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+ glEnableVertexAttribArray(vcol_location);
+ glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) (sizeof(float) * 2));
+
+ while (!glfwWindowShouldClose(window))
+ {
+ float ratio;
+ int width, height;
+ mat4x4 m, p, mvp;
+
+ glfwGetFramebufferSize(window, &width, &height);
+ ratio = width / (float) height;
+
+ glViewport(0, 0, width, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ mat4x4_identity(m);
+ mat4x4_rotate_Z(m, m, (float) glfwGetTime());
+ mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
+ mat4x4_mul(mvp, p, m);
+
+ glUseProgram(program);
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ glfwDestroyWindow(window);
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
+//! [code]
diff --git a/libs/glfw-3.3.8/examples/splitview.c b/libs/glfw-3.3.8/examples/splitview.c
new file mode 100644
index 0000000..079c2cb
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/splitview.c
@@ -0,0 +1,546 @@
+//========================================================================
+// 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)
+//========================================================================
+
+#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);
+}
+
diff --git a/libs/glfw-3.3.8/examples/wave.c b/libs/glfw-3.3.8/examples/wave.c
new file mode 100644
index 0000000..7acb8b9
--- /dev/null
+++ b/libs/glfw-3.3.8/examples/wave.c
@@ -0,0 +1,462 @@
+/*****************************************************************************
+ * Wave Simulation in OpenGL
+ * (C) 2002 Jakob Thomsen
+ * http://home.in.tum.de/~thomsen
+ * Modified for GLFW by Sylvain Hellegouarch - sh@programmationworld.com
+ * Modified for variable frame rate by Marcus Geelnard
+ * 2003-Jan-31: Minor cleanups and speedups / MG
+ * 2010-10-24: Formatting and cleanup - Camilla Löwy
+ *****************************************************************************/
+
+#if defined(_MSC_VER)
+ // Make MS math.h define M_PI
+ #define _USE_MATH_DEFINES
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <linmath.h>
+
+// Maximum delta T to allow for differential calculations
+#define MAX_DELTA_T 0.01
+
+// Animation speed (10.0 looks good)
+#define ANIMATION_SPEED 10.0
+
+GLfloat alpha = 210.f, beta = -70.f;
+GLfloat zoom = 2.f;
+
+double cursorX;
+double cursorY;
+
+struct Vertex
+{
+ GLfloat x, y, z;
+ GLfloat r, g, b;
+};
+
+#define GRIDW 50
+#define GRIDH 50
+#define VERTEXNUM (GRIDW*GRIDH)
+
+#define QUADW (GRIDW - 1)
+#define QUADH (GRIDH - 1)
+#define QUADNUM (QUADW*QUADH)
+
+GLuint quad[4 * QUADNUM];
+struct Vertex vertex[VERTEXNUM];
+
+/* The grid will look like this:
+ *
+ * 3 4 5
+ * *---*---*
+ * | | |
+ * | 0 | 1 |
+ * | | |
+ * *---*---*
+ * 0 1 2
+ */
+
+//========================================================================
+// Initialize grid geometry
+//========================================================================
+
+void init_vertices(void)
+{
+ int x, y, p;
+
+ // Place the vertices in a grid
+ for (y = 0; y < GRIDH; y++)
+ {
+ for (x = 0; x < GRIDW; x++)
+ {
+ p = y * GRIDW + x;
+
+ vertex[p].x = (GLfloat) (x - GRIDW / 2) / (GLfloat) (GRIDW / 2);
+ vertex[p].y = (GLfloat) (y - GRIDH / 2) / (GLfloat) (GRIDH / 2);
+ vertex[p].z = 0;
+
+ if ((x % 4 < 2) ^ (y % 4 < 2))
+ vertex[p].r = 0.0;
+ else
+ vertex[p].r = 1.0;
+
+ vertex[p].g = (GLfloat) y / (GLfloat) GRIDH;
+ vertex[p].b = 1.f - ((GLfloat) x / (GLfloat) GRIDW + (GLfloat) y / (GLfloat) GRIDH) / 2.f;
+ }
+ }
+
+ for (y = 0; y < QUADH; y++)
+ {
+ for (x = 0; x < QUADW; x++)
+ {
+ p = 4 * (y * QUADW + x);
+
+ quad[p + 0] = y * GRIDW + x; // Some point
+ quad[p + 1] = y * GRIDW + x + 1; // Neighbor at the right side
+ quad[p + 2] = (y + 1) * GRIDW + x + 1; // Upper right neighbor
+ quad[p + 3] = (y + 1) * GRIDW + x; // Upper neighbor
+ }
+ }
+}
+
+double dt;
+double p[GRIDW][GRIDH];
+double vx[GRIDW][GRIDH], vy[GRIDW][GRIDH];
+double ax[GRIDW][GRIDH], ay[GRIDW][GRIDH];
+
+//========================================================================
+// Initialize grid
+//========================================================================
+
+void init_grid(void)
+{
+ int x, y;
+ double dx, dy, d;
+
+ for (y = 0; y < GRIDH; y++)
+ {
+ for (x = 0; x < GRIDW; x++)
+ {
+ dx = (double) (x - GRIDW / 2);
+ dy = (double) (y - GRIDH / 2);
+ d = sqrt(dx * dx + dy * dy);
+ if (d < 0.1 * (double) (GRIDW / 2))
+ {
+ d = d * 10.0;
+ p[x][y] = -cos(d * (M_PI / (double)(GRIDW * 4))) * 100.0;
+ }
+ else
+ p[x][y] = 0.0;
+
+ vx[x][y] = 0.0;
+ vy[x][y] = 0.0;
+ }
+ }
+}
+
+
+//========================================================================
+// Draw scene
+//========================================================================
+
+void draw_scene(GLFWwindow* window)
+{
+ // Clear the color and depth buffers
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // We don't want to modify the projection matrix
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ // Move back
+ glTranslatef(0.0, 0.0, -zoom);
+ // Rotate the view
+ glRotatef(beta, 1.0, 0.0, 0.0);
+ glRotatef(alpha, 0.0, 0.0, 1.0);
+
+ glDrawElements(GL_QUADS, 4 * QUADNUM, GL_UNSIGNED_INT, quad);
+
+ glfwSwapBuffers(window);
+}
+
+
+//========================================================================
+// Initialize Miscellaneous OpenGL state
+//========================================================================
+
+void init_opengl(void)
+{
+ // Use Gouraud (smooth) shading
+ glShadeModel(GL_SMOOTH);
+
+ // Switch on the z-buffer
+ glEnable(GL_DEPTH_TEST);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(3, GL_FLOAT, sizeof(struct Vertex), vertex);
+ glColorPointer(3, GL_FLOAT, sizeof(struct Vertex), &vertex[0].r); // Pointer to the first color
+
+ glPointSize(2.0);
+
+ // Background color is black
+ glClearColor(0, 0, 0, 0);
+}
+
+
+//========================================================================
+// Modify the height of each vertex according to the pressure
+//========================================================================
+
+void adjust_grid(void)
+{
+ int pos;
+ int x, y;
+
+ for (y = 0; y < GRIDH; y++)
+ {
+ for (x = 0; x < GRIDW; x++)
+ {
+ pos = y * GRIDW + x;
+ vertex[pos].z = (float) (p[x][y] * (1.0 / 50.0));
+ }
+ }
+}
+
+
+//========================================================================
+// Calculate wave propagation
+//========================================================================
+
+void calc_grid(void)
+{
+ int x, y, x2, y2;
+ double time_step = dt * ANIMATION_SPEED;
+
+ // Compute accelerations
+ for (x = 0; x < GRIDW; x++)
+ {
+ x2 = (x + 1) % GRIDW;
+ for(y = 0; y < GRIDH; y++)
+ ax[x][y] = p[x][y] - p[x2][y];
+ }
+
+ for (y = 0; y < GRIDH; y++)
+ {
+ y2 = (y + 1) % GRIDH;
+ for(x = 0; x < GRIDW; x++)
+ ay[x][y] = p[x][y] - p[x][y2];
+ }
+
+ // Compute speeds
+ for (x = 0; x < GRIDW; x++)
+ {
+ for (y = 0; y < GRIDH; y++)
+ {
+ vx[x][y] = vx[x][y] + ax[x][y] * time_step;
+ vy[x][y] = vy[x][y] + ay[x][y] * time_step;
+ }
+ }
+
+ // Compute pressure
+ for (x = 1; x < GRIDW; x++)
+ {
+ x2 = x - 1;
+ for (y = 1; y < GRIDH; y++)
+ {
+ y2 = y - 1;
+ p[x][y] = p[x][y] + (vx[x2][y] - vx[x][y] + vy[x][y2] - vy[x][y]) * time_step;
+ }
+ }
+}
+
+
+//========================================================================
+// Print errors
+//========================================================================
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+
+//========================================================================
+// Handle key strokes
+//========================================================================
+
+void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ case GLFW_KEY_SPACE:
+ init_grid();
+ break;
+ case GLFW_KEY_LEFT:
+ alpha += 5;
+ break;
+ case GLFW_KEY_RIGHT:
+ alpha -= 5;
+ break;
+ case GLFW_KEY_UP:
+ beta -= 5;
+ break;
+ case GLFW_KEY_DOWN:
+ beta += 5;
+ break;
+ case GLFW_KEY_PAGE_UP:
+ zoom -= 0.25f;
+ if (zoom < 0.f)
+ zoom = 0.f;
+ break;
+ case GLFW_KEY_PAGE_DOWN:
+ zoom += 0.25f;
+ break;
+ default:
+ break;
+ }
+}
+
+
+//========================================================================
+// Callback function for mouse button events
+//========================================================================
+
+void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
+{
+ if (button != GLFW_MOUSE_BUTTON_LEFT)
+ return;
+
+ if (action == GLFW_PRESS)
+ {
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ glfwGetCursorPos(window, &cursorX, &cursorY);
+ }
+ else
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+}
+
+
+//========================================================================
+// Callback function for cursor motion events
+//========================================================================
+
+void cursor_position_callback(GLFWwindow* window, double x, double y)
+{
+ if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
+ {
+ alpha += (GLfloat) (x - cursorX) / 10.f;
+ beta += (GLfloat) (y - cursorY) / 10.f;
+
+ cursorX = x;
+ cursorY = y;
+ }
+}
+
+
+//========================================================================
+// Callback function for scroll events
+//========================================================================
+
+void scroll_callback(GLFWwindow* window, double x, double y)
+{
+ zoom += (float) y / 4.f;
+ if (zoom < 0)
+ zoom = 0;
+}
+
+
+//========================================================================
+// Callback function for framebuffer resize events
+//========================================================================
+
+void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ float ratio = 1.f;
+ mat4x4 projection;
+
+ if (height > 0)
+ ratio = (float) width / (float) height;
+
+ // Setup viewport
+ glViewport(0, 0, width, height);
+
+ // Change to the projection matrix and set our viewing volume
+ glMatrixMode(GL_PROJECTION);
+ mat4x4_perspective(projection,
+ 60.f * (float) M_PI / 180.f,
+ ratio,
+ 1.f, 1024.f);
+ glLoadMatrixf((const GLfloat*) projection);
+}
+
+
+//========================================================================
+// main
+//========================================================================
+
+int main(int argc, char* argv[])
+{
+ GLFWwindow* window;
+ double t, dt_total, t_old;
+ int width, height;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ window = glfwCreateWindow(640, 480, "Wave Simulation", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetKeyCallback(window, key_callback);
+ glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
+ glfwSetMouseButtonCallback(window, mouse_button_callback);
+ glfwSetCursorPosCallback(window, cursor_position_callback);
+ glfwSetScrollCallback(window, scroll_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ glfwGetFramebufferSize(window, &width, &height);
+ framebuffer_size_callback(window, width, height);
+
+ // Initialize OpenGL
+ init_opengl();
+
+ // Initialize simulation
+ init_vertices();
+ init_grid();
+ adjust_grid();
+
+ // Initialize timer
+ t_old = glfwGetTime() - 0.01;
+
+ while (!glfwWindowShouldClose(window))
+ {
+ t = glfwGetTime();
+ dt_total = t - t_old;
+ t_old = t;
+
+ // Safety - iterate if dt_total is too large
+ while (dt_total > 0.f)
+ {
+ // Select iteration time step
+ dt = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
+ dt_total -= dt;
+
+ // Calculate wave propagation
+ calc_grid();
+ }
+
+ // Compute height of each vertex
+ adjust_grid();
+
+ // Draw wave grid to OpenGL display
+ draw_scene(window);
+
+ glfwPollEvents();
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+